Endspiel /ˈɛnt.ʃpiːl/ n. (German) 1. the final, decisive game. 2. the final phase of a chess game.
A UCI chess engine written in Rust. Bitboards, move generation, search, NNUE inference, and the UCI front end are hand-written — no external chess libraries. The eval is a layer-stacked, state-aware HalfKP net trained mostly on Endspiel's own self-play, not a borrowed network file. It ships as a self-contained binary (embedded net, Pi / Android / AVX-512 builds).
Rust NNUE bots on Lichess are common. What is less common is a from-scratch chess stack, a modern king-bucketed net with castling and en passant in the features, and packaging that is meant to be installed rather than compiled on a VPS. Strength sits well above human play and below the top of the engine lists; details in ABOUT.md.
- Built from scratch — move generation, search, NNUE inference, and the
UCI layer are hand-rolled in Rust with no external chess libraries. Net
training uses Bullet; Syzygy probing
uses
pyrrhic-rs. Those are the only third-party pieces in the pipeline. - Full UCI compliance — works in any UCI GUI (Arena, CuteChess, Fritz, Banksia, Scid, …)
- Chess960 — Fischer Random / Freestyle: X-FEN and Shredder-FEN,
UCI_Chess960king-takes-rook castling - NNUE evaluation (default) — state-aware HalfKP 785×32→(1024 pairwise 512)×2→16→32→1 (8 material-keyed output buckets), with castling rights and en passant in the input. Trained mainly on Endspiel self-play (search scores as labels); the current mix also uses a public eval dump as one source. The net is embedded in the binary. Older piece-only nets still load.
EvalFilenets may use dense L1/L2 widths other than 16/32 (up to 64), taken from the ESPNNUE2 header. - HCE fallback — tapered hand-crafted evaluation (
UseNNUE=false) with pawn hash, mobility, king safety, pawn structure, threats, space, and endgame scaling - Modern search — alpha-beta + PVS with iterative deepening and
aspiration windows, null move, reverse futility, futility, razoring,
ProbCut, SEE pruning, LMR, LMP, IIR, singular and passed-pawn extensions,
1- and 2-ply continuation history, capture history, and multi-facet
correction history (pawn, non-pawn, minor/major, and continuation keys).
History and correction tables persist across moves within a game
(reset on
ucinewgame) - Pondering — thinks on the opponent's time (
go ponder/ponderhit); enable via thePonderUCI option in your GUI. A legal ponder move is still advertised when the PV is only one ply (book, tablebase, forced lines) - Multi-threading — Lazy SMP with depth diversity (
ThreadsUCI option) - MultiPV — up to 256 principal variations for analysis (
MultiPVUCI option) - Syzygy tablebases — WDL/DTZ probing (up to 7-man) via
pyrrhic-rs; at the root the engine stays on win- or draw-preserving moves instead of drifting into a loss - Opening books — load Polyglot
.bin, EPD (.epd, withbmopcodes), or PGN (.pgn) at runtime; format is auto-detected by extension - WDL output — optional
wdl W D Lannotation on eachinfoline (UCI_ShowWDL), with the win/draw/loss mapping fit per net - Contempt and configurable time management (
Move Overhead,Slow Mover) - Performance — runtime-dispatched AVX-512 and AVX2 inference on x86-64, NEON on AArch64, and a scalar fallback; selected release builds also use profile-guided optimisation and fat LTO
- Cross-platform — Linux x86_64/ARM64, Windows x86_64/ARM64, macOS Apple Silicon
- Self-contained binary — no runtime dependencies, no external net file
Prebuilt binaries are on the Releases page. Current release: v1.6.0.
x86-64 ships in three micro-architecture tiers (each faster than the one
below, all but -v4 profile-guided-optimised). Pick the highest your CPU
supports:
| Tier | Linux | Windows | CPU requirement |
|---|---|---|---|
v4 |
endspiel-linux-x64-v4 |
endspiel-win-x64-v4.exe |
AVX-512 — AMD Zen 4/5, Intel Skylake-X / Ice Lake+ (typically 30–60% faster NNUE eval) |
v3 (default) |
endspiel-linux-x64-v3 |
endspiel-win-x64-v3.exe |
AVX2 — Intel Haswell (2013)+, AMD Zen / Excavator+ |
v2 |
endspiel-linux-x64-v2 |
endspiel-win-x64-v2.exe |
SSE4.2 + POPCNT — pre-2013 mainstream + recent low-end Intel (Gemini/Jasper Lake) without AVX2 |
Other platforms:
| Platform | Binary | Notes |
|---|---|---|
| macOS Apple Silicon | endspiel-mac-arm64 |
apple-m1, PGO-optimised |
| Windows ARM64 | endspiel-win-arm64.exe |
generic ARM64 |
| Raspberry Pi 5 | endspiel-linux-arm64-pi5 |
cortex-a76, fat LTO + PGO; needs Raspberry Pi OS (Trixie / Debian 13) or newer — glibc ≥ 2.39 |
| Android arm64 | endspiel-android-arm64.apk |
arm64-v8a, minSdk 24 — installs like an app; pick "Endspiel" as a UCI engine in DroidFish / Chess for Android |
Picking an x86-64 build. -v3 (AVX2) is the safe default — it runs on
essentially any CPU sold since ~2013. Go up to -v4 if your CPU has AVX-512
(AMD Zen 4/5, recent Intel) for a sizeable NNUE-eval speedup; drop to -v2
only for older or low-end (no-AVX2) hardware. If a build aborts immediately with an illegal-
instruction crash, your CPU lacks that tier's instructions — step down one
tier. On Linux you can check support with
lscpu | grep -oE 'avx512f|avx2|sse4_2' (highest match wins).
Raspberry Pi 5. Any RAM tier runs the engine; hash size is the only
thing that scales with it. Set Hash in your GUI rather than relying on
the 256 MB default:
| Pi 5 RAM | Recommended Hash |
Notes |
|---|---|---|
| 4 GB | 512–1024 MB | usable for blitz/rapid; leave ≥2 GB for the desktop/browser |
| 8 GB | 2048–4096 MB | sweet spot — full Pi-5 strength at all time controls |
| 16 GB | 4096–8192 MB | only worth it for deep analysis or running other workloads alongside |
Expect NPS roughly 5–10× lower than a modern x86 desktop, so plan for a noticeable Elo drop at fixed time controls and compensate by giving the engine more thinking time. Active cooling is recommended: under sustained engine load the SoC will thermally throttle without a fan.
Endspiel is a command-line program used through a UCI-compatible chess GUI (Arena, CuteChess, Fritz, Banksia, etc.). Point the GUI at the binary — no further setup is required.
Bench — runs a fixed-depth search across a small set of positions and prints the total node count, elapsed time, and NPS. Useful as a sanity check that the binary runs end-to-end:
./endspiel bench # default depth 14
./endspiel bench 18 # deeper, for performance tuningmacOS users — run this once from a terminal before pointing a chess GUI at the binary. The release binaries are not code-signed, so macOS Gatekeeper will quarantine them. If that's the case, this command surfaces the error clearly instead of leaving the GUI to silently fail to launch the engine. To clear the quarantine flag:
chmod +x endspiel-mac-arm64 xattr -d com.apple.quarantine endspiel-mac-arm64Then re-run
./endspiel-mac-arm64 bench— once you get a Nodes/NPS line, the GUI will also be able to launch it.
| Option | Default | Description |
|---|---|---|
Hash |
auto (RAM + threads) | Transposition table size in MB |
Threads |
auto (cores) | Search threads |
Move Overhead |
20 | Time safety margin in ms |
Slow Mover |
100 | Time usage scaling (%) — >100 thinks longer, <100 plays faster |
Ponder |
false | Think on the opponent's time; the GUI toggles this and drives go ponder / ponderhit |
Contempt |
20 | Draw avoidance in centipawns |
SingularExt |
1 | Singular extension: 0 = off, 1 = conservative, 2 = aggressive |
UseNNUE |
true | Use NNUE evaluation; false falls back to HCE |
EvalFile |
(embedded) | Path to an external .nnue / quantised.bin net |
BookFile |
(disabled) | Path to an opening book: Polyglot .bin, EPD .epd, or PGN .pgn (auto-detected by extension) |
SyzygyPath |
(disabled) | Path to Syzygy tablebase directory |
MultiPV |
1 | Number of principal variations to report (1–256) |
OpeningVariety |
0 | Opening spice: 0 = off; otherwise pick at random among MultiPV moves within this many centipawns of the best, for the first 8 plies of standard chess (ignored in Chess960) |
UCI_ShowWDL |
false | Append wdl <win> <draw> <loss> (0–1000) to each info line |
Set BookFile or SyzygyPath to a valid path to enable; clear to disable. No separate toggle is needed.
Hash— increase for long time controls or analysis; watchhashfullin engine output (permille, so 950 = 95%). The default adapts to the machine: ~128 MB per search thread (more threads fill the table faster), capped at ~1/16 of available RAM, floor 16 MB. So it grows with both core count and RAM, and tracks an explicitThreadssetting.Threads— Lazy SMP; scaling is sub-linear. Stick to physical core count. On Linux/Android the default is the performance-core count (the top CPU-frequency tier), which avoids the slow LITTLE cores and the thermal throttling they invite; elsewhere it'smin(available, 16).EvalFile— load an alternate net at runtime without rebuilding. Clear to revert to the embedded net.SyzygyPath— WDL/DTZ probing for up to 7-man endgames. Multiple directories::on Linux/macOS,;on Windows.OpeningVariety— only affects the first 8 plies of standard chess; 0 (default) always plays the best move. Chess960 always plays the best move, and also skips the early-opening time throttle so the first moves use the normal clock budget.
Fritz 20 (Windows): Fritz manages Syzygy and opening books through its own systems. Set the tablebase path in Fritz's settings — it forwards it to Endspiel automatically. To let Endspiel use its own
BookFile, disable Fritz's opening book in the match settings. Verify loading via View → Engine Output: a successful load printsinfo string BookFile loaded from '...'.
Requires Rust 1.98.0+.
cargo build --release
# binary: target/release/endspielCross-compile a standalone CLI binary for 64-bit Android. It speaks UCI over
stdin/stdout like the desktop build and runs under Termux or via adb shell.
rustup target add aarch64-linux-android
cargo install cargo-ndk
export ANDROID_NDK_HOME=/path/to/android-ndk # r23+
scripts/build-android.sh
# binary: target/aarch64-linux-android/release/endspielOn-device the engine auto-tunes its defaults: Threads is the performance-core
count of the SoC, and Hash is ~128 MB per such core, capped at ~1/16 of
available RAM (floor 16 MB). Both remain overridable via UCI setoption.
The raw binary is only convenient under Termux or adb — a non-rooted device
can't exec a binary off shared storage. For normal use, install the Open
Exchange (OEX) engine APK instead (endspiel-android-arm64.apk on the release
page): it installs like an app and is auto-discovered as a UCI engine by GUIs
such as DroidFish and Chess for Android. The APK wraps the same binary; the
release workflow builds it, and android/oex/ documents building it by hand.
DroidFish doesn't list Endspiel? DroidFish only scans for installed OEX engines when it has storage permission. Grant it under Settings → Apps → DroidFish → Permissions → Files and media (Storage) → Allow, then reopen the engine selector (⋮ → Set engine…) — Endspiel will appear next to Stockfish and CuckooChess.
GPL-3.0-or-later — see LICENSE.
Third-party software, tablebase/training tooling, and technique inspirations are documented in CREDITS.md.