A population of neural networks learns to race procedurally generated tracks with no gradients, no training data, and no human demonstration — only survival. This is the complete record: how it works, every experiment run, and the four separate occasions when the measurements lied and had to be caught.
View the source on GitHub →Everything below was built from an empty directory. The end state is a driver that completes a 125-track held-out benchmark — including its hardest tier — without crashing once. The more interesting story is the path there, because roughly half the work was not making the cars better; it was discovering that measurements which looked convincing were wrong.
No gradients, no labelled data, no demonstrations. Just selection.
Each car carries a tiny neural network — its brain. The network sees eleven distance readings from rays cast around the car, plus its own speed, and outputs two numbers: how hard to steer and how hard to accelerate. That is the entire interface between mind and world.
The network is small enough to write down: twelve inputs, sixteen hidden units, two outputs. 242 weights in total. Those 242 numbers, flattened into a single vector, are the car's genome. Five hundred and twelve of them form a population, stored as one matrix.
Every generation, all 512 cars drive the same freshly generated tracks simultaneously. The ones that travel furthest become parents. Their genomes are recombined and perturbed with random noise to make the next generation. Repeat three hundred times. Nothing ever computes a derivative; the entire learning signal is who got further.
Evolution here is doing something backpropagation cannot: optimising through a simulator that is full of hard discontinuities — crashes, wall contact, episode termination. None of it is differentiable. Selection does not care.
The five components, and the engineering decision inside each one.
Every track is built from a seed: scatter control points around a circle at jittered angles and radii, run a closed Catmull-Rom spline through them (a curve that passes exactly through each point rather than merely approaching it), then resample the result to 400 points spaced equally by arc length. That resampling matters twice over — raw spline samples bunch up in tight corners, and equal spacing turns "how far along the track am I?" into a simple array index.
Candidate tracks are then rejected if a corner is too tight for the corridor width, if two sections pinch together, or if the geometry runs off the world. Invalid seeds re-roll. This is rejection sampling, and it is what lets a difficulty knob crank up corner sharpness without silently producing undrivable tracks.
Finally the corridor is stamped into two boolean grids: one representing walls as the sensors see them, and one inflated by the car's radius. That second grid is a small piece of robotics: testing the car's centre point against an inflated obstacle is mathematically identical to testing the car's whole body against the real one — collision detection collapses to a single array lookup.
Rays could be intersected against wall segments analytically. Instead each ray marches a fixed number of sample points through the occupancy grid and takes the first one that lands in a wall. This was roughly ten times faster when measured, and its cost is independent of how complicated the track shape is.
The car never receives its coordinates, the track layout, or a map. It sees only local wall distances and its own speed. That poverty is the whole point: a brain with no track-specific information available to it has nothing track-specific to memorise, so the only strategy that scores well is general driving.
The standard way to use a GPU or SIMD unit is to run one model over many data points. Here the situation is inverted: 512 different networks each need to process their own single observation. The trick is to store all populations' weights as one stacked tensor and contract it with a matched batch of observations:
h = tanh(einsum('pi,pih->ph', obs, W1) + b1)
out = tanh(einsum('ph,pho->po', h, W2) + b2)
Two lines evaluate 512 distinct neural networks simultaneously. Combined with storing car state as parallel arrays rather than objects, the whole population advances one timestep in about 1.5 milliseconds.
A later optimisation only computed sensing and thinking for cars still alive — early generations are dominated by wrecks — which made training 3.1× faster while producing bit-identical results, a property pinned by a test.
Four operators, each a deliberate choice. Elitism copies the best few genomes untouched, so a good solution can never be lost to an unlucky mutation. Tournament selection picks each parent as the best of three randomly drawn candidates — only rank matters, never the magnitude of fitness, and weak genomes still occasionally reproduce, which preserves diversity. Uniform crossover mixes two parents gene by gene. Gaussian mutation perturbs about 10% of genes, with the noise scale annealing over time: coarse exploration early, fine tuning late.
Crossover of neural network weights is genuinely contested in the literature — two parents can encode identical behaviour with permuted hidden neurons, making their blend meaningless. It was left as a configurable knob rather than asserted to be correct.
Every one of these exploits appeared or was anticipated. An optimiser does exactly what you write, never what you meant.
| The exploit | The defence |
|---|---|
| Drive backwards over the start line repeatedly | No reverse gear exists in the physics; speed clips at zero. Backward progress counts negative. |
| Oscillate back and forth across the line | Progress is monotone-maximum — only the furthest point ever reached counts — and lap crossings are signed. |
| Creep forward at 1 px/s forever | A stagnation rule freezes any car that fails to gain ground over a rolling window. |
| Cut across the infield | Progress is only searched in a small window around the car's last known checkpoint, so it cannot teleport across a nearby fold of track. |
| Spin in tight circles to farm distance | Circling gains no net centreline progress, so the stagnation rule collects it. |
| Drive recklessly fast and crash late | Measured, not assumed: crashing forfeits the remainder of the episode, worth ~5.4 laps on average. The explicit penalty is almost irrelevant. |
There is deliberately no explicit speed bonus. Faster cars simply get further before the step limit, so speed is rewarded without ever being directly payable — a reward that cannot be farmed.
The difference between a car that memorised a track and a driver.
Three mechanisms, all structural rather than incidental:
Fresh tracks, every generation. No genome is ever scored twice on the same track. Memorisation is not a strategy that exists in this environment; generalising is the objective function.
An adaptive curriculum. Tracks begin wide and gentle. When the median car (not the best — one lucky elite must not drag the population somewhere it cannot survive) handles the current difficulty for several generations running, the tracks get narrower and twistier. Difficulty is sampled from a trailing band so easy tracks stay in the mix and skills are not forgotten.
A three-way data split, borrowed wholesale from supervised learning: training tracks drive evolution, a validation ladder selects champions, and a frozen test bank is read only to report a final number. This turned out to matter more than any modelling decision in the entire project.
Built after the first result turned out to be an illusion.
Paired experiments. Every A/B runs both arms on identical random seeds, so replicate i of the treatment sees exactly the same sequence of training tracks as replicate i of the control. The comparison is then a paired one-sided t-test on per-seed differences, which cancels the run-to-run variance that would otherwise drown any real effect.
A decision suite separate from the test bank. A 300-track suite gates every ship-or-kill decision. The frozen 125-track bank is read once per milestone and never used to choose between options — because any set you select on, you eventually overfit.
Capacity controls. When adding a new input to the network, the genome grows. Does a win come from the new information, or merely from having more parameters? The answer requires a third arm: same enlarged genome, but the extra inputs carry duplicated data and no new information. This design caught a result that would otherwise have been misattributed.
Adversarial review. Each significant change was reviewed by independent automated critics whose findings were then cross-examined by further skeptics instructed to refute them. Four real bugs were caught this way, three of them while training runs were already in flight.
Each round's measurement determined what the next one attempted.
The first trained driver looked excellent: 9.6 laps on the validation set. The frozen test bank told a different story — it crashed on 92% of maximum-difficulty tracks. The validation number was not wrong, it was simply not the number that mattered. This is the exact reason the three-way split exists, learned by walking into it.
Rather than guess, the braking distance was computed: decelerating from top speed to what a tight corner permits consumes about 125 px, while the sensors only reached 160 px. The car physically could not see far enough to brake in time. Rays were lengthened and the forward fan densified. Difficulty was also pushed past the evaluation point during training, so that maximum difficulty became interpolation rather than extrapolation. 92% → 8%.
Running three independent seeds instead of one exposed something alarming. The validation ladder held only two maximum-difficulty tracks, so a fragile car that got lucky on both could win the checkpoint. Every seed had locked in a champion with a true crash rate of 42–65% wearing a perfect validation record.
Fixing selection alone — no change whatsoever to the learning algorithm — moved the method from 52% crashes to 4%. The single largest improvement in the project came from measuring better, not training better.
A failure heat-map decomposed difficulty into two independent axes, corridor narrowness and corner sharpness, and showed the remaining crashes lived almost entirely on the narrowness axis. The obvious fix — a bigger brain, more sensor channels — was not what the data asked for.
Instead the side rays were shortened. A ray that marches the same number of samples over a shorter distance resolves finer detail, dropping lateral measurement error from 4.4 px to 1.9 px against a corridor margin of roughly 12 px. Zero new parameters, zero extra compute, a one-line config change. 4% → 1.8%.
Training tracks gained corridors that pinch and widen along their length. Measured first: the existing champion crashed on 32% of variable-width tracks, so the capability gap was real and worth closing. After training on them, 1.3% — and the frozen test bank came back clean at every difficulty, zero crashes. An unexpected bonus: the margin-keeping learned from variable width transferred, unprompted, to low-grip ice the cars had never encountered.
Recurrent memory, self-adaptive mutation rates, physics randomisation, island populations, champion ensembling, evolution-strategy fine-tuning, multi-car racing, low-grip training, obstacle training. Every one properly run, every one killed. Each is in the ledger below with the reason.
One weakness resisted everything: cones placed mid-corridor caused crashes on 97% of tracks. The cause was angular aliasing — a 12 px cone slips between rays spaced 4–6° apart, so beyond about 120 px it is literally invisible. This was verified directly: a cone at 7° and 130 px returned zero ray hits.
Densifying the fan helped (97% → 64%) but could not finish the job. What did was changing the kind of sensing: three channels reporting the nearest visible cone's true distance and bearing, independent of whether any ray happens to line up with it.
The bearing-based driver uses fewer parameters than the dense-ray version it beat (290 versus 338). When a smaller model wins, the result cannot be attributed to capacity — the right representation simply beat more resolution, and the comparison proves it by construction.
The new sensing worked brilliantly on one random seed and poorly on another, which looked like a discovery lottery worth fixing. Five hypotheses were tested; all five died. The input was not dead (weight analysis). Selection was not missing good champions (every archived champion of the failing seed was re-scored — none existed). Crashing was not under-punished (measured). Training was not cut short (five seeds at double the generations: no improvement in solve rate). And capacity was not the limit — though that last one very nearly got away.
Four separate incidents. This section exists because a showcase that omits them would be teaching the wrong lesson.
The first failure heat-map returned all zeros and looked like a solved problem. It was not: at ten tracks per cell, a champion with an 8% crash rate shows zero crashes in a cell 43% of the time. The instrument had been designed for the 50–90% rates of an earlier era and could no longer see the effect it was pointed at. Re-run denser, the real structure appeared immediately.
A headline figure of "4% crashes" was published. It was one crash in twenty-five tracks. The same genome scored three in twenty-five on a fresh probe. Pooled over both, the honest figure is roughly 7–8% — the improvement was real and large, the precision was invented.
Doubling the network's hidden layer appeared to be the answer: two of five seeds "solved" the cone problem at 8%, something no smaller network had ever managed. Re-measured on a four-times-larger probe, those same champions sit at 11–13% — worse than the smaller network's 7%. The apparent breakthrough was produced entirely by the measuring instrument. A 25-track probe carries ±6 points of binomial noise; every effect being chased was the same size as the noise.
The residual crashes suggested the fitness function was under-punishing collisions — the explicit penalty really is negligible. A fix was about to be written. Measuring first showed crashed episodes average 1.44 fitness against 6.86 for clean ones: dying forfeits the rest of the episode, which is punishment enough. The theory was elegant, cheap to implement, and wrong.
The same 25-track probe flipped conclusions twice, in opposite directions — inflating one champion and manufacturing an entire fake winning method. Small samples do not merely add noise; they generate confident, specific, wrong answers. All sub-20% comparisons now run on a 100-track suite as a matter of policy.
Adversarial review, run concurrently with training.
| The bug | What it would have caused |
|---|---|
| Champion-selection ratchet keyed on noise | Caught mid-run: the saved "best" champion had already decayed from 4.77 laps to 0.23 while its selection score improved. The run was killed and restarted. |
| A NumPy view aliasing bug | Silently zeroed an entire new input channel in the fast path while the slow path computed it correctly — the feature would have looked deterministic and simply useless. |
| Sensing straight through walls | 62% of the new sensor's readings were phantom obstacles on other folds of the track. Training on it would have taught blanket caution — the exact failure the sensor existed to avoid. |
| Infinite recursion in track generation | A difficulty back-off that could never terminate, plus non-atomic checkpoint writes that could destroy a champion on interrupt. |
Fifteen experiments. Three shipped. Negative results are results.
| Experiment | Verdict | What it taught |
|---|---|---|
| Precision side rays | SHIP | Sensor resolution must match the tolerance being judged. Halved hard-track crashes for zero parameters. |
| Variable-width training | SHIP | Task diversity closed a 32%-crash capability hole, and transferred to hazards never trained on. |
| Bearing-based obstacle sensing | SHIP | The right representation beat more resolution — with fewer parameters, so the win is unambiguous. |
| Closure-rate inputs | KILL | More information made it worse. The capacity control proved the signal, not the parameter count, was at fault. |
| Self-adaptive mutation | KILL | Sigma collapse: myopic selection anneals its own exploration away an order of magnitude too fast. |
| Physics randomisation | KILL | A clean null. The failures were never physics-brittleness, and the data said so plainly. |
| Low-grip training | KILL | Total ice mastery bought with a lap of global caution: invisible hazards get priced in everywhere. |
| Recurrent memory | KILL | Memory was not the missing ingredient. Untargeted capacity rarely is. |
| Obstacle training alone | KILL | Real progress, capability still unachieved, and 1.7 laps of collateral cost everywhere else. |
| Island populations | KILL | The safest arm in the project and flat on capability. Diversity preserves; it does not create. |
| Champion ensembling | KILL | Champions from the same distribution fail on the same tracks. Nothing to decorrelate. |
| Evolution-strategy fine-tuning | KILL | A converged champion has nothing to gain from a noisy gradient estimate, only robustness to lose. |
| Multi-car racing | KILL | Slower alone, but its champions sweep the podium when a race turns to carnage. Robustness versus pace, quantified. |
| Doubled training time | KILL | Twice the generations, identical solve rate. Time was not the constraint. |
| Doubled network capacity | KILL | Looked like the breakthrough until measured properly. See Correction 3. |
| Hairpin traps | NO ARM | Measured before building: the champion already handled them at 0% crash. No gap, no experiment, 80 minutes saved. |
1. Measure where the failure is before deciding what to build. A heat-map pointed at corridor width, so the fix was shorter sensor rays — a config one-liner — rather than the bigger brain intuition suggested. On another occasion the measurement showed no gap at all and cancelled a planned experiment outright.
2. Any metric you select on, you will eventually overfit. Champion selection quietly optimised a validation set until fragile cars won. Separating decision-making data from reporting data was worth more than any architectural change.
3. Resolution must match effect size. Learned three times, twice the hard way. An instrument sized for 50% failure rates cannot adjudicate between 4% and 12%, and it will not fail loudly — it will return a confident wrong answer.
4. When adding capability, add a capacity control. Otherwise "the new input helped" and "the bigger network helped" are indistinguishable, and the wrong lesson gets learned and generalised.
5. Run replicates. One run is an anecdote. Three runs revealed that the entire method was performing far worse than any single run suggested — and made every subsequent comparison honest.
6. A well-measured negative result is a real deliverable. Twelve of fifteen experiments failed. Each one narrowed the space of things worth trying next, and several were more informative than the successes.
The final driver clears the frozen 125-track benchmark without a crash, handles corridors that pinch and widen, drives hairpin traps it was never trained on, and carries margin onto low-grip surfaces it has never seen. Its one documented weakness is mid-corridor obstacles, now reduced from 97% to roughly 7–20% and characterised precisely: 83% of the remaining crashes are genuine contacts by cars that can see the obstacle, are paid to avoid it, and have both the time and the capacity to learn. That is a policy class at its limit, not a search problem.
The honest next step is not another parameter. The simulator's episode loop is already shaped like a standard reinforcement-learning environment, so the natural sequel is training a policy-gradient agent on the identical fitness function and racing the two approaches against each other — a genuine comparison of two optimiser families under one measurement regime. It also constitutes a decisive test of the frontier claim above: if a completely different optimiser plateaus in the same place, the conclusion is confirmed independently; if it does not, the conclusion was wrong.