自制Python残局国际象棋引擎遇AssertionError报错求助
残局国际象棋引擎AssertionError问题修复
问题场景
自主开发残局国际象棋引擎时,运行代码触发AssertionError,报错提示san() and lan() expect move to be legal or null, but got f6g6 in 6k1/8/6K1/2Q5/8/8/8/8 b - - 1 1。
原始代码
import chess import time def ddfs(board, depth, is_white_turn): if board.is_checkmate(): return True, [] if depth == 0 or board.is_game_over(): return False, [] legal_moves = list(board.legal_moves) for move in legal_moves: board.push(move) found_mate, mate_path = ddfs(board, depth - 1, not is_white_turn) board.pop() if found_mate: return True, [move] + mate_path return False, [] def iterative_deepening_dfs(board): depth = 0 while True: found_mate, mate_sequence = ddfs(board.copy(), depth, board.turn == chess.WHITE) if found_mate: return depth, mate_sequence depth += 1 def print_move_sequence(board, move_sequence): print("Initial board state:") print(board, "\n") for move in move_sequence: if board.is_legal(move): board.push(move) print(f"Move {board.san(move)}:") print(board, "\n") else: print("An illegal move found in the sequence, which should not happen.") break def main(): initial_fen = "6k1/8/5K2/2Q5/8/8/8/8 w - - 0 1" board = chess.Board(initial_fen) start_time = time.time() mate_depth, mate_sequence = iterative_deepening_dfs(board) elapsed_time = time.time() - start_time if mate_depth is not None: print(f"Mate found in {mate_depth} move(s), time taken: {elapsed_time:.2f} seconds.") board.reset() board.set_fen(initial_fen) # Reset the board to initial state print_move_sequence(board, mate_sequence) else: print("No mate found.") if __name__ == "__main__": main()
报错信息
Mate found in 3 move(s), time taken: 0.00 seconds. Initial board state: . . . . . . k . . . . . . . . . . . . . . K . . . . Q . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . --------------------------------------------------------------------------- AssertionError Traceback (most recent call last) Cell In[19], line 55 52 print("No mate found.") 54 if __name__ == "__main__": ---> 55 main() Cell In[19], line 50, in main() 48 board.reset() 49 board.set_fen(initial_fen) # Reset the board to initial state ---> 50 print_move_sequence(board, mate_sequence) 51 else: 52 print("No mate found.") Cell In[19], line 32, in print_move_sequence(board, move_sequence) 30 if board.is_legal(move): 31 board.push(move) ---> 32 print(f"Move {board.san(move)}:") 33 print(board, "\n") 34 else: File ~\myenvGPU32\Lib\site-packages\chess\__init__.py:2866, in Board.san(self, move) 2861 def san(self, move: Move) -> str: 2862 """ 2863 Gets the standard algebraic notation of the given move in the context 2864 of the current position. 2865 """ -> 2866 return self._algebraic(move) File ~\myenvGPU32\Lib\site-packages\chess\__init__.py:2879, in Board._algebraic(self, move, long) 2878 def _algebraic(self, move: Move, *, long: bool = False) -> str: -> 2879 san = self._algebraic_and_push(move, long=long) 2880 self.pop() 2881 return san File ~\myenvGPU32\Lib\site-packages\chess\__init__.py:2884, in Board._algebraic_and_push(self, move, long) 2883 def _algebraic_and_push(self, move: Move, *, long: bool = False) -> str: -> 2884 san = self._algebraic_without_suffix(move, long=long) 2886 # Look ahead for check or checkmate. 2887 self.push(move) File ~\myenvGPU32\Lib\site-packages\chess\__init__.py:2920, in Board._algebraic_without_suffix(self, move, long) 2917 return "O-O" 2919 piece_type = self.piece_type_at(move.from_square) -> 2920 assert piece_type, f"san() and lan() expect move to be legal or null, but got {move} in {self.fen()}" 2921 capture = self.is_capture(move) 2923 if piece_type == PAWN: AssertionError: san() and lan() expect move to be legal or null, but got f6g6 in 6k1/8/6K1/2Q5/8/8/8/8 b - - 1 1
问题根源
在print_move_sequence函数中,执行board.push(move)后调用board.san(move):
push(move)会修改棋盘状态,将棋子从move.from_square移走,此时该位置为空san()方法需要基于走法执行前的棋盘状态生成代数记谱,此时无法找到起始位置的棋子,触发断言失败
修复方案
修改print_move_sequence函数,在push之前获取走法的代数记谱,避免修改原棋盘后再调用san():
修改后的代码
import chess import time def ddfs(board, depth, is_white_turn): if board.is_checkmate(): return True, [] if depth == 0 or board.is_game_over(): return False, [] legal_moves = list(board.legal_moves) for move in legal_moves: board.push(move) found_mate, mate_path = ddfs(board, depth - 1, not is_white_turn) board.pop() if found_mate: return True, [move] + mate_path return False, [] def iterative_deepening_dfs(board): depth = 0 while True: found_mate, mate_sequence = ddfs(board.copy(), depth, board.turn == chess.WHITE) if found_mate: return depth, mate_sequence depth += 1 def print_move_sequence(board, move_sequence): print("Initial board state:") print(board, "\n") for move in move_sequence: if board.is_legal(move): # 在push之前获取代数记谱 move_san = board.san(move) board.push(move) print(f"Move {move_san}:") print(board, "\n") else: print("An illegal move found in the sequence, which should not happen.") break def main(): initial_fen = "6k1/8/5K2/2Q5/8/8/8/8 w - - 0 1" board = chess.Board(initial_fen) start_time = time.time() mate_depth, mate_sequence = iterative_deepening_dfs(board) elapsed_time = time.time() - start_time if mate_depth is not None: print(f"Mate found in {mate_depth} move(s), time taken: {elapsed_time:.2f} seconds.") board.reset() board.set_fen(initial_fen) # Reset the board to initial state print_move_sequence(board, mate_sequence) else: print("No mate found.") if __name__ == "__main__": main()
额外优化提示
- 深度计数逻辑修正:当前
iterative_deepening_dfs返回的depth是递归深度,但国际象棋中"mate in N"通常指进攻方走N步(包含防守方的应对),可根据需求调整深度计数逻辑 - 剪枝优化:在
ddfs中加入剪枝逻辑,跳过明显无法形成杀棋的走法,提升搜索效率 - 终止条件完善:当前
ddfs仅判断is_checkmate()和is_game_over(),可补充判断长将、和棋等情况
内容的提问来源于stack exchange,提问作者user22758952
相关产品推荐
相关产品推荐

