def next_move(self, board: Board): """ returns the called AIPlayer's next move for a game on the specified Board object. input: board is a Board object for the game that the called Player is playing. return: row, col are the coordinated of a vacant location on the board """ assert (not board.is_full()) assert (self.depth % 2 == 1) self.num_moves += 1 self.__init_nextMove() if board.is_empty(): # if it's the 1st move, and it's AI's turn # then ai will randomly choose one in the center of the board. cent_row = board.width // 2 cent_col = board.height // 2 buff_row = round(board.width / 20) buff_col = round(board.width / 20) self.__next_moves.append( (random.randint(-buff_row, buff_row) + cent_row, random.randint(-buff_col, buff_col) + cent_col)) else: self.__temp_record = np.zeros((board.height, board.width, 4), dtype=np.bool) self.__my_max(board) # in the most case, AI will choose the 1st move row, col = self.__next_moves[0] # TODO: in the rare case, AI will choose other moves # row, col = random.choice(self.__next_moves) return row, col
def __my_max(self, board: Board, depth=0, alpha=float("-inf"), beta=float("inf")): # quit condition: if depth >= self.depth: # here the negative symbol is a key point. return -self.__evaluate(board) me_chk_id = board.get_checker_id(self.checker) op_chk_id = board.get_checker_id(self.opponent_checker) for n_row, n_col in board.iter_recent_empty(): if not board.has_neighbor( n_row, n_col): # TODO: this step can also be optimized! continue if depth % 2 == 0: board.add_checker_id(me_chk_id, n_row, n_col) else: board.add_checker_id(op_chk_id, n_row, n_col) value = -self.__my_max(board, depth + 1, -beta, -alpha) board.delete_checker(n_row, n_col) if value > alpha: if depth == 0: self.__next_moves.clear() self.__next_moves.append((n_row, n_col)) if value >= beta: return beta alpha = value elif value == alpha: if depth == 0: self.__next_moves.append((n_row, n_col)) return alpha
def gomoku(p1, p2): """ Plays the Gomoku between the two specified players, and returns the Board object as it looks at the end of the game. inputs: p1 and p2 are objects representing players One player should use 'X' checkers and the other should use 'O' checkers. """ # Make sure one player is 'X' and one player is 'O'. if p1.checker not in 'XO' or p2.checker not in 'XO' \ or p1.checker == p2.checker: print('need one X player and one O player.') return None print('Welcome to Gomoku!') print() b = Board(10, 10) print(b) p1.num_moves = 0 p2.num_moves = 0 while True: if process_move(p1, b) == True: return b if process_move(p2, b) == True: return b
def __evaluate(self, board: Board): """ Evaluate the score of the current board. :param board: The board object :return: A score, numeric. """ assert (self.__temp_record is not None) self.__temp_record.fill(0) # score_board has 3 dims: row, col, and 4 different directions. me_chk_id = board.get_checker_id(self.checker) op_chk_id = board.get_checker_id(self.opponent_checker) score_count = {me_chk_id: [0] * 8, op_chk_id: [0] * 8} # we only iterate slots which has been used nz = board.slots != 0 rows, cols = np.where(nz) for row, col in zip(rows, cols): if board.slots[row, col] == me_chk_id: self.__check_point(board, row, col, score_count) else: self.__check_point(board, row, col, score_count) return self.__calc_score(score_count, me_chk_id, op_chk_id)