Skip to content

How to Adjudicate Tournament Draws

This guide demonstrates how to evaluate FIDE tournament draw claims (the fifty-move rule and threefold repetition) along any game line using Arbiter.


1. Overview of FIDE Tournament Rules

Under World Chess Federation (FIDE) Laws of Chess, a game is drawn upon a correct claim by the player having the move under two specific conditions:

  • Fifty-Move Rule (FIDE Article 9.3): The last 50 consecutive moves (100 plies) have been completed by each player without the movement of any pawn and without any capture.
  • Threefold Repetition (FIDE Article 9.2): The exact same board position has arisen (or is about to arise) at least three times. Identical positions require the same active side to move, identical piece placement, identical castling rights, and identical en passant capture possibilities.

2. Checking Claims with the Arbiter

The Arbiter component provides evaluation for draw claims. Access the arbiter through the Cursor.arbiter property or by passing a cursor to Arbiter(cursor):

import libscid

# Game repeating positions:
pgn = """
1. Nf3 Nf6 2. Ng1 Ng8 3. Nf3 Nf6 4. Ng1 Ng8 *
"""
game = libscid.Game.from_pgn(pgn)

# Create cursor at final position:
cursor = game.create_cursor().to_game_end()

# Check draw conditions via properties:
can_repeat = cursor.arbiter.can_claim_threefold_repetition
can_fifty = cursor.arbiter.can_claim_fifty_move_rule

print(f"Can claim threefold repetition: {can_repeat}")  # True
print(f"Can claim fifty-move rule:      {can_fifty}")   # False

3. Inspecting the Halfmove Clock

For fifty-move rule tracking, inspect halfmove_clock directly on Position:

import libscid

game = libscid.Game.from_pgn("1. e4 e5 2. Nf3 Nc6 3. Bb5 a6 *")
cursor = game.create_cursor().to_game_end()

pos = cursor.position
print(f"Halfmove clock (plies): {pos.halfmove_clock}")  # 0 (after pawn push a6)
print(f"Fullmove number:        {pos.fullmove_number}")  # 3

When pos.halfmove_clock >= 100, 50 full moves have elapsed without a pawn advance or capture, making a draw claim valid.


4. Evaluating Draw Claims in Playback Loops

In chess engines, tournament arbiters, or game validator scripts, check draw claims at every step:

import libscid


def monitor_game_status(game: libscid.Game) -> None:
    cursor = game.create_cursor()

    while not cursor.is_line_end:
        cursor = cursor.next()

        if cursor.arbiter.can_claim_threefold_repetition:
            print(
                f"Draw claim available at ply {cursor.position.fullmove_number} "
                f"({cursor.position.side_to_move}): Threefold Repetition"
            )

        if cursor.arbiter.can_claim_fifty_move_rule:
            print(
                f"Draw claim available at ply {cursor.position.fullmove_number} "
                f"({cursor.position.side_to_move}): Fifty-Move Rule"
            )