From a207488b6e626b5d089e62112be26c82039385d2 Mon Sep 17 00:00:00 2001 From: Booyaka101 Date: Sun, 7 Jun 2026 23:06:06 +0800 Subject: [PATCH] Allow fifty-move claim ahead of a checkmating or stalemating move can_claim_fifty_moves() pushed each non-zeroing legal move and tested the result with is_fifty_moves(), which is False when the resulting position has no legal moves. A move that completes the 100 halfmoves but also gives checkmate or stalemate was therefore rejected. Under FIDE 9.3 the claim is made ahead of the move: the player only declares a non-pawn, non-capture move that completes the count; it is not played, so its being game-ending does not invalidate the claim. With the clock at 99 any non-zeroing legal move completes the count, so the push/is_fifty_moves() test becomes a direct check. Positions that are already checkmate or stalemate still return False (no legal moves to iterate). Closes #1188. --- chess/__init__.py | 9 +++------ test.py | 9 +++++++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/chess/__init__.py b/chess/__init__.py index b74d710f..1509da86 100644 --- a/chess/__init__.py +++ b/chess/__init__.py @@ -2297,14 +2297,11 @@ def can_claim_fifty_moves(self) -> bool: return True if self.halfmove_clock >= 99: + # The claim can also be made ahead of a move that would itself give + # checkmate or stalemate (FIDE 9.3): it is declared, not played. for move in self.generate_legal_moves(): if not self.is_zeroing(move): - self.push(move) - try: - if self.is_fifty_moves(): - return True - finally: - self.pop() + return True return False diff --git a/test.py b/test.py index cc8a95b7..42fa0beb 100755 --- a/test.py +++ b/test.py @@ -1376,6 +1376,15 @@ def test_fifty_moves(self): self.assertFalse(board.can_claim_fifty_moves()) self.assertFalse(board.can_claim_draw()) + # Claiming ahead of a move that would itself give checkmate is valid + # (FIDE 9.3): the move is only declared, not played. + board = chess.Board("6rk/6pp/7N/5p2/6p1/8/2q5/K7 w - - 99 60") + self.assertFalse(board.is_game_over()) + self.assertTrue(board.gives_checkmate(chess.Move.from_uci("h6f7"))) + self.assertFalse(board.is_fifty_moves()) + self.assertTrue(board.can_claim_fifty_moves()) + self.assertTrue(board.can_claim_draw()) + def test_promoted_comparison(self): board = chess.Board() board.set_fen("5R2/3P4/8/8/7r/7r/7k/K7 w - - 0 1")