You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自制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()

额外优化提示

  1. 深度计数逻辑修正:当前iterative_deepening_dfs返回的depth是递归深度,但国际象棋中"mate in N"通常指进攻方走N步(包含防守方的应对),可根据需求调整深度计数逻辑
  2. 剪枝优化:在ddfs中加入剪枝逻辑,跳过明显无法形成杀棋的走法,提升搜索效率
  3. 终止条件完善:当前ddfs仅判断is_checkmate()和is_game_over(),可补充判断长将、和棋等情况

内容的提问来源于stack exchange,提问作者user22758952

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 02:05:59