≈ 25 minutes · hook → predict → explain → play → check
Split games,
not positions.
Every decision in this course — architecture, learning rate, data size — will be judged by one number: validation accuracy. This mission is about making sure that number tells the truth.
00 · Why this matters
A score that lies politely.
Our count-based baseline currently predicts the next human move in 22.4% of held-out positions. Suppose that next month a shiny new neural model reports 78% validation accuracy. Champagne? Not yet — because there is a boring way to reach 78% without learning any more chess: let the evaluation quietly reuse games the model already studied.
That failure mode is called data leakage, and it is not a beginner’s mistake — leaky evaluations regularly slip into published research. For us it is also the single most expensive mistake available, because every later experiment chooses between models by comparing validation numbers. If those numbers reward memorization, we will spend the tournament’s 100 MB and one exaFLOP optimizing for the wrong thing, and discover it on match night.
The plan: commit to a prediction, see the exact mechanism of the leak with rows you can point at, watch it inflate a score in a simulator, and then approve the split our real code uses.
01 · Predict before you read
Predict the direction of the error.
Here is the setup. We expand each game into overlapping next-move examples, shuffle all examples from every game together, then take 10% as validation. Positions from one game can land on both sides of the boundary.
02 · The mechanism
The exam answers are hiding in the study guide.
What a validation score is supposed to measure
Training accuracy answers an easy question: “how well did the model fit the games it studied?” With enough capacity, memorizing wins that contest without any understanding. The question we actually care about is different: “how will it move in a game nobody has played yet?” — because that is literally what the tournament is. A validation set is a stand-in for that future: games we possess but the model has never touched. The score it produces is trustworthy only while “never touched” stays literally true.
One game becomes many training rows
Language models don’t train on games; they train on (context → next move) rows. Here is a six-ply Ruy Lopez opening and every row it generates:
| Row | Context the model sees | Target it must predict |
|---|---|---|
| R1 | (game start) | e4 |
| R2 | e4 | e5 |
| R3 | e4 e5 | Nf3 |
| R4 | e4 e5 Nf3 | Nc6 |
| R5 | e4 e5 Nf3 Nc6 | Bb5 |
| R6 | e4 e5 Nf3 Nc6 Bb5 | a6 |
Notice the redundancy: R6’s context contains R2, R3, R4, and R5 in their entirety. These six rows are not six independent facts about chess — they are one game photographed from six angles.
Hold out one row, leak it through its neighbours
Say the shuffle sends R4 to validation and the other five rows to training. The exam question is: “after e4 e5 Nf3, what came next?” The answer is Nc6. Now look at what the model studied. Training row R5 begins e4 e5 Nf3 Nc6… — the exam’s answer is printed, verbatim, inside a study row. So is R6. A model with spare capacity doesn’t need to understand knight development; it can simply remember “that game where Nc6 followed Nf3” and recite it back.
Nf3 Nc6, the exact transition validation is supposed to test.To be fair: a different game that reached e4 e5 Nf3 and continued Nc6 would also “give away” this answer — but that is legitimate generalization, a pattern learned across many players. The leak is that this exact game answers its own exam. And the distinction we care about — pattern versus memory — is precisely the distinction the leaky split erases.
Randomness is not independence
The shuffle in the leaky protocol is genuinely random, and that is the trap: randomness controls which rows cross the boundary, not whether related rows may cross it. The fix is choosing the right split unit — the smallest thing that must stay whole. Rows from one game share information, so the unit is the game. The same reasoning appears everywhere in machine learning: split by patient in medical imaging, by speaker in audio, by day in market data. Whenever examples share a source, the source is the unit.
Three lines that keep the evidence honest
Our baseline decides each game’s fate with one tiny function:
def is_validation_game(site: str, seed: int, validation_percent: int) -> bool:
digest = hashlib.blake2b(f"{seed}:{site}".encode(), digest_size=8).digest()
return int.from_bytes(digest) % 100 < validation_percentsiteis the game’s Lichess URL — a stable identity that never changes, no matter how the file is sorted or re-downloaded.blake2b(...)turnsseed + identityinto a huge, effectively random — but perfectly deterministic — number.% 100drops that number into one of a hundred buckets;< validation_percentsends, say, buckets 0–9 to validation. That is a 10% split.
Four properties fall out of those three lines: the whole game lands on one side (assignment depends only on identity), the split is reproducible on any machine in any row order, no chronology bias can sneak in, and changing the seed deals a completely fresh split without touching the data. Read the real function — pinned to the exact commit, it is these same three lines.
What honesty costs
A game-level split scores lower — that is the point, it stopped grading memory — and its estimate wobbles more when validation holds only a few games, because whole games are coarser coins to flip. The rough rule: quadrupling the number of validation games halves the wobble. How much data to spend on a steadier estimate is a genuine trade-off, and it is Mission 4’s topic. You can feel it first, in the simulator below.
03 · See it happen
Watch the leak inflate a score.
This simulator runs the whole story as a repeatable experiment: 200 synthetic games of 30 positions each, played by a model whose true skill on new games is 30%, but which recalls about 80% of the lines it studied. Each dot is one complete experiment — a fresh split, then an evaluation.
Four things to notice while you play:
- The amber cloud sits far above the truth at every setting — it reports the model’s memory, not its chess. It drifts down as you hold out more of each game, because partial memorization leaks partially. Diluted leakage is still leakage.
- The green cloud brackets the dashed truth line. Lower, honest, useful.
- Drag validation share down to 5%: the green dots scatter, because a handful of validation games makes a noisy estimate. Push it to 50%: steadier — but watch the training-games counter fall.
- The leak is not noise, it is bias: no amount of redrawing pulls the amber cloud toward the truth.
The toy’s assumptions (skill 30%, recall 80%, uniform games) are made up, but the shape survives any reasonable numbers. The Chapter 1 experiment measures the real effect on the real baseline.
04 · Implementation comparison
Which split would you approve?
You are reviewing three pull requests. Each claims to split games 90/10. Choose the one that keeps the evidence trustworthy for our current dataset — and know why the other two fail.
05 · Retrieval checkpoint
Close the loop without looking back.
All three must be correct at the same time. Wrong answers unlock hints — retry as often as you like; there is no penalty.
What is the split unit for our current evaluation?
Why hash the stable game ID with a seed?
On a fixed dataset, what does a larger validation split usually buy and cost?
Mission handoff
The code is still sealed.
Commit the forecast, approve the trustworthy implementation, and answer all three retrieval questions correctly.
06 · Go deeper · pick what you need
The trail beyond this page.
The source assignment for this mission (20–40 minutes): read the real split function and the pinned dataset manifest, then answer — what identity must remain stable for our split to remain reproducible? The rest of the list is optional depth, each entry labelled with what it gives you.