Matilda: a free human-like UCI chess engine.
Project description
Matilda
A free human-like UCI chess engine.
Unlike Stockfish, Matilda doesn't try to play the best move — it plays the move a human at a given Elo would most likely play. It speaks the standard UCI protocol, so it plugs into any chess GUI (CuteChess, Arena, BanksiaGUI, En Croissant) or engine runner that can talk to Stockfish.
The default backend is Matilda: frozen Maia-3
(23M) re-ranked by the paper's trained set-transformer, with optional engine
features from any plugged-in search controller (Stockfish, Lc0, or your own).
The legacy Maia-2 backend remains available
via --backend maia2.
Install & run
pip install -e ".[dev]" # from a clone (PyPI release TBD)
# the matilda backend additionally needs the pinned Maia-3 runtime:
pip install 'maia3 @ git+https://github.com/CSSLab/maia3.git@1e13597c42d4858b7cfd7cfdae01e297263364b2'
matilda-uci --elo 1500 --checkpoint checkpoints/base_3k.pt
Maia-3's 23M weights auto-download from HuggingFace on first use. The
re-ranker checkpoint (base_3k.pt, ~6 MB) is required for the default
backend and validated at startup; ask the maintainers or see the paper repo
for the released weights. The UCI handshake itself is instant — models load
on the first move request.
The legacy backend needs neither: matilda-uci --backend maia2 --maia-type rapid.
To use it from a chess GUI, point the "add engine" dialog at the
matilda-uci executable — walkthroughs for CuteChess, cutechess-cli, and
En Croissant are in docs/gui-demo.md.
CLI flags
| Flag | Default | Meaning |
|---|---|---|
--backend |
matilda |
matilda (Maia-3 + re-ranker) or maia2 (legacy) |
--elo |
1500 | Playing strength (the Elo the engine imitates) |
--opp-elo |
1500 | The opponent Elo the model conditions on |
--temperature |
0.0 | 0 = always the most-human move; higher = sample |
--device |
cpu |
matilda: cpu/mps/cuda; maia2: cpu/gpu |
--name |
Matilda |
Engine name reported to the GUI |
--log-level |
WARNING |
Logging goes to stderr (stdout is the UCI channel) |
Matilda-backend flags:
| Flag | Default | Meaning |
|---|---|---|
--checkpoint |
checkpoints/base_3k.pt |
The trained re-ranker weights |
--tc-base / --tc-inc |
180 / 0 | Time control fed to the model (blitz default) |
--no-auto-tc |
off | Don't latch the real TC from the first go clocks |
--style-checkpoint |
— | Optional style-token overlay (personalization) |
--style-posthoc |
— | Optional post-hoc new-player embeddings |
--style-player-id |
-1 | Player row to imitate (-1 = style-free) |
--engine-cmd |
— | Search controller, e.g. stockfish or lc0 --weights=... |
--engine-depth / --engine-nodes |
12 / 0 | Controller search budget (nodes>0 = node limit) |
--maia3-model |
23m |
Maia-3 variant |
Legacy maia2-backend flag: --maia-type (rapid/blitz) — only valid with
--backend maia2.
UCI options (matilda backend)
Set from any GUI's engine-options dialog: UCI_LimitStrength, UCI_Elo
(1000–3200), OpponentElo, TimeControlBase/TimeControlInc/AutoLatchTC,
Temperature, Checkpoint, StyleCheckpoint/StylePosthoc/StylePlayerId,
EngineCmd/EngineDepth/EngineNodes, and Device.
The model is rating-conditioned, so UCI_Elo maps directly onto playing style
and strength: the engine plays like a human of that rating, including
human-characteristic mistakes. Time control is a real model input — the same
position is played differently at 60+0 than at 900+10 — and is latched
automatically from the clock at the first go of each game.
Architecture
UciEngine (protocol loop) ──drives──> MovePolicy (swap seam)
├── MatildaPolicy (default)
│ └── MatildaModel
│ ├── Maia3Wrapper (frozen 23M prior)
│ ├── TXTC re-ranker (base_3k.pt)
│ └── SearchController (optional:
│ Stockfish / Lc0 / custom)
└── MaiaPolicy (Maia-2, legacy)
MovePolicy is a small protocol and the deliberate extension point. The
SearchController protocol is the second seam: any engine that can score up
to 16 candidate moves plugs in, and without one the model degrades gracefully
to the pure human prior. go infinite runs on a worker thread and holds
bestmove until stop, per the UCI spec.
Botvinnik's programme, inverted
Long before brute-force search won, world champion Mikhail Botvinnik argued that a chess program should work the way a master does: don't examine every move — build a small candidate set the way a human would, and spend your calculation only there. He devoted his post-championship career (the PIONEER project; Computers, Chess and Long-Range Planning, 1970) to formalizing that selective, human-feature-focused search. Hardware went the other way, and Botvinnik's programme was shelved.
Matilda's architecture is that idea, made practical by learning the human
part instead of hand-coding it. A frozen human-move model (Maia-3) proposes
the up-to-16 moves a human would actually consider in the position — the
candidate set Botvinnik wanted — and the pluggable search controller
decides how to spend engine effort across only those moves. Stockfish and
Lc0 plug in today; the SearchController API (see
developer.md) exists precisely so anyone can implement their
own allocation policy — deeper on the moves that look most interesting for a
human, shallower elsewhere. The re-ranker then blends what the engine found
back into the human distribution. It should "just work" with any controller:
that composability, not any single engine, is the point.
Photo: Harry Pot / Anefo, Dutch National Archives — licensed CC BY-SA 3.0 NL.
Demos, docs, numbers
- developer.md — Python API, custom search controllers, style personalization, embedding in your own service.
- docs/gui-demo.md — playing it from CuteChess & co.
- docs/profiling.md — inference throughput by game phase (~100 predictions/s on an Apple M3 Pro CPU), rliable bootstrap CIs.
- demos/games/ — sample games vs lichess-level Stockfish;
regenerate with
demos/play_vs_stockfish.py. - demos/style_demo.py — measure how player-style embeddings condition the policy.
Development
pip install -e ".[dev]"
python -m pytest
python -m ruff check .
PYTHONPATH=src python scripts/verify_checkpoint.py checkpoints # model parity
The test-suite injects fake policies/models/wrappers, so it runs without the
model runtimes or downloads. scripts/verify_checkpoint.py proves the ported
model matches the paper implementation bitwise (requires the checkpoints and,
for the parity checks, a clone of the paper repo).
License
GPL-3.0 — see LICENSE.
Citation
If you use Matilda in your research, please cite:
@article{carlson2026matilda,
title = {Matilda: Engine-Agnostic Search with Human Policy Guidance},
author = {Carlson, Jason},
year = {2026},
note = {Preprint},
}
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file matilda_uci-0.1.0.tar.gz.
File metadata
- Download URL: matilda_uci-0.1.0.tar.gz
- Upload date:
- Size: 85.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e72823d5629476655c59e78e107d94bc52cd9cc37bd9ffd2324816a2a20901ee
|
|
| MD5 |
c82e2a220a272f62c26d55cd51d9946a
|
|
| BLAKE2b-256 |
4f146729ac8b3b14293e72844f0fcd288018228984608db317fd2c4f8c7edd6d
|
Provenance
The following attestation bundles were made for matilda_uci-0.1.0.tar.gz:
Publisher:
release.yml on GarryChess/matilda-uci
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
matilda_uci-0.1.0.tar.gz -
Subject digest:
e72823d5629476655c59e78e107d94bc52cd9cc37bd9ffd2324816a2a20901ee - Sigstore transparency entry: 2186727229
- Sigstore integration time:
-
Permalink:
GarryChess/matilda-uci@9c59a9a3d4d10e902f8970ea0ad705d6d44262c1 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/GarryChess
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9c59a9a3d4d10e902f8970ea0ad705d6d44262c1 -
Trigger Event:
push
-
Statement type:
File details
Details for the file matilda_uci-0.1.0-py3-none-any.whl.
File metadata
- Download URL: matilda_uci-0.1.0-py3-none-any.whl
- Upload date:
- Size: 67.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e54df6d50ce22e498f60c94c2af1e98e7240b2b19fef85655e9a31d5cbc78cd7
|
|
| MD5 |
2ad5d9d420a58e9c219d2c7851a0429b
|
|
| BLAKE2b-256 |
3fd31ee0cf1c43d0d6978296c99251b0575545104dab8eb9a368319b3712c0ba
|
Provenance
The following attestation bundles were made for matilda_uci-0.1.0-py3-none-any.whl:
Publisher:
release.yml on GarryChess/matilda-uci
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
matilda_uci-0.1.0-py3-none-any.whl -
Subject digest:
e54df6d50ce22e498f60c94c2af1e98e7240b2b19fef85655e9a31d5cbc78cd7 - Sigstore transparency entry: 2186727553
- Sigstore integration time:
-
Permalink:
GarryChess/matilda-uci@9c59a9a3d4d10e902f8970ea0ad705d6d44262c1 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/GarryChess
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9c59a9a3d4d10e902f8970ea0ad705d6d44262c1 -
Trigger Event:
push
-
Statement type: