def simulate_move(board, move): if move == chess.Move.null(): return None # if its a legal move, don't change it at all (generate_pseudo_legal_moves does not include pseudo legal castles) if move in board.generate_pseudo_legal_moves() or is_psuedo_legal_castle(board, move): return move if is_illegal_castle(board, move): return None # if the piece is a sliding piece, slide it as far as it can go piece = board.piece_at(move.from_square) if piece.piece_type in [chess.PAWN, chess.ROOK, chess.BISHOP, chess.QUEEN]: move = slide_move(board, move) return move if move in board.generate_pseudo_legal_moves() else None
def get_moves(board: chess.Board) -> List[chess.Move]: """ Accounts for the ability to castle through check. """ return list(set(board.pseudo_legal_moves) | set(move for move in util.moves_without_opponent_pieces(board) if util.is_psuedo_legal_castle(board, move)))
def choose_move(self, move_actions: List[chess.Move], seconds_left: float) -> Optional[chess.Move]: if seconds_left <= 20: return None # skip analyzing if in replay and colors don't match if REPLAY and self.turn_number % 2 != REPLAYCOLOR: return table = {} start = time.time() # Ability to pass moves = list(set(move for board in self.states for move in get_moves(board))) + [None] # Refresh engine self.engine = chess.engine.SimpleEngine.popen_uci(os.environ[STOCKFISH_ENV_VAR]) # If only one board just let stockfish play it if len(self.states) == 1: board = self.states[0] # overwrite stockfish only if we are able to take the king this move # OR in checkmate but are able to castle out for move in get_moves(board): # assert board.turn == self.color if move.to_square == board.king(self.opponent_color): return move elif board.is_checkmate() and util.is_psuedo_legal_castle(board, move): return move # weird UCI exception stuff on valid board try: board.clear_stack() r = self.engine.play(board, chess.engine.Limit(time=(MIN_TIME + MAX_TIME)/2)) best, score = r.move, r.info.get("score", "unknown") # default to move analysis except Exception as e: logger.error("Caught Stockfish error as " + str(self.color) + " (move may not be accurate)") logger.error("Error: " + str(e)) while(1): pass limit = find_time(len(moves)) table = {move: (evaluate(board, move, self.color) if evaluate(board, move, self.color) is not None else self.stkfsh_eval(board, move, limit)) for move in moves} best = max(table, key=lambda move: table[move]) score = table[best] else: states = self.remove_boards() node_count = len(moves)*len(states) logger.info(f"Number of nodes to analyze: {node_count}") limit = find_time(node_count) for move in moves: for state in states: state.turn = self.color # move is invalid and equivalent to a pass new_move = result(state, move) points = evaluate(state, new_move, self.color) if points is None: points = self.stkfsh_eval(state, new_move, limit) # assuming probability is constant, may change later table[move] = table.get(move, 0) + points/len(states) if len(table) == 0: return best = max(table, key=lambda move: table[move]) score = table[best] logger.info(f"{best} {score if isinstance(score, str) else round(score, 2)}") logger.info(f"Time left before starting calculations for current move: {time_str(seconds_left)}") logger.info(f"Time left now: {time_str(seconds_left - time.time() + start)}") logger.debug(f"{ {str(k): round(v, 2) for k, v in table.items()} }") return best