niklasf/python-chess

getting `chess.IllegalMoveError` despite the fact that move is legal

HarshilPatel007 opened this issue · 5 comments

Hello all,
thanks for the great library. I'm creating a chess application GUI using this library. but, I'm facing some issues. After trying for a 3 days to solve it. I just decided to open an issue for this.

  1. I'm trying to show the move in san format after user makes a move on the chessboard. But, getting the illegal move error despite the fact that move is legal. e2e4 (I've pasted the error in the code. the link is given at the end of the issue)

  2. when pawn is trying to capture on the last rank, the move highlight which is shown based on legal moves (self.chessboard.board.legal_moves) is showing the highlight in dark (100%) but, I've used opacity in the highlight. the weirdness is, it is working perfectly except this case.

  3. IDK why but, when I try to use if move in self.chessboard.board.legal_moves to show pawn promotion dialog, it is not working. as soon as I removed this check, it worked. but, the problem is, the dialog opens when user trying to capture on last rank illegally. however, I've stopped the actual promotion if the move is illegal. but, the dialog still opens which is the problem.

    here is the project code link: https://gist.github.com/HarshilPatel007/7c1dd43862003a537cf26136624b1611

Hi. Checking the entire output, it looks something like:

Updated FEN: rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1
Traceback (most recent call last):
  File "/home/niklas/Projekte/python-chess/chesss.py", line 117, in mousePressEvent
    self.move_manager.move_piece(square)
  File "/home/niklas/Projekte/python-chess/chesss.py", line 193, in move_piece
    pgn_move = self.chessboard.board.variation_san([move])
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/niklas/Projekte/python-chess/chess/__init__.py", line 2970, in variation_san
    raise IllegalMoveError(f"illegal move {move} in position {board.fen()}")
chess.IllegalMoveError: illegal move e2e4 in position rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1

So the you're already in the position after 1. e4, where e4 is no longer a legal move (because it's black to move).

To fix that, you can reorder the code to create the notation before board.push(...).

hi @niklasf ,
thanks a lot. it worked.

But, what about the points 2 and 3 ?
The pawn's legal move at the last rank highlighted with 100% dark. which means that it doesn't considers the opacity settings there.
I think the code is logic wise is correct. because, it works fine except the last rank with the pawn moves. All others pieces legal moves highlighted correctly with the opacity settings in all over the board. In fact, the pawn moves highlighted correctly excepts the last rank. (I'm facing the same issue with GTK as well)

  • pawn's legal moves highlight at the last rank. ( see the highlight on the knight. it's 100% dark)
    Screenshot from 2024-01-15 10-41-05

  • queen's legal moves highlight at the last rank. ( see the highlight on the knight. it considers the opacity settings)
    Screenshot from 2024-01-15 10-41-32

also, the self.chessboard.board.legal_moves check is working fine with all the pieces except the last rank+pawn combination.
means, if, I change the code from,

            if move in self.chessboard.board.legal_moves:
                if (
                    self.chessboard.board.piece_type_at(self.selected_square)
                    == chess.PAWN
                ):
                    if (
                        self.chessboard.board.turn == chess.WHITE
                        and chess.square_rank(target_square) == 7
                    ):
                        self.handle_pawn_promotion(move)
                    elif (
                        self.chessboard.board.turn == chess.BLACK
                        and chess.square_rank(target_square) == 0
                    ):
                        self.handle_pawn_promotion(move)

to,

            if move in self.chessboard.board.legal_moves:
                if (
                    self.chessboard.board.piece_type_at(self.selected_square)
                    == chess.PAWN
                ):
                    if (
                        self.chessboard.board.turn == chess.WHITE
                        and chess.square_rank(target_square) == 6
                    ):
                        self.handle_pawn_promotion(move)
                    elif (
                        self.chessboard.board.turn == chess.BLACK
                        and chess.square_rank(target_square) == 1
                    ):
                        self.handle_pawn_promotion(move)

then it works with all the ranks except the last rank. It's just mind boggling thing for me. As I'm getting same kind of (Bugs?) with GTK as well.

MarkZH commented

@HarshilPatel007

For point (2), the reason for the circles being darker is that there are multiple circles being drawn on those squares. In the first image in your last post, the pawn has four legal moves: cxb8r, cxb8n, cxb8b, and cxb8q. So, each of those moves causes a circle to get drawn on square b8. In the loop starting on line 126, you need to loop over squares where pieces can land, not over legal moves.

For point (3), line 170, move = chess.Move(self.selected_square, target_square), does not create a legal move when the piece is a pawn and the target_square is a square on the back rank because it doesn't include a promotion piece. Instead of checking if the move is legal, you should check if there is any legal move from self.selected_square to target_square in self.chessboard.board.legal_moves list.

hi @MarkZH ,
thanks a lot for providing insights. this helped me to solve those bugs.

for point 2, i don't know any other way to get squares where pieces can land other then getting it from a legal moves of a selected pieces. (If you know then, please give me some ideas) so, I just hacked around the code and used a set to keep track of the squares that have already has a circle to skip drawing a circle on that square. 😆

for point 3, I've done exactly as you've said and bug gone.

Thanks.

MarkZH commented

@HarshilPatel007

I would also use a set, but in a different way.

for target_square in set(move.to_square for move in legal_moves):