Skip to main content

py-draughts — fastest Python draughts & checkers library

GitHub Actions PyPI version Downloads Docs Python License

py-draughts (import name: draughts) is a fast, modern Python library for draughts (also known as checkers). Bitboard-backed move generation, PDN/FEN parsing, built-in engines (the ML-trained TurboEngine plus a general-purpose SimpleEngine), a HUB protocol bridge for external engines like Scan and Kingsrow, an interactive web UI, and tensor exports for RL / ML — all in one package.

py-draughts vs pydraughts

py-draughts pydraughts
Legal moves generation 21.4 µs 5.18 ms (243x slower)
Make move 1.2 µs 552.50 µs (460x slower)
Board init 3.3 µs 579.45 µs (176x slower)
FEN parse 27.4 µs 295.10 µs (11x slower)
Variants 8 (Standard, American, Frisian, Russian, Brazilian, Antidraughts, Breakthrough, Frysk!) 6
Built-in AI engine ✅ TurboEngine (learned eval) + SimpleEngine (all variants) ❌ External only
Engine benchmarking suite ✅ ❌
Web UI ✅ FastAPI + interactive board ❌
SVG rendering ✅ ❌
ML/RL helpers (tensors, masks) ✅ ❌
Test suite 260+ tests, real Lidraughts PDN replays Limited
HUB protocol (Scan, Kingsrow) ✅ ✅
Implementation Bitboards (NumPy uint64) Object lists

Full comparison →

Features: 8 variants • Built-in AI engine • External engines via HUB protocol (Scan, Kingsrow) • RL/ML ready (tensors, masks) • SVG rendering • Web UI

Installation

pip install py-draughts

Core

>>> import draughts
>>> board = draughts.Board()

>>> board.legal_moves
[Move: 31->27, Move: 31->26, Move: 32->28, ...]

>>> board.push_uci("31-27")
>>> board.push_uci("18-22")
>>> board.push_uci("27x18")
>>> board.push_uci("12x23")

>>> board
 . b . b . b . b . b
 b . b . b . b . b .
 . b . b . . . b . b
 . . . . b . b . b .
 . . . b . . . . . .
 . . . . . . . . . .
 . w . w . w . w . w
 w . w . w . w . w .
 . w . w . w . w . w
 w . w . w . w . w .

>>> board.pop()  # Unmake the last move
Move: 12->23

>>> board.turn
Color.WHITE

Make and unmake moves

>>> board.push_uci("32-28")  # Make a move
>>> board.pop()              # Unmake the last move
Move: 32->28

Show ASCII board

>>> board = draughts.Board()
>>> print(board)
 . b . b . b . b . b     .  1 .  2 .  3 .  4 .  5
 b . b . b . b . b .      6 .  7 .  8 .  9 . 10 .
 . b . b . b . b . b     . 11 . 12 . 13 . 14 . 15
 b . b . b . b . b .     16 . 17 . 18 . 19 . 20 .
 . . . . . . . . . .     . 21 . 22 . 23 . 24 . 25
 . . . . . . . . . .     26 . 27 . 28 . 29 . 30 .
 . w . w . w . w . w     . 31 . 32 . 33 . 34 . 35
 w . w . w . w . w .     36 . 37 . 38 . 39 . 40 .
 . w . w . w . w . w     . 41 . 42 . 43 . 44 . 45
 w . w . w . w . w .     46 . 47 . 48 . 49 . 50 .

Detects draws and game end

>>> board.is_draw
False
>>> board.is_threefold_repetition
False
>>> board.game_over
False
>>> board.result
'-'

FEN parsing and writing

>>> board.fen
'[FEN "W:W31,32,33,...:B1,2,3,..."]'

>>> board = draughts.Board.from_fen("W:WK10,K20:BK35,K45")
>>> board
 . . . . . . . B . .
 . . . . . . . . . .
 . . . . . . . . . .
 . . . . . . . . . .
 . . . . . . . . . .
 . . . . . . . . . .
 . . . . W . . . . .
 . . . . . . . . . .
 . . . . W . . . . .
 B . . . . . . . . .

PDN parsing and writing

>>> board = draughts.Board()
>>> board.push_uci("32-28")
>>> board.push_uci("18-23")
>>> board.pdn
'[GameType "20"]
[Variant "Standard (international) checkers"]
[Result "-"]
1. 32-28 18-23'

>>> board = draughts.Board.from_pdn('[GameType "20"]\n1. 32-28 19-23 2. 28x19 14x23')

Variants

Variant Class Board Flying Kings Max Capture Notes
Standard StandardBoard 10×10 Yes Required International / FMJD rules
American AmericanBoard 8×8 No Not required Men capture forward only
Frisian FrisianBoard 10×10 Yes Required (by value) Diagonal + orthogonal captures
Russian RussianBoard 8×8 Yes Not required Mid-capture promotion
Brazilian BrazilianBoard 8×8 Yes Required International rules on 8×8
Antidraughts AntidraughtsBoard 10×10 Yes Required Lose all pieces (or get blocked) to win
Breakthrough BreakthroughBoard 10×10 Yes Required First player to make a king wins
Frysk! FryskBoard 10×10 Yes Required (by value) Frisian rules with 5 men per side
>>> from draughts import (
...     StandardBoard,
...     AmericanBoard,
...     FrisianBoard,
...     RussianBoard,
...     BrazilianBoard,
...     AntidraughtsBoard,
...     BreakthroughBoard,
...     FryskBoard,
... )

>>> board = AmericanBoard()
>>> board
 . b . b . b . b
 b . b . b . b .
 . b . b . b . b
 . . . . . . . .
 . . . . . . . .
 w . w . w . w .
 . w . w . w . w
 w . w . w . w .

SVG Rendering

>>> import draughts
>>> board = draughts.Board()
>>> draughts.svg.board(board, size=400)  # Returns SVG string
Standard draughts board
>>> board = draughts.Board.from_fen("W:WK10,K20:BK35,K45")
>>> draughts.svg.board(board, size=400)
Board with kings

Engine

Two built-in engines.

For the standard international (10x10) board, TurboEngine combines Scan's 63-bit bitboard layout, a PVS search, and a machine-learned pattern evaluation trained on Scan self-play — it is the strongest built-in engine:

>>> from draughts import Board, TurboEngine

>>> engine = TurboEngine(time_limit=0.5)  # or depth_limit=...
>>> move, score = engine.get_best_move(Board(), with_evaluation=True)

SimpleEngine (all variants)

SimpleEngine is a lightweight general-purpose engine — alpha-beta with transposition tables and iterative deepening — that works on every variant (TurboEngine is international-only):

>>> from draughts import Board, SimpleEngine

>>> engine = SimpleEngine(depth_limit=5)
>>> move, score = engine.get_best_move(Board(), with_evaluation=True)
>>> score
0.15

External Engines (Hub Protocol)

Use external engines like Scan via the Hub protocol:

>>> from draughts import Board, HubEngine

>>> with HubEngine("path/to/scan.exe", time_limit=1.0) as engine:
...     board = Board()
...     move, score = engine.get_best_move(board, with_evaluation=True)
...     print(f"Best: {move}, Score: {score}")
Best: 32-28, Score: 0.15

Compatible engines:

  • Scan - World champion level, supports 10x10
  • Kingsrow - Multiple variants, endgame databases
  • Any engine implementing the Hub protocol

Engine Benchmarking

Compare engines against each other with comprehensive statistics:

>>> from draughts import Benchmark, SimpleEngine

>>> stats = Benchmark(
...     SimpleEngine(depth_limit=4),
...     SimpleEngine(depth_limit=6),
...     games=20
... ).run()

>>> print(stats)
============================================================
  BENCHMARK: SimpleEngine (d=4) vs SimpleEngine (d=6)
============================================================
  RESULTS: 2-12-6 (W-L-D)
  SimpleEngine (d=4) win rate: 25.0%
  Elo difference: -191
  ...

Writing Your Own AI

Build custom agents with neural networks, MCTS, or any algorithm:

>>> from draughts import Board, BaseAgent, AgentEngine, Benchmark

>>> class GreedyAgent(BaseAgent):
...     def select_move(self, board):
...         return max(board.legal_moves, key=lambda m: len(m.captured_list))

>>> board = Board()
>>> agent = GreedyAgent()
>>> move = agent.select_move(board)

# Use with Benchmark
>>> stats = Benchmark(agent.as_engine(), SimpleEngine(depth_limit=4), games=10).run()

ML-ready features:

>>> board.to_tensor()        # (4, 50) tensor for neural networks
>>> board.legal_moves_mask() # Boolean mask for policy outputs
>>> board.features()         # Material, mobility, game phase
>>> clone = board.copy()     # Fast cloning for tree search

Example: a neural net that learns to play

examples/train_value_net.py trains a small evaluation network (~630K parameters) for 10x10 International draughts on a laptop CPU — no GPU. It is taught by the world-class Scan engine over the Hub protocol, using the same data recipe as chess NNUE nets:

  1. Data. Scan plays games from random openings; we keep only quiet positions (no capture pending), each with Scan's evaluation and the game's result, and deduplicate them.
  2. Target. Blend the squashed engine eval with the game result — a WDL label.
  3. Features. Hand the small MLP a few scalars it can't extract from raw bits — material balance and parity ("the move") — next to the board planes.
  4. Augment + train. Double the data with the board's 180° rotation (a valid symmetry), weight decisive positions more, and regress with early stopping.
  5. Play with a negamax + alpha-beta search that has quiescence: it never evaluates a position with a pending capture, so every leaf is quiet — the distribution the net was trained on.

The lesson: representation beats optimisation. Once the data was clean, the net plateaued — more data and training tricks (EMA, LR schedules) barely moved it. The jump at fixed size and speed came from what the net sees: adding material and parity features (a flat MLP can't recover a sum or a mod‑2 count from 200 raw bits) plus the 180° augmentation. Same ~630K params, ~0.2 s/move (25–40 games each):

Opponent Result (W–L–D) Verdict
SimpleEngine(depth=2) 22–2–1 dominates
SimpleEngine(depth=4) 27–2–11 dominates (was 15–3–12 without features)
SimpleEngine(depth=6) 11–9–5 now ahead of a depth‑6 search

Load the trained weights and play:

import torch
import torch.nn as nn
from draughts.boards.standard import Board  # 10x10 International

def board_features(x):   # material + parity — what a flat MLP can't see in raw bits
    c = x.sum(2); om, ok, pm, pk = c[:, 0], c[:, 1], c[:, 2], c[:, 3]; total = c.sum(1)
    return torch.stack([om/15, ok/5, pm/15, pk/5, (om + 3*ok - pm - 3*pk)/15,
                        (ok - pk)/5, total/40, total % 2, (om + ok) % 2,
                        om % 2, pm % 2], dim=1)

class ValueNet(nn.Module):                    # ~630K params
    def __init__(self, h=512):
        super().__init__()
        self.body = nn.Sequential(
            nn.Linear(4*50 + 11, h), nn.ReLU(), nn.Dropout(0.3),
            nn.Linear(h, h), nn.ReLU(), nn.Dropout(0.3),
            nn.Linear(h, h), nn.ReLU(),
            nn.Linear(h, 1), nn.Tanh(),
        )
    def forward(self, x):
        flat = x.reshape(x.shape[0], -1)
        return self.body(torch.cat([flat, board_features(x)], dim=1)).squeeze(-1)

net = ValueNet(); net.load_state_dict(torch.load("examples/value_net_standard.pt")); net.eval()

@torch.no_grad()
def evaluate(board):                          # score for the side to move
    return float(net(torch.from_numpy(board.to_tensor()).unsqueeze(0)))

@torch.no_grad()
def search(board, depth, alpha=-1e9, beta=1e9):
    moves = board.legal_moves
    if not moves:
        return -1.0                           # side to move has lost
    quiet = not moves[0].captured_list
    if depth <= 0 and quiet:
        return evaluate(board)                # only ever score quiet leaves
    depth = depth if not quiet else depth - 1  # quiescence: captures are "free"
    best = -2.0
    for m in moves:
        child = board.copy(); child.push(m)
        best = max(best, -search(child, depth, -beta, -alpha))
        alpha = max(alpha, best)
        if alpha >= beta:
            break
    return best

def after(board, move):
    child = board.copy(); child.push(move); return child

board = Board()
# 3-ply search: play the move that leaves the opponent worst off.
best = max(board.legal_moves, key=lambda m: -search(after(board, m), 2))
board.push(best)

The example script packages this as a ValueAgent (a normal BaseAgent) and a Benchmark harness. To retrain, download Scan (SCAN_EXE=/path/to/scan.exe) and run python examples/train_value_net.py; without Scan it falls back to the built-in engine as a weaker teacher.

Aside: unlike chess, a 10x10 draughts board has no left-right mirror symmetry on its playable squares (reflecting columns flips square colour), so there is no free spatial data augmentation — the only board symmetry is a 180° rotation, which the side-to-move-relative tensor already encodes.

Server

Interactive web interface for playing and engine testing:

from draughts import Board, Server, SimpleEngine, HubEngine

server = Server(
    board=Board(),
    white_engine=SimpleEngine(depth_limit=6),
    black_engine=HubEngine("path/to/scan.exe", time_limit=1.0)
)
server.run() # Open http://localhost:8000
image

Performance

Legal moves generation in ~10-30 microseconds:

Operation py-draughts pydraughts Speedup
Board init 3.30 µs 579.45 µs 176x faster
FEN parse 27.40 µs 295.10 µs 11x faster
Legal moves 21.35 µs 5.18 ms 243x faster
Make move 1.20 µs 552.50 µs 460x faster
Speed Comparison Chart

Engine search at various depths:

Depth Time Nodes
5 274 ms 3,263
6 619 ms 7,330
7 2.20 s 21,642
8 6.55 s 98,987
Engine Benchmark

Testing

Comprehensive test suite with 260+ tests covering all variants and edge cases:

pytest test/ -v

Tests include:

  • Move generation - Push/pop roundtrips, legal move validation
  • Real game replays - PDN games from Lidraughts for all variants
  • Edge cases - Complex king captures, promotion mid-capture, draw rules
  • Engine correctness - Hash stability, transposition tables, board immutability
  • All 8 variants - Standard, American, Frisian, Russian, Brazilian, Antidraughts, Breakthrough, Frysk!

Contributing

Contributions welcome! Please open an issue or submit a pull request.

License

py-draughts is licensed under the GPL 3.

Release files for py-draughts 1.9.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for py-draughts 1.9.0
File Size Uploaded
py_draughts-1.9.0.tar.gz 213.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for py-draughts 1.9.0
File Interpreter ABI Platform
py_draughts-1.9.0-py3-none-any.whl Python 3 none any Details

Total release size: 435.6 kB

Release files / py_draughts-1.9.0.tar.gz

Download URL py_draughts-1.9.0.tar.gz
Size 213.9 kB
Tags Source
SHA-256 checksum
How to use checksums
29f8346896ddbb98314a1d38c6e94b6cb7108fbb8f6070bbe68993d75773353e
BLAKE2b-256 checksum
How to use checksums
18a8ff12d42832711ec18f4bfa1b5352bcc97a6b3e0878bd951287d1c92ad88f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 16, 2026.

Transparency log

Release files / py_draughts-1.9.0-py3-none-any.whl

Download URL py_draughts-1.9.0-py3-none-any.whl
Size 221.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2699452289b644286b8ad2f51bc93b2166bb8a89ffea47e826dceb4dd41b423e
BLAKE2b-256 checksum
How to use checksums
b6fef860d6e92af21a8c93f75e2c8f4829d5b2683a1af55b5a122541cbeb43cb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 16, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.9.0 This release

2 release files

1.8.4

2 release files

1.8.3

2 release files

1.8.1

2 release files

1.7.1

2 release files

1.6.4

2 release files

1.6.3

2 release files

1.6.2

2 release files

1.6.1

2 release files

1.6.0

2 release files

1.5.8

2 release files

1.5.5

2 release files

1.5.2

2 release files

1.5.0

2 release files

1.4.2

2 release files

1.3.1

2 release files

1.3.0

2 release files

1.2.9

2 release files

1.2.8

2 release files

1.2.7

2 release files

1.2.5

2 release files

1.2.4

2 release files

1.2.3

2 release files

1.2.2

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.7

2 release files

1.1.6

2 release files

1.1.5

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.6

2 release files

1.0.4

2 release files

1.0.3

2 release files

1.0.0

2 release files

0.9.9

2 release files

0.9.0

2 release files

0.3.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page