I'm trying to create an endgame chess engine fully on my own and in Python. I'm keeping getting an error as below. I'm unable to get rid of it. Can someone kindly fix it or give me a hint how to fix it? Or, where is the problem.
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()
I'm getting this error and cannot get rid of it
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 the move (board.san()
) before you push that move the board with board.push()
.
Just make board.san()
line before board.push
.
So the print_move_sequence(board, move_sequence)
function would be like this:
def print_move_sequence(board, move_sequence):
print("Initial board state:")
print(board, "\n")
for move in move_sequence:
if board.is_legal(move):
print(f"Move {board.san(move)}:") # This line should be first.
board.push(move) # This line shuold be second.
print(board, "\n")
else:
print("An illegal move found in the sequence, which should not happen.")
break
The issue was that when you push
a move to the board, that move is actually made on the board, and it might sometimes not become legal after it's already executed. In your example, when f6g6 move is executed, it becomes black's turn instead of white. Thus, when doing board.san()
to see the SAN representation of the move, you won't be able to because it's no longer legal. So the better approach to this is to print the SAN representation before making the move.