def _minimax(self, board: chess.Board, alpha: float, beta: float, depth: int): if depth == 0 and any(board.legal_moves): return self._evaluate_board(board), None # Draw elif (board.can_claim_draw() or board.is_stalemate() or board.is_insufficient_material()): return 0, None # White wins elif board.is_checkmate() and board.turn == chess.BLACK: return float('inf'), None # Black wins elif board.is_checkmate() and board.turn == chess.WHITE: return float('-inf'), None best_eval, best_move = float('-inf'), None for move in board.legal_moves: board.push_uci(str(move)) cur_eval, cur_move = self._minimax(board, -beta, -alpha, depth - 1) cur_eval = -cur_eval board.pop() if best_eval < cur_eval: best_eval = cur_eval alpha = cur_eval best_move = move if best_eval >= beta: break return best_eval, best_move
def minimax_root(depth: int, board: chess.Board) -> chess.Move: # White always wants to maximize (and black to minimize) # the board score according to evaluate_board() maximize = board.turn == chess.WHITE best_move = -float("inf") if not maximize: best_move = float("inf") moves = get_ordered_moves(board) best_move_found = moves[0] for move in moves: board.push(move) # Checking if draw can be claimed at this level, because the threefold repetition check # can be expensive. This should help the bot avoid a draw if it's not favorable # https://python-chess.readthedocs.io/en/latest/core.html#chess.Board.can_claim_draw if board.can_claim_draw(): value = 0.0 else: value = minimax(depth - 1, board, -float("inf"), float("inf"), not maximize) board.pop() if maximize and value >= best_move: best_move = value best_move_found = move elif not maximize and value <= best_move: best_move = value best_move_found = move return best_move_found
def self_play(b: Board): while not b.is_game_over(): if b.can_claim_draw(): print("Draw") break m = pick_move(b) print(f"\nMaking move {m}") b.push(m) print(b) result = b.result() print(f"\n\nGame Over: {result}") return result, b
def dfs(state, num_searches, isplayer): comp = max if isplayer else min board = Board(state) if (board.is_checkmate()): return 0.0 if isplayer else 1.0 if (board.is_stalemate()): return 0.5 if (board.can_claim_draw()): return 0.5 rv = 0.0 if isplayer else 1.0 moves = list(board.legal_moves) # if number of nodes to search exceeds the search limit provided # score all nodes if (len(moves) > num_searches): for move in moves: board.push(move) rv = comp(rv, score(board.fen())) board.pop() return rv search_move = [False for _ in range(len(moves))] search_count = 0 i = 0 while i < len(moves): inc = min(60, len(moves) - i) # gives 50% chance for all nodes to be searched # TODO make an adjustable probability parameter tp = random.randrange(1 << inc) while tp > 0: if (tp & 1): search_move[i] = True search_count += 1 tp >>= 1 i += inc score_count = len(moves) - search_count nodes_per_search = 0 if (search_count != 0): nodes_per_search = (num_searches - score_count) // search_count vals = [] for i in range(len(moves)): move = moves[i] if search_move[i]: board.push(move) rv = comp(rv, dfs(board.fen(), nodes_per_search, not isplayer)) board.pop() else: board.push(move) rv = comp(rv, score(move)) board.pop() return rv
def score_material_and_win(b: Board) -> int: if b.can_claim_draw(): return 0 if b.is_game_over(): res = b.result() if res == '1/2-1/2': return 0 elif res == '1-0': return 9999 elif res == '0-1': return -9999 else: raise RuntimeException(f"Unknown result {res}") else: return score_material(b)
def write_json(self, board: chess.Board): """ Writes all of the board info in json """ best_move = self.get_best_move(board) output = OrderedDict([ ('fen', board.fen()), ('fullmoveNumber', board.fullmove_number), ('result', board.result()), ('isGameOver', board.is_game_over()), ('isCheckmate', board.is_checkmate()), ('isStalemate', board.is_stalemate()), ('isInsufficientMaterial', board.is_insufficient_material()), ('isSeventyfiveMoves', board.is_seventyfive_moves()), ('isFivefoldRepetition', board.is_fivefold_repetition()), ('white', OrderedDict([ ('hasKingsideCastlingRights', board.has_kingside_castling_rights(chess.WHITE)), ('hasQueensideCastlingRights', board.has_queenside_castling_rights(chess.WHITE)), ])), ('black', OrderedDict([ ('hasKingsideCastlingRights', board.has_kingside_castling_rights(chess.BLACK)), ('hasQueensideCastlingRights', board.has_queenside_castling_rights(chess.BLACK)), ])), ('turn', OrderedDict([ ('color', 'white' if board.turn is chess.WHITE else 'black'), ('isInCheck', board.is_check()), ('bestMove', best_move), ('legalMoves', [move.uci() for move in board.legal_moves]), ('canClaimDraw', board.can_claim_draw()), ('canClaimFiftyMoves', board.can_claim_fifty_moves()), ('canClaimThreefoldRepetition', board.can_claim_threefold_repetition()), ])), ]) self.finish(output)
def move(self, board: chess.Board) -> chess.Move: value = -float('inf') if self.player_color else float('inf') best_move = None legal_moves = list(board.legal_moves) for move in legal_moves: board.push(move) if board.is_checkmate(): board.pop() return move if board.can_claim_draw(): temp = 0 else: temp = minimizer( board, self.max_depth, self.evaluate) if self.player_color else maximizer( board, self.max_depth, self.evaluate) if board.is_game_over(): value = max( value, self.evaluate(board)) if self.player_color else min( value, self.evaluate(board)) if self.player_color: if temp >= value: value = temp best_move = move else: if temp <= value: value = temp best_move = move board.pop() continue if self.player_color: if temp >= value: value = temp best_move = move else: if temp <= value: value = temp best_move = move board.pop() self.move_count += 1 return best_move
def featureEngineering(id, game, game_collection): counter = 0 update_dict = {} first_check = True first_queen_move = True features_dict = defaultdict(int) # { # 'promotion': 0, # 'total_checks': 0, # 'is_checkmate': 0, # 'is_stalemate': 0, # 'insufficient_material': 0, # 'can_claim_draw': 0 # } board = None for moveDBObject in game['moves']: # print(f"\tUpdating {move['uci']} -> {move['turn']}") board = Board(moveDBObject['fen']) uci = moveDBObject['uci'] move = Move.from_uci(uci) moved_piece = board.piece_at(move.from_square) captured_piece = board.piece_at(move.to_square) if moved_piece == QUEEN and first_queen_move: features_dict['queen_moved_at'] = board.fullmove_number first_queen_move = False if captured_piece == QUEEN: features_dict['queen_changed_at'] = board.fullmove_number if move.promotion: features_dict['promotion'] += 1 if board.is_check(): features_dict['total_checks'] += 1 if first_check: features_dict['first_check_at'] = board.fullmove_number first_check = False # castling if uci == 'e1g1': features_dict['white_king_castle'] = board.fullmove_number elif uci == 'e1c1': features_dict['white_queen_castle'] = board.fullmove_number elif uci == 'e8g8': features_dict['black_king_castle'] = board.fullmove_number elif uci == 'e8c8': features_dict['black_queen_castle'] = board.fullmove_number # update_dict[f"moves.{counter}.turn"] = counter % 2 == 0 counter += 1 if counter > 0: if board.is_checkmate(): features_dict['is_checkmate'] += 1 if board.is_stalemate(): features_dict['is_stalemate'] += 1 if board.is_insufficient_material(): features_dict['insufficient_material'] += 1 if board.can_claim_draw(): features_dict['can_claim_draw'] += 1 features_dict['total_moves'] = board.fullmove_number piece_placement = board.fen().split()[0] end_pieces = Counter(x for x in piece_placement if x.isalpha()) # count number of piece at end position features_dict.update( {'end_' + piece: cnt for piece, cnt in end_pieces.items()}) # print(features_dict) game_collection.update_one({"_id": ObjectId(id)}, {"$set": features_dict}, upsert=False)