I like building things from scratch to understand how they work. Right now that means a chess engine in C++ — the search, the board, the evaluation network, the testing rig, all of it — and I'll automate anything that annoys me enough.
A UCI chess engine written from nothing in C++17. No engine libraries, no borrowed nets: bitboards with magic sliders, a principal-variation alpha-beta search with the modern pruning stack, Lazy SMP, and an NNUE evaluation trained entirely on the engine's own self-play games.
How strong. Measured with a 400-game gauntlet against engines with published CCRL ratings, one thread, blitz:
| Opponent | CCRL | IxEngine |
|---|---|---|
| Halogen 10 | 3194 | 56% |
| Weiss 2.0 | 3265 | 43% |
| Zahak 10.0 | 3292 | 37% |
| Alexandria 3.5 | 3321 | 24% |
That works out to ≈3198 (±24) on the CCRL blitz scale. The number has moved from ~3090 to ~3200 over the summer, one SPRT-gated patch at a time — every change, kept or thrown away, is written up in TESTING.md.
What's inside.
board bitboards · fancy magic sliders · Zobrist keys · perft-exact movegen
search PVS · aspiration · null move · LMR · RFP/LMP/SEE pruning
singular extensions with multicut + double extensions
TT move → captures → killers → countermove → continuation history
threads Lazy SMP, staggered helper depths, weighted best-move vote
eval 768→512 NNUE, SCReLU, 8 output buckets, AVX2, compiled into the exe
bootstrapped over two self-play generations (+240 Elo over the hand eval)
testing self-play SPRT at two time controls · CCRL-anchored rating gauntlet
How it got here.
hand eval, bitboards, PVS ─► NNUE gen1 (self-play, +124) ─► gen2 (+237)
─► singular extensions, time management, countermoves, continuation history
─► embedded net, tuned extensions, history fix (+74), SMP voting ─► 1.1
My first proper systems project after a lot of Python, and easily the one I've had the most fun with.