Draft — v0.2.0 (alpha) · 2026-06-20
Repository: https://github.com/carlok/labzero · License: MIT
We present labzero, an original Rust chess engine built as a research baseline for LLM-assisted, verifiably independent engine development. The alpha release (v0.2.0) implements full FIDE-legal move generation, negamax search with alpha–beta pruning, and a hand-written material + piece-square evaluation. Strength is intentionally modest: the primary claims are legality, UCI compliance, reproducible verification, and originality—not competitive Elo.
Independent oracles (perft, fuzz, cross-engine legality checks) and a 200-game tournament gauntlet show zero illegal moves. A host benchmark ladder against strength-limited Stockfish 18 brackets bullet performance Elo at ≈ 1700–1750 on this protocol (see §5). We document a concrete beta roadmap adding classical search and evaluation techniques, each measurable on the same ladder without copying existing engine source.
Modern top engines (Stockfish, Leela Chess Zero, and derivatives) dominate computer-chess benchmarks. For research on how engines are built—especially workflows where large language models propose patches—a useful baseline is:
- A small, readable core with no copied evaluation or search code.
- Independent verification that behavior matches chess rules, not another engine’s bugs.
- A fixed strength ladder so each future change has a quantifiable effect.
labzero alpha satisfies (1)–(3). It is “weak but legal”: suitable for human play, Lichess-bot deployment, and gauntlet survival, while leaving substantial headroom for beta improvements (§7).
The engine core (engine/) must not copy or closely imitate source from Stockfish, Leela, Ethereal, or similar projects. Allowed: public rule specs (FEN, UCI, perft tables), general algorithms (negamax, bitboards, PSTs written for this project), and Stockfish as a binary opponent only. See originality_policy.md.
~1,700 lines of Rust across 14 modules. Bitboard + mailbox representation; make/unmake with Zobrist hashing (hash computed but not yet used for search).
| Module | Role |
|---|---|
movegen.rs |
Legal moves: pins, castling, en passant, promotions |
search.rs |
Negamax, α–β, iterative deepening (depth cap ~6 by default) |
eval.rs |
Material + original piece-square tables (not copied from SF/LC0) |
uci.rs |
UCI loop, go time/depth handling |
time.rs |
Budget: time / movestogo + increment |
Negamax with α–β window
Mate scores use a large constant
Not present in alpha: quiescence, transposition table, null-move pruning, late-move reduction (LMR), aspiration windows, killer/history heuristics, SEE.
Move ordering: captures and promotions first (MVV-LVA-style priority without explicit victim values).
Static score for side to move:
where
All automated gates run in Podman for reproducibility (reproducibility.md).
| Gate | Command | Alpha result |
|---|---|---|
| Smoke CI | ./scripts/podman/ci |
PASS |
| Deep verify | ./scripts/podman/verify-deep |
Perft d1–6, 200-game random fuzz, cozy/shakmaty cross-check |
| Gauntlet | ./scripts/podman/gauntlet |
200 games aggregate, 0 illegal moves / crashes |
| UCI dry-run | ./scripts/podman/bot --dry-run |
20 plies, legal |
Perft anchor (startpos, depth 6):
Human GUI QA (Banksia / similar) remains operator-driven; see human_play_checklist.md.
Script: ./scripts/host-benchmark.sh
-
Opponent: Stockfish 18 binary,
UCI_LimitStrength=true,UCI_Elo = E_{\text{SF}},Skill Level = 0 - Time control: 1+0 bullet (~1 s/move per side via engine time budget)
-
Sample:
$N = 32$ games, colors alternate -
Artifacts: incremental
.txtlog +.pgnper run (strength/)
Let
This is not a FIDE or CCRL rating—it maps a single opponent’s limited-Elo knob to a score percentage. Use only for relative comparisons on the same protocol.
Binary search on
1320 → 89.1% → next 2000
2000 → 17.2% → next 1800 (perf Elo ≈ 1727)
Reference context: CCRL 40/15 lists hobby engines (Micro-Max ~1869, CDrill 1800 ~1795) at different time controls; bullet SF-Elo numbers are not directly comparable to CCRL.
| Round | Score (W–L–D) |
|
Status | |||
|---|---|---|---|---|---|---|
| 1 | 1320 | 32 | 27–2–3 | 89.1% | ≈ 1520+ | complete |
| 2 | 2000 | 32 | 2–23–7 | 17.2% | ≈ 1727 | alpha |
| Alpha |
Beta |
Beta artifacts | |
|---|---|---|---|
| 1320 | 89.1% | 93.8% | benchmark_20260620T214301Z |
| 1800 | — | 51.6% (≈1812 perf) | benchmark_20260620T221648Z |
| 2000 | 17.2% | 34.4% (≈1888 perf) | benchmark_20260620T230310Z |
Interpretation: Beta adds qsearch, TT ordering, null move, LMR, SEE, and tapered eval. Vs SF@2000, score roughly doubled (17% → 34%); vs SF@1800 ≈ 50% → performance Elo ≈ 1800–1900 on this protocol. Gauntlet: 0 illegal.
Detailed logs: strength/ladder.md.
- An LLM-iterated workflow can produce a self-contained, UCI-compliant engine that survives automated tournaments.
- Independent verification catches legality bugs before human or Lichess exposure.
- A strength ladder turns “it feels stronger” into reproducible numbers.
- Competitive play vs humans or full-strength engines.
- Optimality of evaluation or search.
- Transfer of bullet SF-Elo to CCRL 40/15 or Lichess rating pools.
Engines like Micro-Max (~137 lines C, CCRL ~1869 at 40/15) implement quiescence and pruning tricks labzero alpha lacks. The gap is search quality per node, not movegen correctness. That gap is the beta target.
Beta keeps the originality policy: implement standard algorithms from textbooks and papers, not transcribe Stockfish source. Each milestone re-runs ./scripts/host-benchmark.sh at fixed ./scripts/podman/gauntlet (illegal-move count must stay 0).
- Finish round 2 (
$E_{\text{SF}} = 2000$ ,$N = 32$ ) - B1a quiescence — implemented
- B1b transposition table — implemented (ordering; mate-aware store)
- B2a null move + B2b LMR — implemented
- B2c killer/history/SEE — implemented
- B3a tapered mg/eg eval — implemented
- B3b structure helpers — in
eval.rs(disabled at runtime for bullet NPS; tune in B4)
Exit: Bracketed performance Elo ±100 on 1+0 protocol.
Extend leaf evaluation with capture/promotion/check continuations until quiet:
Acceptance: measurable lift vs SF@1800; fewer blunders in PGN tail positions; gauntlet still 0 illegal.
Use existing Zobrist board.hash as key; store (depth, score, flag, best_move).
Replace depth-0 cutoff:
Acceptance: node count drops at same depth; same or better score vs alpha at equal time; memory cap documented (e.g. 64–256 MB).
If not in check and
Acceptance: depth +1 at same time on benchmark positions; no regression in gauntlet illegal count.
For move index
Acceptance: improved nodes/sec; ladder score vs SF@2000 increases.
- Killer moves: two slots per ply for quiet moves that caused cutoffs.
- History heuristic: bonus
history[side][from][to]on cutoff. - SEE (static exchange eval): order captures by estimated gain.
Combined ordering key (example):
Acceptance: fewer nodes to fixed depth on perft-with-search smoke positions.
Split PSTs and phase:
with
Acceptance: fewer eval-blunders in king-and-pawn endings in self-play PGN review.
Add original terms (tuned on labzero self-play, not SF tables):
- Doubled/isolated/backward pawns
- Bishop pair, rook on open/semi-open file
- King safety (pawn shield, open files near king)
- Mobility (legal move count per piece type, capped)
Acceptance: improved
| Item | Description | Acceptance |
|---|---|---|
| Aspiration windows |
|
Fewer re-searches at same depth |
| Iterative deepening polish | PV move first on next depth | Higher depth within time budget |
| Time management | Soft stop, panic margin, optional ponder | Stable 1+0 and 3+2 gauntlet |
| Bitboard movegen speed | Precomputed attacks, less cloning in search | Higher NPS; no rule changes |
| Opening book (optional) | Small original EPD book, UCI ownbook
|
Diversity in gauntlet; off by default for ladder |
-
./scripts/podman/ci+ verify-deep + gauntlet 200 — all PASS - Strength ladder: report
$\Delta p$ per phase vs alpha baseline - Version 0.3.0 (beta), CHANGELOG, updated submission pack
- Live Lichess bot (lichess-bot + host binary): 5+ rated games logged
- Paper revision: beta draft with ablation table
| Metric | Alpha | Beta target |
|---|---|---|
| Illegal moves (gauntlet 200) | 0 | 0 |
| Performance Elo (1+0, bracketed) | ~1600–1800 | +200–400 (hypothesis) |
| Search depth @ 1 s (midgame) | ~6 | ≥ 8–10 with TT + pruning |
| Originality audit | pass | pass (no SF/LC0 source) |
Implemented in v0.4.0 (original code only):
| Sprint | Items |
|---|---|
| C1 | Depth cap 64, aspiration, PV ordering, root make/unmake; TT ordering under movetime |
| C2 | Soft stop, panic reserve, UCI info, Hash, wtime/increment time model |
| C3 | Pawn structure, rook files, king safety in eval |
| C4 | Check evasions in qsearch, optional OwnBook/BookFile |
| C6 | Lazy SMP (Threads 1–8, shared TT) |
Measurement: anchor ladder TC_SEC=1 THREADS=1; spot blitz TC_MODE=wtime 3+2; spot rapid TC_SEC=10 THREADS=4. See strength/ladder.md.
| Sprint | Items |
|---|---|
| D1a | Tactical EPD + fixed-depth regression tests |
| D1b | TT complete flag; score cutoffs re-enabled for timed search (post-Zobrist) |
| D1c | LMR / aspiration single-knob tune |
| D2 | wtime spot benchmark, stop/ucinewgame hardening, UCI matrix |
| D3 | Eval weight tune (existing terms) |
| D4 | Ablation table, TC caveats, ladder sync |
Ablation (1+0 anchor, T=1):
| Version | SF 1320 | SF 1800 | SF 2000 |
|---|---|---|---|
| alpha v0.2.0 | 89.1% | — | 17.2% |
| beta v0.3.0-beta | 93.8% | 51.6% | 34.4% |
| gamma v0.5.0 | 93.8% | 46.9% (probe) | 37.5% (32-game confirm, ≈1911 perf) |
TC caveat: All published ladder rows use TC_MODE=movetime TC_SEC=1 THREADS=1 unless marked otherwise. Performance Elo is project-relative vs Stockfish UCI_LimitStrength; not Lichess/CCRL/FIDE Elo.
SMP (not anchor): Lazy SMP v2 + null-move EP fix + timed TT cutoffs, 3+2 wtime @ SF@2000 32g T=4 → 64.1% (18–9–5, perf ≈ 2100, benchmark_20260622T160120Z). Prior SMP v2 anchor 51.6% (13–12–7). Use Threads=4, not 8.
Blitz confirm (not anchor): TC_MODE=wtime 3+2, 32 games @ SF@2000 → 10–11–11 (48.4%, perf ≈ 1989, CI ≈ 1860–2115), 0 illegal (benchmark_20260621T140403Z). Not the 1+0 headline row (37.5%, ≈1911).
labzero v0.2.0 demonstrates that a minimal, original chess engine can be built, verified independently, and measured on a reproducible strength ladder—without claiming competitive Elo. Alpha is the control experiment; beta adds well-known search and evaluation machinery under the same originality and verification discipline, with each phase producing a datapoint on the ladder.
- CCRL 40/15 rating list: https://computerchess.org.uk/4040/index.html
- UCI specification: https://www.chessprogramming.org/UCI
- Perft results: https://www.chessprogramming.org/Perft_Results
- Project docs: architecture.md, submission_package.md, strength/ladder.md
# Verification (Podman)
./scripts/podman/ci
./scripts/podman/verify-deep
./scripts/podman/gauntlet
# Host strength ladder (macOS; set your Stockfish path)
export STOCKFISH="/path/to/stockfish"
./scripts/build-host-engine.sh
SF_ELO=1320 SF_SKILL=0 SF_LIMIT=1 TC_SEC=1 GAMES=32 ./scripts/host-benchmark.sh| Symbol | Meaning |
|---|---|
Stockfish UCI_Elo when UCI_LimitStrength=true
|
|
| Score rate |
|
| Performance Elo from §4.2 | |
| Remaining search depth | |
| Alpha–beta window bounds |
Draft status: update §5 round 2 row when benchmark completes; mark beta phases complete in §7 as implemented.