-
Notifications
You must be signed in to change notification settings - Fork 0
/
Alak.py
369 lines (324 loc) · 15.1 KB
/
Alak.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
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
import numpy as np
import Prediction
from Prediction import Prediction
class Alak:
# board: initial board to start the game
# interactive : if True, human play with computer; else computer play with computer
# random : if True, computer play random moves; else computer play moves generated by model
# random_start: if True, randomly select the first player(x); else Computer is always the one move first(x)
# training: if True, games play for training data; else games won't be used for training data
def __init__(self, board, model, type, user_first, interactive, random, random_start, training):
self.interactive = interactive
self.random = random
self.board = board
self.prev_dest = -1
self.cur_piece = ''
self.round_counter = 0
self.user_piece = ''
self.computer_piece = ''
self.X = []
self.y = []
self.round = []
self.is_suicide = False
self.winner = ''
self.random_start = random_start
self.training = training
self.user_first = user_first
self.type = type
self.model = model
self.p = Prediction()
print("\nInitial board: ")
self.print_board()
def pick_starting_piece(self):
if self.random_start: # system automatically assign user a piece:
self.user_piece = np.random.choice(['x', 'o'])
if self.user_piece == 'x': # user goes first
self.computer_piece = 'o'
print("Your side is 'x'") # model side is o
else:
self.computer_piece = 'x'
print("Your side is 'o'") # model side if x
else: # set machine is always the first one to start (as x)
if self.user_first: # user is x
self.computer_piece = 'o'
self.user_piece = 'x'
else: # computer is x
self.computer_piece = 'x'
self.user_piece = 'o'
self.cur_piece = 'x' # x always goes first
print("\n~~~~~~~~~~~~~~~ " + self.cur_piece + " starts the game: ~~~~~~~~~~~~~~~~")
# go through the board and return indices of all given slots
def find_all_specific_slots(self, slot):
arr = np.array(list(self.board))
return np.where(arr == slot)[0]
# move the piece from src to dest, print the move and increase the step_counter
def move_piece(self, src, dest):
if self.cur_piece == 'x':
self.round_counter += 1
print("-> Round " + str(self.round_counter))
print("Move " + self.cur_piece + " from position " + str(src) + " to position " + str(dest))
arr = np.array(list(self.board))
arr[src] = '_'
arr[dest] = self.cur_piece
self.board = "".join(arr)
# given the current piece, go from the source(src) to the destination(dest)
def update_board(self):
# check if prev dest create any captures (previous suicide move)
if self.prev_dest != -1:
self.check_suicide_capture(self.prev_dest, self.cur_piece)
# if not self.is_suicide:
# print("clean board: ", self.board)
print("clean board:")
self.print_board()
# save moves after embedding
if not self.is_suicide and self.training:
self.embedding()
# if not self.is_suicide:
print("\n~~~~~~~~~~~~~~~ " + self.cur_piece + "'s turn: ~~~~~~~~~~~~~~~~")
if self.interactive and self.user_piece == self.cur_piece: # use's turn: use input prompt
# if self.random:
print("Please enter your move below:")
from_pos = input("Move from position:")
to_pos = input("To position:")
# from_pos = 7
# to_pos = 1
print("You are moving {} to {}".format(from_pos, to_pos))
# define illegal moves
while (from_pos not in (['a', 'b', 'c', 'd']) and not from_pos.isnumeric())\
or (to_pos not in (['a', 'b', 'c', 'd']) and not to_pos.isnumeric())\
or (from_pos.isnumeric() and (int(from_pos) > len(self.board) - 1 or int(from_pos) < 0\
or self.board[int(from_pos)] != self.user_piece)) \
or (to_pos.isnumeric() and (int(to_pos) > len(self.board) or int(to_pos) < 0\
or self.board[int(to_pos)] != '_')) \
or from_pos == to_pos:
print("This is an illegal move, please re-enter your move again:")
from_pos = input("Move from position:")
to_pos = input("To position:")
src = from_pos
dest = to_pos
else: # computer's turn: randomly make move
if not self.training: # not for training, but for baseline: model vs. random
if self.cur_piece == self.user_piece: # user's turn -> random
list_of_empty_slots = self.find_all_specific_slots('_')
list_of_cur_piece_slots = self.find_all_specific_slots(self.cur_piece)
# randomly pick moves but later will model and predict the move
src = np.random.choice(list_of_cur_piece_slots)
dest = np.random.choice(list_of_empty_slots)
else: # computer's turn -> model
successors = self.p.generate_successor(self.board, self.cur_piece)
optimal_move, optimal_move_probability = self.p.predict(successors, self.model, self.board, self.type, self.cur_piece)
src = optimal_move[0]
dest = optimal_move[1]
else: # training = true
if self.random:
list_of_empty_slots = self.find_all_specific_slots('_')
list_of_cur_piece_slots = self.find_all_specific_slots(self.cur_piece)
# randomly pick moves but later will model and predict the move
src = np.random.choice(list_of_cur_piece_slots)
dest = np.random.choice(list_of_empty_slots)
else: # use model to play
successors = self.p.generate_successor(self.board, self.cur_piece)
optimal_move, optimal_move_probability = self.p.predict(successors, self.model, self.board, self.type, self.cur_piece)
src = optimal_move[0]
dest = optimal_move[1]
index_map = {'a': 10, 'b': 11, 'c': 12, 'd': 13}
if not str(src).isdigit():
src = index_map[src]
else:
src = int(src)
if not str(dest).isdigit():
dest = index_map[dest]
else:
dest = int(dest)
# move the piece from src to the dest
self.move_piece(src, dest)
print("board after move:")
self.print_board()
# check capture in current board
self.check_capture(dest, self.cur_piece)
self.prev_dest = dest
# check if there's any captures:
# 1. after put down the piece, check both left and right side if the first one is the opposite piece
# 2. if so, keep going til find the same piece, then we capture the opposite pieces in between
def check_capture(self, dest, piece_to_check):
opp_piece = self.find_opponent(piece_to_check)
left_capture_counter = 0
right_capture_counter = 0
no_capture_flag_left = True
no_capture_flag_right = True
# check to the left
pos = dest
while pos > 0:
pos -= 1
if self.board[pos] == '_':
left_capture_counter = 0 # nothing captured, continue the game
self.capture(dest, left_capture_counter, 'left')
break
elif self.board[pos] == self.cur_piece:
self.capture(dest, left_capture_counter, 'left')
no_capture_flag_left = False
break
elif self.board[pos] == opp_piece: # find opposite, continue look fo more
left_capture_counter += 1
# check to the right
pos = dest
while pos < len(self.board) - 1:
pos += 1
if self.board[pos] == '_':
right_capture_counter = 0 # nothing captured, continue the game
self.capture(dest, right_capture_counter, 'right')
break
elif self.board[pos] == self.cur_piece:
self.capture(dest, right_capture_counter, 'right')
no_capture_flag_right = False
break
elif self.board[pos] == opp_piece: # find opposite, continue look fo more
right_capture_counter += 1
if no_capture_flag_left is False and no_capture_flag_right is False:
print(self.cur_piece + " captured " + str(
left_capture_counter + right_capture_counter) + " piece(s)!")
if no_capture_flag_left:
left_capture_counter = 0
if no_capture_flag_right:
right_capture_counter = 0
if no_capture_flag_left or no_capture_flag_right:
print(self.cur_piece + " captured " + str(left_capture_counter + right_capture_counter) + " piece(s)!")
self.print_board()
# discard pieces from the board based on the counter and direction
def capture(self, start_from, capture_counter, direction):
if capture_counter != 0:
while capture_counter > 0:
if direction == 'left':
start_from -= 1
capture_counter -= 1
self.board = self.board[0: start_from] + '_' + self.board[start_from + 1: len(self.board)]
if direction == 'right':
start_from += 1
capture_counter -= 1
self.board = self.board[0: start_from] + '_' + self.board[start_from + 1: len(self.board)]
def check_suicide_capture(self, dest, piece_to_check):
opp_piece = self.find_opponent(piece_to_check)
left_capture_counter = 0
right_capture_counter = 0
# check to the left
pos = dest
while pos > 0:
pos -= 1
if self.board[pos] == '_':
left_capture_counter = 0 # nothing captured, continue the game
break
elif self.board[pos] == self.cur_piece:
left_capture_counter += 1
break
elif self.board[pos] == opp_piece: # find opposite, continue look fo more
left_capture_counter += 1
if pos == 0:
left_capture_counter = 0
# check to the right
pos = dest
while pos < len(self.board) - 1:
pos += 1
if self.board[pos] == '_':
right_capture_counter = 0 # nothing captured, continue the game
break
elif self.board[pos] == self.cur_piece:
right_capture_counter += 1
break
elif self.board[pos] == opp_piece: # find opposite, continue look fo more
right_capture_counter += 1
if pos == len(self.board) - 1:
right_capture_counter = 0
if left_capture_counter > 0 and right_capture_counter > 0:
self.capture(dest + 1, left_capture_counter, 'left')
self.capture(dest - 1, right_capture_counter, 'right')
## only allow user suicide, model suicide won't be in the training data
if self.cur_piece == self.computer_piece and self.training:
print("suicide move alert: " + self.cur_piece + " captured " + str(
left_capture_counter + right_capture_counter - 1) + " piece(s)!")
print("!!! Detected suicide move, have to start a new game! ")
self.is_suicide = True
# check if the game ends with a winner
def check_if_game_over(self):
# game ends when only one side has 1 piece left
count_x = self.board.count('x')
count_o = self.board.count('o')
user = ''
if self.is_suicide:
return -1
if count_x <= 1:
self.winner = 'o'
if self.winner == self.user_piece:
user = ': USER SIDE'
print("\n!!!! GAME OVER!! Winner is '{}' {}".format(self.winner, user))
self.label_round()
self.embedding()
return self.winner
elif count_o <= 1:
self.winner = 'x'
if self.winner == self.user_piece:
user = ': USER SIDE'
print("\n!!!! GAME OVER!! Winner is '{}' {}".format(self.winner, user))
self.label_round()
self.embedding()
return self.winner
else: # not ended
return '_'
def switch_piece(self):
self.cur_piece = self.find_opponent(self.cur_piece)
def find_opponent(self, piece_to_check):
if piece_to_check == 'o':
return 'x'
else:
return 'o'
def print_board(self):
expanded_board = ' '.join(self.board)
print(expanded_board)
print("0 1 2 3 4 5 6 7 8 9 a b c d\n")
def embedding(self): # x side + o side
if len(self.round) < len(self.board) * 2:
for c in self.board:
if c == self.computer_piece:
self.round.append(-1)
elif c == '_':
self.round.append(0)
else:
self.round.append(1)
if len(self.round) == len(self.board) * 2:
self.X.append(self.round) # append current full round
# for l in self.X:
# print('[' + str(l) + ']\n')
self.round = [] # reset the list
def label_round(self):
if self.winner == self.user_piece: # user is the winner
self.y = [1] * self.round_counter
else:
self.y = [0] * self.round_counter
def play_1_game(self):
# X_data is used for stacking all games, first row set as dummies, need to remove later
X_data = np.array([[0] * len(self.board) * 2])
y_data = np.array([])
self.pick_starting_piece()
while self.check_if_game_over() == '_' and len(self.round) != len(self.board) * 2 and not self.is_suicide:
self.update_board()
if self.is_suicide and self.training:
break
self.switch_piece()
# show training data, label for the game
if not self.is_suicide: # after the game is done, if the game is not suicide
self.embedding()
X_data = np.vstack((X_data, np.array(self.X)))
# print("X_data shape: {}".format(X_data.shape))
y_data = np.append(y_data, self.y)
# final training data, label
X_data = X_data[1:]
print("\nEND OF THE GAME!!! ")
print("final X_data shape:", X_data.shape)
print("final y_data shape:", y_data.shape)
# play 1 game
if __name__ == '__main__':
my_board = 'xxxxx____ooooo'
predict = Prediction()
loaded_model = predict.load_model
model, type = loaded_model('models/alak_model_v14(85%).h5', 'tf')
game = Alak(my_board, model, type, user_first=False, interactive=True, random=False, random_start=True, training=False)
game.play_1_game()