Source study · CCRL 40/15 · corpus frozen 9 July 2026
The strongest chess engines in the world agree on almost every idea in their search — and share almost none of the code that implements it. This measures both, pairwise, across eleven codebases and four languages.
Author's note — why this exists
Every engine developer already knows the top of the list looks alike. We all read each other's work: it's on GitHub, it's discussed everywhere, and a good idea moves through the field fast. That much was never in doubt. What I never had was a metric for it.
I'm also close to the worst person to produce one. Being deep in writing this code leaves you unable to look at another author's search without seeing your own decisions reflected back at you, and I'd be doubly biased assessing my own engine. What I've always wanted is to be an independent author writing unique code — but who knows how much of the shared, freely accessible knowledge leaks into one's own work anyway? You cannot audit your own influences from the inside.
With the advent of LLMs such as Claude Code, an unbiased assessment finally became
possible. So that is what I set out to do. I asked Claude to compare search()
across the top ten engines with source on GitHub, pairwise against each other, and then to
drop Chess System Tal 2 into the matrix for assessment alongside them.
I wanted it line by line. Claude objected — lines measure brace style, not meaning — and insisted on doing it its own way. Fair enough; I'm not going to argue. The results are interesting. The numbers are there to be interpreted, and the interpretation below is Claude's own: what the top of the list shares is a commonality of ideas, not copied code.
A monoculture of ideas, built eleven times from scratch.
two metrics, deliberately answering different questions
Two engines can implement the same search and share no text, or share text and mean different things. So this study measures both axes separately.
Code overlap parses each search module to an AST and canonicalises it
into a language-neutral form: identifiers alpha-renamed, literals erased (so every tuned
constant, margin and weight collapses to LIT), types erased, parentheses and
braces gone, C++ -> and Rust auto-deref unified. Two statements match when
their canonical forms are identical. This is what makes a Rust statement comparable to a
C++ one.
Idea overlap scores every engine against a fixed 52-technique
taxonomy — is null-move pruning here, is correction history here — with a
file:line citation required for each of the 572 judgements. This is invariant
to how the idea is written.
The search module is defined by function, not by directory: search core plus
move ordering plus history. Transposition tables, zobrist, tablebase probing, threading
and time management are excluded everywhere, even where an engine files them under
search/. Assertions are excluded from code overlap — they are shared
convention, not search logic, and they inflated one pair fivefold.
first ten open-source engines on CCRL 40/15, plus one
The list is ranks 1–15 on the day it was pulled; the gaps are private or commercial engines (Torch, Dragon, Ethereal) which cannot be read. Chess System Tal 2 is not on the CCRL 40/15 list and enters unranked, with no Elo anchor.
| Engine | CCRL | Elo | Lang | Licence | Source | Stmts | Distinct | Techniques |
|---|---|---|---|---|---|---|---|---|
| Stockfish | 1 | 3650 | C++ | GPL-3.0 | official-stockfish/Stockfish | 694 | 532 | 48 |
| pawnocchio | 2 | 3648 | Zig | GPL-3.0 | JonathanHallstrom/pawnocchio | 649 | 411 | 46 |
| Reckless | 3 | 3646 | Rust | AGPL-3.0 | codedeliveryservice/Reckless | 470 | 324 | 46 |
| PlentyChess | 4 | 3644 | C++ | GPL-3.0 | Yoshie2000/PlentyChess | 615 | 384 | 50 |
| Obsidian | 6 | 3637 | C++ | GPL-3.0 | gab8192/Obsidian | 445 | 314 | 49 |
| Alexandria | 7 | 3636 | C++ | GPL-3.0 | PGG106/Alexandria | 408 | 276 | 46 |
| Berserk | 10 | 3630 | C | GPL-3.0 | jhonnold/berserk | 466 | 308 | 48 |
| Halogen | 13 | 3625 | C++ | GPL-3.0 | KierenP/Halogen | 357 | 252 | 45 |
| Quanticade | 14 | 3624 | C | GPL-3.0 | Quanticade/Quanticade | 412 | 273 | 44 |
| Caissa | 15 | 3622 | C++ | MIT | Witek902/Caissa | 755 | 448 | 46 |
| Chess System Tal 2 | — | — | C++ | not stated | local · private | 990 | 506 | 46 |
the control that makes every other number readable
An overlap percentage means nothing without a ceiling. So: Stockfish against its own past. Same engine, same authors, same files — separated only by development time.
| Stockfish HEAD vs | Age | Overlap |
|---|---|---|
| sf_17 | ~1 year | 54.6% |
| sf_16 | ~2 years | 32.8% |
| sf_15 | ~3 years | 30.6% |
Two years back, Stockfish matches itself only 32.8%. Every cross-engine pair in this study scores below that.
shared distinct canonical statements · assertions excluded
| Stockfish | pawnocchio | Reckless | PlentyChess | Obsidian | Alexandria | Berserk | Halogen | Quanticade | Caissa | CST2 | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Stockfish | · | 28 | 29 | 44 | 36 | 21 | 16 | 20 | 20 | 40 | 13 |
| pawnocchio | 28 | · | 14 | 30 | 24 | 17 | 8 | 17 | 12 | 22 | 11 |
| Reckless | 29 | 14 | · | 19 | 16 | 13 | 14 | 11 | 11 | 21 | 18 |
| PlentyChess | 44 | 30 | 19 | · | 44 | 32 | 26 | 25 | 19 | 29 | 18 |
| Obsidian | 36 | 24 | 16 | 44 | · | 29 | 24 | 19 | 16 | 29 | 15 |
| Alexandria | 21 | 17 | 13 | 32 | 29 | · | 29 | 19 | 24 | 23 | 18 |
| Berserk | 16 | 8 | 14 | 26 | 24 | 29 | · | 13 | 24 | 25 | 23 |
| Halogen | 20 | 17 | 11 | 25 | 19 | 19 | 13 | · | 12 | 29 | 12 |
| Quanticade | 20 | 12 | 11 | 19 | 16 | 24 | 24 | 12 | · | 14 | 19 |
| Caissa | 40 | 22 | 21 | 29 | 29 | 23 | 25 | 29 | 14 | · | 17 |
| Chess System Tal 2 | 13 | 11 | 18 | 18 | 15 | 18 | 23 | 12 | 19 | 17 | · |
Nothing here approaches the calibration ceiling. The densest pairs cluster around Stockfish and its closest stylistic relatives, and even those top out near a seventh of their smaller module.
| Pair | Shared | % of smaller | Strong |
|---|---|---|---|
| PlentyChess / Obsidian | 44 | 14.0% | 2 |
| PlentyChess / Alexandria | 32 | 11.6% | 2 |
| Halogen / Caissa | 29 | 11.5% | 0 |
| Stockfish / Obsidian | 36 | 11.5% | 0 |
| Stockfish / PlentyChess | 44 | 11.5% | 1 |
| Obsidian / Alexandria | 29 | 10.5% | 1 |
| Alexandria / Berserk | 29 | 10.5% | 1 |
| PlentyChess / Halogen | 25 | 9.9% | 0 |
Raw structural matching produces false friends: erasing constants makes
rule50_count() < 96 identical to boardHistory.size() < 3.
The strong tier filters those out, and it is nearly empty — 0 to 3 statements per pair.
What survives looks like this:
bestValue = (bestValue * depth + beta) / (depth + 1);best_score = (best_score * depth + beta) / (depth + 1);if best_score >= beta && !is_decisive(best_score)if (bestScore >= beta && !isDecisive(bestScore))shared techniques as a percentage of the union · Jaccard
| Stockfish | pawnocchio | Reckless | PlentyChess | Obsidian | Alexandria | Berserk | Halogen | Quanticade | Caissa | CST2 | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Stockfish | · | 96 | 96 | 96 | 94 | 88 | 92 | 94 | 92 | 88 | 84 |
| pawnocchio | 96 | · | 92 | 92 | 90 | 92 | 88 | 90 | 96 | 88 | 88 |
| Reckless | 96 | 92 | · | 92 | 90 | 88 | 92 | 94 | 88 | 84 | 80 |
| PlentyChess | 96 | 92 | 92 | · | 98 | 92 | 96 | 90 | 88 | 92 | 88 |
| Obsidian | 94 | 90 | 90 | 98 | · | 90 | 94 | 88 | 86 | 90 | 90 |
| Alexandria | 88 | 92 | 88 | 92 | 90 | · | 92 | 86 | 91 | 88 | 92 |
| Berserk | 92 | 88 | 92 | 96 | 94 | 92 | · | 90 | 84 | 92 | 84 |
| Halogen | 94 | 90 | 94 | 90 | 88 | 86 | 90 | · | 85 | 90 | 78 |
| Quanticade | 92 | 96 | 88 | 88 | 86 | 91 | 84 | 85 | · | 84 | 88 |
| Caissa | 88 | 88 | 84 | 92 | 90 | 88 | 92 | 90 | 84 | · | 84 |
| Chess System Tal 2 | 84 | 88 | 80 | 88 | 90 | 92 | 84 | 78 | 88 | 84 | · |
The same field, measured by idea, inverts completely. There is no spread here — only a floor. 37 of 52 techniques are present in every single engine: iterative deepening, PVS, aspiration windows, null-move, reverse futility, razoring, ProbCut, late move reductions with history, improving and cutnode adjustments, singular extensions, multicut, continuation history, staged move generation. None of them distinguishes anybody.
Only 14 techniques vary at all. Ranked by agreement on those uncommon ideas — which is the only part carrying information — the ordering is:
| Pair | Shared | Jaccard | Rarity-wtd |
|---|---|---|---|
| PlentyChess / Obsidian | 49 | 98% | 95% |
| PlentyChess / Berserk | 48 | 96% | 84% |
| Stockfish / Reckless | 46 | 96% | 81% |
| Reckless / Halogen | 44 | 94% | 80% |
| Obsidian / Berserk | 47 | 94% | 79% |
| pawnocchio / Quanticade | 44 | 96% | 78% |
| Stockfish / Halogen | 45 | 94% | 71% |
| PlentyChess / Caissa | 46 | 92% | 71% |
everything else is unanimous
| Technique | Engines | Adoption | Present in |
|---|---|---|---|
| correction_history | 10/11 | Stockfish, pawnocchio, Reckless, PlentyChess, Obsidian, Alexandria, Berserk, Halogen, Quanticade, Caissa | |
| cuckoo | 10/11 | Stockfish, pawnocchio, Reckless, PlentyChess, Obsidian, Alexandria, Berserk, Halogen, Caissa, Chess System Tal 2 | |
| iir | 10/11 | Stockfish, pawnocchio, PlentyChess, Obsidian, Alexandria, Berserk, Halogen, Quanticade, Caissa, Chess System Tal 2 | |
| lmr_ttpv | 9/11 | Stockfish, pawnocchio, Reckless, PlentyChess, Obsidian, Alexandria, Berserk, Quanticade, Chess System Tal 2 | |
| nmp_verification | 9/11 | Stockfish, pawnocchio, Reckless, PlentyChess, Alexandria, Berserk, Halogen, Quanticade, Caissa | |
| pawn_history | 9/11 | Stockfish, pawnocchio, Reckless, PlentyChess, Obsidian, Alexandria, Halogen, Quanticade, Chess System Tal 2 | |
| tt_pv_flag | 9/11 | Stockfish, pawnocchio, Reckless, PlentyChess, Obsidian, Alexandria, Berserk, Quanticade, Chess System Tal 2 | |
| tb_probe | 8/11 | Stockfish, pawnocchio, Reckless, PlentyChess, Obsidian, Berserk, Halogen, Caissa | |
| delta_pruning | 7/11 | Stockfish, pawnocchio, PlentyChess, Obsidian, Quanticade, Caissa, Chess System Tal 2 | |
| multipv | 7/11 | Stockfish, Reckless, PlentyChess, Obsidian, Berserk, Halogen, Caissa | |
| countermove | 6/11 | PlentyChess, Obsidian, Alexandria, Berserk, Caissa, Chess System Tal 2 | |
| killers | 6/11 | PlentyChess, Obsidian, Alexandria, Berserk, Caissa, Chess System Tal 2 | |
| root_move_ordering | 6/11 | Stockfish, Reckless, PlentyChess, Obsidian, Berserk, Halogen | |
| check_extensions | 1/11 | Chess System Tal 2 |
the one clean signal in the dataset
Killers and countermove — the two heuristics every chess programming tutorial teaches — divide the field almost exactly in half. And it is the same engines both times, with no partial adopters: an engine either has both or neither.
subsumed into continuation history
classical move ordering retained
Stockfish leads the group that has deleted them outright — verified directly: the only
matches for killer or countermove anywhere in its source are two
comments in the history-bonus code. Two more unanimous verdicts: nobody
implements IID (ten of eleven use internal iterative reduction
instead — the shallow-search-to-find-a-move variant is extinct at this level), and
nobody implements check extensions — with a single exception, below.
the outlier on both axes
Added to the study as an eleventh engine, and it lands outside the cluster on both measurements: mean code overlap 5.0% against 7.4% for every other pair, and zero strong matches with anybody — the only engine in the study with none. Its lowest code overlap of all is with Stockfish. Idea overlap sits at 86% mean against 91%: inside the monoculture, but at its edge.
| vs | Shared | Code | Strong | Ideas |
|---|---|---|---|---|
| Berserk | 23 | 7.5% | 0 | 84% |
| Quanticade | 19 | 7.0% | 0 | 88% |
| Alexandria | 18 | 6.5% | 0 | 92% |
| Reckless | 18 | 5.6% | 0 | 80% |
| Obsidian | 15 | 4.8% | 0 | 90% |
| Halogen | 12 | 4.8% | 0 | 78% |
| PlentyChess | 18 | 4.7% | 0 | 88% |
| Caissa | 17 | 3.8% | 0 | 84% |
| pawnocchio | 11 | 2.7% | 0 | 88% |
| Stockfish | 13 | 2.6% | 0 | 84% |
It is alone in eleven engines on two counts, in opposite directions. It is the only engine that still extends on checks — in two distinct variants, one for moves giving check and one for king moves while in check — a technique the entire top ten has abandoned. And it is the only engine without correction history, which is otherwise universal: every engine from rank 1 to rank 15, in all four languages, keeps a static-eval correction table.
It also carries the one technique the taxonomy could not score at all. Chess System Tal runs six small neural networks (24 inputs, 8 hidden units) as learned gates on classical heuristics: three of them decide whether to even attempt reverse futility pruning, null-move and ProbCut respectively, and only then does the hand-tuned margin logic run. A fourth feeds the iterative-deepening stop decision. The remaining two — good-capture ordering and LMR adjustment — are present but switched off. No other engine in the study does anything of the kind; all ten of the others use hand-tuned margins throughout.
Read this the right way
Chess System Tal scoring 46 techniques against Stockfish's 48 is not a deficit of two. The taxonomy was built from mainstream top-ten engines and structurally cannot score learned gating, so it undercounts this engine specifically.
Equally, 5% code overlap is a statement about independence, not quality — against a field whose ceiling is 14%, it means the search was written from scratch. The one directional claim the data supports is correction history: universal in the top ten, absent here.
limitations, in order of how much they matter
The taxonomy is shaped by what it knew to ask. It was built from mainstream engines, so the 78% idea floor is partly a ceiling effect of the checklist itself. The tell is that not one technique landed in the “rare” bucket among the top ten: the list has no resolving power at the tail. A taxonomy derived from Caissa's or Halogen's unusual ideas would spread these numbers out. Read the idea figures as “they agree on everything a mainstream reader knew to ask about.”
Code overlap counts exact structural equality. One extra pruning condition breaks a match. Every figure here is a lower bound on semantic equivalence, which is why the 54.6% self-comparison ceiling matters more than any absolute value.
Zig is under-measured. Zig's try has no C++ analogue, so
pawnocchio's error handling systematically suppresses some cross-language matches. Its
row is understated by an unknown margin.
The bugs were in the measurement, not the field. Three separate faults in this pipeline each produced a confident, wrong answer before being caught: a Rust field-name mismatch suppressed Reckless roughly eightfold; assertions inflated one pair fivefold; a first-pass Zig adapter reported 0.5% overlap. Every “engine X shares nothing” result turned out to be a bug. That is why cross-language equivalence suites gate the numbers, and why any figure here should be read as provisional on them.