Backgammon AI Play

This page was mostly written by CLAUDE AI, with the instructions to be technical and include all information required to program this software yourself. It is the technical companion to the User's Guide, which describes the controls. Several of those controls — the depth menus, the brain editor, EVOLVE, LEARN, COMPETE and TOURNAMENT — only make sense once you know what is behind them. Where an algorithm is easier to state as a program than as prose it is given as pseudocode.

There are two kinds of artificial player here, developed one after the other and both still in use. The older kind judges a position by adding up a couple of dozen hand-chosen features with weights found by an evolutionary search; the newer kind judges it with a neural network that taught itself by playing against itself. They share the same search, the same rules and the same tournament machinery, and they can play each other. Sections 2 to 4 describe the first kind, section 5 the second, and section 6 how either is measured.

The Table of Contents below takes you to any part of this page. Clicking on a symbol like this    anywhere on the page brings you back here.

Table of Contents

1. Two Kinds of Brain

1.1. What both kinds share

A brain is whatever answers one question: given a position, how good is it? Everything else — how the legal moves are generated, how far ahead the game is searched, how a match is scored, how a tournament is run — is the same for both kinds and is described in section 2. That is not a tidy coincidence but a deliberate design: the two kinds of brain meet the rest of the program through a single function, so a net can be dropped into any seat a parameter AI can occupy, and the two can play each other in the same match.

Both kinds work on the same board model. The points are numbered 1 to 24 in a fixed absolute frame. White moves from 24 towards 1 and bears off from points 1–6; Red moves from 1 towards 24 and bears off from 19–24. The bar is treated as a virtual point one step behind each player's entry edge: point 25 for White, point 0 for Red. A position is therefore three things: the 24 points, each holding a count and an owner; the two bar counts; and the two borne-off counts.

Both kinds also answer in a single currency and from a single point of view. Every value in the search is stated from White's point of view: positive is good for White, negative is good for Red. White maximizes it and Red minimizes it. The two kinds do not use the same units — that is the one place they differ visibly, and section 1.2 says what the units are — but within one search there is only ever one brain's currency, because a search is one player deciding.

1.2. What tells them apart

Parameter AIJudges a position by measuring about twenty features of it — the race, blots, primes, made points — and adding them up with 24 weights, of which 22 score the board and 2 govern the doubling cube. The result is an integer in a band of roughly ±1000 with no probabilistic meaning: it is a comparison device, not a prediction. You can read every weight, edit it, export it and evolve it. Section 3.
NetJudges a position with a neural network that predicts the probability of each of the six ways the game can end. Its value is therefore an equity: the expected number of points, between −3 and +3. Its knowledge is spread over tens of thousands of numbers that mean nothing individually; it cannot be edited or evolved, only trained and measured. Section 5.

The consequence you see on screen is that the two report differently. A parameter AI's judgment of a position appears as a band number such as 332; a net's appears as a probability, 55%, or as an equity, +0.298. They are deliberately not rescaled onto a common axis, because there is no honest conversion between them: one is a heuristic ordering and the other is a prediction.

2. Choosing a Move: the Search

2.1. Legal complete turns

A backgammon turn is not one move but a sequence: two moves for an ordinary roll, four for a double, and fewer only when the rules leave no choice. The rules also require you to use as much of the roll as you can — both dice if any legal sequence plays both, the higher die if only one can be played, as many as possible for a double. So the unit the AI chooses between is not a single checker move but a complete turn: an entire legal, maximum-usage sequence, together with the position it leads to.

The generator therefore produces every distinct complete turn from a position and a roll, de-duplicated by the resulting position rather than by the sequence of moves, since the same position can usually be reached by playing the dice in either order and there is no sense in evaluating it twice. An opening position offers roughly 15 to 20 distinct complete turns for a typical roll; a double with the whole army in play can offer many more. If a roll admits no legal play at all, the generator returns the position unchanged as a single "turn", so a dance needs no special case anywhere in the search.

2.2. How deep: the depth menus

The search depth, set in the depth menus of the control column, is measured in plies. A ply is one player's turn — a half move.

  1. Depth 0 — no judgment at all: the first legal complete turn the generator produced is played. It is not a way to play; it exists as a floor to measure against.
  2. Depth 1 — apply the static judgment to every position reachable with the current roll and take the best. No future roll is considered. This is the default and it is instant.
  3. Depth 2 — for each of my candidate turns, consider what the opponent would do with each of the 21 distinct rolls he might get, average those values, and choose the candidate with the best average.
  4. Depth 3 — the same, one turn deeper: my move, his roll and reply, my next roll and reply.

⚠ The numbering here is one higher than the literature's. What this page calls depth 1 — evaluate every legal turn, take the best, look no further — is what GNU Backgammon and the TD-Gammon papers call 0-ply, because no future roll is averaged over. So depth 2 here is 1-ply there, and depth 3 is 2-ply. Depth 0 has no counterpart anywhere else. When comparing figures on this page with published ones, line them up that way.

2.3. Why expectimax and not minimax

The minimax search used for chess, and for the Mill and Reversi games elsewhere on this site, assumes the opponent moves next and chooses. In backgammon he does not: the dice choose first. The correct search alternates two kinds of node.

decisionA player is on roll with a known roll and picks a complete turn. White takes the maximum of the values below, Red the minimum — one currency, two directions.
chanceA roll is about to happen. The value is the average over the 21 distinct rolls, each weighted by how often it occurs: a non-double two ways out of 36, a double one way. The weights sum to 36, so a chance node is $\left(\sum_r w_r\,V_r\right)/36$.

This is expectimax. Note that averaging happens over the dice, never over the opponent's choices: given his roll he is assumed to play his best, in the same currency, which is the usual zero-sum assumption. It is an approximation whenever the two players are different brains, since the search models the opponent as a copy of the deciding brain — but it is the only assumption available, and the error is second order.

Alpha-beta pruning, which makes minimax practical in chess, helps very little here: a chance node has to inspect essentially all of its children to know their average, so the pruning that makes deep chess search possible has almost nothing to bite on. Sharper methods exist for expectimax trees (the *-minimax family) and none is implemented; the cost figures in section 2.7 are therefore the cost of a full search.

2.4. The algorithm

The whole search is three short functions. leafValue is the seam described in section 1.1: it is the only place a brain is consulted, and it is where the two kinds of brain differ.

Choosing a move at the root
function bestTurn(position, player, roll, depth, brain):
    turns = generateCompleteTurns(position, player, roll)
    if turns is empty: return none
    if depth == 0: return turns[0]                  // depth 0 plays the first one
    opponent = other(player)
    best = none
    bestValue = (player == White) ? -infinity : +infinity
    for turn in turns:
        v = terminalValue(turn, brain)                 // null unless the game just ended
        if v == null:
            if depth == 1: v = leafValue(turn, brain, onRoll = opponent)
            else:          v = expecti(turn, opponent, depth - 1, brain)
        v = v + rootBonus(turn, brain, player)         // section 2.6; zero for a net
        if (player == White) ? (v > bestValue) : (v < bestValue):
            bestValue = v; best = turn
    return best
A chance node: average over the 21 rolls
function expecti(position, player, plies, brain):
    v = terminalValue(position, brain)
    if v != null: return v
    total = 0
    for (d1, d2, weight) in DICE:                   // 21 entries, weights sum to 36
        dice = (d1 == d2) ? [d1,d1,d1,d1] : [d1,d2]
        total = total + weight * rollValue(position, player, dice, plies, brain)
    return total / 36
A decision node: the best reply to one known roll
function rollValue(position, player, dice, plies, brain):
    opponent = other(player)
    best = (player == White) ? -infinity : +infinity
    for turn in generateCompleteTurns(position, player, dice):
        v = terminalValue(turn, brain)
        if v == null:
            if plies <= 1: v = leafValue(turn, brain, onRoll = opponent)
            else:         v = expecti(turn, opponent, plies - 1, brain)
        best = (player == White) ? max(best, v) : min(best, v)
    return best

Three details are worth keeping if you reimplement this. The turn generator must accept an arbitrary position, not just the current one, since the search asks it about positions that were never played. A roll with no legal play must yield one "turn" equal to the position itself, so that a dance costs no special case. And leafValue is told who is on roll at the leaf: a parameter AI ignores that argument, but a net's answer depends on it entirely (section 5.6).

2.5. Positions that are already decided

A position in which one side has borne off all fifteen checkers is not judged, it is known, and the two kinds of brain express that differently.

A parameter AI uses a sentinel: a win is worth $10^9$ and a loss $-10^9$, so that a certain win outranks every heuristic score. A net uses the exact equity of the finished game — ±1 for a single, ±2 for a gammon, ±3 for a backgammon — because that is the scale everything else it says is on. The side effect was accepted deliberately: for a net, a certain single win (+1) does not outrank every alternative, so a position it reads at +1.4 can be preferred to winning at once. That is correct play, not a bug — it is the search declining to take a single when a gammon is nearly certain.

2.6. Two adjustments made only at the root

Two terms are added when the AI actually chooses a move, and are not part of any position's stored value. Both apply to parameter AIs only.

  1. The disengagement bonus, DE. One of the 22 weights. It is added to a candidate turn that breaks all contact while the mover is ahead in the race: $+\mathrm{DE}$ for White, $-\mathrm{DE}$ for Red. A positive DE says "race away when you are ahead", a negative one says "keep the contact". It is deliberately excluded from the search interior: inside the tree it would be counted repeatedly for the same disengagement.
  2. The race tie-break. Once contact is broken the static score ties for almost every legal turn — see section 3.8 for why — and something has to decide. The tie-break prefers the turn that leaves the mover with fewer pips still to travel before every checker is home: $\sum_{p>6}\,\mathrm{count}_p\cdot(p-6)$ for White, the mirror for Red, with a checker on the bar counted as 19. It is scaled by $\varepsilon = 0.001$, and its largest possible value is $15\cdot19\cdot\varepsilon = 0.285$, safely under the half-unit that separates two different integer scores. So it breaks ties and can never overrule a real difference.

A net gets neither. DE is a weight it does not have, and its equity is of order 1 rather than of order 1000, so an $\varepsilon$ of 0.001 would be a real perturbation rather than a tie-break. It needs neither: it does not tie, and it prices a race directly.

2.7. What a ply costs

Each extra ply multiplies the work by the number of dice outcomes times the number of replies to each: 21 rolls by about 18 to 20 complete turns, so roughly 400. That is not a rule of thumb, it is what the measurement says. The figures below are single-threaded, on one core of a 32-thread desktop, and you can reproduce the ratio (though not the exact numbers, which are your machine's) by running COMPETE at two depths and comparing how long each takes.

depthtime per move, one coretimes the previous ply
00.14 ms
10.58 ms4
2245 ms419
3~89 s364

Divide the two large factors by 21 and you get 20.0 and 17.3 — the average number of legal complete turns, exactly as the arithmetic predicts, with nothing unexplained hiding in the constant. Depth 0 to 1 is only a factor of 4 because depth 0 does no evaluation at all.

The practical consequences run through the whole design. Depth 2 is comfortable for interactive play and affordable for a tournament. Depth 3 is a fine setting for a person taking their time over one move, and it is not a tournament setting: a single depth-3 decision is tens of seconds, so one match is hours.

2.8. Running the search in parallel

The game uses as many Workers — independent processes in your browser — as row 1 of the control column allows, and it splits the work two different ways depending on what is being done.

batchA tournament, a competition, an evolution run or a training run gives each worker one whole match (or one whole self-play game). The jobs are independent and there is nothing to combine, so throughput is very nearly the number of workers.
interactiveOne move at depth 2 or more is split into one job per (my candidate turn × opponent's roll) pair — a few hundred jobs — and the results are recombined exactly as the serial search would: sum $w_r V_r/36$ within a candidate, then take the maximum for White or the minimum for Red.

The parallel decomposition returns the same move as the serial search, not merely a similar one; that equality has been verified position by position, including at depth 3, against an independent serial implementation. That matters because it means the depth menus mean the same thing whether or not your browser gave the game any workers.

Two consequences of the batch split are worth knowing when you time something. A run of one match uses exactly one worker however many you have, because the unit of work is a whole match. And a run of exactly as many matches as workers is the worst case for measuring speedup, since the wall clock then collapses to the single longest match: to see the real throughput, give the pool several times as many matches as it has workers.

3. The Parameter AI

3.1. The static evaluation

The static evaluation takes a position and returns one number, the score, saying how good it is for White. Every feature is measured for both sides and entered as a difference, White minus Red. With $w_i$ for the weights and $f_i$ for the features,

$ \displaystyle S \;=\; \sum_i w_i\,\bigl(f_i^{\,\text{White}} - f_i^{\,\text{Red}}\bigr). $

Writing it as a difference buys an exact property, and the property is worth more than the tidiness: the score is anti-symmetric. Mirror a position — swap the colors, reflect point $i$ to $25-i$, swap the bars and the trays — and the score must come out exactly negated. That is the strongest cheap correctness check available for an evaluation function, since a feature that scans the board in one direction for White and the other for Red is easy to get subtly wrong, and two real bugs were caught this way. It is tested by mirroring some twenty thousand positions from real games after every change.

The score is rounded to a whole number, rounding halves away from zero rather than JavaScript's default of rounding towards $+\infty$, since the default would break the anti-symmetry on positions landing exactly on a half.

There are 22 such weights, plus 2 more for the cube (section 3.10), which is why the parameter grid in the control column has 24 fields. Several of the features are divided by a constant before being weighted — the pip count by 20, borne-off checkers by 15, the encumbrance counts by 36 — purely so that every weight lives in the same numeric band. Those divisors are a representation choice and nothing else; when the pip divisor was changed from 167 to 20, every brain's PC weight was rescaled by the same factor and no brain's play changed at all.

3.2. The race: PC and BO

PCPip count. How far a side still has to travel: $\sum_p \mathrm{count}_p \cdot \mathrm{distance}_p$, with a checker on the bar counted from its entry edge. Entered as $(\mathrm{pip}_W - \mathrm{pip}_R)/20$ and carried with a negative weight, so that having fewer pips left is good.
BOBorne off. $(\mathrm{off}_W - \mathrm{off}_R)/15$, positive weight. It exists because the pip count cannot tell bearing a checker off from stacking it deeper in the home board — both reduce the pip count by the same amount — and without it brains piled checkers on the 1-point instead of taking them off.

⚠ The pip count is nearly blind to move choice, and this shapes the whole roster. Whatever you do with a roll, your own pip count falls by exactly the dice total. So PC does not distinguish one legal turn from another except through hits, which put the opponent back, and through the efficiency of a bear-off. Section 4.6 returns to this: it is the reason a "pure racer" still hits, and hits soundly.

3.3. Mobility: EC1 and EC0

Both count over the 36 ordered dice rolls, weighted by frequency, how badly a side would be stuck. For each roll the generator is asked how many dice could be used at all.

EC1Encumbrance, one die. The number of the 36 rolls that would let the side use exactly one die. Divided by 36, negative weight.
EC0Encumbrance, no die. The number of the 36 rolls that would let it use none — a dance. Divided by 36, negative weight.

These are expensive features: computing them means asking, 21 times per side, how much of a roll is playable. They are also the two weights evolution has been most consistent about, with EC0 pinned near the bottom of the band in almost every brain in the roster.

3.4. Containment: the escape count and F0–F5

Six weights grade how trapped a side's checkers are. They rest on one primitive, the escape count of a single checker: how many of the six die-values would take it past the front of the wall in front of it. The wall matters, not the next square: a checker with an open point immediately ahead but a five-prime beyond it is trapped, and the escape count says so.

The escape count of one checker (White; Red is the mirror)
function escapeCount(points, p, me):                // p = 25 means a checker on the bar
    opponent = other(me)
    made(t) = (t is on the board) and points[t].owner == opponent and points[t].count >= 2
    // find the first opposing point ahead of us, and the far end of the block it belongs to
    back = the largest t < p with made(t), or none
    if back == none:                                // nothing ahead: count open landings
        return #{ d in 1..6 : p-d < 1 or not made(p-d) }
    front = back;  while made(front - 1): front = front - 1
    if back - front + 1 == 1:                        // a LONE point: weave around it
        return #{ d in 1..6 : p-d < 1 or not made(p-d) }
    return #{ d in 1..6 : p-d < 1 or (p-d < front and not made(p-d)) }

Landing beyond the edge of the board — bearing off — counts as an escape. The result is 0 for a checker that is sealed in and 6 for one that is free.

F0Sealed. No die frees the checker: entombed behind a full prime, or on the bar against a closed board. The worst place a checker can be.
F1 … F5One, two, … five of the six dice free it.

Each side's checkers are counted into these six buckets — a checker on the bar included, as a virtual checker at point 25 or 0 — and the term is $\sum_{n=0}^{5} w_{Fn}\,(\mathrm{White}_n - \mathrm{Red}_n)$.

There is deliberately no F6. A completely free checker carries no information: the count of free checkers is 15 minus the others and minus those borne off, so a sixth weight would be a linear combination of the ones already there, and it would overlap with BO.

3.5. The bar: BE

A checker on the bar is worse than a checker on a point with the same escape count, because it freezes the whole army: nothing else may move until it enters. BE prices that at one weight per expected frozen turn, added on top of the checker's ordinary F-term.

If a bar checker enters on $e$ of the six die-values, both dice miss with probability $\left(\frac{6-e}{6} \right)^{2}$, and the expected number of frozen turns is the geometric sum $\frac{q}{1-q}$ with that $q$:

escape count123456
expected frozen turns2.270.800.330.1250.0290

A sealed bar checker (escape count 0) is the interesting case, because how long it stays there is not a dice question but a question about the opponent: he must break his closed board when he runs out of spare pips. A bare closed board is twelve checkers on six points, or 42 pips, and about 8 pips are played per turn, so the freeze is priced at $(\mathrm{opponent's\ pips} - 42)/8$, floored at 0 and capped at 8 turns.

The term is $\mathrm{BE}\cdot(\sum_{\text{Red}} \mathrm{freeze} - \sum_{\text{White}} \mathrm{freeze})$: a White checker on the bar hurts White, a Red one helps him. Note that the sealed case reads the opponent's pip count, which makes this a direction-dependent feature and therefore one to re-check against the mirror test after any change.

3.6. Made points: HB, G5, G7, G4, AN, GA

A point is made when a side has two or more checkers on it. Not all made points are worth the same, so the valuable ones carry their own weights and the rest are counted in bulk. All of these are owner-relative: White's $n$-point is board point $n$, Red's is board point $25-n$, which is what keeps them anti-symmetric.

G5The golden point — the owner's 5-point. The single most valuable point on the board.
G7The bar point — the owner's 7-point.
G4The owner's 4-point. With G5 and G7 it completes the prime-building trio.
HBHome-board points, excluding the 4- and 5-points, which have their own weights above: so it counts the owner's 1-, 2-, 3- and 6-points.
GAThe golden anchor — the owner's 20-point, which is the opponent's 5-point, held from the back. Counted only while the owner is behind in the race, since an anchor is a defensive asset.
ANAnchors: points made in the opponent's home board, excluding the golden anchor. Same behind-in-the-race gate.

The exclusions matter: HB and AN cover exactly the points the four golden weights do not, so nothing is counted twice. The same board point can read as White's G5 and as Red's GA — point 5 is White's 5-point and Red's 20-point — but only one of them owns it, so there is no conflict.

These six weights were the change that broke a long-standing deadlock. Before they existed, evolution converged on pure racing every time; with them, and with the race terms deliberately reined in so they no longer dominated the scale, a positional brain that prizes the 5-point finally won the roster.

3.7. Blots: DO, IO, DP, IP

A blot is a lone checker, which may be hit. For every blot on the board the evaluation asks whether the opponent can hit it with a single die (direct) or with a combination of dice landing somewhere legal on the way (indirect), and whether the blot stands in the near half of its owner's board or the far half. That is four weights.

DP / IPDirect / indirect shot at a blot in the near half — points 1–12 for White, 13–24 for Red.
DO / IODirect / indirect shot at a blot in the far half.

Each threatened blot contributes its weight once, signed so that a blot belonging to Red is good for White. The same number therefore describes both an exposure and a threat: they are the same event seen from two sides, which is why four weights suffice where an earlier version of the evaluation had eight.

One simplification is deliberate: when the hitter has a checker on the bar, only hits that enter from the bar — either directly, or entering and continuing with the other die — are counted. That under-counts a few shots and never over-counts.

3.8. What switches off in a race

Two sides are in contact while the rearmost White checker is still behind the rearmost Red one, so that a hit is possible; a checker on the bar always counts as in contact. Once contact is broken the game is a pure race, and most of the evaluation is then measuring things that cannot happen: a made point blocks nobody, an anchor traps nobody, a blot cannot be hit. So HB, G5, G7, G4, AN and GA are all switched off when there is no contact, and the blot and containment terms fall to zero on their own.

This is a symmetric $0/1$ scalar multiplying anti-symmetric terms, so it leaves the mirror property intact.

⚠ It also leaves the evaluation with almost nothing to say. In a pure race PC is constant across move choices, BO is unchanged until you start bearing off, and everything else is gated off — so the integer score is frequently identical for every legal turn, and the tie-break of section 2.6 decides. That is a known weakness of this evaluation, not a subtlety: in the race the tie-break is effectively the whole policy. The net has no such gap, which is one reason it is stronger.

3.9. Normalization

Only the proportions between the weights affect play: multiplying every weight by a positive constant multiplies every score by it and changes no comparison. Brains are therefore normalized so that the largest evaluation weight in absolute value is exactly 1000.

The rule has two details that matter. The scale factor is chosen from the 22 evaluation weights only — the two cube thresholds do not get a vote — but it is then applied to all 24, because DT and AT are compared against scores: if every score doubles, the thresholds must double too or the doubling behavior changes. And a weight that lands exactly on zero is set to $\pm 1$ rather than left at zero, so that no feature can vanish from a brain irrecoverably.

3.10. The cube: DT and AT

A parameter AI's score has no probabilistic meaning — there is no way to derive a take point from "$-603$ times a pip difference" — so its cube policy is two more evolvable numbers, in the same units as the score. Let the own score of a player be the static score seen from that player's side (the score for White, its negation for Red).

DTDouble threshold. Offer a double, or redouble, when own score $>$ DT.
ATAccept threshold. Accept an offered double unless own score $< -$AT.

A sensible take window needs AT larger than DT. Evolved brains vary, and a few invert it — an aggressive doubler that is also a tight taker. Note that this rule has no upper end: there is no "too good to double", so a parameter AI will double away a position it should have played on for the gammon. The net's cube (section 5.11) does not have that defect, and gets it for free rather than by a special rule.

3.11. The current roster

The site ships with eight named brains kept in order of playing strength, Arwen strongest, and re-sorted after each tournament, plus Origin, the hand-guessed historic baseline, which never changes and is always listed last. The table below is a snapshot; the live values are always in the parameter grid of the control column, and can be exported from there.

Brain PCBOEC1EC0F0F1F2F3F4F5BEHBG5G7G4ANGADOIODPIPDEDTAT
Arwen-6031000-70-970-16-19-20-6-7-18292911338-68-471010290594308735
Bilbo-435633-62-1000-13-14-21-9-5-1928228631-46-41611205603241836
Celebrian-412738-60-1000-11-12-19-8-4-1723190625-41-33610168583236686
Dwalin-778834-184-1000-13-13-22-3-10-210663821726-32-43189373224400999
Eowyn-43322-140-1000-8-15-22-6-8-27661812130-19-801912267324330588
Frodo-90332-153-1000-8-16-25-12-10-616582811844-47-6118134694663981009
Galadriel-1000492-118-771-7-8-25-6-9-211272271223-46-52913298454322716
Hamfast-978351-117-1000-73-57-7-20-21-41426827710539-3-9943472567220857
Origin-80500-133-400000000013313301332002001000600500300667100200

A glance down the table tells a story. Origin, the hand-tuned baseline, pours its weight into hitting (DO = 1000), knows nothing of containment (F0–F5 and BE are all zero, since those features did not exist when it was written), and doubles on very little (DT = 100). The evolved brains do the opposite: they hold hitting to almost nothing, prize the race (PC), bearing off (BO) and the golden 5-point, and double far later. That is why they beat the original so decisively.

4. Evolution: Where the Numbers Come From

None of the values in the table above were chosen by hand — except Origin's, which were guessed at the start of the project and never touched since. All the others were evolved, by the EVOLVE button in row 14 of the control column.

The difficulty is that there is no formula telling us whether one set of 24 numbers is better than another. The only honest test is to let two brains play and see which wins more — and because backgammon is a dice game, even that answer is noisy: a weaker brain wins a short series often enough. So the search has to be driven entirely by match results and has to be skeptical of them. What follows is a hill climber: it keeps a single parent, tries a random variation, and promotes it only if it survives two increasingly demanding tests.

4.1. What you control

BrainThe brain selected in the editor menu (row 13) is the starting parent. Evolution never modifies it — results arrive as downloaded files.
GenerationsHow many rounds to attempt before stopping. You can stop earlier at any time with the same button, which reads Stop E while a run is going, or with STOP in row 5.
MatchesMatches per sample: how many matches each individual comparison is decided by. Larger is slower and less noisy.
R %The mutation rate: the largest random change, as a percentage, that any single weight may undergo in one step.
D:The look-ahead depth used for every game played during the run. Depth 1 is the practical setting; see section 2.7.

4.2. One generation

  1. Mutation. Every one of the 24 numbers — the 22 evaluation weights plus the two cube thresholds — is nudged independently by a random amount of up to R % of its own current value:

    $ \delta_i = \operatorname{round}\!\left( w_i \cdot \frac{R}{100} \cdot u \right), \qquad u \ \text{uniform on}\ (-1,1). $

    Because the change is proportional to the weight itself, a single mutation can never flip a sign, and large weights move in large absolute steps while small ones move in small steps — which is what you want when the weights span three orders of magnitude. (If by chance nothing changed at all, one weight is nudged by 1 so the generation is not wasted.)

  2. Normalization. The mutant is rescaled as in section 3.9, so that every candidate is measured on the same scale as its parent.
  3. The scout. The mutant plays the parent over the chosen number of matches, colors alternating so that neither side keeps any advantage of moving first. If it does not come out ahead it is discarded and the generation ends here. Most generations end here, and that is the point: the scout is the cheap filter that lets a bad idea die quickly.
  4. The line search. A promising mutation tells us not only that a point is better but which direction was better. So instead of accepting the mutant as it stands, the algorithm treats the difference between mutant and parent as a direction vector and walks along it: it tries a step further on, and a step back, keeps whichever wins, and doubles the step while it keeps improving. When a step overshoots, the step is halved instead. Because the weights are integers, repeated halving reaches zero exactly, so the walk always terminates — and in a few dozen matches it can travel much further than a single mutation ever could. This is also the only place a weight can change sign, by being extrapolated through zero.
  5. The gauntlet. The survivor faces a much longer series — ten times the sample size — against a fixed baseline brain, and is crowned a champion only if it beats that baseline by a wider margin than any previous candidate in this run. Testing against a fixed opponent rather than against the current parent matters: it is a common yardstick, so successive champions are measured against each other and not merely against whatever the parent happened to be at the time.
  6. Sweeps. If a champion beats the baseline in every single match, the baseline has become too weak to distinguish candidates. The new champion replaces it, the record resets, and the gauntlet length doubles — so the standard of proof rises as the brains improve.

4.3. A caveat on granularity

Because weights are whole numbers and the mutation is proportional, a weight can only move if $|w|\cdot R/100$ can reach one half — that is, if $|w| > 50/R$. At the default R = 50 % a weight of ±1 is therefore frozen: every proposed change rounds back to zero. Lowering R to search more finely freezes more of them (at R = 10 %, everything up to ±5). Such a weight is not trapped forever — re-normalization can lift it when some larger weight shrinks — but it escapes only slowly, and only upward in magnitude. Since a weight of 1 against a scale of 1000 has almost no effect on play this costs little strength, but it does mean a feature crushed to ±1 early in a lineage is hard to rediscover later.

4.4. Matches, not points

Every comparison above is decided by matches won, never by points scored. This is deliberate, and it was learned the hard way: when brains were scored on total points, evolution discovered it could inflate its score by escalating the doubling cube without limit, and optimized for that instead of for playing well. One game worth a million points swamped a tournament of thousands of games. Counting matches removes the incentive entirely — a match is a match however extravagantly it was won. Section 6.1 describes the match rules this rests on.

4.5. Nothing is installed automatically

Each champion is downloaded as a small file the moment it is crowned, and the parameter grid updates to show it, but the roster is never changed behind your back. Evolution runs produce candidates; deciding which candidates deserve a place among the nine brains is a separate, human decision, made by importing a champion (row 13) and running a tournament to see where it really belongs. Since results over a few dozen matches are noisy, several hundred matches per pairing are worth the wait before trusting any re-ordering — see section 6.3.

4.6. What evolution has found

Evolution beats hand-tuning, decisively. Every evolved brain outplays the hand-tuned Origin, and by a wide margin. Hand-tuning two dozen interacting numbers turns out to be something humans are simply bad at.

It compounds. Evolving a brain that is itself the product of an earlier evolution keeps yielding improvements, several generations deep, although the margins narrow.

It found the racer — and then found its way past it. For most of the project every run converged on nearly the same strategy: pour everything into the race and almost nothing into hitting. That is less strange than it sounds, and section 3.2 has the reason. Your own pip count falls by the dice total whatever you do, so the race term barely distinguishes one legal play from another — except through hits, which set the opponent back, and through efficient bearing off. A "pure racer" therefore still hits, and hits soundly, for reasons of the race; whereas an explicit hitting weight tempts a brain into hits its blunt evaluation cannot properly judge. Only after the golden-point weights were added, and the race terms deliberately reined in so that they no longer dominated the scale, did a genuinely positional brain — one that prizes the 5-point — finally win the roster.

And there is a ceiling. Gauntlet margins have been shrinking for some generations, and the whole of the parameter roster is now beaten comfortably by a net trained for a few minutes. That is the honest summary of what a hand-designed feature set can reach here, and it is why the second kind of brain exists.

5. The Net

A net replaces the twenty-two hand-designed features with a neural network that is shown nothing but the raw position and taught by playing against itself. It is never given a human game, and nobody ever tells it what a prime or an anchor is. The design follows Gerald Tesauro's TD-Gammon, with the differences noted where they occur.

5.1. Four decisions taken before any code

  1. The encoding is mover-relative. Every position is presented from the point of view of the side on roll; if that is Red, the board is mirrored first. Color symmetry then becomes a property of the encoder, exact to the last bit, instead of something the network has to learn — and every training game teaches both colors at once. One consequence: Tesauro's two turn-indicator inputs are constant here and are omitted, so the input is 196 wide rather than 198.
  2. The output is a distribution over six outcomes, not a score: my single, gammon and backgammon, and the opponent's three. Six units rather than five, although one is redundant: softmax is invariant to a constant shift, so the redundancy is harmless, and keeping all six is what makes the output layer symmetric under the mirror.
  3. The cube is not an input. The net predicts the cubeless outcome distribution, and every doubling decision is computed from those probabilities afterwards (section 5.11).
  4. No genetic algorithm. Self-play learning replaces it entirely. A net cannot be evolved and the EVOLVE button refuses one.

5.2. The encoding

The 196 inputs are laid out as follows. Remember that the position has already been mirrored if necessary, so the mover is always the first side.

0–95The mover's 24 points, four units each.
96–191The opponent's 24 points, four units each.
192, 193The mover's and the opponent's checkers on the bar.
194, 195The mover's and the opponent's checkers borne off.

The four units of a point encode how many checkers of that owner stand on it, in truncated unary: the first three are 1 when the count is at least 1, 2 and 3 respectively, and the fourth carries the excess, $(n-3)/2$. So one checker reads $(1,0,0,0)$, three read $(1,1,1,0)$, and five read $(1,1,1,1)$ with the last unit at 1. The point is that "a blot", "a made point" and "a stack" are distinct features the first layer can weight separately, rather than one number the network would have to learn to threshold.

Bar and borne-off counts are compressed as $\sqrt{n}$, which keeps them in the same band as the indicator units. Tesauro's original is reported variously as $n/2$ and $\sqrt{n}$; the choice here is one line and has never been measured against the alternative.

The encoding is sparse by construction: at most thirty checkers are on the board, so at most about thirty of the 196 inputs are ever nonzero — the measured median is 28. Both the forward pass and the first layer's gradient iterate over the nonzero entries only, which is roughly seven times less work than a dense pass and is the reason a wide input encoding costs memory rather than time.

Encoding a position
function encode(points, bar, off, mover):
    if mover == Red: (points, bar, off) = mirror(points, bar, off)
    x = 196 zeros
    for side in {mover: 0, opponent: 96}:
        for p in 1..24:
            n = number of that side's checkers on point p
            b = side_base + (p-1)*4
            if n >= 1: x[b]   = 1
            if n >= 2: x[b+1] = 1
            if n >= 3: x[b+2] = 1
            if n >  3: x[b+3] = (n-3)/2
    x[192] = sqrt(bar[mover]);       x[193] = sqrt(bar[opponent])
    x[194] = sqrt(off[mover]);       x[195] = sqrt(off[opponent])
    return x

5.3. The shape of the network

The network is a plain feed-forward stack: 196 inputs, one or more hidden layers of equal width, and six outputs. The width and the number of layers are the W and L fields of the LEARN row; the activation function $\varphi$ applied at every hidden unit is the menu beside them.

sigmoid$1/(1+e^{-z})$. What TD-Gammon used, and measurably the best of these on this encoding.
tanh$\tanh z$. Note $\tanh z = 2\,\mathrm{sigmoid}(2z) - 1$, so a tanh net and a sigmoid net represent exactly the same function class; they differ only in parameterization and in gradient scale.
lrelu / relu$\max(z,0)$, with leaky ReLU passing $0.01z$ instead of 0 below zero.
arctan$\arctan z$: the same odd saturating shape as tanh but with polynomial rather than exponential tails, so a hard-driven unit keeps passing a usable gradient.
ident$z$ — a control, not a brain. A composition of affine maps is affine, so an identity network of any depth collapses to a single $196 \to 6$ linear map with a softmax: multinomial logistic regression on this encoding. It answers "how much of the net's strength is the nonlinearity rather than the representation?", and the layer menu is dimmed when it is chosen because depth is meaningless for it.

Weights are initialized from a Gaussian with standard deviation $\sqrt{g/\mathrm{fan\_in}}$, where $g$ is 2 for the ReLU family and 1 otherwise, and biases at zero.

⚠ The first layer's fan-in is the number of ACTIVE inputs, not the input width. The textbook rule uses $1/196$, which assumes every input carries signal; here about 86% of them are zero, so $1/196$ would start every hidden unit in the near-linear region doing nothing at all, and the net would crawl for thousands of games before anything happened. The expected active count, 27, is used instead. The output layer's weights are additionally scaled by 0.1: large early logits saturate the softmax, and a net that starts out confident spends its first thousands of games unlearning a confidence it had no reason to have.

5.4. The forward pass

Each layer computes $z = Wa + b$ and then $a = \varphi(z)$, except the last, which applies a softmax:

$ \displaystyle p_k = \frac{e^{z_k}}{\sum_{j} e^{z_j}}, \qquad k = 1 \ldots 6. $

In practice the maximum $z$ is subtracted before exponentiating, which softmax is invariant to and which keeps the exponentials from overflowing. The six outputs are then a probability distribution over the six ways the game can end, from the mover's point of view.

The equity — the expected number of points — is a fixed dot product with no free parameters at all:

$ \displaystyle E \;=\; p_1 + 2p_2 + 3p_3 - p_4 - 2p_5 - 3p_6. $

That is the whole reason for predicting a distribution rather than a score. The evaluation has to learn probabilities, which are meaningful and checkable; how much a gammon is worth is arithmetic, not a parameter somebody has to tune. The same six numbers also carry the cube decision (section 5.11) and the win probability shown in the move list.

One more operation is needed everywhere: the same distribution seen from the other side of the table is $(p_4,p_5,p_6,p_1,p_2,p_3)$. The search needs it because a child position is the opponent's to move, and the learning rule needs it for the same reason.

5.5. Positions the net is never asked about

If the game is over, the answer is known exactly and the network is not consulted: a finished position is encoded directly as a one-hot outcome. The loser's borne-off count decides between a single and a gammon; a gammon becomes a backgammon if the loser still has a checker on the bar or in the winner's home board. Asking the network there would train it against its own noise on the one class of position where the truth is free.

5.6. How a net chooses a move

Exactly as in section 2: the search is the same code. The only difference is what leafValue does, and it has one wrinkle worth stating carefully.

A net answers from the point of view of the side on roll at that position. After I play a candidate turn it is the opponent's turn, so the equity the net reports for the resulting position is the opponent's, and mine is its negative. Getting this wrong does not crash anything; it produces a net that plays plausibly and badly. The same reasoning explains why the seam is told who is on roll: for a parameter AI that argument is ignored, and for a net it decides the sign.

function leafValue(position, brain, onRoll):
    if brain is a parameter AI: return evaluate(position, brain.weights)   // White's view already
    e = equity(net(position, mover = onRoll))                    // the ON-ROLL side's view
    return (onRoll == White) ? e : -e                            // into White's view

5.7. Learning from self-play

Training is a loop over complete games the net plays against itself, at depth 1, cubeless, with seeded dice. Each game produces a trace: the sequence of positions at which a side was about to roll, which is exactly the situation the net is asked about when a search reaches a leaf. The two sides alternate strictly — a turn with no legal move still ends the turn — and the trainer asserts that, because the learning rule depends on position $t+1$ belonging to the other player.

One self-play game
function selfPlayGame(net, seed):
    g = new game with dice seeded from `seed`
    g.rollForFirstTurn()
    trace = [ (encode(g, mover = g.player), g.player) ]
    while not g.over:
        turn = argmax over legal complete turns of  -equity(net(result, mover = other))
        play turn
        if g.over: break
        g.endTurn()
        trace.append( (encode(g, mover = g.player), g.player) )
        g.rollDice()
    return trace, terminalOutcome(g)

5.8. The λ-return

The question every learning rule has to answer is what each position in the trace should be trained towards. Two extremes are obvious and both are bad.

λ = 1Monte Carlo. Every position in the game is labeled with the outcome that actually happened. Unbiased and simple — but one game then delivers some sixty perfectly correlated gradient steps, which forces the learning rate down and, at any rate high enough to learn quickly, walks backwards.
λ = 0TD(0). A position is trained towards its successor's current estimate. Only the last position of a game carries any truth, and the net collapses towards the base rate: near-uniform predictions and games that get longer rather than shorter.

The lambda-return interpolates between them. Walking the trace backwards, with $V$ the network's own estimate and $\mathrm{swap}$ the outcome swap of section 5.4:

$ \displaystyle G_t \;=\; \operatorname{swap}\Bigl( (1-\lambda)\,V(s_{t+1}) \;+\; \lambda\,G_{t+1} \Bigr), \qquad G_{\text{last}} = \text{the actual outcome}. $

Both terms inside the swap are in the successor's frame, which is why they can be blended directly and the swap happens once, at the end. Walking backwards propagates the outcome the whole length of the game in a single sweep rather than one step per visit. At $\lambda = 1$ this reduces exactly to Monte Carlo and at $\lambda = 0$ exactly to TD(0), which is worth checking in an implementation because it is easy to get the frames wrong and hard to notice afterwards.

λ = 0.7 is the default and it is the largest single effect measured in this project — larger than the width, the depth, or the choice of activation, by an order of magnitude. Section 6.4 has the numbers.

5.9. Backpropagation

The loss is cross-entropy between the target distribution and the network's output, $L = -\sum_k t_k \log p_k$. Pairing a softmax output with cross-entropy has a convenient consequence: the gradient with respect to the output logits is exactly

$ \delta^{\text{out}} = p - t, $

with no quotient rule and nothing that can blow up. From there it is the ordinary chain rule: for each earlier layer, $\delta^{(l-1)} = \bigl(W^{(l)\top}\delta^{(l)}\bigr)\odot\varphi'(z^{(l-1)})$, the weight gradient of a layer is $\delta$ times the incoming activation, and the bias gradient is $\delta$ itself. The first layer's weight gradient is nonzero only in the roughly thirty active input columns, so the backward pass through the largest matrix touches those and no others.

The update is plain gradient descent, $w \leftarrow w - \eta\,\partial L/\partial w$, applied once per position in game order. There is no momentum, no weight decay and no adaptive rate. The learning rate is fixed at $\eta = 0.01$ and there is deliberately no control for it: a wrong value ruins a run silently and cannot be told apart afterwards from a bad idea, whereas the value that is there has evidence behind it.

Two checks are worth building if you reimplement this, because between them they catch nearly every mistake. Compare the analytic gradient against a central finite difference — but require agreement in absolute or relative terms, since wherever the true gradient is tiny the relative error is meaningless roundoff. And overfit a single position: cross-entropy should fall to nearly zero. That second test, not the gradient check, is what catches a sign error in the update.

5.10. Training in parallel

Self-play looks inherently serial — game $n+1$ is played by the net that game $n$ produced — but a single game barely moves the weights: about 78 positions at a rate of 0.01, and each move is an argmax over some twenty candidates. The net that plays game 1001 is, for practical purposes, the net that played game 1000.

So training goes in rounds. The weights are frozen, a round of games is generated in parallel across the workers, and then every update is applied on one thread in game order, exactly as a serial trainer would. The learning code does not change at all; the only approximation is that a round's games were played by a net one round stale instead of one game stale.

The round size is set by bandwidth, not by staleness: broadcasting the weights costs about 130 kB per worker per round, so one game per round would spend more time copying than playing. Eight games per worker puts a round at 256 games on 32 workers — a quarter of one percent of a 100,000-game run.

⚠ A seed reproduces a run only at the same number of workers, since the round size, and with it the staleness boundary, is the worker count times eight. The same seed on 32 and on 16 workers gives two different training runs. That is why the worker count is recorded in a net's file and displayed beside its name.

Generation is about seven-eighths of the work, so parallelizing only the generation captures nearly all of the available speedup. Measured on a 32-thread desktop: about 390 games per second, so a hundred thousand games is a few minutes and a million is under an hour.

⚠ A run is written out only when it finishes. There is no checkpointing, so closing the tab part-way through a long run loses all of it.

5.11. The cube: Janowski's formula

Because a net predicts the whole outcome distribution, its cube decisions are computed rather than tuned: it has no DT and no AT. The model is Rick Janowski's (1993). Reduce the position, from the relevant side's point of view, to three numbers:

pThe probability of winning at all, $p_1+p_2+p_3$.
WThe average value of the games won: $(p_1 + 2p_2 + 3p_3)/p$.
LThe average value of the games lost: $(p_4 + 2p_5 + 3p_6)/(1-p)$.

A dead cube (never usable again) and a perfectly live one (recubed at exactly the right moment) are both solvable, and real play lies between them. One cube-life index $x \in [0,1]$ interpolates:

$ \displaystyle \mathrm{TP} = \frac{L - 0.5}{W + L + 0.5x}, \qquad E_{\text{own}} = p\,(W + L + 0.5x) - L, $

$ \displaystyle E_{\text{opp}} = E_{\text{own}} - 0.5x, \qquad E_{\text{centre}} = \frac{4}{4-x}\Bigl( p\,(W+L+0.5x) - L - 0.25x \Bigr). $

All four are per unit of the current cube value, so the cube value cancels out of every comparison. Owning the cube is worth exactly $0.5x$. The take point TP is the classic 25% at $x = 0$ and 20% at $x = 1$ when $W = L = 1$, which is the check to write first.

The decisions follow directly:

doubleOffer or redouble when $\min(2E_{\text{opp}},\,1) > E_{\text{hold}}$, where $E_{\text{hold}}$ is $E_{\text{own}}$ or $E_{\text{centre}}$ according to who holds the cube. Doubling hands the cube to the opponent at twice the stake and he takes only if that beats dropping, so my equity is the smaller of the two.
takeTake when $p \ge \mathrm{TP}$, with $p$, $W$ and $L$ read from the taker's side.

"Too good to double" needs no rule of its own. When gammons are heavy, $E_{\text{hold}}$ exceeds 1 by itself and the comparison declines the double. That falls out of the arithmetic, and it is exactly the case the two-threshold rule of section 3.10 gets wrong.

Two details are easy to get wrong and both were. The take is priced with the DOUBLER on roll — he doubles, then rolls — so the taker's distribution is the doubler's, swapped. Evaluating as though the taker were on roll overstates his position by the value of a roll. And $x$ is forced to 0 when a redouble is impossible, that is when four times the current cube value exceeds what the match still allows: a live-cube model on a dead cube doubles far too readily. Otherwise $x$ is 0.7 in contact and 0.6 in a race.

⚠ This is money-game theory. It is a good approximation early in a match and wrong near the end, where the right answer needs a match-equity table. That is a known and deliberate limitation.

5.12. What is in a net file

A net is a JSON file of about a third of a megabyte: a spec recording the recipe — hidden layer widths, activation, seed, $\lambda$, learning rate, worker count — the number of games it was trained on, and then the layers, each an array of weights and one of biases. It is validated on import: shapes must agree with one another and with this build's 196 inputs and 6 outputs, and every number must be finite. A malformed file is refused with a reason, since a brain full of NaNs would poison every score derived from it and be far harder to notice later.

A trained net's systematic name spells the recipe out: N80-L2-sigmoid-800k-lam07 is two hidden layers of eighty units, sigmoid, 800,000 training games, $\lambda = 0.7$. The nets that ship with the game carry mythological names instead, kept in order of strength; row 18 of the control column shows the recipe of whichever is selected.

⚠ A shipped net's NAME is a slot, not an identity. The roster has twice been rebuilt and the names reassigned wholesale, so a claim about "Apollo" is only meaningful with a date attached. Identify a net by its recipe and seed, which is what the field beside the menu is for.

6. Playing and Measuring

6.1. Matches, the Crawford rule and the dead cube

Everything the AIs do against one another is scored in matches won, for the reason given in section 4.4. A match is a series of games, colors alternating, played until one side reaches an agreed point total X — 11 by default, odd so that it cannot be tied. A match win counts as one win however lopsided the games were.

Two standard rules are implemented, and both matter to the AIs' cube decisions.

CrawfordThe single game played immediately after either side first reaches $X-1$ is played without doubling. Without it the trailing player would double at once, having nothing to lose.
dead cubeThe cube is never raised beyond the points the trailing player still needs: $\mathrm{maxCube} = X - \min(\mathrm{score}_A, \mathrm{score}_B)$. Anything above that cannot change who wins the match.

At $X = 1$ the cap makes the cube dead altogether, which is why a match length of 1 gives plain single games. Since version 153 the cube can also be switched off independently of the match length, from the Doubling menu of row 20 — so a cubeless match to 11 is possible, which it was not before. The difference is large: measured in the app, 13.6 games per match cubeless against 6.2 with the cube.

A match to 11 needs at least 11 games if every game is worth one point and can run to 21, but the measured average is about 5 to 6, because of gammons and the cube.

6.2. Duplicate play

Backgammon results are noisy, and most of the noise is the dice rather than the play. Duplicate play, borrowed from duplicate bridge, cancels some of it: every match is played twice, the second time with the same dice and the two brains exchanged between the seats. Each brain then receives, in the second half, exactly what its opponent received in the first.

The mechanism is cheap because the batch runners already alternated seats: the mirror of a match is the same call with the arguments swapped and the same seed. Mirroring is at the match level, so the whole match — Crawford, the cube cap, the ending — is mirrored, and every game within it is still paired with its counterpart by index. The dice come from a seeded generator (a mulberry32, so it is bit-identical in the browser, in a worker and in Node), with each game's seed derived from the match seed and the game index.

One consequence is exact rather than statistical: two brains that play identically now tie exactly. With equal weights the two halves are the same computation, so the same seat wins both, and since the brains change seats the pair cancels to zero. Not approximately — zero. That is what makes duplicate play valuable during evolution, where a mutant is compared with a parent it differs from barely at all: a behaviorally identical mutant nets 0 instead of a couple of matches of noise, and can no longer be crowned by luck.

⚠ Between brains that play differently the gain is slight, and the reason is worth understanding. Backgammon's randomness is interleaved with the decisions, so the dice are genuinely shared only up to the first divergence: after that the same number arrives at a different board. Two roster brains playing the same match diverge on average by turn 11. Longer matches make it worse. Measured effective sample multipliers in tournaments between real, differing brains have been 1.01 to 1.08 — that is, almost nothing.

The exported spreadsheet reports the multiplier as the ratio of an independent-play baseline to the observed rate of sweeps, a sweep being the same brain winning both halves of a pair. The baseline is not one half. Two independent matches between brains with win probability $p$ sweep with probability $p^2 + (1-p)^2$, which is one half only for an even pairing and rises towards 1 as the field spreads out; a 98%-vs-2% pairing sweeps 96% of the time with no pairing at all. The baseline is therefore computed as the mean of $p^2+(1-p)^2$ over the pairings actually played.

6.3. How many matches are enough

This is the question that decides whether any of the numbers on this page mean anything, so it is worth stating plainly. For a head-to-head of $n$ matches with a true probability near one half, the standard error of the observed share is $1/(2\sqrt{n})$: about 1.6 percentage points at 1,000 matches and 0.5 at 10,000.

Every result on this page is therefore quoted with a z-score: the number of standard errors separating the observed result from a dead heat. For a head-to-head it has a particularly simple form. With $n$ matches played and a margin of $d$ matches (the winner's total minus the loser's),

$ \displaystyle z \;=\; \frac{d}{\sqrt{n}}. $

So a 572–428 result over 1,000 matches is $z = 144/\sqrt{1000} = 4.6$, and over 10,000 matches the rule of thumb is simply $z = d/100$. Where the statistic is a mean rather than a count — the millipoint figures of sections 6.5 and 8 — $z$ is the same idea: the measured difference divided by its own standard error.

What the number means: if the two players were in truth exactly equal, chance alone would produce $|z| \ge 2$ about one time in twenty, $|z| \ge 3$ about one time in 370, and $|z| \ge 4$ about one time in 16,000. So $z$ below about 2 means the run did not settle the question, whatever the percentages look like; $z$ above 4 means it did. The convention throughout is that a positive $z$ favours the player named first.

matches per pairingstandard errorwhat it can resolve
507 pointsnothing in a tight field — the order swings by ±30 matches run to run
1,0001.6 pointsa real difference of about 5 points
10,0000.5 pointsa difference of about 1.5 points

Two warnings, both learned by getting them wrong. Do not read a running total. A watched score crosses two standard errors far more often than 5% of the time; one comparison that finished at 58.3% stood at 64.6% after 65 matches. Decide the threshold before the run and read only the final number. And a measurement is not the only source of noise: two nets trained to the same recipe with different seeds differ by about a point, so at 10,000 matches per pairing the training seed, not the match count, is what limits how finely two recipes can be told apart.

6.4. What has been measured

All of the following come from the tournament and competition machinery in the control column, and can be re-run there. Since the dice are not seeded across runs, re-running gives a statistically equivalent answer rather than the same number, which is why sample sizes are quoted.

Search depth pays, and it is the biggest single dial after λ. The strongest net at depth 1 against the same net at depth 2, 1,000 matches to 11: depth 2 won 583–417, that is 58.3%, z = 5.25, and +0.22 game points per game. The control — the same net at the same depth on both sides — returned exactly 500–500 with duplicate play on, which is what says the harness is sound.

What each net parameter is worth. Ten nets, each differing from a common reference in exactly one respect, played a round robin of 10,000 matches per pairing at depth 1 — 450,000 matches, about 71 minutes. The reference is two hidden layers of 80, sigmoid, λ = 0.7, 800,000 training games, all trained from one seed.

the one changeagainst the referencez
twice the training (1.6M games)53.4%+6.7
twice the width (160)50.8%+1.7
a third hidden layer49.8%−0.4
leaky ReLU instead of sigmoid42.9%−14.1
tanh41.5%−17.0
ReLU39.3%−21.5
arctan37.8%−24.4
λ = 1 (Monte-Carlo targets)8.9%−82
no nonlinearity at all (the control)1.6%−97

Read from the bottom up, that table is the argument of this whole page. The learning rule matters most by a distance: Monte-Carlo targets cost more than every architecture choice put together, and get you barely more than logistic regression. The activation matters next, and sigmoid wins decisively — which is striking, since tanh is the same function class rescaled, so the difference is gradient scale rather than what the network can represent. Training volume is the most reliable positive dial and has not begun to saturate. Width helps only once there is enough training to support it: the same comparison at 100,000 games measured 48.3%, that is, harmful. A third layer buys nothing at any volume tried, while costing about 1.5 times the training time.

⚠ Every one of those figures was measured at depth 1, and the architecture effects are already known to interact with the activation. None of them should be quoted as unconditional.

Gammons are a third of the stake. Over 3,000 cubeless self-play games: 69.0% single, 29.9% gammon, 1.1% backgammon, so the average cubeless game is worth 1.32 points rather than 1. And they change the policy, not just the score: ranking moves by full equity rather than by win probability alone picks a different move 16.9% of the time. That is a large part of why a net beats a parameter AI, which has no representation of a gammon at all.

6.5. An outside yardstick

Everything above is self-relative: this net beats that one, which beats Arwen, which beats Origin. A chain of relative wins carries no absolute scale, so it says nothing about how the play here compares with serious backgammon. For that, the AIs were scored against GNU Backgammon, a mature open-source engine whose evaluation is the product of far more work than this project, and which prices every legal move.

⚠ The measurement below is NOT reproducible from this web page. It was made with an external program, on a fixed corpus of 2,400 decisions taken from 160 games of GNU Backgammon's own play, each scored by how much equity the choice gave away against that engine's best move. The unit is the millipoint per decision (mpr) — thousandths of a point — and it is the standard yardstick for rating backgammon play. Lower is better.

playerplays the reference engine's top movemean error
the net, depth 164.4%12.6 mpr
the net, depth 269.8%6.9 mpr
the net, depth 375.6%4.9 mpr
GNU Backgammon, at its shallowest setting2.0 mpr

Two things follow, and they point in opposite directions.

All three rows are the same net, and it is the champion of section 7.1 — 80+80 sigmoid, 1,600,000 training games — measured at every depth over the same 2,400 decisions. Section 8 takes the depth comparison further and gives the error bars.

Every ply pays, and the returns diminish smoothly. The three depths are 12.6, 6.9 and 4.9 mpr; each extra ply removes roughly 45% and then 29% of the remaining error. This is an independent confirmation of the 583–417 match result above, by a completely different instrument, and it is the measurement that justifies playing at depth 2 or 3 when you have the patience.

But the like-for-like comparison is humbling. At the same search depth, the net gives away 12.6 mpr where the reference engine gives away 2.0 — a factor of six. The net needs two extra plies, and seconds per move rather than milliseconds, to reach 4.9, which is still nearly two and a half times the reference at its cheapest setting. The gap is in the evaluation, not the search, and that is exactly what the raw 196-input encoding predicts: the reference engine's inputs include hand-designed features for containment, timing and distribution which this encoding must discover for itself and largely does not.

The same conclusion arrives from the other direction. Doubling the training volume is worth about 0.66 mpr, or 5% of the error — and a constant fraction per doubling, at both depths. Closing a factor of six that way would take on the order of $10^{11}$ self-play games. Training volume is the cheapest and most reliable dial available here, and it is not the one that reaches a modern engine. Features, phase-split networks and rollout targets are; none of them is implemented.

For orientation, the reference engine sorts play into named bands by exactly this error rate: under about 2 mpr it calls "supernatural", 2 to 4 "world class", and around 6 "expert". Those names describe checker play only, cubeless, and they are that program's nomenclature rather than a claim about tournament results against human champions.

7. Three Runs, in Full

The figures quoted earlier on this page are summaries. This section gives three complete runs, made on the 3rd of September 2026 with version 154, as they came out of the TOURNAMENT and COMPETE buttons. All three used 10,000 matches per pairing (1,000 for the competition), matches to 11, search depth 1 unless stated, the money cube, and DUPLO on. Between them they took a little over four and a half hours on a 32-thread desktop.

Everything here is reproducible from the control column, which is the point of showing it. The dice are not seeded across runs, so repeating one of these gives a statistically equivalent answer rather than the same number — which is exactly what makes the comparison in section 7.4 worth making.

7.1. The nets, with the best parameter AI among them

All ten nets plus Arwen, the strongest of the parameter AIs: 11 players, 55 pairings, 550,000 matches and 2.4 million games in 1 h 53 m. Each player therefore played 100,000 matches.

rankplayerwhat it ismatches won of 100,000share
1Apollo80+80 sigmoid, 1,600,000 games68,22968.2%
2Belona160+160, 800,000 games67,19667.2%
3Castor80+80, 800,000 games — the reference65,67765.7%
4Diana80+80+80, 800,000 games65,23465.2%
5Erosleaky ReLU58,92758.9%
6Floratanh57,95658.0%
7GanymedeReLU55,75855.8%
8Heraarctan54,14054.1%
9Arwenthe best parameter AI33,29933.3%
10Icarusλ = 118,94818.9%
11Junono nonlinearity — the control4,6364.6%

The headline is where Arwen finishes. The best brain the genetic algorithm has produced — the top of a roster that took months of evolution — comes ninth of eleven, below every net that has a working activation function, and above only the two deliberately crippled controls. Head to head it loses to Apollo 83.8% of the time, and to Hera — the worst of the four real activations, a net built to lose — still 71.6% of the time. It beats Icarus, the λ = 1 net, 69.5%, and Juno 96.5%.

Read down the standings and the shape of section 6.4 is there again: training volume at the top, then width, then the third layer buying nothing, then a five-point gap down to the non-sigmoid activations, then a chasm to the two controls.

7.2. The parameter roster, with one net among them

The nine parameter AIs plus Apollo: 10 players, 45 pairings, 450,000 matches in 1 h 54 m. Each player played 90,000 matches.

rankplayermatches won of 90,000sharenet matches over the next one down
1Apollo (the net)76,81985.4%6,756
2Arwen49,28354.8%84
3Bilbo48,26553.6%104
4Celebrian48,14053.5%490
5Dwalin47,03852.3%240
6Eowyn46,86552.1%540
7Frodo44,52649.5%498
8Galadriel42,95647.7%402
9Hamfast42,30347.0%9,088
10Origin3,8054.2%

Three things are worth pulling out.

The roster is in very nearly the right order, and the top of it is a three-way tie. The last column is the head-to-head margin over the next brain down, and at 10,000 matches a margin of $d$ carries $z = d/100$. So Arwen over Bilbo (84, $z = 0.8$) and Bilbo over Celebrian (104, $z = 1.0$) are not resolved — those three are indistinguishable even at ten thousand matches each. Every pairing further down is resolved, at $z$ between 2.4 and 5.4. There are two small inversions in the full head-to-head table as well: Arwen beats Galadriel by more than it beats Hamfast, and Bilbo beats Dwalin by more than it beats Eowyn, though in both cases the standings put those opponents the other way round. Both gaps are under one standard error, so they are noise rather than a genuine circle of superiority.

Origin is a different order of thing. The hand-guessed baseline wins 4.2% of its matches and loses to Arwen 95.9%. Whatever else these numbers say, they say that hand-tuning two dozen interacting weights is hopeless, and that the genetic algorithm of section 4 has done a great deal of real work.

And a net beats all of it comfortably. Apollo took 85.4% of its 90,000 matches, beating every parameter AI between 83.2% and 84.5% individually. Note the shape of that: the whole nine-brain roster, from Arwen down to Hamfast, spans about eight percentage points, while the gap from Arwen up to Apollo is more than thirty.

7.3. One net against itself, one ply apart

Apollo in both seats, one searching at depth 1 and the other at depth 2, 1,000 matches to 11 — the cleanest possible measurement of what a ply is worth, since the two players are the same evaluation function and nothing but the search differs.

seatdepthmatches wongame points
Red25729,251
White14287,931

Depth 2 wins 572–428 — 57.2%, $z = 4.6$, with a 95% interval of 54.1% to 60.3% — and takes +0.21 game points per game over the 6,437 games played. An earlier run of the same comparison finished 583–417; pooled, that is 1,155 of 2,000 matches, 57.8% at $z = 6.9$.

The price is the whole story of why depth 1 is the default. The tournament of section 7.1 played 550,000 matches in 113 minutes. This competition played 1,000 matches in 56 minutes — and only one of the two seats was searching deeper.

That gives a clean check on the estimate in section 2.7 that a ply costs a factor of about four hundred. Per move, and in the same units, the depth-1 tournament cost 1.8 and this competition 361.8. Half the moves here are depth 1, so the depth-2 moves cost about $2 \times 361.8 - 1.8 = 722$, and $722 / 1.8 = 401$. The measured factor is 401 against a predicted 400, from two runs made for entirely different reasons.

7.4. What the three runs say together

The 9/2 measurements reproduced. Section 6.4 reports a ten-net tournament of the same design run the previous day. Scoring every net in 7.1 against the same reference (Castor) gives 53.3, 52.2, 50.7, 42.4, 41.0, 39.4, 36.7, 9.1 and 1.6 per cent, against 53.4, 50.8, 49.8, 42.9, 41.5, 39.3, 37.8, 8.9 and 1.6 the day before. Every one of the nine agrees within 1.4 points, and the standard error on a difference of two independent 10,000-match measurements is 0.7, so the whole table reproduces within about two standard errors. Nine independent agreements is a much stronger statement than any single one of them.

Search and evaluation are separate axes, and the second is where the distance is. One extra ply of search buys 57.2% head to head; being a net rather than the best parameter AI buys 83.8%. The two are not in competition — section 6.4 found the training gain survives search almost undiminished — but if you are choosing where to spend effort, the evaluation function is worth more than the search, and it is also the cheaper of the two at run time.

Duplicate play did nothing measurable in any of the three. The multipliers came out 1.01, 1.04 and 0.97 — the last of them below 1, which simply means the sweep rate fell a little the wrong side of its baseline by chance. That is the expected result between brains that play differently, for the reason given in section 6.2, and it is not an argument for turning DUPLO off: it costs nothing, and the case it is there for is evolution, where a mutant and its parent play almost identically.

A weak opponent is cheap to beat, which shows up in the game counts. A match against Juno lasted 2.4 games on average and one against Origin 2.8, against tournament-wide averages of 4.4 and 4.8. A match ends when someone reaches eleven points, so a hopeless opponent concedes it in two or three gammons. That is worth knowing when you plan a run: adding a very weak player to a tournament costs far less time than adding a strong one.

⚠ All three runs are at depth 1 and at one training seed. Two nets built to the same recipe with different seeds differ by about a point, so at 10,000 matches per pairing the seed, not the sample size, is what limits how finely two recipes can be told apart — and 7.2's three-way tie at the top of the parameter roster would need a different instrument, not merely more matches, to break.

8. Depth 2 against Depth 3

Section 7.3 settled depth 1 against depth 2 by playing a thousand matches. The next question is the obvious one — is a third ply worth having? — and it is answered here by a different instrument, for reasons section 8.3 explains.

8.1. The measurement

The same corpus as section 6.5: 2,400 positions drawn from 160 games of GNU Backgammon's own play, each one scored by how much equity the net's chosen move gives away against that engine's best. The player is the strongest net on the roster — 80+80 sigmoid, 1,600,000 training games — run at each depth over the same positions, so the three rows are paired: the same 2,400 decisions, three times.

search depththe reference's own name for itplays the top movemean errormedianp90
10-ply64.4%12.62 ± 0.84 mpr0.035
21-ply69.8%6.89 ± 0.55 mpr0.018
32-ply75.6%4.89 ± 0.40 mpr0.010

Paired, depth 2 against depth 3: +1.99 ± 0.57 mpr in favour of depth 3, z = 3.5, over the full 2,400 decisions at every depth. The two depths choose the same move 87.8% of the time; all of the difference comes from the 293 decisions where they part company.

The rig was rebuilt from scratch for this run and checked before any of these numbers were believed. Scoring the previous champion — the same recipe at 800,000 training games — reproduced its recorded figures exactly at both depth 1 (62.3%, 13.28 mpr) and depth 2 (68.0%, 7.30 mpr), and every gnubg move on every position mapped to a distinct move of our own generator, 2,400 positions with nothing unmatched. That net measures 5.4 mpr at depth 3, so the champion's 4.89 is a real improvement on it rather than a change of instrument.

The second column is worth a moment. Because this page numbers depths one higher than the literature does (section 2.2), depth 3 here is the reference engine's 2-ply — the very setting used to score it. So the last row is a like-for-like comparison at equal nominal depth, and it is the harshest reading available.

8.2. What it says

The third ply is real, and it is worth about two thirds of what the second was. Depth 1 to 2 removes 45% of the error; depth 2 to 3 removes a further 29%. The agreement rate tells the same story from another angle — 64.4%, 69.8%, 75.6% — and so does the tail, which is where the damage in backgammon actually lives: the 90th-percentile error falls 35, 18, 10 millipoints, and the worst single decision improves from 464 to 308. The median is 0 at every depth, because most decisions are not close.

It is worth having, and it is not free. A depth-3 decision takes tens of seconds (section 2.7), so this is a setting for a considered game or for examining one position with MOV, and not one for a tournament.

⚠ An early version of this measurement said the opposite, and the reason is instructive. A first pass over 24 positions put depth 3 behind depth 2 by 4.6 mpr — wrong sign, and it was flagged at the time as meaningless at that sample size (z = 1.5). The full corpus reversed it at z = 4.0. A backgammon measurement that is not significant is not weak evidence for its own sign; it is no evidence at all.

⚠ And a corpus of the net's own games flatters it. The 800,000-game net was scored both ways: on positions from its own play it measured 4.9 mpr at depth 3, against 5.4 on the reference engine's positions — about half a millipoint of self-flattery. Small, but in the direction you would expect, and the reason every figure on this page comes from a corpus the player did not generate.

⚠ One measurement, one seed, one net. These are three depths of a single trained net on a single corpus. The depth ordering is now solid — it has been reproduced on two different nets — but the exact millipoint figures carry the training seed's noise as well as the corpus's, and should be read to about a tenth of a millipoint, not better.

8.3. Why this one is not a competition

Depth 1 against depth 2 was settled by playing matches. The same approach at depth 3 is not impractical by a small margin, it is out of reach, and the arithmetic is worth setting down because it applies to any future question of this kind.

A depth-3 decision costs roughly 460 core-seconds, and a batch match runs on a single worker. One cubeless game asks the deep side for about 27 decisions, so a single game costs some 3.4 core-hours; with 32 workers busy that is about nine matches an hour. The effect to be detected is small — if the head-to-head advantage scales with the error reduction above, depth 3 should win a single game about 51.5 to 52% of the time — and separating that from a coin needs on the order of four thousand matches. That is two to three weeks of continuous running, for a result the corpus delivers in an afternoon at z = 4.

⚠ And a shorter match does not help, which is the part that surprises people. It is tempting to play to 1 point instead of 11 on the grounds that matches are then six times cheaper. They are — but a match to $X$ points amplifies a small per-game edge in proportion to the square root of the games it contains. Writing $\varepsilon$ for the per-game edge and $g$ for the games per match, the significance of $N$ matches goes as

$ \displaystyle z \;\propto\; \varepsilon\,\sqrt{g}\,\sqrt{N} \;=\; \varepsilon\,\sqrt{gN} \;=\; \varepsilon\,\sqrt{G}, $

where $G$ is the total number of games played. The match length cancels exactly: a short match is cheap and carries proportionally less information. What match length changes is what is measured — at a match length of 1 the cube is dead, so the comparison covers checker play alone — and how well duplicate play works, since two brains diverge less within one game than across eleven points. Neither of those is a reason to expect more significance per hour.

← Back to the User's Guide