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 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
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
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
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
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
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)
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
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
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)
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
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
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
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())
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
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
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 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())
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
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 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
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
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")
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 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
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
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
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
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.')
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