github.com
2026-07-11 (late) — Batch 6: Lazy SMP — +147 Elo at 4 threads (LOS 100%)
Root-parallel search replaced by true Lazy SMP with a persistent thread pool: helper threads search the same root position at staggered depths and coordinate purely through the shared lock-free transposition table — no root splitting, no shared alpha, zero spawn cost (helpers sleep on a condition variable between moves). Measured 4-threads-vs-4-threads against the previous build: 69.4% over 116 games = +147 Elo ±61, LOS 100%, zero time losses. NPS 420k → 1.73M (×4.1), one extra ply at equal time. Day total across all validated batches: ~+550 Elo; with 4 threads the engine plays at roughly ~2300 on the Stockfish UCI_Elo scale.
2026-07-11 — From educational toy to measured competitor: ~+400 Elo in validated batches
The engine went through a scientific improvement campaign: every batch of search changes validated with cutechess-cli SPRT matches (fixed openings, tc=8+0.08, level 10). Batch 1 (PVS at all nodes, aspiration widening, history malus, SEE pruning): ~+200 Elo. Batch 2 (lazy legality, lock-free 4-way cache-line TT buckets with prefetch, AVX2): ~+120 Elo, NPS +38%. Batch 3+4 (staged move picker with lazy SEE, TT in quiescence, timeout-latch abort, endgame mop-up and drawish scaling, O(1) occupancy restore): +35 Elo (LOS 98.7%). Batch 5 (continuation history, TT generation aging): +8 Elo, search tree −15% at equal depth. Absolute calibration vs Stockfish 17 at fast time control: ~2160 Elo on the SF UCI_Elo scale — holds SF 2100 at 80% score. Next stop: small self-trained NNUE net and Texel tuning.

A competitive chess engine written in modern C++20 with pure WinAPI interface. Built on a deliberately contrarian architecture — a cache-line-aligned mailbox board instead of magic bitboards — and optimized with HFT-style engineering: lock-free data structures, cache-conscious memory layout, and latency hiding via prefetch. Small enough to read, strong enough to compete.
Most chess engines are either too simple (lacking modern techniques) or too complex (thousands of files, external dependencies). This project implements the full modern search arsenal — PVS, singular extensions, ProbCut, aspiration windows, lock-free transposition table — in clean, readable code, while refusing to copy the standard bitboard blueprint.
This is a case study in building a high-performance chess system using Data-Oriented Design (DOD) principles, maximizing computational throughput while drastically reducing binary size and eliminating all external library dependencies. Every strength-affecting change is validated the scientific way: engine-vs-engine matches under cutechess-cli with fixed openings and time controls.
staticEval >= beta, hoisted before move generation, disabled in endgames (zugzwang)log(depth) × log(moveIndex) / 2.25, re-search on alpha improvement(keyXorData, data) atomic pair; zero mutexes in the hot pathalignas(64) groups of four entries; any probe touches exactly one cache line_mm_prefetch issued on the child's Zobrist key immediately after making a moveucinewgame resets-depth² penaltyComplete NNUE (Efficiently Updatable Neural Network) infrastructure is on board: HybridEvaluator (Classical/NNUE/Auto), inference engine with incremental accumulators, HalfKP feature extraction, SIMD dense layers, and a .nnue weight loader. Currently runs in Classical mode; a small self-trained net is the next roadmap item.
Every batch of search changes is validated with cutechess-cli matches (fixed opening set, tc=8+0.08, 1 thread, level 10). The SPRT harness ships with the repo (testing/run_match.ps1).
| Batch | Changes | Result |
|---|---|---|
| 1 | PVS at all nodes, TT-probe-before-pruning, NMP eval gate, aspiration widening, history malus, SEE pruning | ~+200 Elo (76% over 194 games) |
| 2 | Lazy legality, 4-way cache-line TT buckets, TT prefetch, NMP before movegen, AVX2 codegen | ~+120 Elo; NPS +38% |
| 3+4 | Staged move picker with lazy SEE, TT in quiescence, 50-move rule in search, timeout-latch abort, endgame knowledge, O(1) occupancy restore | +35 Elo (LOS 98.7%, 410 games) |
| 5 | Continuation history, TT generation aging, killers ply+2 clearing | +8 Elo; search tree −15% at equal depth |
| 6 | Lazy SMP: persistent helper pool, whole-root search at staggered depths, TT-only coordination | +147 Elo at 4 threads (69.4% over 116 games, LOS 100%) |
Absolute calibration (single thread): ~2160 Elo on the Stockfish UCI_Elo scale (200-game match vs SF 17 @ 2200, plus a descending ladder — holds SF 2100 at 80%). With 4 threads: roughly ~2300 on the same scale.
Batch 3 taught the most valuable lesson of the project: three consecutive test matches showed a "regression" that turned out to be time forfeits, not chess. A gated clock check let the search overrun its budget by hundreds of milliseconds after timeout. Moral: check the match logs for loses on time before touching your evaluation.
Download chess.zip or grab the latest GitHub release (Chess.7z, password: github.com):
Chess_x64.exe - 64-bit GUI version (~500 KB)Chess_x64_minSize.exe - 64-bit minimal (~200 KB)Chess_x86.exe / Chess_x86_minSize.exe - 32-bit versionsChessEngineUCI_x64.exe - UCI console engine for Arena / Cutechess / FritzJust download and run - no installation needed!
Requirements: Windows 10+, Visual Studio 2022 or newer with C++20 support (tested with VS 2026, v145 toolset).
git clone https://github.com/wesmar/Chess.git
cd Chess
build_all.bat
Binaries land in bin\. Builds are reproducible (/BREPRO). build_uci.bat builds just the UCI engine (x64 Release).
ChessEngineUCI_x64.exe is a standard UCI console engine — drop it into any UCI-compatible GUI.
| Option | Range | Description |
|---|---|---|
Threads |
1..64 | Parallel root-search workers |
Hash |
1..4096 MB | Transposition table size; 128-256 MB recommended |
Level |
1..10 | AI strength; use 10 for tournament play |
Move Overhead |
0..5000 ms | Safety margin for GUI/network latency |
The engine emits live info depth/score/nodes/nps/time/pv after every completed iteration. Full Arena setup walkthrough: arena.txt.
Why mailbox instead of bitboards? A deliberate architectural bet. Raw move generation is somewhat slower than magic bitboards, but the entire board state lives in one L1 cache line, make/unmake is trivially cheap, and the engine wins the time back where it matters more: move ordering, pruning quality, and a cache-conscious transposition table. Strength is validated by measurement, not by copying the standard blueprint.
Why 1-byte pieces? No polymorphism, no v-tables, bit masking for properties, predictable layout — the whole board fits in L1.
Why hand-crafted evaluation (for now)? Good strength at minimal size, fast per-node cost, easy to understand and modify. NNUE comes next, on the engine's own terms.
MIT License — see LICENSE.
Marek Wesołowski (WESMAR)
📧 marek@wesolowski.eu.org
🌐 kvc.pl
Made with ♟️ in C++20 | AVE!