The Doubling Cube

Janowski’s model, measured against 33,395 rolled-out equities — and what Raccoon will build from it.

Rick Janowski’s cube-life-index model is how a cubeless evaluation becomes a cube decision. GNU Backgammon and eXtreme Gammon both ship it, and its published constant — \(x = 0.68\) for contact positions — has stood essentially unchanged since 1993. It is rarely measured, because measuring it needs observed cubeful equities for a large and varied set of real positions.

Those observations were already on disk. The BGSage money benchmark rolled positions out to completion with the cube live, recording for each candidate a measured cubeful equity next to the cubeless probabilities for the same position — and the cube sits in a different place across them, which is what makes the model separable. This page tests it on 33,395 of those, then says exactly what Raccoon should implement.

The short answer is that the model is in good shape and the conventional constant survives. Two of the three equity formulas are correct at \(x = 0.68\) to within 0.018 of a point. The third, for a centred cube, wants a much higher index — but adopting it buys nothing where the parameter is actually read, and we cannot explain it, so we are not acting on it. What is worth fixing is structural: use the piecewise form, respect the provable bounds, and floor \(W\) and \(L\) at one point.

Two numbers this page used to report were wrong, and both errors are the same kind, so they are worth stating rather than quietly correcting. Reference quality is recorded in two fields, not one: filtering on eval_level alone admitted truncated rollouts whose leaves call Janowski. Against references that include them, retuning the index looks worth +1.167 ± 0.271 cube PR. Against references that are actually measurements it is worth +0.128 ± 1.242 — nothing. The measurement section sets out the filter that separates them.

Everything here is money play with the Jacoby rule and beavers on, matching the benchmark. Match play, where the score moves every take point, is out of scope and is not calibrated by anything below.

The model is three formulas

A network evaluates a position cubelessly: six probabilities for winning or losing a plain game, a gammon, or a backgammon, if the game were played out with no cube. Under real rules that game never happens. Janowski’s model says what those probabilities are worth once the cube is on the table.

It starts by compressing the outcome distribution into two numbers — the average value of a win and of a loss:

\[W=\frac{\sum_i i \cdot P(\text{win } i)}{P(\text{win})} \qquad L=\frac{\sum_i i \cdot P(\text{lose } i)}{P(\text{lose})}\]

A gammonless position has \(W = L = 1\). Two extremes can then be solved exactly. A dead cube can never be turned again, so the game plays out at the current stake and a take risks \(2L-1\) to gain \(2W+1\) — the familiar 25% take point. A live cube is redoubled at exactly the right moment every time, which adds a constant bonus and gives the continuous model’s 20%. Real positions sit between, at a fraction \(x\) — the cube life index, 0 for dead, 1 for live.

What matters below is that the model then produces three different equity formulas, one per cube location (Janowski’s equations 5–7):

\[E_O = C_V\left[p(W + L + 0.5x) - L\right] \qquad \text{we own the cube}\]

\[E_U = C_V\left[p(W + L + 0.5x) - L - 0.5x\right] \qquad \text{opponent owns it}\]

\[E_C = \frac{4C_V}{4-x}\left[p(W + L + 0.5x) - L - 0.25x\right] \qquad \text{centred}\]

Every take point, cash point and doubling point in the paper is derived by setting two of these equal. So the model stands or falls on them, and — crucially — they can fail independently. That is what makes the measurement below possible, and it is not something a cube decision can reveal: a double compares only two locations at a time.

One property of \(x\) matters later. It is meant to be a physical property of the position — its volatility. Janowski derives it as proportional to the cube-life of the position and inversely proportional to its long-term volatility. The same position has the same volatility whoever holds the cube, so \(x\) cannot legitimately depend on where the cube sits.

Janowski’s three formulas in a gammonless position (W = L = 1) at the published index x = 0.68 — pure theory, no data. Read the y-axis carefully: these curves give the equity of NOT turning the cube, which is what the formulas are defined to produce. Above the cash point that is the wrong quantity to act on — you double, your opponent passes, and gammonless the position is worth exactly +1 (dash-dotted). The gap between the blue curve and that ceiling is not an error in the formula; it is the difference between holding the cube and using it.

What is Janowski’s model? → Three equity formulas sharing one parameter, one per cube location, from which every take point and doubling point is derived. They can be wrong independently, and \(x\) is meant to describe the position, not the cube’s location.

What the engines ship, and what to ship

Three implementation decisions, all settled before any fitting. Each is either provable or measurable, so none of them costs a parameter.

Use the piecewise form, not equations 5–7

Equations 5–7 are linear in \(p\), so they run straight past the values a game can actually pay. GNU Backgammon and BGSage instead pin the live-cube equity at the four points where it is known — \(-L\) at \(p = 0\), \(-1\) at the take point, \(+1\) at the cash point, \(+W\) at \(p = 1\) — and interpolate linearly between them, flattening the outer segments to \(\pm 1\) under Jacoby. That is a substitution for equation (7), not an approximation of it.

It is also more accurate. On owned cubes the piecewise form reaches RMSE 0.0177 against 0.022–0.027 for the algebra, and its fitted indices sit close to the convention rather than scattering. raccoon/cube/janowski.py implements the piecewise form.

The bounds are provable, so they should never be fitted

If you double and your opponent takes, they took because taking beat passing for them, so you collect less than \(+1\); if they pass you collect exactly \(+1\). No doubling line pays more than a point. Undoubled you cannot collect more than the natural size of the win. So

\[-L \;\le\; E \;\le\; \max(1, W) = W\]

and under Jacoby with the cube centred this tightens to \(\pm 1\), since an undoubled gammon counts one anyway. The measured rollouts respect it. The piecewise form satisfies it by construction — both the dead and the live branch lie inside the bounds, so any blend of them does too — which is a second reason to prefer it over the algebra, which breaks the bound by up to a full point. We pin this as a property test rather than enforcing it with a clamp, so a change that breaks it fails loudly instead of being silently patched over.

Floor \(W\) and \(L\) at one point

Not a defensive nicety; the labels violate it. Variance reduction reports corrected means rather than counts, so a rare outcome can come back very slightly negative, and \(W = 1 + (P_{gw} + P_{bgw}) / P_{win}\) divides that by a win probability that can be minute. Across this benchmark that produces 550 rows with \(W < 1\), ten of them below zero, and a worst case of \(-5.29\) — which would put the take point and both envelope endpoints through the floor. A win cannot be worth less than a point. Flooring inside compute_wl repairs every consumer at once.

What should the implementation look like before any tuning? → Piecewise interpolation, bounds as a test rather than a clamp, and \(W, L \ge 1\). All three are free: no parameter, no fitting, no benchmark needed.

How to measure it

Cube models are usually argued about rather than measured, because the references are hard to come by. This benchmark has them — but most of its entries cannot judge a Janowski variant, and it takes two fields to tell which can.

It takes two fields to identify a measurement

eval_level records how a reference was produced; tier records how much effort the decision earned. Neither is sufficient alone, because BGSage gives truncated rollouts the same Rollout label as full ones. Crossed:

reference tier eval_level cube entries rolled-out candidates
played to completion rollout Rollout 282 33,395
truncated at 3-ply 3T Rollout 418 9,241
3-ply search 3P 3-ply 1,496

Only the first row is an observed average. The truncated rollouts stop at a cubeful 3-ply evaluation that calls Janowski at its own leaves, and the 3-ply references are that same search all the way down — so scoring a Janowski variant against either is scoring it partly against itself.

The bias does not show up where you would look for it. Pooled cube PR barely moves — 2.353 over all references against 2.259 over the measured ones — because the contaminated groups pull in opposite directions: the model scores an implausible 0.649 against the 3-ply references it half-agrees with, and 7.726 against the truncated ones, which are the hardest decisions in the benchmark. The level looks fine. What breaks is every comparison made on it:

references baseline one refitted index paired gain
all 2,196 entries 2.353 1.185 +1.167 ± 0.271
the 282 measured ones 2.259 2.130 +0.128 ± 1.242

Deeper cubeful search sees cube value a static evaluation cannot — market losers, mostly — so a larger \(x\) lets a 0-ply model imitate a 3-ply one. Refitting against search-derived references buys depth, not accuracy, and it does not survive contact with a real payoff.

The checker rollouts are the instrument, not the cube decisions

The benchmark’s cube entries look like the natural place to test a cube model, but only 282 of them are measured, and a cube decision structurally cannot separate the three formulas — it compares two locations at a time. The checker candidates can. Every one carries a measured cubeful equity beside the cubeless probabilities for the same position, and the parent decision records where the cube sat, so each formula is testable on its own against 33,395 observations.

Two properties of these positions carry into every conclusion below. They are post-move positions, so the opponent is on roll — the formulas do not model whose turn it is, so we evaluate from the on-roll player’s side and negate. And the cube decisions taken during each rollout come from a bot that also uses Janowski, so these measure equity under Janowski-quality cube play rather than optimal cube play.

The references have a noise floor

Every rolled-out candidate carries a standard error, so the benchmark can be asked how well it knows its own answer:

Across 5,651 rolled-out decisions the median standard error on the gap between the best move and the runner-up is 0.0033 of a point. 32% of those decisions have their best move within one standard error of the second-best, and 51% within two.

That noise is fixed and identical for every engine scored, so comparisons stay fair. But it puts a floor under achievable PR, and it is why a near-parity engine cannot be separated on move accuracy alone — a third of the closest decisions in the benchmark do not have a knowable right answer.

Compare like with like, or the model is charged for an option

Janowski’s formulas give the no-double equity — what a position is worth if the cube stays put — while a rollout reference always includes the option to double. Comparing the two directly charges the formula for an option it was never asked to price. Since these are post-move positions the option belongs to the opponent, so the matching quantity is \(\min(\text{ND}, \max(\text{DT}, -1))\): a minimum over their choice, a maximum over ours. Getting that backwards inflates the centred error by half. Four positions checked by hand in GNU Backgammon confirm the distinction — its No double line reproduces cl2cf_money closely and its optimal line matches the rollout.

Can Janowski be measured with what we already have? → Yes — on 33,395 observed cubeful equities, spanning all three cube locations, at zero play cost. But only an eighth of the benchmark’s cube entries can judge it, and a pooled score hides that: the level looks normal while every comparison built on it inflates.

What the measurement says

formula cube location n bias at 0.68 RMSE at 0.68 best \(x\) RMSE there
\(E_O\) we own the cube 14,813 +0.0007 0.0179 0.710 0.0177
\(E_C\) cube centred 10,677 +0.0135 0.0430 0.925 0.0307
\(E_U\) opponent owns it 7,905 +0.0025 0.0162 0.720 0.0155

\(E_O\) and \(E_U\) are right, and the published constant is right for them. Bias under 0.003 of a point across 22,718 positions, RMSE under 0.018, and freely fitted indices of 0.71 and 0.72 — close to each other and close to 0.68, which is what a genuine property of the position should look like. Refitting moves their RMSE by about 1%.

\(E_C\) is the outlier. Nineteen times the bias, more than twice the RMSE, and it wants 0.925 — a nearly perfect cube. Even there it stays worse than the other two are for free.

An independent check that the method is sound rather than the model being uniformly bad: let the index float against the take/pass line alone, told nothing about what it should be.

Across all 282 measured cube positions the take/pass index comes out at 0.700, against GNU Backgammon’s published 0.68. Within pure races it comes out at 0.515, against GNUBG’s documented 0.60 race constant.

The measurement reproduces both published constants without being told either.

Model minus rollout for each formula, at the published index. Zero is agreement; below zero the model says a position is worth less than it proved to be. Two panels are a narrow band about zero; the centred panel is more than twice as wide and visibly tilted — high on the left, low on the right.

Averages hide where the failures are. Binning the same errors by observed equity, with intervals clustered on the checker decision — candidates within one decision are the same position played different ways, and clustering roughly doubles the intervals against treating them as independent:

Bin means with 95% intervals, clustered on the checker decision. Marker area is proportional to the number of positions in the bin, so the thin tails do not read as loudly as the dense middle. E_O tracks zero until the position is nearly won, then sags through the run-up to a cashing decision and recovers once the cash is banked. E_C is off in both directions and crosses zero near even money. E_U sits on zero wherever it has weight.

\(E_U\) is correct wherever it has weight — on zero from about \(-0.2\) equity upward. \(E_O\) is correct until the position is nearly won, then sags through the band where a cashing decision is live and snaps back above \(+1.0\). That band is thin, which is why the aggregate barely notices it. \(E_C\) is wrong in both directions — positive when the position is losing, negative when it is winning, crossing zero near even equity. A residual that changes sign cannot be removed by any constant, any index, or any offset.

The \(E_O\) sag has a mechanism worth naming, because it is a modelling choice rather than a measurement question. The model blends a dead branch with a live branch in fixed proportion, \((1-x)E_{\text{dead}} + xE_{\text{live}}\). As a position approaches its cash point the live branch has reached its ceiling of \(+1\) while the dead branch is still only \(2p-1\), so the blend drags the answer down by roughly \((1-x)\) times the gap between them. But a position near its cash point does not need future cube efficiency — you double and your opponent passes. A fixed weight is defensible in the middle of the range and not near the boundary.

The same errors split by BGSage’s game-plan classification. The centred series is the widest in every panel, so this is not a quirk of one position type, and the same tilt appears in each. Per-plan centred RMSE is in the panel titles.

Where is the model weak? → Only in the centred formula, and everywhere in its range rather than in one position type or one game plan. The other two are correct at the published constant.

Why we are not acting on the centred index

A centred cube fits at 0.925 — almost a perfect cube. The result is robust: it survives clustered bootstrapping, disjoint halves of the games, and every formulation of the Jacoby handling we tried, and it reproduces on the benchmark’s own cube entries, which need no perspective flip at all. It is also the case where \(x\) matters most. And we still cannot explain it.

Two honest readings remain, and this data cannot separate them. Either an initial double really is more efficient than a redouble — both players can pick their moment, and a full ladder of recubes lies ahead — or the centred formula is missing something structural that \(x\) absorbs. The second is the better bet given the sign change above, since no constant can produce one.

What settles the practical question is that adopting it buys nothing.

The index is read for move selection, not for takes

Everything above is an estimation metric: every candidate weighted equally, scored on how close the model lands. That is not how the parameter earns its keep. Over 500 games the benchmark asks:

question times in 500 games relative frequency
which move? (every turn) 14,693 22.5×
should I double? (every turn with cube access) 2,189 3.4×
should I take? (only when actually doubled) 653 1.0×

So the index is read for move selection roughly 22 times as often as for a take decision — and move selection only cares about the ranking of candidates within one position, where an error common to all of them cancels. A change can look valuable on RMSE and be worth nothing here. On the 5,695 measured decisions that offer a real choice:

setting picks the best move mean cubeful equity lost
published x = 0.68 88.0% 0.00053
centred 0.925, owned 0.68 87.6% 0.00060
one refitted index (0.800) 88.0% 0.00053

Retuning moves this by a fraction of a percentage point, and the centred refit makes it slightly worse. There is no case for shipping it.

Should the centred index be changed? → No. It is a real and unexplained feature of the data, but acting on it costs accuracy where the parameter is actually read. Keep 0.68 and record the anomaly as open.

exp024, re-scored

Hypothesis. Janowski’s functional form is adequate and its published cube-life index is simply mis-set: re-fitting \(x\) recovers the cube-decision error that \(x = 0.68\) leaves.

Primary metric. Cube PR on the BGSage money benchmark — mean equity thrown away per decision, times 500 — restricted to the 282 positions whose references were played to completion, n = 432 sub-decisions, fed the benchmark’s own reference probabilities so the net’s evaluation error is out of the comparison. Variants are fitted on half the games and confirmed on the held-out half, and compared to the baseline paired.

variant cube PR paired vs baseline coherent?
\(x = 0.68\) (published) 2.259 ± 0.488 yes
0.68 contact / 0.60 race 2.228 ± 0.489 +0.031 ± 0.112 yes
one refitted index 2.130 ± 1.140 +0.128 ± 1.242 yes
refitted per cube state 1.306 ± 0.326 +0.953 ± 0.528 no (93% violations)
0.68 plus a fitted shift 2.138 ± 0.477 +0.121 ± 0.190 yes
\(x = 0\) (dead cube) 5.566 ± 1.657 -3.307 ± 1.647 no (94% violations)
\(x = 1\) (live cube) 6.680 ± 1.950 -4.422 ± 2.058 yes

Nothing beats the published constant. The two fitted variants land about 0.12 lower with intervals many times that wide, the race split is inside noise, and the dead and live envelopes are far worse — which is the sanity check that the model is doing real work between them.

The coherence gate matters more than the PR column. For any one position \(E_O \ge E_C \ge E_U\) must hold: owning the cube cannot be worth less than it being centred, which cannot be worth less than the opponent owning it. Nothing in the fitting enforces that, because each entry is either centred or owned, so the three lines are fitted on disjoint position sets. The per-state fit scores best on PR and drives the owned-cube index to 0.42 against the 0.71 the equity data gives, inverting the ordering on 93% of positions. PR cannot see that, because each scored decision involves only one cube location. A mis-set constant cannot produce an impossible ordering; a mis-specified form can.

Power, stated rather than assumed. 432 sub-decisions cannot resolve a small change: the paired interval on the refitted index is ±1.242, so this test would miss anything under about a point of PR. It is a null in the sense of no evidence of benefit, not proof of no effect. The move-selection comparison above, at 5,695 decisions, is the better-powered instrument and agrees.

Where Raccoon’s own evaluations land

probabilities cube PR (n = 432)
benchmark reference (the model alone) 2.259 ± 0.488
Raccoon exp018/ep22 2.684 ± 0.622
GNUBG 0-ply 2.658 ± 0.638

Raccoon’s net and GNUBG at 0-ply are indistinguishable on cube decisions, and both sit a little above what the model reaches on perfect probabilities — the cost of evaluation error rather than of cube modelling.

Is the cube-life index mis-set? → No. Against references that are measurements, no variant beats \(x = 0.68\) (best: one refitted index, +0.128 ± 1.242 paired, n = 432). The earlier +3.206 came from references produced by a search that uses Janowski at its own leaves.

What Raccoon will build

Raccoon has no cube at all today. The shipped net already emits the six-outcome distribution the cube needs, raccoon/cube/janowski.py is written and tested, and nothing in the playing path references it. This is the specification for closing that gap, in the order the measurements above say to do it.

The model, settled. Piecewise dead/live blend; \(x = 0.68\) for contact and \(0.60\) in races; one index for all three cube locations, never one per location; \(W\) and \(L\) floored at 1; the \([-L, W]\) bound as a test.

The inputs, all present. RaccoonNet.value_probs6janowski.probs6_to_cumulative5janowski.cube_action. No model work is needed; the value head has carried the six-outcome distribution since exp018.

Cube-aware checker play comes first — not the double/take decision. Ranking afterstates by cubeful rather than cubeless equity in raccoon/train/lookahead.py is where the model gets read 22 times as often, and the prize is already measured: a perfect cubeless player throws away

PR 0.336 ± 0.042 on the cubeful metric, with cubeless-best and cubeful-best differing on 992 of 14,693 decisions (6.8%). It concentrates exactly where the cube is most alive — PR 0.553 with the cube centred against 0.145 when the opponent owns it — and is nearly absent in pure races (0.046).

Then the cube decision itself, via cube_action at the top of a turn, and the plumbing it needs: cube_value and cube_owner threaded through the play loop, raccoon/eval/arena.py and raccoon/protocol/rgp.py, none of which have any concept of a cube today.

How to score each. Cube decisions on the 432 measured sub-decisions, knowing that is underpowered for anything small. Move selection on the 33,395 clean candidates, by picks-best-move rate and mean cubeful equity lost — the metric that matches the frequency with which the parameter is read.

Explicit non-goals. Do not tune \(x\) against 3-ply references. Do not score a cube change on take decisions alone. Do not chase the centred index until there is a reference not generated by a 0.68 policy.

What this page does not establish

  • The population is not the target. These are 500 games of one engine playing itself. An index fitted here describes how efficiently that bot uses the cube, on the positions its own style produces.
  • The references are not optimal cubeful equity. The payoff is empirical, but the cube decisions taken during each rollout come from a bot that also bottoms out in Janowski. Policy error can only lower realised equity, so this understates what a better model could reach rather than overstating it.
  • The benchmark is played under Jacoby, and Raccoon’s target is not. The project aims at money game without Jacoby, as the stepping stone to match play (goal.md). Jacoby zeroes gammons while the cube is centred, so on those positions the reference engine plays boldly — it leaves the cubeless-best move 25.2% of the time against 10.7% with the cube owned, giving up five times as much cubeless equity and trading away gammon wins as readily as it accepts gammon losses, since the rule makes both worthless. That is 38.5% of the benchmark’s checker decisions, and it taints the labels rather than just the target: the rollout continuations behind them were played bold too. Regenerating without Jacoby means rebuilding every tier from new games, so we report the Jacoby-clean owned-cube subset alongside the full number instead.
  • Nothing here transfers to match play beyond that: score-dependent take points, no beavers, and Janowski in match-winning-chance space are all uncalibrated here.
  • No position is observed at two cube locations, so the ordering constraint is a model check and can never be validated against data.
  • The benchmark’s cube positions are selected for being non-trivial, so an index fitted on them is tuned for close decisions and should not be quoted as a general-purpose constant.
  • Coordinate descent on a grid finds a coordinate-wise minimum, not a certified global one.

Still open

  • The centred index. Two readings, no way to separate them here; it needs a reference not generated by a 0.68 policy.
  • Whether an index per cube value is real. A centred cube is always a 1-cube here, so cube value and cube location tell the same story and cannot be told apart.
  • Volatility measured rather than proxied. Janowski derives \(x\) as inversely proportional to volatility, and it can be measured directly — the spread of the 0-ply value over the 21 next rolls, reusing raccoon/eval/luck.py and the roll table in raccoon/search/expectimax.py. A cheap proxy does not work: the residual correlates with contact at only \(-0.15\), with no monotone trend, which is precisely why the direct measurement is the experiment worth running.
  • Cubeful search. GNUBG does not use the closed form as its cube engine — it uses it as a leaf evaluator inside a search that makes cube decisions at every node. The gap that refitting appeared to close against 3-ply references is that search, and running it is the principled way to close it.

References

  • Rick Janowski, Take-Points in Money Games (PDF) — the cube-life index, equations (5)–(7) for the three cube locations, the refined model of Appendix 2, and the Jacoby factors of Appendix 3.
  • Keeler and Spencer, Optimal Doubling in Backgammon (1975) (online) — the continuous model behind the live-cube take point.
  • raccoon/cube/janowski.py — the model, ported from the paper and cross-checked against its worked examples and against GNU Backgammon.
  • experiments/pipeline_exp024.sh — every number on this page, reproducible end to end.