I'd like to flag a corner of Board.can_claim_fifty_moves(). The predicate answers the existence question — is there at least one legal move that would satisfy a 50-move draw claim? — and accepts non-pawn, non-capture moves that complete the 50 moves, except when such a move would itself deliver checkmate or stalemate, in which case it rejects them. FIDE 9.3 does not require the announced claim move to be a non-checkmate / non-stalemate move; only that it is neither a pawn move nor a capture and that it completes the 50 moves without progress.
The procedural framing makes the distinction sharp:
- When checkmate or stalemate is already on the board, the game has ended and the draw-claim procedure cannot be initiated. The answer "no" is correct.
- In the claim-ahead case the procedure can be initiated: the player announces the move (the move is not yet played) and claims the draw on that announcement. A move that would deliver checkmate or stalemate when played is still a valid announced move under FIDE 9.3, provided it is non-pawn, non-capture, and completes the 50 halfmoves.
Because can_claim_fifty_moves() answers at the position level (it doesn't take a candidate move), the disagreement only surfaces in the special positions where the only non-zeroing legal move ends the game — vanishingly rare in practice. But the predicate's answer at those positions is, I think, not quite right.
Reproducer
import chess
board = chess.Board("6rk/6pp/7N/5p2/6p1/8/2q5/K7 w - - 99 60")
print(board.can_claim_fifty_moves()) # False -- per FIDE 9.3, should be True
I'd like to flag a corner of
Board.can_claim_fifty_moves(). The predicate answers the existence question — is there at least one legal move that would satisfy a 50-move draw claim? — and accepts non-pawn, non-capture moves that complete the 50 moves, except when such a move would itself deliver checkmate or stalemate, in which case it rejects them. FIDE 9.3 does not require the announced claim move to be a non-checkmate / non-stalemate move; only that it is neither a pawn move nor a capture and that it completes the 50 moves without progress.The procedural framing makes the distinction sharp:
Because
can_claim_fifty_moves()answers at the position level (it doesn't take a candidate move), the disagreement only surfaces in the special positions where the only non-zeroing legal move ends the game — vanishingly rare in practice. But the predicate's answer at those positions is, I think, not quite right.Reproducer