-
Notifications
You must be signed in to change notification settings - Fork 0
/
checkers_bot.py
244 lines (202 loc) · 8.91 KB
/
checkers_bot.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
import requests
from copy import deepcopy
import time
LINK = 'http://localhost:8081'
TEAM_NAME = 'EMAN_MAET'
SIDE_CELLS = {1, 2, 3, 4, 5, 12, 13, 20, 21, 28, 29, 30, 31, 32}
RED_CORNER = {1, 2, 3, 4}
BLACK_CORNER = {29, 30, 31, 32}
def wait(color):
"""Wait until my turn."""
now = {}
while now.get('whose_turn') != color and not now.get('is_finished'):
# time.sleep(0.5)
now = requests.get(f"{LINK}/game")
if now.status_code != 200:
continue
now = now.json()['data']
def get_positions(color):
"""Get all allies and enemies checkers."""
board = requests.get(f"{LINK}/game").json()['data']['board']
allies, enemies = [], []
for checker in board:
if checker['color'] == color:
allies.append(checker)
else:
enemies.append(checker)
return allies, enemies
def make_move(move, header):
resp = requests.post(f"{LINK}/move", headers=header, json={"move": move})
print(resp.text)
def abs_move(my, enemy, old_position, new_position, beated_position, row, column):
"""Make abstract move to get beats chain."""
my, enemy = deepcopy(my), deepcopy(enemy)
the_checker = None
for checker in my:
if checker['position'] == old_position:
checker['position'] += new_position
checker['row'] += row
checker['column'] += column
the_checker = checker
break
for checker in enemy:
if checker['position'] == old_position + beated_position:
enemy.remove(checker)
break
return my, enemy, the_checker
def find_to_beat(my, enemy, checker, rec=''):
"""Find beats chains."""
checker = deepcopy(checker)
positions = [checker['position'] for checker in my + enemy]
enemy_positions = [checker['position'] for checker in enemy]
alpha_beats = []
# Top
if checker['column'] != 0 and 't' not in rec:
# Top left
if checker['row'] > 1 and checker['position'] - 9 not in positions and 'l' not in rec \
and (checker['color'] == 'BLACK' or checker['king']):
beat_position = -4 - (checker['row'] % 2)
if checker['position'] + beat_position in enemy_positions:
temp_beat = [checker['position'] - 9]
temp_my, temp_enemy, temp_checker = abs_move(my, enemy, checker['position'],
-9, beat_position, -2, -1)
temp_route = find_to_beat(temp_my, temp_enemy, temp_checker, rec='tl')
for route in temp_route:
alpha_beats.append(temp_beat + route)
if not temp_route:
alpha_beats.append(temp_beat)
# Top right
if checker['row'] < 6 and checker['position'] + 7 not in positions and 'r' not in rec \
and (checker['color'] == 'RED' or checker['king']):
beat_position = 4 - (checker['row'] % 2)
if checker['position'] + beat_position in enemy_positions:
temp_beat = [checker['position'] + 7]
temp_my, temp_enemy, temp_checker = abs_move(my, enemy, checker['position'],
7, beat_position, 2, -1)
temp_route = find_to_beat(temp_my, temp_enemy, temp_checker, rec='tr')
for route in temp_route:
alpha_beats.append(temp_beat + route)
if not temp_route:
alpha_beats.append(temp_beat)
# Bottom
if checker['column'] != 3 and 'd' not in rec:
# Bottom left
if checker['row'] > 1 and checker['position'] - 7 not in positions and 'l' not in rec \
and (checker['color'] == 'BLACK' or checker['king']):
beat_position = -3 - (checker['row'] % 2)
if checker['position'] + beat_position in enemy_positions:
temp_beat = [checker['position'] - 7]
temp_my, temp_enemy, temp_checker = abs_move(my, enemy, checker['position'],
-7, beat_position, -2, 1)
temp_route = find_to_beat(temp_my, temp_enemy, temp_checker, rec='dl')
for route in temp_route:
alpha_beats.append(temp_beat + route)
if not temp_route:
alpha_beats.append(temp_beat)
# Bottom right
if checker['row'] < 6 and checker['position'] + 9 not in positions and 'r' not in rec \
and (checker['color'] == 'RED' or checker['king']):
beat_position = 5 - (checker['row'] % 2)
if checker['position'] + beat_position in enemy_positions:
temp_beat = [checker['position'] + 9]
temp_my, temp_enemy, temp_checker = abs_move(my, enemy, checker['position'],
9, beat_position, 2, 1)
temp_route = find_to_beat(temp_my, temp_enemy, temp_checker, rec='dr')
for route in temp_route:
alpha_beats.append(temp_beat + route)
if not temp_route:
alpha_beats.append(temp_beat)
return alpha_beats
def one_way_step(coif, checker, positions, check):
"""Make one step (top or down)."""
if checker['column'] != check or checker['row'] % 2 == check // 3:
if checker['row'] % 2 == 0 and checker['position'] + coif[0] not in positions and \
0 < checker['position'] + coif[0] < 33:
return checker['position'], checker['position'] + coif[0]
elif checker['row'] % 2 == 1 and checker['position'] + coif[1] not in positions and \
0 < checker['position'] + coif[1] < 33:
return checker['position'], checker['position'] + coif[1]
def corner_heuristic(move, color):
total = 0
if move[1] in SIDE_CELLS:
total -= 1
if (color == 'RED' and move[0] in RED_CORNER) or (color == 'BLACK' and move[0] in BLACK_CORNER):
total += 2
if (move[1] in RED_CORNER and color == 'BLACK') or (move[1] in BLACK_CORNER and color == 'RED'):
total -= 1
return total
def moves_sort(move, color, allies, enemies, minimax=False):
enemy_color = 'RED' if color == 'BLACK' else 'BLACK'
allies, enemies = deepcopy(allies), deepcopy(enemies)
total = num = 0
for ally in allies:
if move[0] == ally['position']:
ally['position'] = move[1]
ally['row'] = (move[1] - 1) // 4
ally['column'] = (move[1] - 1) % 4
break
step = get_next_step(allies, enemies, enemy_color, minimax=True)
if step and abs(step[0] - step[1]) > 6:
total += 5
if not minimax:
num -= moves_sort(step, color, allies, enemies, minimax=True)
return total + num
def get_next_step(allies, enemies, color, minimax=False):
"""Get all possible steps and beats."""
positions = [checker['position'] for checker in allies + enemies]
route, beats = [], []
coif = [4, 3, 5, 4] if color == "RED" else [-4, -5, -3, -4]
enemy_coif = [-4, -5, -3, -4] if color == 'RED' else [4, 3, 5, 4]
# Beats
for checker in allies:
temp_beats = find_to_beat(allies, enemies, checker)
for temp_beat in temp_beats:
if temp_beat:
beats.append((checker['position'], temp_beat))
if beats:
beats.sort(key=lambda b: -len(b[1]))
return beats[0][0], beats[0][1][0]
# Basic moves
for checker in allies:
# Top
step = one_way_step(coif[:2], checker, positions, 0)
if step:
route.append(step)
if checker['king']:
step = one_way_step(enemy_coif[:2], checker, positions, 0)
if step:
route.append(step)
# Bottom
step = one_way_step(coif[2:], checker, positions, 3)
if step:
route.append(step)
if checker['king']:
step = one_way_step(enemy_coif[2:], checker, positions, 3)
if step:
route.append(step)
if route and not minimax:
route.sort(key=lambda r: corner_heuristic(r, color) + moves_sort(r, color, allies, enemies))
return route[0] if route else None
def run():
# Init the game
resp = requests.post(f"{LINK}/game?team_name={TEAM_NAME}")
j = resp.json()
color = j['data']['color']
print(f'My color is {color}')
header = {'Authorization': f'Token {j["data"]["token"]}'}
# Start game loop:
while True:
wait(color)
allies, enemies = get_positions(color)
step = get_next_step(allies, enemies, color)
if not step or requests.get(f"{LINK}/game").json()['data']['is_finished']:
break
print(step, end=' \t')
make_move(step, header)
# Define a winner
if color == requests.get(f"{LINK}/game").json()['data']['winner']:
print('I won!')
else:
print('I lost:(')
if __name__ == '__main__':
run()