Example #1
0
    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
Example #2
0
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
Example #3
0
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
Example #4
0
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
Example #5
0
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)
Example #6
0
    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)
Example #7
0
    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)