Exemplo n.º 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
Exemplo n.º 2
0
    def random_with_first_level_search(self, board: chess.Board):
        moves = list(board.legal_moves)

        best_move = random.sample(moves, 1)[0]
        best_move_value = 0

        for move in moves:
            board.push(move)
            if board.is_checkmate():
                move_value = 100
                if move_value > best_move_value:
                    best_move = move
            board.pop()

            if board.is_into_check(move):
                move_value = 90
                if move_value > best_move_value:
                    best_move = move

            if board.is_capture(move):
                move_value = 80
                if move_value > best_move_value:
                    best_move = move

            if board.is_castling(move):
                move_value = 70
                if move_value > best_move_value:
                    best_move = move

        return best_move
Exemplo n.º 3
0
def foo():
    col = True
    bo = Board('7K/8/k1P5/7p/8/8/8/8 w - -')
    end = True
    while end:
        start = time()
        if col:
            mov = mover(bo, 3, col)
        else:
            mov = move2(bo, 3, col)

            #print(_move)
        if mov == None:
            break
        bo.push(mov)
        print('_______________', col, str(round(time() - start, 4)))
        print('Score ', minimax_score(bo))
        print(bo)

        if bo.is_checkmate():
            print('Checkmate ', col, ' Wins')
            end = False
        elif bo.is_stalemate():
            print('Stalemate')
            end = False

        col = not col
Exemplo n.º 4
0
def evaluate(board: chess.Board, turn: bool):

    if board.is_checkmate():
        score = 10000 - board.ply()                                
        if turn != board.turn:        
            score = -score                
        return score                        

    score = 0
    
    # Iterate over all 64 squares of board    
    for square in chess.SQUARES: 

        piece = board.piece_type_at(square)  # 1 .. 6 or None   

        if not piece: continue
            
        # Calculate material and positional score            
        if board.color_at(square) == chess.WHITE:
            score += pieceWeights[piece]
            score += positionWeights[square]                
        else:    
            score -= pieceWeights[piece]
            score -= positionWeights[square]

    if board.turn == chess.BLACK:
        score = -score
                    
    return score                        
Exemplo n.º 5
0
def _recursiveMiniMax(depth, board: chess.Board, isMaximazing, alpha, beta):
    if board.is_stalemate() or board.is_fivefold_repetition(
    ) or board.is_seventyfive_moves():
        return 0
    if board.is_checkmate():
        return (-1 if isMaximazing else 1) * 1e9
    elif depth == 0:
        return boardValue(board)

    else:
        bestMoveValue = (-1 if isMaximazing else 1) * 1e9
        op = max if isMaximazing else min

        for move in board.legal_moves:
            board.push(move)
            bestMoveValue = op(
                bestMoveValue,
                _recursiveMiniMax(depth - 1, board, not isMaximazing, alpha,
                                  beta))
            board.pop()
            if isMaximazing:
                alpha = max(alpha, bestMoveValue)
                if beta <= alpha:
                    break
            else:
                beta = min(beta, bestMoveValue)
                if beta <= alpha:
                    break
        return bestMoveValue
Exemplo n.º 6
0
def evalEndNode(board:chess.Board):
    if board.is_checkmate():
        return winScore(not board.turn)
    elif board.is_stalemate() or \
         board.is_insufficient_material() or \
         board.is_seventyfive_moves():
         return 0
Exemplo n.º 7
0
 def evaluate(self, board: chess.Board):
     if board.is_checkmate():
         return 9001  # insert "it's over 9000" Vegeta DBZ meme
     # basically the simplest weights I know
     queen_weights, bishop_weights, rook_weights, knight_weights, pawn_weights = 9, 3, 5, 3, 1
     return Eval1.AmateurEval.material_diff(board, queen_weights, bishop_weights, rook_weights,
                                            knight_weights, pawn_weights)
Exemplo n.º 8
0
    def check_game_over(
        self,
        message: Dict[str, str],
        bot_handler: Any,
        new_board: chess.Board
    ) -> bool:
        """Checks if a game is over due to
         - checkmate,
         - stalemate,
         - insufficient material,
         - 50 moves without a capture or pawn move, or
         - 3-fold repetition.
        Replies to the bot handler if it is game over.

        Parameters:
             - message: The Zulip Bots message object.
             - bot_handler: The Zulip Bots bot handler object.
             - new_board: The board object.

        Returns: True if it is game over, false if it's not.
        """
        # This assumes that the players will claim a draw after 3-fold
        # repetition or 50 moves go by without a capture or pawn move.
        # According to the official rules, the game is only guaranteed to
        # be over if it's  *5*-fold or *75* moves, but if either player
        # wants the game to be a draw, after 3 or 75 it a draw. For now,
        # just assume that the players would want the draw.
        if new_board.is_game_over(True):
            game_over_output = ''

            if new_board.is_checkmate():
                game_over_output = make_loss_response(
                    new_board,
                    'was checkmated'
                )
            elif new_board.is_stalemate():
                game_over_output = make_draw_response('stalemate')
            elif new_board.is_insufficient_material():
                game_over_output = make_draw_response(
                    'insufficient material'
                )
            elif new_board.can_claim_fifty_moves():
                game_over_output = make_draw_response(
                    '50 moves without a capture or pawn move'
                )
            elif new_board.can_claim_threefold_repetition():
                game_over_output = make_draw_response('3-fold repetition')

            bot_handler.send_reply(
                message,
                game_over_output
            )

            return True

        return False
Exemplo n.º 9
0
    def get_reward(self, board: chess.Board, next_board: chess.Board):
        if next_board.is_checkmate(): return 100
        if next_board.is_stalemate(): return 0

        # get which side the bot was on when it moved
        white_multiplier = 1 if self.is_white else -1

        value_change = self.get_board_values(next_board, white_multiplier) - \
                       self.get_board_values(board, white_multiplier)

        return value_change
Exemplo n.º 10
0
def evaluation(board:chess.Board):
	# is board in checkmate
	if board.is_checkmate():
		if(board.turn):
			return mate
		return -mate
	# is it a draw
	if is_draw(board):
		return 0
    
	return (materialScore(board) + pieceSquareScore(board)) * eval_signal(board)
Exemplo n.º 11
0
def minimizer(board: chess.Board, depth: int, evaluate=vanilla_eval) -> float:
    if board.is_checkmate():
        return float("inf")
    if depth == 0 or board.is_game_over():
        return evaluate(board)
    value = float('inf')
    for move in board.legal_moves:
        board.push(move)
        value = min(value, maximizer(board, depth - 1, evaluate))
        board.pop()
    return value
Exemplo n.º 12
0
 def render_board(board: chess.Board) -> BytesIO:
     boardimg = chess.svg.board(
         board=board,
         lastmove=board.peek() if board.move_stack else None,
         check=board.king(turn)
         if board.is_check() or board.is_checkmate() else None,
         flipped=board.turn == chess.BLACK)
     res = BytesIO()
     svg2png(bytestring=boardimg, write_to=res)
     res.seek(0)
     return res
Exemplo n.º 13
0
 def evaluate(self, board: chess.Board):
     if board.is_checkmate():
         if board.result() == '1-0':
             return 9001  # insert "it's over 9000" Vegeta DBZ meme
         else:
             return -9001
     sum = AmateurEval.material_diff(board, self.__stockfish_queen_weight, self.__stockfish_bishop_weight,
                                     self.__stockfish_rook_weight, self.__stockfish_knight_weight,
                                     self.__stockfish_pawn_weight)
     mult = 1 if self.__color else -1
     sum += self.piece_location_score(board) * mult
     sum += self.mobility(board) * mult
     return sum
Exemplo n.º 14
0
def motif_fin(board: chess.Board):
    print("motif de fin:", end="")

    if board.is_fivefold_repetition():
        print("repitition 5 fois")
    elif board.is_seventyfive_moves():
        print("Règle des 70 coups")
    elif board.is_stalemate():
        print("Pat")
    elif board.is_checkmate():
        print("Echec et mat")

    print("resultat : ", board.result())
Exemplo n.º 15
0
def eval_position(board: chess.Board, model: neuralnet.NN) -> (float, bool):
    
    # doesn't use is_game_over to get more performance

    if board.is_checkmate():
        # if white is checkmate black gets an -infinit score and inf for black in checkmate
        return (float("-inf"), True) if board.turn else (float("inf"), True)

    if board.is_stalemate() or board.is_insufficient_material():
        # 0 score for a draw
        return 0, True

    # if the game is not over evaluate the board using the nn
    return model.forward(util.board_2_tensor(board)), False
Exemplo n.º 16
0
def evaluate_game_state(board: chess.Board, possible: bool) -> float:
    if board.is_checkmate():
        if possible:
            return Macros.BOARD_POSSIBLE_CHECKMATE_VALUE
        return Macros.BOARD_CHECKMATE_VALUE

    if board.is_stalemate() or board.is_insufficient_material():
        return Macros.BOARD_STALEMATE_VALUE

    if board.is_check():
        if not possible:
            return Macros.BOARD_CHECK_VALUE

    return 0
Exemplo n.º 17
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
Exemplo n.º 18
0
def game_over_reason(board: chess.Board):
    """
    Function that will print the result and the reason of the endgame
    :param board: board to analyse
    """
    print("Game over:", end="")
    if board.is_fivefold_repetition():
        print("fivefold repetition")
    if board.is_seventyfive_moves():
        print("70 moves")
    if board.is_stalemate():
        print("Pat")
    if board.is_checkmate():
        print("Check mate")
    print("result: ", board.result())
Exemplo n.º 19
0
def minimax(
    depth: int,
    board: chess.Board,
    alpha: float,
    beta: float,
    is_maximising_player: bool,
) -> float:
    debug_info["nodes"] += 1

    if board.is_checkmate():
        # The previous move resulted in checkmate
        return -float("inf") if is_maximising_player else float("inf")
    # When the game is over and it's not a checkmate it's a draw
    # In this case, don't evaluate. Just return a neutral result: zero
    elif board.is_game_over():
        return 0

    if depth == 0:
        return evaluate_board(board)

    if is_maximising_player:
        best_move = -float("inf")
        moves = get_ordered_moves(board)
        for move in moves:
            board.push(move)
            best_move = max(
                best_move,
                minimax(depth - 1, board, alpha, beta, not is_maximising_player),
            )
            board.pop()
            alpha = max(alpha, best_move)
            if beta <= alpha:
                return best_move
        return best_move
    else:
        best_move = float("inf")
        moves = get_ordered_moves(board)
        for move in moves:
            board.push(move)
            best_move = min(
                best_move,
                minimax(depth - 1, board, alpha, beta, not is_maximising_player),
            )
            board.pop()
            beta = min(beta, best_move)
            if beta <= alpha:
                return best_move
        return best_move
Exemplo n.º 20
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)
Exemplo n.º 21
0
    def evaluate(self, board: chess.Board) -> float:
        """
        This method evaluates a chess position given a chess.Board object
        Keyword arguments:
            board -- A chess.Board object representing the state of a chess game
        """
        if board.is_checkmate():
            if self.whites_turn(board):  # black is checkmated
                return -1000000
            else:
                return 1000000
        if board.is_stalemate() or board.is_insufficient_material():
            return 0

        material_eval = self.material_eval(board)
        piece_square_eval = self.piece_square_eval(board)

        return material_eval + piece_square_eval / 10.0
def check_state (_board: chess.Board):
	states = {
		"checkmate"                : _board.is_checkmate(),
		"stalemate"                : _board.is_stalemate(),
		"insufficient material"    : _board.is_insufficient_material(),
		"the seventyfive-move rule": _board.is_seventyfive_moves(),
		"fivefold repetition"      : _board.is_fivefold_repetition(),
		"variant win"              : _board.is_variant_win(),
		"variant loss"             : _board.is_variant_loss(),
		"variant draw"             : _board.is_variant_draw(),
		"variant end"              : _board.is_variant_end(),
	}

	for i, j in states.items():
		if j is True:
			return i

	return None
Exemplo n.º 23
0
    def evaluate(self, position: chess.Board, move: chess.Move = chess.Move.null()) -> int:
        """Evaluate position and return white's advantage in centipans.

        :param chess.Board position: Position to evaluate.
        :param chess.Move move: Move who will be played.
        :return: White's advantage as centipawns.
        :rtype: int
        """
        score = 0
        if position.is_checkmate():
            if position.turn == chess.WHITE:
                return -10000
            return 10000
        white_score = self.evaluate_position(position)
        white_score += self.evaluate_move(position, move)
        black_score = self.evaluate_position(position.mirror())
        score = white_score - black_score
        return score
Exemplo n.º 24
0
def get_game_over_reason(board: chess.Board,
                         claim_draw: bool = False,
                         both_agreed: bool = False) -> str:
    if claim_draw and both_agreed:
        return "Both opponents agreed to a draw"

    reasons = [
        "Checkmate",
        "Stalemate",
        "Insufficient material",
        "Draw by the seventyfive-move rule",
        "Draw by the fivefold repetition rule",
    ]
    checks = [
        board.is_checkmate(),
        board.is_stalemate(),
        board.is_insufficient_material(),
        board.is_seventyfive_moves(),
        board.is_fivefold_repetition(),
    ]

    if claim_draw:
        reasons = [
            *reasons,
            *[
                "Draw by the fifty-move rule",
                "Draw by the threefold repetition rule"
            ],
        ]
        checks = [
            *checks,
            *[
                board.can_claim_fifty_moves(),
                board.can_claim_threefold_repetition()
            ],
        ]

    possible = [reason for reason, check in zip(reasons, checks) if check]

    if len(possible) > 0:
        return possible[0]
    else:
        raise RuntimeError("No possible game over reason")
Exemplo n.º 25
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
Exemplo n.º 26
0
def minimax(evaluation_fn: Callable, board: Board, depth: int = 2) -> Tuple[float, str]:
    player = 2 * int(board.turn) - 1  # 1 for white, -1 for black
    if depth == 0 or board.outcome() is not None:
        score = evaluation_fn(board) if not board.is_checkmate() else -player * np.inf
        return score, str()
    else:
        final_eval = dict()
        player_function = max if player == 1 else min
        legal_moves = list(board.legal_moves)  # we shuffle for random choice
        shuffle(legal_moves)
        for move in legal_moves:
            board.push(move)
            final_eval[str(move)] = minimax(evaluation_fn, board, depth - 1)[0]
            board.pop()

        best_score = player_function(final_eval.items(), key=operator.itemgetter(1))[1]
        best_moves = [
            k for k, v in final_eval.items() if v == best_score
        ]  # we keep all the moves with highest score
        best_move = best_moves[0]
        return best_score, best_move
Exemplo n.º 27
0
    def evaluate(self, board: chess.Board, player: bool):
        turn = board.turn
        if board.is_checkmate():
            return -10000 if turn == player else 10000
        if board.is_check():
            return -5000 if turn == player else 5000

        material_sum = 0
        weights = {
            'p': 100,
            'n': 320,
            'b': 330,
            'r': 500,
            'q': 900,
            'k': 20000
        }
        for i in range(64):
            piece = board.piece_at(i)
            if piece:
                color_weight = 1 if piece.color == player else -1
                material_sum += weights[piece.symbol().lower()] * color_weight
        return material_sum
Exemplo n.º 28
0
    def alpha_beta(board: Board, depth: int, alpha: float, beta: float) -> float:
        player = 2 * int(board.turn) - 1
        if depth == 0 or board.outcome() is not None:
            score = (
                evaluation_fn(board) if not board.is_checkmate() else -player * np.inf
            )
            return score

        best_score = -player * np.inf
        legal_moves = list(board.legal_moves)
        shuffle(legal_moves)
        for move in legal_moves:
            board.push(move)
            score = alpha_beta(board, depth - 1, alpha, beta)
            player_function = max if player == 1 else min
            best_score = player_function(best_score, score)
            alpha = max(alpha, best_score) if player == 1 else alpha
            beta = min(beta, best_score) if player != 1 else beta

            board.pop()
            if beta <= alpha:
                break
        return best_score
Exemplo n.º 29
0
def search(board: chess.Board,
           turn: bool,
           depth: int,
           alpha: int = -10000,
           beta: int = 10000,
           returnMove: bool = False,
           returnCount: bool = False,
           tree: str = ""):

    # Lets count all nested calls for search within current move
    global count
    count = 0 if returnCount else count + 1

    # Just return evaluation for terminal nodes
    # TODO Check for game_over ONLY if there None move was returned!
    if depth == 0 or board.is_game_over():
        return evaluate(board, turn)

    bestMove = None

    for move in board.legal_moves:

        # TODO Mate in ply! Move to eval function as special heuristic?
        #        capturedPiece = board.piece_type_at(move.to_square)
        #        if capturedPiece == chess.KING:
        #            return 10000 - board.ply()

        #        if board.gives_check(move):
        #            score = -(10000 - board.ply())
        #            print("=== GIVES CHECK :", move, "|", score, "===")

        board.push(move)
        treeBefore = tree
        tree += move.uci() + " > "
        #        score = -search(board, not turn, depth-1, -beta, -alpha, tree = tree)
        # We should see immediate checks
        if board.is_checkmate():
            score = 10000 - board.ply()
            #if board.ply() < 111:
            #    score = -(10000 - board.ply())
            #else:
            #score = 10000 - board.ply()
            #if returnMove:
            #    print("=== MOVE IN IMMEDIATE CHECK :", move, "|", score, "===")
            #if returnMove:
            #    print("=== MOVE IN CHECK ", tree, "|", score, "===")
        else:
            score = -search(
                board, not turn, depth - 1, -beta, -alpha, tree=tree)
        tree = treeBefore
        board.pop()

        if score > alpha:
            #print (tree + move.uci(), "| score > alpha |", score, ">", alpha)
            # TODO Should look for order of later assignments and beta check
            alpha = score
            bestMove = move

            # Print board for "root" moves
            #if returnMove:
            #    print("\n---------------")
            #    print(f"MAX", "WHITE" if board.turn else "BLACK", move, "=>", score)
            #    print("---------------")
            #    board.push(move)
            #    print(board)
            #    board.pop()
            #    print("---------------")

            if score >= beta:
                #    print ("BETA |", beta, "- DEPTH |", depth-1)
                if returnMove and returnCount:
                    return beta, bestMove, count
                elif returnMove:
                    return beta, bestMove
                else:
                    return beta

    if returnMove and returnCount:
        return alpha, bestMove, count
    elif returnMove:
        return alpha, bestMove
    else:
        return alpha
Exemplo n.º 30
0
class GameEngine(Thread):
    def __init__(self):
        super().__init__()
        self.stop_flag = False
        self.halt_flag = False
        self.auto_play = False
        self.edit_mode = False
        self.command_queue = queue.Queue(maxsize=1)

        self.board = Board()
        self.board_view = None
        self.stockfish = StockfishAPI(
            path=f'{os.getcwd()}/stockfish-10-linux/stockfish_10_x64_modern',
            depth=5)

        # Command map
        self.commands = dict(sf=self._play_stockfish_move,
                             rr=self._play_random_move,
                             ff=self._auto_play_fast_forward,
                             psf=self._auto_play_stockfish,
                             pr=self._auto_play_random,
                             re=self._reset_board,
                             rev=self._reverse_move,
                             tt=self._test,
                             ai=self._play_against_ai,
                             si=(lambda: logger.info(self.stockfish.info)))

    def execute(self, cmd):
        try:
            self.command_queue.put(cmd, block=False)
        except Exception as e:
            logger.exception(e)

    def set_board_view(self, board_view):
        self.board_view = board_view

    def run(self):
        while not self.stop_flag:
            cmd = self.command_queue.get(block=True)
            if cmd is None: break
            cmd, *args = cmd.split(' ')

            try:
                self.commands[cmd](*args)  # Command call
            except KeyError:
                try:
                    self._do_move(cmd)
                except ValueError:
                    logger.warning('Command not recognized:' + cmd)
            except (TypeError, ValueError) as tve:
                logger.warning(f'Command arguments error. {tve}')
            except Exception as e:
                logger.exception(e)
            finally:
                self.command_queue.task_done()
                self.halt_flag = False

    def stop(self):
        self.halt_flag = True
        self.stop_flag = True
        self.command_queue.put(None)

    def halt(self):
        logger.debug('Halting auto-play.')
        self.halt_flag = True

    def toggle_edit_mode(self):
        logger.info(
            f'{"Disabling edit mode." if self.edit_mode else "Enabling edit mode."}'
        )
        self.edit_mode = not self.edit_mode

    def check_status(self):
        if self.board.is_checkmate():
            logger.info(
                f'Checkmate player {"White" if self.board.turn else "Black"}!')
        elif self.board.is_game_over():
            logger.info(f'Game over! {self.board.result()}')

    def get_svg_board(self):
        return boardToSvg(self.board)

    def get_piece_at_pos(self, uci):
        x = 'abcdefgh'.index(uci[0])
        y = int(uci[1]) - 1
        return str(self.board.piece_at(x + y * 8))

    def __refresh_view(self):
        if self.board_view:
            self.board_view.refresh(self.get_svg_board())
            sleep(0.1)

    def _do_move(self, uci, refresh=True):
        move = Move.from_uci(uci)
        if move in self.board.legal_moves or self.edit_mode:
            if self.edit_mode:
                piece = self.get_piece_at_pos(uci)
                if ((piece.isupper() and not self.board.turn)
                        or (piece.islower() and self.board.turn)):
                    self.board.turn = not self.board.turn
                    self.board.push(move)
                    self.board.turn = not self.board.turn
            else:
                self.board.push(move)

            if refresh: self.__refresh_view()
            self.check_status()
        else:
            logger.info('Illegal move!')

    def _reverse_move(self):
        logger.info(f'Popping {self.board.pop()} move off stack.')
        self.__refresh_view()

    def _reset_board(self, refresh=True):
        logger.info('Resetting board.')
        self.board.reset()
        if refresh: self.__refresh_view()

    def _play_stockfish_move(self, refresh=True, depth=None):
        move = self.stockfish.get_best_move(self.board.fen(), parse_int(depth))
        if move:
            self._do_move(move, refresh)
        else:
            self.check_status()

    def _play_random_move(self, refresh=True):
        try:
            move = str(random.choice(list(self.board.legal_moves)))
            self._do_move(move, refresh)
        except IndexError as ie:
            self.check_status()

    # One move look-a-head, best-move checkmate check
    def _play_random_lookahead(self, refresh=True):
        move = self.stockfish.get_best_move(self.board.fen(), depth=1)
        self._do_move(move, False)
        if self.board.is_game_over(): return

        self.board.pop()
        self._play_random_move(refresh)

    def _play_worst_move(self, refresh=True):
        # Can stockfish do it?
        pass

    def __auto_play(self, function, **kwargs):
        self.auto_play = True
        i = 0

        while not (self.board.is_game_over() or self.halt_flag):
            function(**kwargs)
            i += 1

        self.auto_play = False
        return i

    def _auto_play_fast_forward(self, refresh=True):
        logger.info('Playing fast forward mode.')
        move = toggle((lambda r: self._play_stockfish_move(r)),
                      (lambda r: self._play_random_move(r)))
        return self.__auto_play((lambda t, r: next(t)(r)), t=move, r=refresh)

    def _auto_play_stockfish(self, refresh=True):
        logger.info('Playing stockfish vs stockfish.')
        return self.__auto_play(self._play_stockfish_move, refresh=refresh)

    def _auto_play_random(self, refresh=True):
        logger.info('Playing random moves.')
        return self.__auto_play(self._play_random_move, refresh=refresh)

    def _auto_play_random_lookahead(self, refresh=True):
        logger.info('Playing random with look-a-head.')
        return self.__auto_play(self._play_random_lookahead, refresh=refresh)

    def _test(self, N=100):
        N = parse_int(N)
        logger.info(f'Running benchmarks. N={N}')
        functions = [
            self._auto_play_stockfish, self._auto_play_random,
            self._auto_play_fast_forward, self._auto_play_random_lookahead
        ]
        durations = [[] for _ in range(len(functions))]
        iterations = [[] for _ in range(len(functions))]

        self._reset_board(True)
        logger.setLevel(logging.WARNING)
        for f in range(len(functions)):
            for _ in range(N):
                self._reset_board(False)
                t0 = clock()
                i = functions[f](False)
                dt = clock() - t0
                durations[f].append(dt)
                iterations[f].append(i)

        self._reset_board(False)
        logger.setLevel(logging.INFO)
        try:
            logger.info(
                f'stockfish        - avg elapsed time: {round((sum(durations[0])*10)/N, 2)} avg iterations: {sum(iterations[0])/N}'
            )
            logger.info(
                f'random           - avg elapsed time: {round((sum(durations[1])*10)/N, 2)} avg iterations: {sum(iterations[1])/N}'
            )
            logger.info(
                f'fast forward     - avg elapsed time: {round((sum(durations[2])*10)/N, 2)} avg iterations: {sum(iterations[2])/N}'
            )
            logger.info(
                f'random lookahead - avg elapsed time: {round((sum(durations[3])*10)/N, 2)} avg iterations: {sum(iterations[3])/N}'
            )
        except:
            pass

    def _play_against_ai(self, ai='sf', depth=None):
        if ai == 'sf':
            move = self.stockfish.get_best_move(self.board.fen(), depth)
        else:
            raise Exception(f'ai argument {ai} not known.')
Exemplo n.º 31
0
    def say_last_move(game: chess.Board):
        """Take a chess.BitBoard instance and speaks the last move from it."""
        move_parts = {
            'K': 'king.ogg',
            'B': 'bishop.ogg',
            'N': 'knight.ogg',
            'R': 'rook.ogg',
            'Q': 'queen.ogg',
            'P': 'pawn.ogg',
            '+': '',
            '#': '',
            'x': 'takes.ogg',
            '=': 'promote.ogg',
            'a': 'a.ogg',
            'b': 'b.ogg',
            'c': 'c.ogg',
            'd': 'd.ogg',
            'e': 'e.ogg',
            'f': 'f.ogg',
            'g': 'g.ogg',
            'h': 'h.ogg',
            '1': '1.ogg',
            '2': '2.ogg',
            '3': '3.ogg',
            '4': '4.ogg',
            '5': '5.ogg',
            '6': '6.ogg',
            '7': '7.ogg',
            '8': '8.ogg'
        }

        bit_board = game.copy()
        move = bit_board.pop()
        san_move = bit_board.san(move)
        voice_parts = []

        if san_move.startswith('O-O-O'):
            voice_parts += ['castlequeenside.ogg']
        elif san_move.startswith('O-O'):
            voice_parts += ['castlekingside.ogg']
        else:
            for part in san_move:
                try:
                    sound_file = move_parts[part]
                except KeyError:
                    logging.warning('unknown char found in san: [%s : %s]', san_move, part)
                    sound_file = ''
                if sound_file:
                    voice_parts += [sound_file]

        if game.is_game_over():
            if game.is_checkmate():
                wins = 'whitewins.ogg' if game.turn == chess.BLACK else 'blackwins.ogg'
                voice_parts += ['checkmate.ogg', wins]
            elif game.is_stalemate():
                voice_parts += ['stalemate.ogg']
            else:
                if game.is_seventyfive_moves():
                    voice_parts += ['75moves.ogg', 'draw.ogg']
                elif game.is_insufficient_material():
                    voice_parts += ['material.ogg', 'draw.ogg']
                elif game.is_fivefold_repetition():
                    voice_parts += ['repetition.ogg', 'draw.ogg']
                else:
                    voice_parts += ['draw.ogg']
        elif game.is_check():
            voice_parts += ['check.ogg']

        if bit_board.is_en_passant(move):
            voice_parts += ['enpassant.ogg']

        return voice_parts