A modern C++ chess engine with a single command-line binary (euclid_cli), targeting an estimated 1500–2400 Elo playing strength, that supports:
- UCI mode (plug into chess GUIs)
- Move generation + perft/divide
- Classical evaluation
- Neural evaluation (native model or ONNX Runtime)
- Alpha-beta search with common time controls
- Self-play and dataset generation
- Built-in bench commands
This repository is source-first: you build the engine locally and run
euclid_cli.
- Requirements
- Download
- Build
- Test
- Run
- UCI mode (GUI integration)
- Command reference
- Examples
- Neural evaluation
- Datasets
- Benchmarks
- Stats for Nerds
- Troubleshooting
- CMake (modern CMake recommended)
- A C++ compiler with C++17 support (AppleClang/Clang/GCC)
If you plan to use the ort subcommands (e.g., eval ort, search ort, etc.), you need ONNX Runtime available to the build.
On macOS (Homebrew), a common approach is:
brew install onnxruntimeIf your environment is different, install ONNX Runtime using your platform’s preferred package manager or from upstream binaries.
Clone the repository:
git clone <REPOSITORY_URL>
cd euclid-engineReplace <REPOSITORY_URL> with your GitHub repository URL.
From the repository root:
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -jThe main executable will be:
build/euclid_cli
Run the full test suite:
ctest --test-dir build --output-on-failurePrint help:
./build/euclid_cliEuclid runs as a UCI engine when invoked with the uci subcommand:
./build/euclid_cli uciThis command starts an interactive loop and will wait for UCI commands on stdin (it does not “finish” by itself).
If you launched it in a terminal, you can type:
quit
Most GUIs let you configure an engine command with arguments.
- Engine command: path to
euclid_cli - Arguments:
uci
If your GUI does not support arguments, create a small wrapper script that runs euclid_cli uci and point the GUI to that script.
euclid_cli provides the following top-level commands:
Euclid CLI
Usage:
euclid_cli uci
euclid_cli perft <depth> [fen...]
euclid_cli divide <depth> [fen...]
euclid_cli eval [fen...]
euclid_cli eval nn <model_path> [fen...]
euclid_cli eval ort <model.onnx> [fen...]
euclid_cli search [nn <model_path> | ort <model.onnx>] [depth <N>] [nodes <N>] [movetime <ms>]
[wtime <ms> btime <ms> winc <ms> binc <ms> movestogo <N>]
[fen <FEN...>]
euclid_cli nn_make_const <out_path> <cp>
euclid_cli selfplay [nn <model_path> | ort <model.onnx>] [maxplies <N>] [depth <N>] [nodes <N>] [movetime <ms>]
[wtime <ms> btime <ms> winc <ms> binc <ms> movestogo <N>]
[fen <FEN...>]
euclid_cli dataset selfplay <out_path> [games <N>] [maxplies <N>] [include_aborted <0|1>]
[nn <model_path> | ort <model.onnx>] [depth <N>] [nodes <N>] [movetime <ms>]
[wtime <ms> btime <ms> winc <ms> binc <ms> movestogo <N>]
[fen <FEN...>]
euclid_cli bench perft <depth> [iters <N>] [fen <FEN...>]
euclid_cli bench search [nn <model_path> | ort <model.onnx>] [iters <N>] [depth <N>] [nodes <N>] [movetime <ms>]
[wtime <ms> btime <ms> winc <ms> binc <ms> movestogo <N>]
[fen <FEN...>]
Notes:
- If FEN omitted, uses startpos.
- 'bench search' reports time + NPS based on SearchResult.nodes.
Start position, depth 5:
./build/euclid_cli perft 5Perft on a specific FEN:
./build/euclid_cli perft 5 "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"./build/euclid_cli divide 4./build/euclid_cli evalSearch the start position for a fixed depth:
./build/euclid_cli search depth 8Search with a move time budget (milliseconds):
./build/euclid_cli search movetime 1000Search with time controls:
./build/euclid_cli search wtime 60000 btime 60000 winc 0 binc 0 movestogo 40Search a custom FEN:
./build/euclid_cli search depth 10 fen "<FEN_STRING>"Run a lightweight self-play game (depth-limited example):
./build/euclid_cli selfplay depth 2 maxplies 40Generate a small binary dataset:
./build/euclid_cli dataset selfplay /tmp/euclid_selfplay.bin games 2 maxplies 40 include_aborted 1 depth 2Euclid supports two neural evaluation backends:
- Native model (
nn) - ONNX Runtime (
ort)
./build/euclid_cli eval nn <model_path>./build/euclid_cli eval ort <model.onnx>Native model:
./build/euclid_cli search nn <model_path> movetime 1000ONNX Runtime:
./build/euclid_cli search ort <model.onnx> movetime 1000Native model:
./build/euclid_cli selfplay nn <model_path> movetime 50 maxplies 200
./build/euclid_cli dataset selfplay out.bin games 100 nn <model_path> movetime 50ONNX Runtime:
./build/euclid_cli selfplay ort <model.onnx> movetime 50 maxplies 200
./build/euclid_cli dataset selfplay out.bin games 100 ort <model.onnx> movetime 50dataset selfplay writes a binary dataset file.
- For the exact on-disk format and record layout, see the dataset definitions in the source tree (e.g., the dataset header/record structures in the
include/headers).
Recommended workflow:
-
Generate data via self-play:
euclid_cli dataset selfplay <out_path> ...
-
Train a model externally.
-
Use the resulting model file with:
eval nn <model_path>/search nn <model_path>- or export to ONNX and use
eval ort <model.onnx>/search ort <model.onnx>
./build/euclid_cli bench perft 6 iters 10./build/euclid_cli bench search iters 10 movetime 100Neural search benchmark:
./build/euclid_cli bench search ort <model.onnx> iters 10 movetime 100- Core representation: bitboards, precomputed attack tables for fast move generation.
- Hashing: Zobrist hashing for position keys.
- Search framework:
- iterative deepening alpha–beta
- transposition table (TT)
- quiescence search (tactical stabilization)
- time controls and hard limits (depth/nodes/movetime/time management)
- null-move pruning
- Move ordering (typical stack):
- TT move / PV move preference
- captures (with a static exchange/tactical bias)
- history/killers style heuristics (where applicable)
- Draw rules: repetition and 50-move style rules.
- Neural evaluation support:
- custom compact NN model format (
eval nn) - constant-model generator for integration (
nn_make_const) - optional ONNX Runtime path (
eval ort)
- custom compact NN model format (
- Self-play tooling: engine-driven self-play producing game trajectories.
- Supervised dataset generator:
- writes WDL labels from side-to-move POV (
+1/0/-1) encode_12x64feature spec (12×64 piece planes + STM scalar)
- writes WDL labels from side-to-move POV (
That is expected. UCI mode is an interactive protocol loop; it waits for commands from stdin/a GUI. Type quit to exit when running manually.
If you enabled or built with ONNX Runtime support, ensure ONNX Runtime is installed and discoverable by your build system.
Common remediation steps:
- Install ONNX Runtime (e.g.,
brew install onnxruntimeon macOS). - Clean rebuild:
rm -rf build
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -jrm -rf build
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
ctest --test-dir build --output-on-failureCheck out my repository for more projects! :)