3D implementation of the abstract board game Zèrtz with a Panda3D renderer and a Rust-accelerated Monte Carlo Tree Search engine.
THIS IS A WORK-IN-PROGRESS!
Zèrtz 3D pairs Python controllers with a PyO3 Rust extension that holds the core rule engine and search. The project targets reproducible AI experimentation, human play through Panda3D, and offline analysis of tuning runs.
- 37, 48, and 61 ring boards with Standard and Blitz marble pools
- Deterministic runs via seedable RNG and transcript logging
- Replay support for internal transcripts, official notation, and Boardspace SGF files
- Monte Carlo Tree Search with RAVE, progressive widening, FPU, and optional time limits
- Multi-threaded search with virtual loss and transposition tables
- Shared Python/Rust interfaces for swapping in new heuristics or training loops
- Panda3D renderer with highlight modes, coordinate overlays, and water/lighting effects
- Headless execution for tournaments or profiling runs
- Text renderer for terminal-based inspection
- Python 3.12+
- Rust toolchain with Cargo
uvfor environment management
uv sync
./rust-dev.sh # fast debug build
# ./rust-build.sh # release build once you need the optimised wheelThe game runs without the Rust wheel, but search performance drops noticeably.
uv run main.py # Random vs Random
uv run main.py --player1 mcts:iterations=5000 \
--player2 mcts:iterations=5000 \
--headless --games 100 --stats # Tournament batch
uv run main.py --replay data/sgf/sample.sgf # Replay an SGF logUse uv run main.py --help to see the complete set of switches.
Players are configured via --player1 TYPE[:PARAM=VALUE,...] (same for --player2). TYPE is random, human, or mcts. Supported MCTS parameters:
| Key | Meaning | Default |
|---|---|---|
iterations |
Iterations per move | 1000 |
exploration |
UCB1 exploration constant | 1.41 |
fpu |
First Play Urgency reduction | None |
widening |
Progressive widening constant | None |
rave |
RAVE constant (≈300–3000) | None |
parallel |
Enable parallel search | false |
workers |
Worker threads when parallel is true |
16 |
time_limit |
Seconds per move (stops earlier than iterations) |
None |
seed |
RNG seed for deterministic behaviour | None |
verbose |
Print search statistics each turn | false |
transposition / lookups / clear |
Toggle table usage and flushing | true |
Example: --player1 mcts:iterations=8000,rave=900,widening=12,parallel=1,workers=8.
Board and flow
--rings {37|48|61}selects the lattice size--blitzuses the Blitz marble counts (37-ring only)--games Nruns multiple games back-to-back--seed VALUEseeds the global RNG--move-duration SECONDScontrols animation pacing--statsprints aggregate win/loss and timing data
Logging and replay
--transcript-file [DIR]writes dictionary logs (defaults to current directory)--notation-file [DIR]writes official Zèrtz notation--replay PATHreplays a transcript, notation file, or Boardspace SGF--partialcontinues with AI play after the replayed sequence
Renderer
--headlessskips Panda3D for automation--highlight-choices {uniform|heatmap}visualises legal moves or search preference--show-coordsrenders axial coordinates on the board
game/loaders/sgf_loader.py reads the Boardspace.net variant of Smart Game Format. Player names, timestamps, and board sizes are preserved. Logs generated by Zèrtz 3D can be exported in transcript form or official notation for further analysis.
Tuning outputs land under data/tuning/ and can be inspected with scripts/analysis/visualize_tuning_results.py. Pareto plots (pareto_scatter_dark.png) and heatmaps (heatmap_dark_*.png) highlight the trade-offs between win rate, time per move, and the exploration/RAVE/FPU parameters. The profiling script in scripts/analysis/profile_mcts_features.py captures iterations per second while toggling search features such as RAVE, first-play urgency, and transposition tables.
main.py # CLI entry point
controller/ # Match coordination and scheduling
factory/ # Player/renderer wiring
game/ # Rule engine, players, loaders, utilities
renderer/ # Panda3D scene graph and text renderer
rust/ # hiivelabs_zertz_mcts PyO3 extension
scripts/analysis/ # Tuning, profiling, and visualisation scripts
shared/ # Cross-layer data structures and helpers
data/ # Reference docs, tuning outputs, SGF samples
tests/ # Pytest suites and integration checks
- 37 rings: 7×7 hex grid (standard)
- 48 rings: 8×8 hex grid
- 61 rings: 9×9 hex grid (uses ABCDEFGHJ coordinate scheme, skipping 'I')
Coordinate mapping for other board sizes lives alongside the visualisations in data/docs/visualizations/.
./rust-dev.shbuilds an editable debug wheel./rust-build.shproduces the optimised release builduv run python -c "import hiivelabs_zertz_mcts"sanity-checks the install
uv run pytest
uv run pytest -m "not slow" # skip long simulations
./generate_coverage_report.sh # HTML report under htmlcov/GNU Affero General Public License v3.0
Please open issues for bugs or missing documentation. Coordinated pull requests are welcome once the initial release stabilises; urgent fixes can be proposed sooner.