Engine Study · Bot Performance

Benchmarking Open Sage against eXtreme Gammon

A quantitative comparison of the Open Sage evaluation engine — which powers Backgammon Sage Pro — against eXtreme Gammon's bot engine (XG).

Abstract

We compare the Open Sage bot engine against eXtreme Gammon (XG) across matched evaluation levels — direct neural-network evaluation, multi-ply lookahead, and truncated rollouts. Because XG exposes no programmatic interface, we score it through its Batch Analysis feature. We use three complementary methods. The first constructs a layered set of ground-truth evaluations — trusting fast evaluations where the choice is clear and escalating close calls to full rollouts — and scores each engine as a Performance Rating (PR) against that reference, across both 500 money games (17,535 decisions) and 130 five-point matches (18,292 decisions). The second isolates the positions on which Sage's truncated rollout and XG's strongest setting genuinely disagree, and rolls them out to adjudicate. Across both, the two engines are close, with Sage's truncated rollout (3T) modestly ahead of XG Roller++, most visibly in match play — with the caveat that this ground truth is derived from Sage's own rollouts. The third method turns the question around: on 290 real tournament matches already analyzed in XG, we re-analyze each in Sage and compare the Performance Rating the two engines assign to every player — and find the average difference statistically indistinguishable from zero.

01
Introduction

Why measure against XG?

eXtreme Gammon (XG) is a standard reference for computer backgammon analysis, and its evaluation engine is widely considered one of the strongest available. Any new engine that aims to be taken seriously has to answer a simple question: how does it compare to XG? This study sets out to answer that quantitatively for Open Sage, the neural-network engine behind Backgammon Sage Pro.

Our goal was to compare Open Sage's evaluations against XG's at comparable levels of effort. The natural experiment — having the two engines play many thousands of games head-to-head and tallying the result — is not practical: the XG desktop app exposes no programmatic interface, so feeding positions and moves between the two engines is a manual, click-through process. Reaching a sample size large enough to resolve the small differences between two strong engines would take far too long.

Instead, we use XG's Batch Analysis feature, which can score hundreds of transcribed games in a single unattended pass. Open Sage plays both sides of many games; each game is exported in a standard text format that XG can import; XG analyzes the lot; and we parse its verdicts back out. On top of that shared foundation we apply two distinct methods, described in Section 3 and Section 4. A third study (Section 5) sets the simulations aside altogether and works from real tournament matches analyzed in XG.

02
Background

Levels of evaluation

Both engines can evaluate a position at a range of depths, trading accuracy for speed. Understanding these levels is essential to reading the results, because the comparison is always between matched levels of the two engines.

Direct evaluation (1-ply). The raw output of the neural network for a position, with no lookahead. This is the fastest setting and the foundation every deeper level is built on. (We follow XG's convention, in which 1-ply denotes the raw network evaluation.)

Multi-ply lookahead (2-, 3-, 4-ply). The engine looks several turns into the future, considering the opponent's replies and averaging over the possible dice rolls at each step, with a raw network evaluation at the end of each line. Each additional ply is more accurate and substantially more expensive.

Truncated rollouts (1T, 2T, 3T). Rather than searching to a fixed depth, the engine plays out many short simulated games, truncates them after a few turns, and evaluates the resulting position with multi-ply lookahead. Variance reduction cancels out much of the luck in the sampled dice. These are XG's "Roller" settings, and they are stronger than fixed-depth search while remaining far cheaper than a full rollout.

Full rollout. Many simulated games played all the way to completion. Run at sufficient volume with variance reduction, a full rollout is the closest thing to ground truth that a backgammon engine can produce, and we use it as the reference standard throughout this study.

Matched levels: Sage and XG

Family Sage XG equivalent What it does
Direct 1-ply 1-ply (raw eval) A single neural-network evaluation of the position — no lookahead.
Multi-ply 2P · 3P · 4P 2-ply · 3-ply · 4-ply Searches the opponent's replies several turns deep, averaging over the dice.
Truncated rollout 1T · 2T · 3T Roller · Roller+ · Roller++ Many short simulated games, truncated after 5–7 turns and evaluated with multi-ply, with variance reduction. 3T/Roller++ use 3-ply decisions; 2T/Roller+ use 2-ply; 1T/Roller use 1-ply.
Full rollout Rollout Rollout Simulated games played to completion — the reference "truth" in this study.
Performance Rating (PR). Throughout, an engine's accuracy is summarized as PR — the average equity error per decision, multiplied by 500, measured against the rolled-out reference. A PR of 0 means perfect agreement with the truth; lower is better.
03
Method One

Rollout PR

The first method scores every engine against a fixed set of decisions whose "true" best play we have established as accurately as possible. It follows the same approach used in the well-known 2012 XG study that compared XG against a number of other bots.

We began by simulating 500 money games of Sage 3P playing itself. A moderately strong level like 3P produces a realistic distribution of positions across every game plan — racing, attacking, priming, anchoring — which is exactly the variety we want to test against. We then ran the whole study a second time for 130 five-point matches, where the score on the board changes the value of every decision; results for both appear below.

Building the ground truth

Establishing the true best play for every decision by full rollout would be enormously expensive — and unnecessary, because most decisions are not close. We therefore build the reference in three escalating passes, spending rollout effort only where the choice is genuinely in doubt. The same recipe is applied to both data sets; each pass settles the decisions it can resolve confidently and hands the rest down:

Pass 1 · Sage 3P

Trust 3-ply when the choice is clear

Every decision is evaluated at 3-ply. Where the best move beats the next-best by more than 0.05 equity, the 3P verdict is accepted as truth and the decision is settled.

0.05
equity gap to settle
Closer calls (within 0.05) pass down to Pass 2
Pass 2 · Sage 3T

Re-check close calls with a truncated rollout

The remaining decisions are re-evaluated at 3T. Where the margin is now wider than 0.02 equity, the 3T verdict is accepted as truth.

0.02
equity gap to settle
Anything still within 0.02 passes down to Pass 3
Pass 3 · Full rollout

Roll out the hardest decisions

Everything still inside 0.02 is settled by a full Sage rollout: 3P decisions for both checker play and cube actions, run in batches of 1,296 paths until the 95% confidence band on the equity falls under 0.005 — or a ceiling of 20,736 paths (16 × 1,296) is reached. The slowest back-game positions took well over an hour each.

0.005
target 95% band

The result is a layered reference in which every decision is resolved at exactly the depth its difficulty demands. We ran this same three-pass recipe over both data sets — the 500 money games and the 130 five-point matches — and the resulting tier compositions are shown alongside each set's results below.

Scoring an engine

With a layered set of trusted evaluations in hand, scoring any engine is straightforward: we present it with each benchmark decision, record the equity error of the move or cube action it chooses relative to the truth, and report the average error × 500 as a PR. The same total is broken out into checker-play and cube PRs, and further by game plan.

Scoring XG required the extra step described in the introduction. Because XG has no API, we ran its Batch Analysis over the 500 money-game and 130 match transcripts using a custom level — 3-ply decisions, upgrading to the level under test wherever it disagreed — and parsed XG's preferred decision for each position out of the .xg files it produces, scoring those against the same reference equities.

Money games — ground-truth composition

Across the 500 money games, the three passes resolved 16,889 positions (17,535 decisions) as follows:

Settled at 3P  7,652 Settled at 3T  3,260 Rolled out  5,977 Money games  16,889 positions · 17,535 decisions

Money games — head to head

The five matched levels, scored over those 17,535 decisions. Lower PR is better; the stronger engine in each pair is highlighted.

Sage 3T
Truncated rollout · 3-ply decisions · 7-turn truncation
0.21
vs
0.32
XG Roller++
Truncated rollout · 3-ply decisions · 7-turn truncation
Sage 3T leads by 0.11 PR
Sage 2T
Truncated rollout · 2-ply decisions
0.26
vs
0.41
XG Roller+
Truncated rollout · 2-ply decisions
Sage 2T leads by 0.15 PR
Sage 1T
Truncated rollout · 1-ply decisions · 5-turn truncation · 72 paths
0.50
vs
0.53
XG Roller
Truncated rollout · 1-ply decisions · 5-turn truncation · 42 paths
Sage 1T leads by 0.03 PR
Sage 4P
4-ply lookahead search
0.41
vs
0.46
XG 4-ply
4-ply lookahead search
Sage 4P leads by 0.05 PR
Sage 3P
3-ply lookahead search
0.58
vs
0.57
XG 3-ply
3-ply lookahead search
Effectively tied — XG 3-ply ahead by 0.01 PR

Money games — full breakdown

PR for every engine and level tested, broken out by decision type and game plan. Lower is better.

Engine PR Checker Cube Pure Race Racing Attacking Priming Anchoring
Sage 3T 0.210.180.360.020.240.170.310.28
XG Roller++ 0.320.310.380.040.400.240.410.44
Sage 2T 0.260.230.440.020.320.210.390.30
XG Roller+ 0.410.410.390.050.590.310.470.54
Sage 1T 0.500.520.400.040.570.430.590.73
XG Roller 0.530.540.480.050.630.440.710.66
Sage 4P 0.410.390.500.070.510.370.450.53
XG 4-ply 0.460.450.520.060.580.400.570.58
Sage 3P 0.580.580.570.140.720.520.630.74
XG 3-ply 0.570.570.580.050.710.480.730.71
Sage 2P 1.641.392.880.401.771.831.861.71
Sage 1P 2.592.483.200.782.602.793.102.89
Open Sage eXtreme Gammon Sage 2P and 1P have no XG counterpart in this run and are shown for reference.
Sage's evaluations are stronger than the equivalent XG evaluation at every matched level except 3-ply, where XG edges ahead by 0.01 PR — a difference well inside the noise. The two engines are very close, and at the truncated-rollout levels that most users rely on, Sage holds a consistent edge.

Match play — ground-truth composition

The match set runs the same three passes, with one addition: each player's away-score and the Crawford flag are threaded through every evaluation, so the truth is computed in match-equity (MWC) space against the correct score. Across the 130 five-point matches, the passes resolved 17,892 positions (18,292 decisions) as follows:

Settled at 3P  7,522 Settled at 3T  3,460 Rolled out  6,910 Match play  17,892 positions · 18,292 decisions

Match play — head to head

The five matched levels, scored over those 18,292 decisions. Lower PR is better; the stronger engine in each pair is highlighted.

Sage 3T
Truncated rollout · 3-ply decisions · 7-turn truncation
0.19
vs
0.35
XG Roller++
Truncated rollout · 3-ply decisions · 7-turn truncation
Sage 3T leads by 0.16 PR
Sage 2T
Truncated rollout · 2-ply decisions
0.26
vs
0.44
XG Roller+
Truncated rollout · 2-ply decisions
Sage 2T leads by 0.18 PR
Sage 1T
Truncated rollout · 1-ply decisions · 5-turn truncation · 72 paths
0.49
vs
0.51
XG Roller
Truncated rollout · 1-ply decisions · 5-turn truncation · 42 paths
Sage 1T leads by 0.02 PR
Sage 4P
4-ply lookahead search
0.42
vs
0.46
XG 4-ply
4-ply lookahead search
Sage 4P leads by 0.04 PR
Sage 3P
3-ply lookahead search
0.57
vs
0.54
XG 3-ply
3-ply lookahead search
Effectively tied — XG 3-ply ahead by 0.03 PR

Match play — full breakdown

PR for every engine and level tested on the 5-point match set, broken out by decision type and game plan. Lower is better.

Engine PR Checker Cube Pure Race Racing Attacking Priming Anchoring
Sage 3T 0.190.160.400.010.170.180.250.26
XG Roller++ 0.350.350.410.080.440.320.330.45
Sage 2T 0.260.240.440.020.360.180.340.33
XG Roller+ 0.440.440.410.090.550.440.400.52
Sage 1T 0.490.490.520.050.570.440.570.63
XG Roller 0.510.510.540.090.640.500.510.62
Sage 4P 0.420.420.480.050.520.380.450.55
XG 4-ply 0.460.450.560.090.600.440.420.56
Sage 3P 0.570.560.670.050.740.560.580.68
XG 3-ply 0.540.530.610.090.690.530.520.65
Sage 2P 1.301.261.600.391.591.231.501.41
Sage 1P 2.302.292.310.692.482.432.472.56
Open Sage eXtreme Gammon Sage 2P and 1P have no XG counterpart in this run and are shown for reference.
The match results echo the money study: Sage is stronger than the equivalent XG evaluation at every matched level except 3-ply, where XG edges ahead by 0.03 PR — well inside the noise. Sage's clearest edge is again at the truncated-rollout levels (3T and 2T) that most users rely on.
04
Method Two

Disputed positions

The Rollout PR method scores both engines against a shared reference. A second, more direct lens asks: in realistic play, where do the two engines actually disagree on the best move — and when they do, which one is right?

To find out, we simulated games of Sage 3P self-play, had XG analyze them, and isolated the decisions where Sage 3T and XG Roller++ chose differently. The procedure filters carefully so that only genuine 3T-vs-Roller++ disagreements survive:

  • Generate Sage 3P self-play games and export each as an XG-importable transcript.
  • Run XG Batch Analysis with a custom level: 4-ply decisions, upgrading to Roller++ wherever the played move differs from XG's 4-ply pick.
  • For each position XG flags as a Sage error, re-evaluate the Sage decision at 3T. If 3T now agrees with XG, discard it — that was merely 3P being too weak, not a real 3T-vs-Roller++ dispute.
  • For the genuine disagreements where XG's measured equity gap exceeds 0.02, roll the position out — 5,184 paths with 3P decisions — and treat the rollout as truth to decide who was right.

Results

Money games

200 simulated games · Jacoby + beavers on
7,404
total decisions
130
disputes (1.8%)
11
rolled out (> 0.02)
Most disputes are tiny: only 31 of the 130 (24%) exceeded 0.01 equity.
Who was right (of 11 rolled out)
Sage 3T · 6 XG Roller++ · 2 Neither · 3
Sage 3T avg error
0.005
XG Roller++ avg error
0.015
Averaged across the 11 rolled-out positions · lower is better

5-point matches

70 simulated matches · match score affects play
10,160
total decisions
192
disputes (1.9%)
16
rolled out (> 0.02)
As in money play, most disputes are tiny: only 37 of the 192 (19%) exceeded 0.01 equity.
Who was right (of 16 rolled out)
Sage 3T · 9 XG Roller++ · 4 Neither · 3
Sage 3T avg error
0.004
XG Roller++ avg error
0.013
Averaged across the 16 rolled-out positions · lower is better
In both money play and match play, Sage 3T resolved more of the genuinely disputed positions correctly and carried a smaller average error than XG Roller++. The effect is a little stronger in match play. These disputes are, however, a small fraction of all decisions — fewer than 2% — and on the vast majority of positions the two engines agree closely.
A caveat on the truth. Because the adjudicating rollouts are Sage's own, there may be some bias toward Sage in this analysis. Hand-checked XG rollouts on the same positions show broad agreement with the Sage rollouts, which is reassuring, but the possibility of a small home-field advantage should be kept in mind.
05
Method Three

Agreement on real matches

The first two methods ask which engine is stronger. A third question is just as important to anyone who uses an engine to study their own play: if you analyze a real match in XG, note your Performance Rating, then analyze the very same match in Sage — how close are the two ratings? A player who has spent years building intuition for what a given PR means in XG should get essentially the same number from Sage.

To test this directly, we took a large collection of real tournament matches that had already been analyzed in XG, re-analyzed every one of them from scratch in Sage, and compared the Performance Rating each engine assigned to each player.

The matches

The match files come from three 2026 tournaments, all 7-point matches, generously provided by Máté Fehér — already analyzed in eXtreme Gammon, exactly as a competitive player would study their own games.

UBC Texas 2026
100
matches analyzed
UBC Istanbul 2026
146
matches analyzed
UBC Japan 2026
44
matches analyzed

290 matches and 580 individual player ratings in all. One further match was set aside as a corrupted transcription.

Matched evaluation settings

Each match was re-analyzed in Sage at a 3-ply base, with an expert 3T pass — a 360-path truncated rollout — applied to the decisions where the player's actual move disagreed with the 3-ply best. This mirrors a strong XG analysis (a base ply for the clear decisions, escalating to a truncated rollout for the close ones), at matched strength: Sage 3P ≈ XG 3-ply and Sage 3T ≈ XG Roller++ (see Section 2). Each engine then computes a PR per player from its own evaluations — the number a user sees in each app.

Results

Pooling all 580 player ratings, the two engines agree almost exactly. The average difference is statistically indistinguishable from zero, and the spread of that difference is small next to the spread in PR itself.

+0.002
Mean difference (PR)
Statistically indistinguishable from zero — 95% confidence interval ±0.03, p = 0.90.
0.37
Std. dev of the difference
Small next to the 2.08 spread in PR itself — the disagreement on any one rating is minor relative to how much PR varies.
0.98
Rating correlation (r)
Sage and XG rate the same players almost identically.
Per-player Performance Rating XG Sage DifferenceSage − XG
Average 4.36 4.36 +0.002
Standard deviation 2.08 2.10 0.37
95% range 1.52 – 9.36 1.44 – 9.67 −0.76 – +0.74
In practical terms, a player who analyzes a match in Sage will — in the large majority of cases — see essentially the same Performance Rating that XG would give. As a measure of how well a match was played, the two engines are interchangeable.
06
Conclusion

Two strong engines, very close

Across the two strength studies, Open Sage 3T and XG Roller++ are roughly comparable — two of the strongest backgammon evaluations available, and close to each other. The Rollout PR study — run for both money play and 5-point matches — finds Sage stronger at every matched level except 3-ply, where the two engines are within noise of each other. The disputed-position study finds Sage resolving more of the genuine disagreements correctly, with a smaller average error, most clearly in match play.

There is, then, some evidence that Sage 3T is slightly stronger than XG Roller++, especially in matches. But the differences are small, and part of the measured edge may reflect the use of Sage's own rollouts as the reference truth rather than a true difference in strength. The honest summary is that the two engines play at a very similar, very high level — which is precisely the bar we set out to meet.

And the third study answers the question a Sage user actually faces: across 290 real tournament matches already rated by XG, Sage produces essentially the same Performance Rating — the average difference between a player's Sage and XG rating is statistically indistinguishable from zero, and the spread of that difference is small next to the spread in PR itself. Whether the test is strength against a rolled-out truth or simple agreement on how a real game was played, Open Sage and XG land in the same place.

Try Backgammon Sage Pro Back to home
A
Appendix

Reproducing these results

Open Sage is released as open source, and the complete pipeline behind both methods ships in the engine's repository. Everything below runs against the bgsage package and a local build of its engine alone — no external services, datasets, or infrastructure are required. The only manual dependency is eXtreme Gammon itself, used to regenerate the XG columns, since XG has no programmatic interface.

github.com/markbgsage/bgsage Open Sage engine · AGPL-3.0 · run all scripts from the repo root
Prerequisites. Build the bgsage engine (the bgbot_cpp extension) following the repository's instructions for your platform. Reproducing any XG column additionally requires eXtreme Gammon (Windows) for its Batch Analysis step. Every script resolves its paths inside the repository, so run them from the bgsage/ root.

Reproducing it with Claude Code

You don't have to drive the pipeline by hand. Claude Code — Anthropic's agentic command-line coding tool — can build the engine and run the benchmarks for you. Point it at a checkout of the repository and describe what you want; it works through the same steps detailed below:

"Build this engine, then score Sage 3T and Sage 3P against the shipped benchmark and show me the PR, checker PR, and cube PR for each."
"Reproduce the Rollout PR table: score every Sage level — 1-ply through 4-ply, plus 1T, 2T and 3T — against data/money_benchmark/benchmark.json.gz and print it as a table I can compare to the study."
"Rebuild the ground-truth benchmark from scratch with the three build passes, then score Sage 3T against it."
"I have my own bot in ./mybot — for each decision in the shipped benchmark, evaluate it with my bot and compute its PR against the reference equities."

Method One — Rollout PR

The benchmark — the layered set of trusted evaluations — is built and scored by a single script, scripts/benchmark_money.py. There are two ways in.

A

Score against the shipped dataset

No rebuild · validate the table or score a new engine

The repository ships the assembled benchmark as data/money_benchmark/benchmark.json.gz (the uncompressed JSON exceeds GitHub's file-size limit; the scripts read the gzip transparently). Scoring any Sage level reproduces its row in the Section 3 table:

# lower PR is better
python scripts/benchmark_money.py score --level 3ply        # Sage 3P
python scripts/benchmark_money.py score --level truncated3  # Sage 3T
# --level: 1ply 2ply 3ply 4ply | truncated1/2/3 | rollout

To score a different engine entirely, apply the same rule the script uses internally: for every decision in the dataset, take your engine's choice and compare its equity to the stored reference ("rollout") equity — the mean error × 500 is its PR.

B

Rebuild the ground truth from scratch

Re-derive every decision · seeded, so it reproduces

Three adaptive passes re-create the dataset. The games are seeded (500 games from seed 1), so the decisions and reference equities come out the same:

# 1. simulate 500 Sage-3P self-play games (+ XG transcripts)
python scripts/benchmark_money.py build --stages pass1 --n-games 500 --workers 6
# 2. re-evaluate every decision within 0.05 at 3T
python scripts/benchmark_money.py build --stages pass2 --n-threads 16
# 3. roll out every decision still within 0.02
python scripts/benchmark_money.py build --stages pass3 --n-threads 16

Pass 3 writes data/money_benchmark/benchmark.json (and its .gz); score it exactly as in Option A. Omitting --stages runs all three passes in order.

Reproducing the XG columns

XG has no API, so its results come from XG's Batch Analysis. Pass 1 writes XG-import transcripts to data/money_benchmark/xg/ — these are not shipped, so regenerate them with pass 1 if you started from the dataset alone.

  1. In XG, Batch-Analyze the xg/ folder with a custom level — 3-ply decisions, upgrading to the level under test on disagreements — with Save Games after analyze checked. XG writes one .xg per game.
  2. Run python scripts/benchmark_pr_xg.py, which reads XG's top decision per position from the .xg files and scores it against the same reference equities, printing the same PR breakdown.

Match play

The 5-point match study is reproduced the same way with scripts/benchmark_match.py, the match-play twin of benchmark_money.py — the match length and number of matches are arguments, and the match score is threaded through every evaluation. Its ground truth ships as data/match_benchmark/5pt/benchmark.json.gz.

# score against the shipped match dataset (no rebuild)
python scripts/benchmark_match.py score --match-length 5 --level truncated3
# or rebuild: 130 seeded 5-pt matches, three adaptive passes
python scripts/benchmark_match.py build --match-length 5 --n-matches 130 --stages pass1 --workers 6
python scripts/benchmark_match.py build --match-length 5 --n-matches 130 --stages pass2 --n-threads 16
python scripts/benchmark_match.py build --match-length 5 --n-matches 130 --stages pass3 --n-threads 16
# XG columns: batch-analyze data/match_benchmark/5pt/xg/, then
python scripts/benchmark_pr_xg_match.py --match-length 5

Method Two — Disputed Positions

This method ships no pre-generated artifacts — its intermediate files live under the git-ignored logs/ tree and depend on the manual XG step — so it is reproduced end to end. Each experiment is three stages: simulate, analyze in XG, then aggregate and roll out.

M

Money games

# 1. 200 Sage-3P self-play money games -> logs/sage_vs_sage/
python scripts/run_sage_vs_sage_games.py 1 200 --level 3P --workers 6
# 2. (manual) XG Batch-Analyze: XG 4-ply, upgrading to Roller++
#    on disagreement, Save Games after analyze -> one .xg per game
# 3. find disputes and roll them out (5,184 paths, 3-ply)
python scripts/aggregate_xg_pr.py --threshold 0.02 --rollout-threads 24 --n-trials 5184
5

5-point matches

# 1. 70 Sage-3P self-play 5-point matches -> logs/sage_vs_sage_match/
python scripts/run_sage_vs_sage_match.py 5 70 --level 3P --workers 6
# 2. (manual) XG Batch-Analyze with the same custom level
# 3. match-aware aggregation + rollouts
python scripts/aggregate_xg_match_pr.py --threshold 0.02 --rollout-threads 24 --n-trials 5184

Both aggregators re-evaluate each Sage-3P-vs-XG disagreement at 3T, keep only the genuine 3T-vs-Roller++ disputes, and roll out those whose XG-measured gap exceeds 0.02 — the procedure described in Section 4.

Open Sage is licensed under AGPL-3.0. The shipped benchmark is the exact reference behind the Section 3 tables; the disputed-position results regenerate deterministically from the seeded simulations, with the single manual XG analysis step in between.