Skip to main content

GammonView

CI engine Python 3.10+ Licence: MIT

The backgammon analysis backend behind gammonview.com: read and write the site's .gvab/.gva match files, import .mat/.xg/.bgf, analyze a match or a single position with the bgsage engine, and compute per-decision error rate (PR), luck and cube statistics.

Those files are based on OGXM — the chunked match format from HedgeHog, not ours — extended to carry what bgsage's analysis produces. This repo reads OGXM; what it writes is that extended form. See The format.

It ships as two importable Python packages, plus a JavaScript port of the codec for the browser:

  • gvformat — the pure-stdlib codec: read/write the .gvab binary, .gva JSON conversion, .xg/.bgf import, compute-on-read stats, OGID. Zero third-party deps — pip install gammonview and import gvformat anywhere, no engine required. This is the format layer: everything else here, Python and JavaScript alike, reads and writes through it.
  • gvanalysis — the bgsage-powered analysis: analyze_match (a match file in, analyzed .gvab bytes out), analyze_file (the same work stopping at the OGXM document), and single-position analysis. Depends on gvformat + bgsage (the [engine] extra). Dependencies flow one way: gvanalysis → gvformat, never back.
  • @gammonview/gvformat — the same codec in JavaScript, on npm. What reads the analysis in the browser. See In the browser.

Install

To use the packages, install from PyPI — no clone needed:

pip install gammonview               # the codec alone (gvformat), no engine
pip install "gammonview[engine]"     # adds bgsage, for gvanalysis

To work on them, clone the repo and let uv build the environment — it installs both packages in editable mode, plus bgsage and the test dependencies:

git clone https://github.com/ngvlamis/gammonview-analysis.git
cd gammonview-analysis
uv sync
uv run python tests/run_all.py --fast     # ~3s, no engine needed

Every command below is written uv run … for that checkout; from a plain pip install, drop the prefix.

Quickstart

Server one-liner — analyze a dropped .mat and get the analyzed .gvab back, ready to store and ship to the client:

from gvanalysis import analyze_match
data = analyze_match("match.mat", preset="world_class")   # analyzed .gvab bytes

It runs the engine over every decision, and the bytes carry the match and the analysis. Any input the codec reads works — .mat, .gva, .gvab — and one that already carries analysis keeps it, with ours appended.

It returns .gvab (binary), always — that is the artifact to store and ship. For the JSON, read_gvab(data) is the .gva; to stop a step earlier and work with the document itself, analyze_file returns it directly. (The CLI is the other way round: gvan-match writes .gva unless asked for --gvab, because a file you open by hand is likelier to want to be readable.)

The format

The files this repo reads and writes — .gvab (binary) and .gva (JSON) — are based directly on OGXM (OpenGammon eXtensible Match), the chunked match format from HedgeHog, and extend it with what GammonView's viewer shows and OGXM has no field for: luck, per-decision PR and illegal-move flags, and per-alternative eval levels — all in one GVAN chunk — plus match metadata like event, site and cube_limit.

We read OGXM. read_gvab takes a file this repo never wrote, including one carrying another engine's analysis, and gvan-match appends its own analysis block rather than replacing what is already there.

We make no claim in the other direction, and that is about us, not about OGXM. One of our choices is a genuine departure: in match play we store normalized equity in the three cube equity fields, where the base spec specifies raw MWC. We do it for quantization — these fields are an int16 at 1e-4, which gives normalized equity about 20,000 usable steps at any score, while raw MWC has as few as 16 at a lopsided one — and we recover MWC on read from the shipped MET. It is the right trade for us and it is still a difference a reader cannot see: same fields, same layout, different numbers.

So: treat these as GammonView files that an OGXM reader may well be able to make partial sense of, not as OGXM files with extras. The two spec documents describe what this repo writes — the base spec with every addition marked [GammonView extension] and every divergence marked, including where a base reader would go wrong. The upstream specs they track are in HedgeHog's docs/ and remain the authority on the base format.

In the browser

The codec has a JavaScript twin, published to npm as @gammonview/gvformat:

npm install @gammonview/gvformat
import { read_gvab, convert_mat, parse_ogid } from '@gammonview/gvformat';

const match = read_gvab(bytes);           // .gvab -> .gva JSON
const dropped = convert_mat(matText);     // .mat  -> .gva JSON, no analysis
const pos   = parse_ogid(match.games[0].plies[0].ogid_before);

An ESM port of gvformat — the same reads, writes, source converters and OGID handling, with no engine, no WebAssembly and no build step. It is how gammonview.com parses a dropped .xg/.bgf/.mat client-side and renders the .gvab the Python side produced.

The port lives in gvformat-js/ and is maintained here rather than downstream. Python and JavaScript must agree byte-for-byte — the same .gvab in gives the same .gva JSON out — and the two test suites check that from either side.

There is no JavaScript port of gvanalysis: analysis needs the engine, and the engine is Python-side only.

Command-line tools

Entry points are defined in pyproject.toml and run with uv run:

CLI What it does
gvan-match Analyze a match (.mat, .gva/.ogxm, .gvab), compute per-move PR → .gva/.gvab
gvan-batch The same over many files or a directory, one output each
gvan-position Analyze a single position from an XGID or OGID
xg2gva Convert an eXtreme Gammon .xg file to .gva JSON (no engine)
bgf2gva Convert a BGBlitz .bgf file to .gva JSON (no engine)
uv run gvan-match match.mat --preset world_class --gvab   # binary out, not .gva
uv run gvan-match match.gvab --preset fast                # re-analyze a binary, append
uv run gvan-match match.mat --link                        # print a shareable URL
uv run gvan-batch matches/ --preset fast                  # a dir -> its match files
uv run gvan-position "XGID=-b----E-C---eE---c-e----B-:0:-1:1:31:0:0:0:0"
uv run gvan-position "11ccccchhhjjjjj:66666888dddddoo:N0N:13:B:R:0:0:0:0"

An OGXM input keeps any analysis it already carries — ours is appended as a new block rather than replacing it. --link encodes the whole match into a gammonview.com URL fragment, so it needs no hosted file; --browser opens it.

Analysis presets

--preset selects an eXtreme Gammon–style scheme: a cheap first pass screens every decision, and a stronger pass runs only where it is needed. The five built-ins:

Preset (aliases) 1st pass Middle tier 2nd pass XG equivalent
very_quick (vq) 2ply — — Very quick
fast (f) 2ply — 3ply Fast
deep (d) 3ply — — Deep
world_class (wc) 3ply 4ply (both kinds) truncated2 World Class (XG Roller+)
world_class_fast (wcf) 3ply truncated2 (cube only) truncated2 World Class (3-tier)

The second pass runs on an error; the middle tier runs on a borderline decision the screen would otherwise have settled. fast is the default.

Which one to use

493 matches were analyzed by XG at World Class and by each of the strong presets, then compared decision by decision:

mean gap to XG's match PR same checker play as XG PR within 1 of XG cost, ms/ply (24c → 4c)
world_class 0.423 90.3% 91.7% 291 → 2048
world_class_fast 0.431 90.0% 91.3% 166 → 1016

Re-measured 2026-09-20 over the same 493 matches after the retune. The retune moved both presets about 0.01 PR closer to XG, which is not a significant improvement — the paired confidence intervals span zero — but does establish that it cost nothing. The checker-play column is carried over from the original study; a like-for-like check says the retune moves it by at most 0.6 points, but its exact figure could not be reproduced (see the write-up).

Mean gap is the average distance between the two ratings of the same player-match, in either direction: rate a match with world_class and XG rates it 0.434 PR away, typically. It is not what following the preset costs you — priced by XG's own equities, a player who made world_class's recommended checker play every time would post a PR of 0.333 and world_class_fast 0.353. Both are inside what XG itself calls world-class play. (Those two figures predate the retune and were not re-measured; everything else in this section was.)

Cost is milliseconds per ply over six matches, on a 24-core M2 Ultra and a 4-core i5-7600 — absolutes rather than a multiple of fast, because that multiple is not constant across machines. Individual matches spread about 2× around these means. See the timings.

  • world_class_fast is the recommended analysis. It lands 0.008 PR behind the deepest preset for a little under half the time — a difference the 493-match corpus cannot resolve from zero (95% CI [−0.020, +0.004]). The two sit three times closer to each other (0.136) than either does to XG, because what separates them from XG is the engine, not the search depth — 95% of world_class_fast's squared PR error against XG is that engine floor, which no preset can spend its way past.
  • world_class is for reproducing XG specifically, not for being more accurate. Its only real edge is on borderline cubes, and it gets there by rolling them out less often than world_class_fast does — which is XG-like, not demonstrably better. It costs roughly twice as much for that.
  • If you want cheap, drop to deep, which costs an order of magnitude less than either. There is nothing between them: a balanced preset used to sit in that gap, but after the September 2026 retune it cost about what world_class_fast costs while agreeing with XG less often, so it was retired.

Every number above measures similarity to XG, which is not the same as being right — where the two engines differ there is no arbiter here to say which is correct. docs/PRESET_ACCURACY.md is the full experiment: how the presets split on checker play versus the cube, why rolling out costs agreement with XG, and the evidence that the residual disagreement is systematic rather than noise.

Presets live in gvanalysis/presets.py and are overridable via presets.yaml. See docs/CLI.md for the full reference — every flag, how the tiers fire, parallelism, and preset overrides.

Documentation

Doc What it covers
docs/CLI.md Full command-line reference — every flag, presets, parallelism
gvformat-js/README.md The JavaScript package — API, layout, parity rules
CONTRIBUTING.md How to run the tests, the conventions, what a good change looks like
CHANGELOG.md Release history, including the measurements behind several design decisions
CLAUDE.md Maintainer and coding-agent notes: design rationale, measurements, and the rituals around an engine upgrade
docs/OGXM_JSON_SPEC_GAMMONVIEW.md The JSON shape this repo writes (match → game → ply → analysis) — base spec plus every GammonView addition, marked
docs/OGXM_FORMAT_SPEC_GAMMONVIEW.md The binary .gvab chunk layout, including the GVAN extension chunk
docs/OGXM_COMPUTED_FIELDS.md Fields the reader computes rather than stores (PR, luck-MWC, MWC, classification)
docs/METHODOLOGY.md PR / luck / cube-decision methodology and filters
docs/PRESET_ACCURACY.md What each preset costs in accuracy, measured against XG over 493 matches

Licence

MIT. Every source file carries an SPDX header; LICENSE holds the full text.

MIT because what this repo mainly offers is a format, and a format wants to be copied in pieces — one routine lifted into another project, or the codec ported to another language. It also stays compatible in both directions at once, which matters in this ecosystem: GNU Backgammon is GPL, eXtreme Gammon and BGBlitz are proprietary, and all of them are welcome to interoperate. HedgeHog, whose format this implements, is MIT too.

bgsage, the engine behind the optional [engine] extra, is MPL-2.0, which constrains nothing here. MPL is file-level copyleft with no linking trigger: it attaches to its own files, and §3.3 expressly permits distributing a larger work under other terms. bgsage resolves from PyPI at install time and is never modified or redistributed here, and gvformat does not touch the engine at all.

The JavaScript mirror (gvformat-js/, published as @gammonview/gvformat) is under the same licence and ships its own copy.

The GammonView logo (assets/logo.png) is excluded: it identifies the site, so it is not part of the MIT grant. Fork the code freely; use your own mark.

Third-party material

Two pieces of third-party material are incorporated, both permissively licensed. Their notices must be preserved — see THIRD-PARTY-NOTICES.md, which reproduces them in full:

  • HedgeHog (MIT, © 2026 Eran Lambooij) — OGXM is HedgeHog's format. Most of gvformat is an independent implementation, but binary.py and export.py (and their JS mirrors) are genuine ports of its C++ sources: the chunk struct layouts and parse_ply_analysis_json from ogxm_io.cpp/ogxm_json.cpp, and _TurnState from ogxm_replay.cpp's replay_game(). Those four files say so in their headers.
  • Kazaross-XG2 MET (© 2011 Neil Kazaross; transcribed for GNUbg by Michael Petch) — the match-equity table embedded in gvformat/met.py and gvformat-js/src/met.js. It ships with GNU Backgammon but is not under its GPL; its own terms require only that the notice be preserved, and it is reproduced at the top of both files.

Metadata

Release files for gammonview 1.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for gammonview 1.2.0
File Size Uploaded
gammonview-1.2.0.tar.gz 3.1 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for gammonview 1.2.0
File Interpreter ABI Platform
gammonview-1.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 3.4 MB

Release files / gammonview-1.2.0.tar.gz

Download URL gammonview-1.2.0.tar.gz
Size 3.1 MB
Tags Source
SHA-256 checksum
How to use checksums
78b1cacf3d95c36ed6290ae2ca80a7bbc5f621acf61bd85e16b5075ed2a8d977
BLAKE2b-256 checksum
How to use checksums
8e480aab517d85a925d058ad63f607426be51e0eb413cf5006dde9c289d76056
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.

Transparency log

Release files / gammonview-1.2.0-py3-none-any.whl

Download URL gammonview-1.2.0-py3-none-any.whl
Size 222.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d0e93b430cb109fbbfa71c8dee4507c8de385a1c348906fbb97fc9b83174e64b
BLAKE2b-256 checksum
How to use checksums
182100fbc2d757d5648b40c4d93ff65c80dabadc5e8f628c964a1618a04a7dda
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.

Transparency log

Release history Release notifications | RSS feed

2.0.2

2 release files

2.0.1

2 release files

2.0.0

2 release files

1.5.1

2 release files

1.5.0

2 release files

1.4.0

2 release files

1.3.0

2 release files

This release

1.2.0 This release

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page