A leg of 501, straight in, double out, against an arm you pick. Hold to steady the dart, let go when the ring is smallest — that ring is your scatter in millimetres, and it is the only thing about your throw the game models. Everything else is the board doing what the board does.
The coach is a dynamic program over the whole leg. It knows the real geometry of a London clock, it knows how wide your misses are, and for every score it can tell you the single point on the board that minimises the number of darts you still have to throw. It is worth turning on, because for most of the people who will ever play this, the answer is not the treble twenty.
The twenty stops being the answer at 16.88 mm
Aiming is a convolution. The score field is fixed; your throw is a Gaussian blur of it; the best place to aim is the peak of the blurred field, which moves as the blur widens. One separable pass gives the expected score of every aim point on the board at once.
| sigma | best aim | E[score] | at T20 | at T19 | at bull | 3-dart avg |
|---|---|---|---|---|---|---|
| 2 mm | T20 | 58.02 | 58.00 | 55.17 | 49.82 | 174.1 |
| 5 mm | T20 | 42.97 | 42.97 | 40.83 | 38.70 | 128.9 |
| 8 mm | T20 | 34.00 | 34.00 | 32.48 | 29.72 | 102.0 |
| 11 mm | T20 | 27.60 | 27.56 | 26.68 | 23.72 | 82.8 |
| 14 mm | T20 | 22.97 | 22.87 | 22.52 | 19.82 | 68.9 |
| 17 mm | T19 @ −35,−100 | 19.75 | 19.55 | 19.60 | 17.31 | 59.2 |
| 20 mm | S19 @ −37,−101 | 17.79 | 17.25 | 17.60 | 15.65 | 53.4 |
| 25 mm | S19 @ −39,−100 | 15.80 | 14.93 | 15.60 | 13.95 | 47.4 |
| 30 mm | T19 @ −43,−93 | 14.70 | 13.64 | 14.48 | 13.00 | 44.1 |
| 35 mm | S7 @ −51,−80 | 14.05 | 12.81 | 13.70 | 12.51 | 42.2 |
| 45 mm | S7 @ −52,−57 | 13.20 | 11.62 | 12.46 | 12.25 | 39.6 |
| 60 mm | S8 @ −38,−19 | 12.43 | 10.25 | 10.89 | 12.13 | 37.3 |
| 80 mm | S8 @ −17,−5 | 11.26 | 8.77 | 9.18 | 11.20 | 33.8 |
The crossing is at sigma = 16.88 mm, found by bisection on the argmax. Below it the peak sits on the treble twenty and nothing else is close. Above it the peak jumps to the 19 — not the treble specifically, but the whole lower-left region around it — and then walks steadily in toward the bull as the scatter widens. Past about 55 mm the answer is the middle of the board, because at that point you are not aiming at a number, you are aiming at the board.
A sigma-25 player aiming where they were told loses 2.6 points per three darts against the same arm aiming where the arithmetic says. That is the whole of the advice on an open board, and it is free.
The price of the neighbours
The twenty is worth more than the nineteen and is a worse place to aim, which only makes sense once you look at what is either side of it.
| treble | E[score] aiming at it, sigma 20 | neighbours | their sum |
|---|---|---|---|
| T19 | 17.60 | 3 / 7 | 10 |
| T20 | 17.25 | 5 / 1 | 6 |
| T16 | 16.59 | 7 / 8 | 15 |
| T14 | 16.25 | 11 / 9 | 20 |
| T18 | 15.49 | 1 / 4 | 5 |
| T15 | 14.84 | 10 / 2 | 12 |
| T7 | 14.77 | 19 / 16 | 35 |
| T17 | 14.74 | 2 / 3 | 5 |
| … | |||
| T1 | 10.94 | 20 / 18 | 38 |
| T6 | 10.92 | 13 / 10 | 23 |
| T2 | 10.14 | 15 / 17 | 32 |
Sixty is three points better than fifty-seven and the flanks give it back with interest: a dart that leaks one sector off the twenty scores 5 or 1, and off the nineteen it scores 3 or 7. That is the entire mechanism, and the numbering is what produces it: the standard clock — attributed, on no paper trail anyone has found, to a Lancashire carpenter named Brian Gamlin in 1896 — scatters the big numbers and seats the small ones beside them, so the board punishes the miss rather than rewarding the hit. The table is that punishment, priced.
It is not simply the neighbour sum, either. The 7 has the friendliest neighbours on the board (19 and 16, sum 35) and still ranks seventh, because seven is seven. The ordering is a fight between what you are aiming at and what is beside it, and at 20 mm of scatter the neighbours are winning.
What a whole leg costs
Aiming is one dart. A leg is a hundred decisions, most of them about what to leave rather than what to score, so the coach solves the leg rather than the dart.
The state is the score you have left; the actions are every point on the board; a dart that would bust costs you the dart and leaves the score alone. Every other dart strictly lowers the score, so the recursion solves exactly in one ordered pass from 2 up to 501 — no value iteration, no convergence to argue about. Against it, two policies a person might actually use: the conventional one (treble twenty until a finish is in range, then set up an even double, preferring 32), and the thoughtless one (treble twenty until you are on a double).
| sigma | optimal | conventional | always T20 | convention costs | 3-dart avg | opening aim |
|---|---|---|---|---|---|---|
| 5 mm | 13.04 | 13.33 | 1204.57 | 0.29 | 115.3 | T20 |
| 10 mm | 19.60 | 20.13 | 30.69 | 0.54 | 76.7 | T20 |
| 15 mm | 27.32 | 28.30 | 32.39 | 0.98 | 55.0 | T20 |
| 20 mm | 34.89 | 36.80 | 39.27 | 1.91 | 43.1 | T19 @ −35,−100 |
| 25 mm | 41.62 | 44.61 | 46.32 | 2.98 | 36.1 | S19 @ −40,−100 |
| 30 mm | 47.79 | 51.62 | 52.88 | 3.83 | 31.5 | T19 @ −45,−90 |
| 40 mm | 59.28 | 64.76 | 65.54 | 5.48 | 25.4 | S7 @ −55,−70 |
| 55 mm | 77.12 | 85.03 | 85.51 | 7.91 | 19.5 | S16 @ −45,−30 |
Read the top-right cell first. A player with 5 mm of scatter — better than anyone alive — who insists on the treble twenty until they are on a double needs 1204 darts to finish a leg. They are not missing. That is the point: they hit a hundred and eighty, land on 41, throw at the treble twenty, bust, and do it again, for ever. Accuracy without a checkout is a trap, and the better the arm, the deeper it goes. It is the only number on this page that gets worse as the player gets better.
Everywhere else convention is close to right and quietly expensive, and the gap grows with scatter: half a dart a leg for a good arm, five and a half for a poor one. Convention was written down by people who could already throw.
What the solver says to aim at
| left | convention | sigma 12 | sigma 25 | sigma 40 |
|---|---|---|---|---|
| 170 | T20 | T20 (9.8) | S19 @ −40,−100 (20.7) | S7 @ −55,−70 (34.9) |
| 141 | T20 | T20 (8.7) | S19 @ −40,−100 (18.8) | S7 @ −55,−70 (32.8) |
| 110 | T20 | T20 (7.5) | S19 @ −40,−100 (16.9) | S7 @ −50,−70 (30.5) |
| 100 | T20 | T20 (6.9) | S20 @ −5,+110 (16.2) | S7 @ −55,−65 (29.8) |
| 81 | T19 | T19 (6.5) | T14 @ −100,+25 (15.1) | S7 @ −50,−70 (28.4) |
| 60 | S20 | S20 @ +0,+140 (5.3) | S20 @ +0,+135 (13.6) | T20 @ −5,+100 (26.8) |
| 50 | BULL | S18 @ +80,+110 (5.1) | BULL (13.2) | 25 @ −10,+10 (26.0) |
| 41 | S9 | S9 @ −110,+80 (5.1) | T3 @ +5,−105 (13.0) | S3 @ −10,−90 (25.6) |
| 40 | D20 | D20 @ +0,+166 (4.2) | D20 @ +0,+170 (11.8) | D20 @ +0,+170 (24.5) |
| 36 | D18 | D18 (4.3) | D18 (11.9) | D18 (24.5) |
| 32 | D16 | D16 (4.0) | D16 (11.4) | D16 (24.1) |
| 16 | D8 | D8 (4.0) | D8 (11.1) | D8 (23.5) |
| 2 | D1 | D1 (3.9) | D1 (10.7) | D1 (21.9) |
Two things worth staring at.
On 50, a sigma-12 player is told not to go for the bull. They are told to throw at the fat single 18 and leave 32, because the bull is a 6.35 mm circle and 32 is the best number on the board to be left on. A sigma-25 player is sent at the bull, for the ugly reason that they were never going to hit the 18 reliably either.
On 81, the sigma-25 player is sent to the treble 14, on the far left. Not because they will hit it. Because the neighbourhood is 11 and 9 and the singles around them leave numbers you can work with, where a miss off the T19 leaves 39 and a stranded odd number. At that scatter you are not choosing a target, you are choosing a region, and the solver has been choosing regions the whole time — that is why so many of its answers are bare coordinates rather than a name.
Which side of a double to miss
Ask the solver what each radius along a sector is worth when that double is the whole checkout, and it does not pick the middle of the ring.
| left | best radius | bias off the ring centre | p(double) | E[darts] here | E[darts] in the single | slipping in costs |
|---|---|---|---|---|---|---|
| 40 (D20) | 176 mm | +10 mm | 0.098 | 11.76 | 11.91 | +0.14 |
| 20 (D10) | 171 mm | +5 mm | 0.093 | 11.91 | 12.19 | +0.28 |
| 16 (D8) | 170 mm | +4 mm | 0.094 | 11.09 | 10.87 | −0.22 |
| 32 (D16) | 167 mm | +1 mm | 0.090 | 11.38 | 11.09 | −0.29 |
| 36 (D18) | 167 mm | +1 mm | 0.090 | 11.88 | 12.08 | +0.20 |
| 24 (D12) | 167 mm | +1 mm | 0.090 | 11.67 | 11.38 | −0.30 |
| 8 (D4) | 165 mm | −1 mm | 0.090 | 10.87 | 10.78 | −0.09 |
| 4 (D2) | 165 mm | −1 mm | 0.090 | 10.78 | 10.66 | −0.12 |
The double ring runs 162 to 170 mm. A best radius of 176 is a dart aimed off the board — past the wire, at nothing. On 40 the solver would genuinely rather throw a dart into the wall than into the single twenty, because a dart at nothing costs exactly one dart and leaves you on 40, while a dart in the single leaves you on 20, and 20 is a worse number than 40. It gives up half a percentage point of double for it.
Be honest about the size of this: the curve is flat, and aiming at 166 instead of 176 costs 0.10 darts. The effect is real, it is measured, and only on the twenty is it worth a sentence out loud. Everywhere else the bias is a millimetre or four and the reading is just that the outer single is a 55 mm band of a number you did not want, while outside the wire there is nothing at all — and nothing keeps the score you already had.
Good numbers, bad numbers, cliffs
| rank | best leave under 61 | E[darts] | worst leave under 61 | E[darts] |
|---|---|---|---|---|
| 1 | 2 | 10.66 | 60 | 13.58 |
| 2 | 4 | 10.78 | 57 | 13.50 |
| 3 | 8 | 10.87 | 55 | 13.47 |
| 4 | 16 | 11.09 | 58 | 13.46 |
| 5 | 12 | 11.38 | 53 | 13.40 |
| 6 | 32 | 11.38 | 59 | 13.37 |
| 7 | 24 | 11.67 | 56 | 13.24 |
| 8 | 40 | 11.76 | 52 | 13.22 |
Sixty is the worst number under 61 to be left on, and it is worth nearly three darts more than 2. The ladder at the top is the famous one — 32, 16, 8, 4, 2 — and it is famous because it is the only chain on the board where missing into the single hands you another number on the same chain. Leave 40 and a slipped dart puts you on 20, then 10, then 5, and 5 is odd and you are setting up all over again.
The cost of one point:
| from | E[darts] | to | E[darts] | one point costs |
|---|---|---|---|---|
| 2 | 10.66 | 3 | 12.77 | 2.11 |
| 4 | 10.78 | 5 | 12.72 | 1.94 |
| 8 | 10.87 | 9 | 12.76 | 1.89 |
| 16 | 11.09 | 17 | 12.77 | 1.68 |
| 32 | 11.38 | 33 | 12.83 | 1.45 |
| 40 | 11.76 | 41 | 12.98 | 1.21 |
Expected darts is not monotone in the score. Leaving 41 instead of 40 — one point more on the board, which sounds like nothing — costs a sigma-25 player 1.2 darts, every time. That is the arithmetic behind every mutter about “leaving yourself a shot”, and it is worth about a fifth of a leg.
Where the model is optimistic, measured
The dynamic program is turn-blind: it prices a bust at one dart. A real bust throws away the rest of the visit, and the two darts still in your hand were worth something.
| sigma | DP says | simulated, 4000 legs | the visit tax | busts per leg | 3-dart avg |
|---|---|---|---|---|---|
| 10 mm | 19.60 | 19.60 | 0.00 | 0.13 | 76.7 |
| 20 mm | 34.89 | 35.29 | 0.40 | 1.43 | 42.6 |
| 25 mm | 41.62 | 42.70 | 1.07 | 2.75 | 35.2 |
| 40 mm | 59.28 | 63.78 | 4.51 | 9.62 | 23.6 |
The tax is nothing for a good arm and four and a half darts for a bad one, which is exactly where you would expect an assumption about busts to bite. The simulation is the real rule — three darts a visit, a bust reverts the visit — played by the turn-blind policy, so the gap is the price of the simplification and not of anything else.
It also reorders the leaves, and it reorders them in the direction the pub already believes:
| leave | turn-blind E[darts] | rank | simulated darts | rank | the visit tax |
|---|---|---|---|---|---|
| 2 | 10.66 | 1 | 11.26 | 3 | 0.60 |
| 4 | 10.78 | 2 | 11.23 | 2 | 0.45 |
| 8 | 10.87 | 3 | 11.20 | 1 | 0.33 |
| 16 | 11.09 | 4 | 11.57 | 4 | 0.47 |
| 32 | 11.38 | 5 | 11.90 | 5 | 0.51 |
| 40 | 11.76 | 7 | 12.50 | 6 | 0.74 |
| 24 | 11.67 | 6 | 12.76 | 9 | 1.09 |
| 50 | 13.19 | 10 | 14.07 | 10 | 0.88 |
| 60 | 13.58 | 11 | 14.49 | 11 | 0.91 |
Two is the best leave the turn-blind model can imagine, because from 2 there is literally nothing bad that can happen: you hit D1 or you stay on 2. Once a bust costs the visit, the same fact becomes the problem — every dart that is not the double is a wasted visit — and 2 drops to third behind 8, which can at least slip to 4. 24 falls three places for the same reason.
Same arm, different advice
1001 legs, same scatter, same random stream, one player following the solver and the other following convention.
| sigma | solver darts | convention darts | saved | solver wins the leg | drawn |
|---|---|---|---|---|---|
| 10 mm | 19.48 | 20.16 | 0.68 | 51.7% | 8.2% |
| 20 mm | 34.89 | 37.13 | 2.23 | 56.5% | 3.5% |
| 25 mm | 42.85 | 45.50 | 2.65 | 53.1% | 2.9% |
| 40 mm | 62.73 | 69.14 | 6.41 | 56.8% | 1.0% |
A 57% edge from advice alone, at the level where most people actually throw. Not enough to beat a better arm, which is the honest ceiling on all of this: the solver’s whole contribution at 40 mm of scatter is six darts, and moving from 40 mm to 25 mm is worth twenty.
Playing it
Hold the pointer anywhere on the board, or hold space, and the ring around your hand shrinks as you settle. Hold too long and it grows again. Let go at the bottom of that curve — about 0.85 seconds — and you throw with about 11 mm of scatter; snatch it and you throw with 40. Arrow keys nudge the aim, C toggles the coach, H the heat map, R resets.
The heat map is the leg’s value function for your current score, drawn as a spotlight: the part of the board still lit is the part worth aiming at, and the dashed circle is the single best point with the expected darts to go beside it. Watch what happens to it when you cross 170, and again when you get down to a double.
The scatter the coach uses is yours, fitted from where your darts actually went against where you were aiming — two degrees of freedom per dart, so it settles down after about a dozen. Throw badly for a visit and watch the advice move.
Reuse
src/oche.mjs is headless and framework-free. Millimetres, origin at the bull,
+y up.
scoreAt(x, y)→ value, multiplier, ring, sector, label, and whether it ends a leg.sectorAt,neighbours(n),targetPoint('T19'),TARGETS.buildField()/field()→ the 1 mm lattice, each cell carrying its score and the outcomes it straddles at 4×4 subsampling. Quantising an 8 mm treble ring to whole millimetres is wrong by about a tenth of its width; this is the fix.evField(sigma)→ expected score at every aim point, by separable convolution.bestAim(sigma)→ the peak.outcomeDist(aim, sigma)/sparseDist→ the probability of each of the 63 outcomes.expectedScore(aim, sigma).precompute(sigma, { aims })thensolve(sigma, pre)→ exact expected darts from every score, and the aim that achieves it. One ordered pass.evaluate(policy, sigma, pre)→ the same recursion under a policy you hand it, so convention can be priced on the same board.conventionalTarget(s),POLICIES.conventional,POLICIES.t20.playLeg(sigma, aimFor, rng, { start })→ the real rule, three darts a visit, a bust reverts the visit.throwDart(aim, sigma, rng, { ratio }),fitSigma(darts),makeRng(seed).
node scripts/measure.mjs prints every table on this page (or one section:
aim, trebles, darts, checkout, leaves, sim, match, double).
node scripts/smoke.mjs plays a leg in a real browser at two viewport sizes
and fails on any page error. node scripts/screenshot-demo.mjs regenerates the
thumbnail and the media shots through the demo’s own hooks.
Gotchas
- A miss is a self-loop, not a transition. The first solver returned infinity for every score, because a dart worth zero points leaves the score unchanged, and the recursion dutifully looked up a value it was in the middle of computing. Scoring nothing and busting are the same thing to the solver: you paid a dart and nothing moved.
- The aim set is a lattice, not a list of targets. Restricted to the 62 named regions, the solver’s advice is worse and much less interesting — half of what it wants to do is aim at the seam between two numbers. The write-up uses a 5 mm lattice; the demo uses 9 mm to keep the in-browser solve to about a third of a second.
- Contrast-stretch the heat map or it draws nothing. Expected darts varies by a couple of percent across most of the board, so normalising to the worst cell produces a flat wash. It normalises to the first quartile instead, which is why the lit patch is a patch rather than a gradient.
demo/bundles its own copy ofoche.mjs— self-contained by contract (ADR-0002). Re-copy after editingsrc/.
What this is not
The throw model is one isotropic Gaussian. Real darts scatter more vertically
than horizontally, real players are worse at the doubles than their scoring
sigma predicts, and nobody’s sigma is a constant — it moves with the score,
the leg, and whether anyone is watching. throwDart takes a ratio for the
first of those and the rest are absent by choice.
The solver is turn-blind, which the tables above measure rather than hide, and it optimises expected darts rather than the probability of winning a race. Those differ: a player two visits behind should be taking shots that a darts-minimiser would not. That is a different dynamic program, over two scores instead of one, and it is not this one.



