GitHub release archive (Chess.7z) password: github.com

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.

♔ Modern Chess Engine

Chess Engine Screenshot

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.

Windows
C++
License
Build

Why This Project?

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.

Key Features

🎯 Extreme Optimization

  • No CRT version: ~ 200 KB
  • With CRT version: ~ 500 KB
  • Zero external dependencies - no Qt, SDL, or .NET
  • Portable: Even runs in Windows Recovery Environment (WinRE)!

🧠 Advanced Search Algorithms

Core Search Framework

  • Principal Variation Search (PVS) at every node - first move searched with a full window, all others with a null-window scout; re-search only on fail-high
  • Iterative Deepening with adaptive time management and a timeout latch (gated clock check + per-node abort flag, bounded overshoot)
  • Aspiration Windows with progressive widening - each iteration starts with a ±30cp window around the previous score; doubled on fail-low/fail-high
  • Quiescence Search with TT probing/stores - eliminates horizon effect
  • Lazy Legality Checking - interior nodes search pseudo-legal moves and verify king safety only when a move is actually made

Pruning & Extensions

  • Null Move Pruning (NMP) - gated on staticEval >= beta, hoisted before move generation, disabled in endgames (zugzwang)
  • ProbCut - captures-only shallow search at raised beta with SEE filter
  • Late Move Reduction (LMR) - logarithmic formula log(depth) × log(moveIndex) / 2.25, re-search on alpha improvement
  • Late Move Pruning, Futility Pruning, Reverse Futility Pruning, Delta Pruning, Mate Distance Pruning
  • SEE Pruning in Main Search - losing captures skipped at shallow depths
  • Check Extensions (capped) and Singular Extensions - TT move extended when a reduced search proves it is the only good move

Lock-Free Transposition Table

  • Hyatt XOR-key validation - 16-byte entries with (keyXorData, data) atomic pair; zero mutexes in the hot path
  • 4-way cache-line buckets - alignas(64) groups of four entries; any probe touches exactly one cache line
  • TT Prefetch (HFT-style latency hiding) - _mm_prefetch issued on the child's Zobrist key immediately after making a move
  • Generation aging - stale entries from previous searches evicted first
  • TT reuse across moves - table preserved within a game; only ucinewgame resets

Move Ordering

  • Staged move picker - cheap SEE-free scoring plus lazy selection; SEE computed only for a capture actually about to be searched (NPS +20%)
  • TT move, MVV-LVA, killers (two per ply), countermoves, history with malus - misordered quiets sink fast via -depth² penalty
  • Continuation history - quality of a (piece,to) reply to the opponent's previous (piece,to); much sharper signal than from/to history alone

Parallel Search

  • Root-Parallel Search - multi-threaded with dynamic load balancing and shared alpha
  • Thread-Local Heuristics - separate killer/countermove tables per thread

⚡ High-Performance Memory Architecture

  • 64-byte Mailbox (Cache-Aligned) - entire board state in a single L1 cache line
  • Bitboards for occupancy only - fast sliding piece queries without magic-bitboard lookup tables (~800 KB saved)
  • Stack-allocated MoveList - fixed 256-move array, zero malloc() in the hot path
  • PieceList per color - O(1) piece iteration, swap-with-last removal
  • Incremental everything - Zobrist key, MG/EG scores, pawn bitboards; O(1) occupancy snapshot-restore in UndoMove

📊 Hand-Crafted Evaluation (HCE)

  • Tapered evaluation - separate MG/EG piece-square tables, interpolated by material phase
  • King safety - castling bonus, pawn shield, open-file penalty (O(1) via pawn bitboards)
  • Piece activity - mobility, center control, tropism, exposed-queen penalty
  • Pawn structure - isolated, doubled, backward; passed pawns with king-distance and rook-behind bonuses
  • Endgame knowledge - mop-up evaluation for bare-king conversion, dead-draw detection, opposite-colored-bishop scaling

🤖 NNUE Infrastructure (Prepared)

Complete 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.

Measured Strength

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

Absolute calibration: ~2160 Elo on the Stockfish UCI_Elo scale (200-game match vs SF 17 @ 2200, plus a descending ladder — holds SF 2100 at 80%).

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.

Quick Start

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 versions
  • ChessEngineUCI_x64.exe - UCI console engine for Arena / Cutechess / Fritz

Just download and run - no installation needed!

Building from Source

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).

UCI Engine (Arena / Cutechess / Fritz)

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.

GUI Features

  • Unicode rendering - clean chess pieces (♔♕♖♗♘♙) using native WinAPI vector rendering
  • Full chess rules - castling, en passant, promotion; PGN save/load; undo/redo
  • Game modes - Human vs Human, Human vs AI, AI vs AI; 10 difficulty levels
  • Keyboard shortcuts: Ctrl+N new game, Ctrl+Z/Y undo/redo, F flip board, L legal moves, C coordinates

Architecture Decisions

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.

Roadmap

  • [ ] Small self-trained NNUE net (HalfKP-style, quantized, ~1-4 MB)
  • [ ] Texel tuning for PST and eval weights
  • [ ] Lazy SMP with a persistent thread pool + per-thread history tables
  • [ ] Syzygy tablebase support, MultiPV, real pondering

License & Author

MIT License — see LICENSE.

Marek Wesołowski (WESMAR)
📧 marek@wesolowski.eu.org
🌐 kvc.pl


Made with ♟️ in C++20 | AVE!