Source study · CCRL 40/15 · corpus frozen 9 July 2026

Ten engines, one search

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.

Chris Whittington 16 July 2026

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.

14.0%
Highest statement-level overlap between any two engines
ceiling: 54.6% — Stockfish vs itself, 1 yr
78%
Lowest idea-level overlap between any two engines
37 of 52 techniques are universal
37/52
Techniques present in every single engine, in all four languages
only 14 vary at all

A monoculture of ideas, built eleven times from scratch.

What is actually being measured

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.

The corpus

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 names and source cells link to the upstream repository, read at HEAD. Chess System Tal 2 is a private repository and is unlinked. Module size counts non-trivial statements after canonicalisation; distinct collapses repeats. Techniques is out of 52.
EngineCCRLElo LangLicenceSourceStmts DistinctTechniques
Stockfish13650C++GPL-3.0official-stockfish/Stockfish69453248
pawnocchio23648ZigGPL-3.0JonathanHallstrom/pawnocchio64941146
Reckless33646RustAGPL-3.0codedeliveryservice/Reckless47032446
PlentyChess43644C++GPL-3.0Yoshie2000/PlentyChess61538450
Obsidian63637C++GPL-3.0gab8192/Obsidian44531449
Alexandria73636C++GPL-3.0PGG106/Alexandria40827646
Berserk103630CGPL-3.0jhonnold/berserk46630848
Halogen133625C++GPL-3.0KierenP/Halogen35725245
Quanticade143624CGPL-3.0Quanticade/Quanticade41227344
Caissa153622C++MITWitek902/Caissa75544846
Chess System Tal 2C++not statedlocal · private99050646

Calibration: what does “high” look like?

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.

Search code churns fast enough that a year erases half of it. This is the practical maximum the metric can report for genuinely shared lineage.
Stockfish HEAD vsAgeOverlap
sf_17~1 year54.6%
sf_16~2 years32.8%
sf_15~3 years30.6%

Two years back, Stockfish matches itself only 32.8%. Every cross-engine pair in this study scores below that.

Code overlap

shared distinct canonical statements · assertions excluded

StockfishpawnocchioRecklessPlentyChessObsidianAlexandriaBerserkHalogenQuanticadeCaissaCST2
Stockfish·28294436211620204013
pawnocchio28·14302417817122211
Reckless2914·1916131411112118
PlentyChess443019·44322625192918
Obsidian36241644·292419162915
Alexandria2117133229·2919242318
Berserk16814262429·13242523
Halogen20171125191913·122912
Quanticade2012111916242412·1419
Caissa402221292923252914·17
Chess System Tal 213111818151823121917·

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.

Ranked by share of the smaller module. Strong counts matches that are rare, non-trivial, and where both engines independently chose the same identifier names — the only tier where copying, rather than convergence, would show.
PairShared% of smaller Strong
PlentyChess / Obsidian4414.0%2
PlentyChess / Alexandria3211.6%2
Halogen / Caissa2911.5%0
Stockfish / Obsidian3611.5%0
Stockfish / PlentyChess4411.5%1
Obsidian / Alexandria2910.5%1
Alexandria / Berserk2910.5%1
PlentyChess / Halogen259.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:

Stockfish · search.cpp
bestValue = (bestValue * depth + beta) / (depth + 1);
Quanticade · search.c
best_score = (best_score * depth + beta) / (depth + 1);
Reckless · search.rs (Rust)
if best_score >= beta && !is_decisive(best_score)
Alexandria · search.cpp (C++)
if (bestScore >= beta && !isDecisive(bestScore))

Idea overlap

shared techniques as a percentage of the union · Jaccard

StockfishpawnocchioRecklessPlentyChessObsidianAlexandriaBerserkHalogenQuanticadeCaissaCST2
Stockfish·96969694889294928884
pawnocchio96·929290928890968888
Reckless9692·9290889294888480
PlentyChess969292·98929690889288
Obsidian94909098·909488869090
Alexandria8892889290·9286918892
Berserk928892969492·90849284
Halogen94909490888690·859078
Quanticade9296888886918485·8488
Caissa888884929088929084·84
Chess System Tal 284888088909284788884·

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:

Rarity-weighted discounts the universal freebies, weighting each technique by how few engines have it.
PairSharedJaccard Rarity-wtd
PlentyChess / Obsidian4998%95%
PlentyChess / Berserk4896%84%
Stockfish / Reckless4696%81%
Reckless / Halogen4494%80%
Obsidian / Berserk4794%79%
pawnocchio / Quanticade4496%78%
Stockfish / Halogen4594%71%
PlentyChess / Caissa4692%71%

The 14 techniques that actually vary

everything else is unanimous

Universal across all 11: iterative_deepening, aspiration_windows, pvs, fail_soft, quiescence, mate_distance_pruning, lazy_smp, bestmove_stability, node_tm, tt_probe, tt_store, tt_move_ordering, tt_cutoff, singular_extensions, multicut, negative_extensions, double_extensions, nmp, rfp, razoring, probcut, futility_pruning, lmp, see_pruning_quiet, see_pruning_capture, history_pruning, lmr, lmr_history, lmr_improving, lmr_cutnode, lmr_research, improving, quiet_history, capture_history, continuation_history, history_bonus_malus, staged_movegen. Implemented by nobody: iid.
TechniqueEnginesAdoption Present in
correction_history10/11Stockfish, pawnocchio, Reckless, PlentyChess, Obsidian, Alexandria, Berserk, Halogen, Quanticade, Caissa
cuckoo10/11Stockfish, pawnocchio, Reckless, PlentyChess, Obsidian, Alexandria, Berserk, Halogen, Caissa, Chess System Tal 2
iir10/11Stockfish, pawnocchio, PlentyChess, Obsidian, Alexandria, Berserk, Halogen, Quanticade, Caissa, Chess System Tal 2
lmr_ttpv9/11Stockfish, pawnocchio, Reckless, PlentyChess, Obsidian, Alexandria, Berserk, Quanticade, Chess System Tal 2
nmp_verification9/11Stockfish, pawnocchio, Reckless, PlentyChess, Alexandria, Berserk, Halogen, Quanticade, Caissa
pawn_history9/11Stockfish, pawnocchio, Reckless, PlentyChess, Obsidian, Alexandria, Halogen, Quanticade, Chess System Tal 2
tt_pv_flag9/11Stockfish, pawnocchio, Reckless, PlentyChess, Obsidian, Alexandria, Berserk, Quanticade, Chess System Tal 2
tb_probe8/11Stockfish, pawnocchio, Reckless, PlentyChess, Obsidian, Berserk, Halogen, Caissa
delta_pruning7/11Stockfish, pawnocchio, PlentyChess, Obsidian, Quanticade, Caissa, Chess System Tal 2
multipv7/11Stockfish, Reckless, PlentyChess, Obsidian, Berserk, Halogen, Caissa
countermove6/11PlentyChess, Obsidian, Alexandria, Berserk, Caissa, Chess System Tal 2
killers6/11PlentyChess, Obsidian, Alexandria, Berserk, Caissa, Chess System Tal 2
root_move_ordering6/11Stockfish, Reckless, PlentyChess, Obsidian, Berserk, Halogen
check_extensions1/11Chess System Tal 2

The generational split

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.

Dropped both

subsumed into continuation history

  • Stockfish, pawnocchio, Reckless, Halogen, Quanticade

Kept both

classical move ordering retained

  • PlentyChess, Obsidian, Alexandria, Berserk, Caissa, Chess System Tal 2

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.

Chess System Tal 2

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.

Chess System Tal 2 against each engine, both axes.
vsSharedCode StrongIdeas
Berserk237.5%084%
Quanticade197.0%088%
Alexandria186.5%092%
Reckless185.6%080%
Obsidian154.8%090%
Halogen124.8%078%
PlentyChess184.7%088%
Caissa173.8%084%
pawnocchio112.7%088%
Stockfish132.6%084%

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.

What would change these numbers

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.