gammonview 2.0.2__tar.gz → 3.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gammonview-2.0.2 → gammonview-3.0.0}/CHANGELOG.md +139 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/CLAUDE.md +148 -55
- {gammonview-2.0.2 → gammonview-3.0.0}/PKG-INFO +34 -47
- {gammonview-2.0.2 → gammonview-3.0.0}/README.md +33 -46
- {gammonview-2.0.2 → gammonview-3.0.0}/docs/CLI.md +151 -94
- {gammonview-2.0.2 → gammonview-3.0.0}/docs/OGXM_V2_PROFILE.md +3 -1
- {gammonview-2.0.2 → gammonview-3.0.0}/docs/PRESET_ACCURACY.md +8 -0
- gammonview-3.0.0/experiments/2026-10-21-wc2-corpus.md +118 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvanalysis/__init__.py +11 -1
- {gammonview-2.0.2 → gammonview-3.0.0}/gvanalysis/batch.py +6 -1
- gammonview-3.0.0/gvanalysis/checker_eval.py +656 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvanalysis/game_eval.py +209 -19
- {gammonview-2.0.2 → gammonview-3.0.0}/gvanalysis/match.py +78 -22
- {gammonview-2.0.2 → gammonview-3.0.0}/gvanalysis/ogxm_reconstructor.py +50 -2
- {gammonview-2.0.2 → gammonview-3.0.0}/gvanalysis/presets.py +385 -94
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat/export.py +27 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat/merge.py +7 -4
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat/ogxm2.py +14 -2
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat/ogxm2_writer.py +6 -1
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat/stats.py +22 -1
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat/xg.py +50 -1
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/package.json +1 -1
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/src/export.js +27 -3
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/src/merge.js +7 -4
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/src/ogxm2.js +14 -2
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/src/ogxm2_writer.js +5 -1
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/src/stats.js +25 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/src/xg2gva.js +44 -1
- gammonview-3.0.0/gvformat-js/test/fixtures/ogxm2/annos-docs.json +1 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/annos-sha256.json +4 -4
- gammonview-3.0.0/gvformat-js/test/fixtures/ogxm2/blocks-docs.json +1 -0
- gammonview-3.0.0/gvformat-js/test/fixtures/ogxm2/blocks-sha256.json +42 -0
- gammonview-3.0.0/gvformat-js/test/fixtures/ogxm2/blocks.expected.json +1 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/blocks.ogxm +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-ogxm2-writer.js +16 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-xg-eval-levels.js +32 -0
- gammonview-3.0.0/tests/golden/3WNK_g1Z-PLsh_HyvQ5j4a.fast.gva +1 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/golden/3WNK_g1Z-PLsh_HyvQ5j4a.fast.gvab +0 -0
- gammonview-3.0.0/tests/golden/5nqfGw9bWG3deTaU.fast.gva +1 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/golden/5nqfGw9bWG3deTaU.fast.gvab +0 -0
- gammonview-3.0.0/tests/golden/B4_SrGcsKAQmoTyHlgJCbM.fast.gva +1 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/golden/B4_SrGcsKAQmoTyHlgJCbM.fast.gvab +0 -0
- gammonview-3.0.0/tests/golden/MYdvw1qGuyaRUH__.fast.gva +1 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/golden/MYdvw1qGuyaRUH__.fast.gvab +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/golden/PROVENANCE.json +1 -1
- gammonview-3.0.0/tests/golden/baR-U643iUDpvzTC.fast.gva +1 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/golden/baR-U643iUDpvzTC.fast.gvab +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/run_all.py +1 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_cube_tiers.py +23 -5
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_docs_presets.py +7 -2
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_ogxm2_reference.py +16 -5
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_ogxm2_writer.py +20 -1
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_ogxm_pipeline.py +3 -1
- gammonview-3.0.0/tests/test_presets.py +313 -0
- gammonview-3.0.0/tests/test_resign_errors.py +329 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_screened_checker.py +186 -1
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_xg_eval_levels.py +34 -0
- gammonview-2.0.2/gvanalysis/checker_eval.py +0 -378
- gammonview-2.0.2/gvformat-js/test/fixtures/ogxm2/annos-docs.json +0 -1
- gammonview-2.0.2/gvformat-js/test/fixtures/ogxm2/blocks-docs.json +0 -1
- gammonview-2.0.2/gvformat-js/test/fixtures/ogxm2/blocks-sha256.json +0 -42
- gammonview-2.0.2/gvformat-js/test/fixtures/ogxm2/blocks.expected.json +0 -1
- gammonview-2.0.2/tests/golden/3WNK_g1Z-PLsh_HyvQ5j4a.fast.gva +0 -1
- gammonview-2.0.2/tests/golden/5nqfGw9bWG3deTaU.fast.gva +0 -1
- gammonview-2.0.2/tests/golden/B4_SrGcsKAQmoTyHlgJCbM.fast.gva +0 -1
- gammonview-2.0.2/tests/golden/MYdvw1qGuyaRUH__.fast.gva +0 -1
- gammonview-2.0.2/tests/golden/baR-U643iUDpvzTC.fast.gva +0 -1
- {gammonview-2.0.2 → gammonview-3.0.0}/.gitattributes +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/.github/workflows/ci.yml +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/.github/workflows/engine.yml +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/.github/workflows/publish.yml +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/.gitignore +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/.python-version +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/CONTRIBUTING.md +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/LICENSE +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/THIRD-PARTY-NOTICES.md +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/assets/logo.png +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/docs/METHODOLOGY.md +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/docs/OGXM_COMPUTED_FIELDS.md +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/docs/OGXM_JSON_SPEC_GAMMONVIEW.md +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/docs/upstream-bgsage.md +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/docs/v1/OGXM_FORMAT_SPEC_GAMMONVIEW.md +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/experiments/2026-09-16-checker-mid-tier.md +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/experiments/2026-09-17-checker-mid-tier-followup.md +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/experiments/2026-09-19-cross-machine-costs.md +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/experiments/2026-09-20-corpus-rerun.md +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/experiments/bench.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvanalysis/compare_gva.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvanalysis/loader.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvanalysis/luck.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvanalysis/parentwatch.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvanalysis/position.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvanalysis/progress.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvanalysis/reconstruct_mat.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvanalysis/share.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat/__init__.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat/basefill.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat/bgf.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat/binary.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat/game_reconstructor.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat/legality.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat/mat.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat/mat_parser.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat/met.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat/notation.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat/ogid.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat/ogxm2_passthrough.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat/place.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat/reader.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat/share.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/.gitignore +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/LICENSE +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/README.md +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/THIRD-PARTY-NOTICES.md +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/cli/README.md +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/cli/bgf2gva.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/cli/mat2gva.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/cli/xg2gva.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/package-lock.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/src/basefill.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/src/bgf2gva.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/src/binary.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/src/constants.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/src/index.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/src/legality.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/src/mat2gva.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/src/met.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/src/notation.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/src/og2gva.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/src/ogid.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/src/ogxm2_passthrough.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/src/place.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/src/reader.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/src/sha256.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/src/share.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/mat/3WNK_g1Z-PLsh_HyvQ5j4a.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/mat/5nqfGw9bWG3deTaU.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/mat/B4_SrGcsKAQmoTyHlgJCbM.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/mat/C07OQCKyw4Pe-CnjFiAMYO.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/mat/MYdvw1qGuyaRUH__.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/mat/Sz-2PgKnvb6aFls69-qFXg.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/mat/UL-CV6F9jMM-E1Pu.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/mat/XCu3RJ0UvDg_Tldl.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/mat/baR-U643iUDpvzTC.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/mat/eXBNG5wZHS5Alu3P.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/mat/hQ8sVn2LbTdF4wRm.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/mat/rK7pXm4TqLb9NzWd.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/mat/uwvEbd63UvdX7SDin5o_Iw.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/annos-edits-sha256.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/annos.expected.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/annos.ogxm +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/bgf-writer-sha256.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/cube-on-two.expected.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/cube-on-two.ogxm +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/fields-docs.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/fields-sha256.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/fields.expected.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/fields.ogxm +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/foreign.ogxm +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/gen.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/match.expected.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/match.ogxm +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/passthrough-sha256.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/resign.expected.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/resign.ogxm +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/set-up.expected.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/set-up.ogxm +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/two-blocks.expected.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/two-blocks.ogxm +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/v1-chunks.gvab +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/fixtures/ogxm2/writer-sha256.json +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-alternative-move-steps.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-basefill.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-bgf-alternatives.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-bgf-embedded-cube.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-bgf-move-steps.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-chunk-passthrough.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-equity-loss.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-illegal-move.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-illegal-play-steps.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-legality.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-mat.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-merge.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-met.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-og.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-ogid.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-ogxm2-annos.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-ogxm2-blocks.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-ogxm2-fields.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-ogxm2-limits.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-ogxm2-passthrough.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-ogxm2.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-place.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-robustness.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-roundtrip.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-share.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-stats.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-xg-cube.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-xg-move-steps.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/gvformat-js/test/test-xg-zero-win-eval.js +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/pyproject.toml +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/README.md +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/bgf/3WNK_g1Z-PLsh_HyvQ5j4a.bgf +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/bgf/5nqfGw9bWG3deTaU.bgf +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/bgf/B4_SrGcsKAQmoTyHlgJCbM.bgf +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/bgf/C07OQCKyw4Pe-CnjFiAMYO.bgf +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/bgf/MYdvw1qGuyaRUH__.bgf +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/bgf/Sz-2PgKnvb6aFls69-qFXg.bgf +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/bgf/UL-CV6F9jMM-E1Pu.bgf +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/bgf/XCu3RJ0UvDg_Tldl.bgf +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/bgf/baR-U643iUDpvzTC.bgf +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/bgf/eXBNG5wZHS5Alu3P.bgf +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/bgf/uwvEbd63UvdX7SDin5o_Iw.bgf +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/gv/3WNK_g1Z-PLsh_HyvQ5j4a.gvab +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/gv/5nqfGw9bWG3deTaU.gvab +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/gv/B4_SrGcsKAQmoTyHlgJCbM.gva +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/gv/B4_SrGcsKAQmoTyHlgJCbM.gvab +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/gv/C07OQCKyw4Pe-CnjFiAMYO.gvab +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/gv/MYdvw1qGuyaRUH__.gvab +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/gv/Sz-2PgKnvb6aFls69-qFXg.gvab +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/gv/UL-CV6F9jMM-E1Pu.gvab +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/gv/XCu3RJ0UvDg_Tldl.gvab +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/gv/baR-U643iUDpvzTC.gvab +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/gv/eXBNG5wZHS5Alu3P.gvab +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/gv/uwvEbd63UvdX7SDin5o_Iw.gvab +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/mat/3WNK_g1Z-PLsh_HyvQ5j4a.mat +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/mat/5nqfGw9bWG3deTaU.mat +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/mat/B4_SrGcsKAQmoTyHlgJCbM.mat +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/mat/C07OQCKyw4Pe-CnjFiAMYO.mat +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/mat/MYdvw1qGuyaRUH__.mat +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/mat/Sz-2PgKnvb6aFls69-qFXg.mat +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/mat/UL-CV6F9jMM-E1Pu.mat +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/mat/XCu3RJ0UvDg_Tldl.mat +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/mat/baR-U643iUDpvzTC.mat +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/mat/eXBNG5wZHS5Alu3P.mat +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/mat/hQ8sVn2LbTdF4wRm.mat +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/mat/rK7pXm4TqLb9NzWd.mat +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/mat/uwvEbd63UvdX7SDin5o_Iw.mat +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/xg/3WNK_g1Z-PLsh_HyvQ5j4a.xg +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/xg/5nqfGw9bWG3deTaU.xg +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/xg/B4_SrGcsKAQmoTyHlgJCbM.xg +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/xg/C07OQCKyw4Pe-CnjFiAMYO.xg +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/xg/MYdvw1qGuyaRUH__.xg +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/xg/Sz-2PgKnvb6aFls69-qFXg.xg +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/xg/UL-CV6F9jMM-E1Pu.xg +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/xg/XCu3RJ0UvDg_Tldl.xg +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/xg/baR-U643iUDpvzTC.xg +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/xg/eXBNG5wZHS5Alu3P.xg +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/xg/hQ8sVn2LbTdF4wRm.xg +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/xg/rK7pXm4TqLb9NzWd.xg +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/samples/xg/uwvEbd63UvdX7SDin5o_Iw.xg +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/audit_corpus.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/fixtures.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/gen_js_v2_fixtures.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/gen_ogxm2_annos_fixtures.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/gen_ogxm2_blocks_fixtures.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/gen_ogxm2_fields_fixtures.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/gen_ogxm2_passthrough_fixtures.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/ogxm2_annos_cases.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/ogxm2_blocks_cases.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/ogxm2_fields_cases.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/ogxm2_oracle.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/passthrough_cases.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/regen_golden.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_alternative_move_steps.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_analysis_orientation.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_analyze_position.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_basefill.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_bgf_alternatives.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_bgf_cube_response.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_bgf_move_steps.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_chunk_passthrough.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_count_illegal.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_equity_loss.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_gvab_writer.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_illegal_move.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_illegal_play_steps.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_legality.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_loader_formats.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_luck_fill.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_met.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_ogxm2.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_ogxm2_annos.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_ogxm2_blocks.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_ogxm2_blocks_analysis.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_ogxm2_fields.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_ogxm2_fields_analysis.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_ogxm2_limits.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_ogxm2_passthrough.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_ogxm_conformance.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_ogxm_export.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_ogxm_stats.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_parent_watch.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_position_id.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_read_gvab.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_reconstruct_mat.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_resign_actor.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_restated_play_analysis.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_share_link.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_xg_cube.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_xg_move_steps.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/tests/test_xg_zero_win_eval.py +0 -0
- {gammonview-2.0.2 → gammonview-3.0.0}/uv.lock +0 -0
|
@@ -13,6 +13,145 @@ when the repository was opened; they are kept because they record why things
|
|
|
13
13
|
are the way they are — particularly the breaking changes and the measurements
|
|
14
14
|
behind several design decisions. Dates are the tag dates.
|
|
15
15
|
|
|
16
|
+
## 3.0.0 — 2026-10-22
|
|
17
|
+
|
|
18
|
+
**Presets are versioned, and a built-in cannot be changed** *(breaking)*
|
|
19
|
+
|
|
20
|
+
An analysis now records which version of a preset produced it: `world_class@2`
|
|
21
|
+
for a built-in, `custom/<name>@<digest>` for a preset from `presets.yaml`. Two
|
|
22
|
+
analyses with the same label used the same method, so a player comparing
|
|
23
|
+
matches, or a title-granting body requiring one method, can check that.
|
|
24
|
+
Locking the built-ins is what makes that hold. A `presets.yaml` entry that
|
|
25
|
+
reuses a built-in's name or alias is now skipped with a warning, where it used
|
|
26
|
+
to override the built-in. A custom preset's label comes from a digest of its
|
|
27
|
+
whole definition, screening constants included, so renaming it changes nothing
|
|
28
|
+
and a changed threshold changes the label. An environment override
|
|
29
|
+
(`GVAN_STAGED=0`, `GVAN_NO_SCREEN`, `GVAN_ROLLOUT_SELECT=0`) runs as `world_class@2+<digest>` rather
|
|
30
|
+
than under the real label. `--preset world_class@2` runs exactly that revision,
|
|
31
|
+
and refuses if anything else would run. The label travels in the block's
|
|
32
|
+
`x-gammonview-analysis` annotation (`preset=`), in Python and JS; older files
|
|
33
|
+
carry none.
|
|
34
|
+
|
|
35
|
+
**Checker plays are screened in stages, as XG screens them** *(change)*
|
|
36
|
+
|
|
37
|
+
`fast`, `deep` and `world_class` move to revision 2:
|
|
38
|
+
|
|
39
|
+
1. Every move is screened at 1-ply.
|
|
40
|
+
2. The top 14 are rescored at 2-ply, or every move within 0.2 of the 1-ply
|
|
41
|
+
leader when that is more (at most 40). A fixed count ignored a crowded
|
|
42
|
+
field: on 10,715 decisions the 3-ply best ranked 14th or worse at 1-ply on
|
|
43
|
+
0.19% of them, and the window recovers what that cost.
|
|
44
|
+
3. Moves within 0.16 of the 2-ply leader, at most 12, are evaluated at full
|
|
45
|
+
depth, along with the played move.
|
|
46
|
+
|
|
47
|
+
A middle tier now starts from the screen's numbers, and deepens only the moves
|
|
48
|
+
within `close_threshold` of the best, at most 8. That is XG's search interval,
|
|
49
|
+
4 moves within 0.080. The top-two gap is measured between moves at the same
|
|
50
|
+
depth, so a lone deep move is never "close", and it stays at the screen's
|
|
51
|
+
depth as XG's does. 4-ply checker work falls to about 36% of its cost.
|
|
52
|
+
`world_class` ran 1.31x faster end to end on 12 matches on a 24-core machine,
|
|
53
|
+
and 1.45x on an 8-core one. PR is identical to two decimals on 24 players, and
|
|
54
|
+
8 of 2,323 best moves differ, every one of them a tie. `very_quick` never
|
|
55
|
+
reaches the staged screen and stays at revision 1.
|
|
56
|
+
|
|
57
|
+
**A checker rollout rolls out the moves the tier below chose** *(change)*
|
|
58
|
+
|
|
59
|
+
When a checker play goes to the rollout tier, the moves rolled out are now
|
|
60
|
+
the best move from the tier that sent it (the 3-ply screen, or 4-ply after a
|
|
61
|
+
middle tier), its runner-up when within 0.02 of that best at the same depth,
|
|
62
|
+
and the played move. Every other move keeps that tier's numbers. bgsage
|
|
63
|
+
used to choose the set again from a fresh 2-ply filter, plus a loop that rolled
|
|
64
|
+
out any unrolled move ranked above the rolled ones: 3.85 moves a decision across
|
|
65
|
+
43,691 decisions in our corpus outputs, where XG World Class rolls out 2.06
|
|
66
|
+
(93.9% of its Roller+ decisions roll out exactly the best and the played move).
|
|
67
|
+
Ours now average 2.26. On 11 matches `world_class` ran 28% faster (448s to
|
|
68
|
+
320s); 4 of 1,937 best moves changed, together 0.029 equity of error, and PR
|
|
69
|
+
moved by 0.006 on average (at most 0.11). Each rolled-out row is bgsage's own
|
|
70
|
+
rollout of that board, to the last digit. `GVAN_ROLLOUT_SELECT=0` restores
|
|
71
|
+
bgsage's choice for A/B work. Part of `world_class@2`.
|
|
72
|
+
|
|
73
|
+
**`world_class_fast` is retired** *(breaking)*
|
|
74
|
+
|
|
75
|
+
`--preset world_class_fast` (`wcf`) now fails with a message pointing to
|
|
76
|
+
`world_class` or `deep`. Faster rollouts and the staged screen made
|
|
77
|
+
`world_class` cheap enough that `world_class_fast` stopped earning its place.
|
|
78
|
+
On 11 matches it took 74% of `world_class`'s time (234s against 318s) for PR
|
|
79
|
+
0.13 away from it on average (at most 0.38), while `deep` takes 9% (29s). Two
|
|
80
|
+
world-class methods would also split the comparison that preset labels exist
|
|
81
|
+
for. Files analyzed with it are unaffected, and it is not aliased to
|
|
82
|
+
`world_class`, since that would mislabel the analysis.
|
|
83
|
+
|
|
84
|
+
**`world_class` rolls out every wrong cube** *(change)*
|
|
85
|
+
|
|
86
|
+
A cube is still screened at 3-ply, but deepened to 4-ply within 0.12 of its
|
|
87
|
+
double or take point (was 0.08), and any wrong cube action is now rolled out
|
|
88
|
+
(was: only one wrong by more than 0.02), as XG World Class's "if different"
|
|
89
|
+
does. On 11 matches (1,109 cubes) it costs 2% of wall time and 6 more cube
|
|
90
|
+
rollouts, and it reaches the same optimal action on every cube as screening
|
|
91
|
+
them all at 4-ply would, which costs 11%. Against the old shape it flips 3
|
|
92
|
+
optimal actions; PR moves by 0.02 on average (at most 0.23). A preset's
|
|
93
|
+
`first_pass` also takes a `{checker, cube}` mapping now, for a cube screen at
|
|
94
|
+
its own depth. Part of `world_class@2`.
|
|
95
|
+
|
|
96
|
+
**A custom preset can be passed as data** *(new)*
|
|
97
|
+
|
|
98
|
+
`custom_preset(name, spec)` builds a custom preset from a definition held in
|
|
99
|
+
memory, in the shape of one `presets.yaml` entry, and `analyze_match` (with
|
|
100
|
+
`analyze_file`, `analyze_ogxm` and `resolve_preset`) accepts the `Preset` it
|
|
101
|
+
returns wherever it took a name. It obeys the file's rules and gets the file's
|
|
102
|
+
label, `custom/<name>@<digest>`, so a program that keeps presets somewhere of its
|
|
103
|
+
own no longer has to write a YAML file and re-import the module to run one.
|
|
104
|
+
Both are exported from `gvanalysis`, with `Preset` and `PresetConfigError`.
|
|
105
|
+
|
|
106
|
+
**A preset can size cube errors at a level of their own** *(new)*
|
|
107
|
+
|
|
108
|
+
`second_pass` takes a `{checker, cube}` mapping, as `first_pass` does, so a
|
|
109
|
+
preset can roll out a wrong cube and settle a wrong checker play at 4-ply, or
|
|
110
|
+
the reverse. A rollout of a cube is one position and a rollout of a checker
|
|
111
|
+
play is every candidate that survives, so the level worth paying for is not the
|
|
112
|
+
same one. The checker level is required: every kind still has an on-error
|
|
113
|
+
tier. A mapping naming one level for both is the bare level, with the same
|
|
114
|
+
digest, and no built-in changes. The analysis summary gains
|
|
115
|
+
`second_pass_cube_level`, set only when the two differ, and a cube decision
|
|
116
|
+
records the level it was sized at.
|
|
117
|
+
|
|
118
|
+
**Resign errors** *(new, opt-in)*
|
|
119
|
+
|
|
120
|
+
`--resign-errors` (`resign_errors=True` in the API) judges every accepted
|
|
121
|
+
resignation, as BMAB's configuration of XG does. It is off by default and no
|
|
122
|
+
preset turns it on; an analysis run with it is labelled `<preset>+resign`
|
|
123
|
+
(`world_class@2+resign`), and a pinned `--preset world_class@2` still accepts
|
|
124
|
+
it. A resignation counts as a decision for each player: the resigner is charged
|
|
125
|
+
if they conceded more than the position was worth, and the acceptor if they
|
|
126
|
+
took less. A resignation conceding the position's exact value counts for
|
|
127
|
+
neither. The position is the one the record shows was resigned: before the roll
|
|
128
|
+
after a play or a dance, and after it when the record shows a roll that had a
|
|
129
|
+
legal play and was left unplayed, since the resigner had seen it. In the XG
|
|
130
|
+
corpus 18 of 149 judged resignations come after a seen roll; judged before the
|
|
131
|
+
roll instead, two of them would be charged 0.33 and 0.60 that the roll actually
|
|
132
|
+
seen makes 0. A resignation on a pending double is not judged. The analysis goes
|
|
133
|
+
in v2's resign record (`resign_error`, `take_resign_error`, with its level), and
|
|
134
|
+
`compute_aggregates` / `computeAggregates` now count it in PR.
|
|
135
|
+
|
|
136
|
+
**Auto `--jobs` is `cpu // 3` workers, at least 2** *(behaviour change on
|
|
137
|
+
`--jobs 0`)*
|
|
138
|
+
|
|
139
|
+
It was `ceil(cpu / 7)`. A staged decision keeps fewer cores busy, so 4 workers
|
|
140
|
+
left a 24-core machine short: 8 workers ran 162.6s against 171.4s. On 8 and 4
|
|
141
|
+
cores the count is unchanged, at 2. Output is identical at any setting.
|
|
142
|
+
|
|
143
|
+
**An XG match's header names the deepest level it was read at** *(fix)*
|
|
144
|
+
|
|
145
|
+
`convert_xg` / `convertXg` set the block's `eval_level` to the plain n-ply
|
|
146
|
+
depth, which ignored XG Roller levels (and full rollouts) altogether. Since XG
|
|
147
|
+
stamps no analysis time -- `timestamp` is the match's date -- two runs of one
|
|
148
|
+
match that differed only in sending the doubtful plays to Roller+ or to
|
|
149
|
+
Roller++ converted to identical headers, and GammonView's library took the
|
|
150
|
+
second for the first. The header now names the deepest level any decision in
|
|
151
|
+
the document reports (`truncated2`, `truncated3`, `rollout`), with `ply`
|
|
152
|
+
unchanged. Every decision's own level is untouched; across the 13-file XG
|
|
153
|
+
corpus only headers moved.
|
|
154
|
+
|
|
16
155
|
## 2.0.2 — 2026-10-12
|
|
17
156
|
|
|
18
157
|
**A set-up start is written once** *(fix)*
|
|
@@ -218,58 +218,103 @@ in step when a flag changes; the rationale belongs only here.
|
|
|
218
218
|
two-pass scheme: a cheap first pass screens every decision, a stronger second
|
|
219
219
|
pass runs only on an error (the played checker move or cube action disagrees
|
|
220
220
|
with the first pass). Single-pass presets judge everything at the first pass.
|
|
221
|
-
The
|
|
222
|
-
`presets.yaml`
|
|
223
|
-
winning over global:
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
221
|
+
The four built-in presets live in `gvanalysis/presets.py` (always available)
|
|
222
|
+
and **cannot be overridden**. Optional `presets.yaml` files add custom presets
|
|
223
|
+
from two locations, project-local winning over global:
|
|
224
|
+
`~/.config/bgsage/presets.yaml` then `./presets.yaml`. Create either with
|
|
225
|
+
`--init-presets` (add `--global` for the config-dir one). An entry reusing a
|
|
226
|
+
built-in's name or alias is skipped with a warning; `default:` sets the
|
|
227
|
+
default. Restore defaults by deleting the file(s); a malformed file is skipped
|
|
228
|
+
with a warning (the other layer still loads).
|
|
229
|
+
`custom_preset(name, spec)` is the in-memory twin of one file entry (same
|
|
230
|
+
rules via `_check_custom`, same label), and every entry point that takes a
|
|
231
|
+
preset name also takes the `Preset` it returns.
|
|
232
|
+
|
|
233
|
+
**A preset label names one method.** Output records `Preset.label`:
|
|
234
|
+
`world_class@2` for a built-in, `custom/<name>@<digest>` for a user preset
|
|
235
|
+
(digest of its whole definition). The point is comparability -- a user, or a
|
|
236
|
+
title-granting body, must be able to tell and require that analyses used the
|
|
237
|
+
same version; it is not an anti-tampering measure. `Preset.definition()`
|
|
238
|
+
includes the screening constants from `checker_eval.screening()`, so any
|
|
239
|
+
change that can move results changes the digest, and `_REVISIONS` pins the
|
|
240
|
+
digest of every shipped revision: **`test_presets` fails until a changed
|
|
241
|
+
definition appends a new revision -- append a row, never edit one.** An
|
|
242
|
+
environment override (`GVAN_STAGED`, `GVAN_NO_SCREEN`) shows as
|
|
243
|
+
`world_class@2+<digest>` rather than running under the real label, and
|
|
244
|
+
`--preset world_class@2` is refused unless that exact revision is what runs.
|
|
245
|
+
Old revisions are listed, not runnable. The label travels in the block's
|
|
246
|
+
`x-gammonview-analysis` annotation (`preset=`); before that it was not
|
|
247
|
+
written to `.gvab` at all, so older files carry no preset.
|
|
228
248
|
|
|
229
249
|
| Preset (aliases) | 1st pass | 2nd pass | XG equivalent |
|
|
230
250
|
|---|---|---|---|
|
|
231
251
|
| `very_quick` (`vq`) | `2ply` | — | Very quick |
|
|
232
252
|
| `fast` (`f`) | `2ply` | `3ply` | Fast |
|
|
233
253
|
| `deep` (`d`) | `3ply` | — | Deep |
|
|
234
|
-
| `world_class` (`wc`) | `3ply` → `4ply` on close | `truncated2` | World Class (XG Roller+) |
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
254
|
+
| `world_class` (`wc`) | `3ply` → `4ply` on close (cube: within 0.12) | `truncated2` | World Class (XG Roller+) |
|
|
255
|
+
|
|
256
|
+
`world_class`'s cubes: a 3-ply screen, 4-ply within 0.12 of the double or
|
|
257
|
+
take point (`close_threshold: {checker: 0.08, cube: 0.12}`), and cube
|
|
258
|
+
`error_threshold` 0 -- any wrong action rolled out ("if different"). XG's own
|
|
259
|
+
routing, read off 39,284 cube records in 524 XG files, is shallower: no
|
|
260
|
+
cube is 3-ply; it screens at 2-ply and goes to 4-ply only within ~0.25 of the
|
|
261
|
+
double point (no 2-ply record above a gap of -0.24), every actual double,
|
|
262
|
+
take and pass at 4-ply or Roller+. We keep the deeper screen. Measured on
|
|
263
|
+
11 matches, 1,109 cubes, against the old shape (0.08 window, rollout above
|
|
264
|
+
0.02) and against 4-ply on every cube:
|
|
265
|
+
|
|
266
|
+
| cube shape | wall | 3-ply / 4-ply / rollout | action flips vs all-4-ply |
|
|
267
|
+
|---|---|---|---|
|
|
268
|
+
| old (@1) | 442s | 1013 / 49 / 47 | 3 |
|
|
269
|
+
| all 4-ply | 491s (+11%) | 0 / 1057 / 52 | -- |
|
|
270
|
+
| **@2** | 451s (+2%) | 981 / 75 / 53 | **0** |
|
|
271
|
+
|
|
272
|
+
So @2 reaches every verdict the all-4-ply shape does at a fifth of its cost.
|
|
273
|
+
PR still differs from it by 0.097 mean (max 0.56), and not through any cube
|
|
274
|
+
error -- lost equity is identical on all 1,109 -- but through the decision
|
|
275
|
+
count: 3-ply and 4-ply disagree on whether 1-2 cubes per player are trivial.
|
|
276
|
+
The checker interval stays at XG's Normal: Gigantic (16 within 0.32)
|
|
277
|
+
costs 3x the non-rollout checker CPU for 0.089 total best-move regret over
|
|
278
|
+
11,037 decisions, one decision holding 0.071 of it, rollouts unchanged.
|
|
279
|
+
|
|
280
|
+
**Resign errors are an analysis option, never a preset's** (`--resign-errors`,
|
|
281
|
+
`resign_errors=True`; `Preset.with_resign_errors`). They move PR, so the label
|
|
282
|
+
says so (`world_class@2+resign`), but they stay out of the definition and
|
|
283
|
+
digest: a pin names the preset, and `presets.yaml` refuses the key. The
|
|
284
|
+
position judged is the one the record shows was resigned. After a play or a
|
|
285
|
+
dance it is pre-roll for the next player. After a roll left unplayed that had
|
|
286
|
+
a legal play, it is that roll's best play, because the resigner saw the dice. In the XG
|
|
287
|
+
corpus 18 of 149 judged resignations follow a seen roll, and pre-roll
|
|
288
|
+
judging would have charged two of them 0.33 and 0.60 where the seen roll
|
|
289
|
+
makes both 0. A roll seen but not recorded cannot be detected; XG does record
|
|
290
|
+
one.
|
|
291
|
+
|
|
292
|
+
**`world_class_fast` was retired 2026-10-21**, before any release carried
|
|
293
|
+
preset labels. It was `world_class` without the checker middle tier: a 3-ply
|
|
294
|
+
screen, `truncated2` on error, and cubes that were merely borderline also
|
|
295
|
+
rolled out. Once seeded rollouts and the staged screen made `world_class`
|
|
296
|
+
cheap, it stopped earning a place. Paired on 11 matches it took 234s against
|
|
297
|
+
`world_class`'s 318s, for PR 0.13 away from it (mean; max 0.38). Cube verdicts
|
|
298
|
+
matched on all but 5 of 1,164, and 98 of 1,937 best moves differed, nearly
|
|
299
|
+
all near-ties that `world_class` deepens. `deep` took 29s. A second
|
|
300
|
+
world-class method also splits the pool that preset labels exist to keep
|
|
301
|
+
comparable, and analysis runs in the helper, not on a server, so there is no
|
|
302
|
+
hosting bill to save. Like `balanced`, it stays in `_RETIRED` and is not
|
|
303
|
+
aliased.
|
|
304
|
+
|
|
305
|
+
What its design established still holds for any 3-tier preset. A middle tier
|
|
306
|
+
pays against the *screen below* it, not the tier above, because it fires on
|
|
307
|
+
decisions the screen would otherwise have settled. Measured off a `3ply`
|
|
308
|
+
screen, 4-ply is 19x the screen on a move list (25ms → 467ms) and only 4x on
|
|
309
|
+
one pre-roll cube (24ms → 100ms). At a 0.04 window a cube is borderline ~5%
|
|
310
|
+
of the time and a checker play 53.8%. `mid_pass` takes a `{checker, cube}`
|
|
311
|
+
mapping as well as a bare level for that reason; no built-in uses it now. On
|
|
312
|
+
632 borderline cubes from XG-analyzed matches scored against an independent
|
|
313
|
+
`truncated3` arbiter, 4-ply disagreed with the arbiter's verdict on 14% of
|
|
314
|
+
them (mean gap error 0.0127) against 12%/0.0091 for a single `truncated2`
|
|
315
|
+
run. Averaging ten `truncated2` seeds improved that only to 0.0090, so a
|
|
316
|
+
360-trial rollout is limited by its horizon, not its variance, and 4-ply's
|
|
317
|
+
zero variance buys nothing back.
|
|
273
318
|
|
|
274
319
|
**Two thresholds, not one — they answer different questions.**
|
|
275
320
|
`close_threshold` gates the *middle* tier: how near a tie before the screen's
|
|
@@ -297,8 +342,8 @@ in step when a flag changes; the rationale belongs only here.
|
|
|
297
342
|
threshold, worst 0.56) and a borderline cube was re-checked never (0.4% of
|
|
298
343
|
cubes, median 0.07, max 0.22). The escalation is skipped when `mid_pass` names
|
|
299
344
|
`second_pass`'s own level — analyzers are memoized by level, so identity is
|
|
300
|
-
exact and re-running would buy the same answer twice. That
|
|
301
|
-
for `world_class_fast`'s cube tier by construction.
|
|
345
|
+
exact and re-running would buy the same answer twice. That made it a no-op
|
|
346
|
+
for the retired `world_class_fast`'s cube tier by construction.
|
|
302
347
|
|
|
303
348
|
**Every sizing tier is `truncated2`, never `4ply`, and that holds for both
|
|
304
349
|
decision kinds.** The cube half is the arbiter test above (12%/0.0091 against
|
|
@@ -324,8 +369,8 @@ in step when a flag changes; the rationale belongs only here.
|
|
|
324
369
|
name in `_RETIRED` and fails with an explanation rather than a bare "unknown
|
|
325
370
|
preset"; it is deliberately **not** aliased to `world_class_fast`, since the
|
|
326
371
|
preset name is written into the output document and a silent redirect would
|
|
327
|
-
mislabel the analysis.
|
|
328
|
-
order of magnitude cheaper. Don't reinvent it without a sizing tier cheaper
|
|
372
|
+
mislabel the analysis. With `world_class_fast` retired too, the gap below
|
|
373
|
+
`world_class` is `deep`, an order of magnitude cheaper. Don't reinvent it without a sizing tier cheaper
|
|
329
374
|
than `truncated2`, because that is the whole of what went wrong.
|
|
330
375
|
|
|
331
376
|
- **`gvan-batch`** (`gvanalysis/batch.py`) — Batch-analyze many match files
|
|
@@ -455,7 +500,10 @@ legal — our `.mat` path put bar entries last) and the duplicate played move.
|
|
|
455
500
|
|
|
456
501
|
`tests/test_ogxm2_reference.py` is the older ratchet: the reference `v1_to_v2`
|
|
457
502
|
over each golden's content written as v1, against a **pinned** table. Never edit
|
|
458
|
-
a row to make a run pass. The goldens themselves are v2 now.
|
|
503
|
+
a row to make a run pass. The goldens themselves are v2 now. Since the staged screen every row
|
|
504
|
+
reads `alternatives`: a top-ten list mixes 3-ply and 2-ply plays, and the
|
|
505
|
+
reference's frozen v1 reader gives every alternative the decision's level.
|
|
506
|
+
That is v1's limit; the v2 files we write pass (`test_ogxm2_writer`).
|
|
459
507
|
|
|
460
508
|
The two v1 parity suites, `test_gvab_writer` and `test_ogxm_conformance`,
|
|
461
509
|
compare against libogxm's *v1* writer and projection, and skip — saying so —
|
|
@@ -523,14 +571,59 @@ label. The best move agrees with bgsage's full-width answer on every decision
|
|
|
523
571
|
measured — 4 differences in 1567, all exact ties — while evaluating 1.8x fewer
|
|
524
572
|
moves.
|
|
525
573
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
574
|
+
**The screen is staged, as XG's is** (since the presets' `@2` revisions). A
|
|
575
|
+
decision goes 1-ply on every move -> 2-ply on the top `STAGE_SCREEN_MOVES` (14:
|
|
576
|
+
the display floor, paid at the cheapest real depth), widened to every move
|
|
577
|
+
within `STAGE_SCREEN_WINDOW` (0.2) of the 1-ply leader on a crowded field, at
|
|
578
|
+
most `STAGE_SCREEN_CAP` (40) -> N-ply on the 2-ply moves
|
|
579
|
+
within `STAGE_THRESHOLD` (0.16) of the leader, at most `STAGE_MAX_MOVES` (12),
|
|
580
|
+
plus the played move. A middle tier is handed the screen's result (`seed=`) and
|
|
581
|
+
deepens only its moves within `close_threshold` of the best, at most
|
|
582
|
+
`DEEPEN_MAX_MOVES` (8) -- XG's search interval, "4 moves within 0.080 at
|
|
583
|
+
3-ply" -- keeping the screen's numbers for the rest. `game_eval` measures the
|
|
584
|
+
top-two gap only between moves at the same depth, so a lone deep move is never
|
|
585
|
+
"close" (XG's rule: nothing in the window, it stays at 3-ply). Why: 4-ply was
|
|
586
|
+
90% of world_class's non-rollout checker CPU, re-screened from 1-ply on 14+
|
|
587
|
+
moves; staged it is 36% of that cost, PR identical to two decimals on 24
|
|
588
|
+
players, 8 of 2,323 best moves different and every one a tie, and
|
|
589
|
+
world_class 1.31x faster end to end (727s -> 554s, 12 matches), 1.45x on the
|
|
590
|
+
8-core M3. The XG facts behind it (window AND cap, no display floor -- a
|
|
591
|
+
quarter of XG World Class's listed moves are 1-ply) were read off the corpus.
|
|
592
|
+
A fixed 14 ignores how crowded the field is: on 10,715 decisions the 3-ply best
|
|
593
|
+
ranked 14th or worse at 1-ply on 0.19% of them, and the window recovers all that
|
|
594
|
+
loss (0.175 -> 0.130 total equity, the floor of sending every move to 2-ply) for
|
|
595
|
+
1.4 more 2-ply evaluations a decision. It went into `@2` before `@2` shipped.
|
|
596
|
+
`GVAN_STAGED=0` restores the flat screen for A/B work, and the preset label
|
|
597
|
+
then says `@2+<digest>`.
|
|
598
|
+
|
|
599
|
+
The flat screen's constants, still used at 2-ply and with `GVAN_STAGED=0`, are
|
|
600
|
+
documented with their measurements in the module: `ELEVATE_THRESHOLD` /
|
|
601
|
+
`ELEVATE_MAX_MOVES` (the accuracy filter) and `ELEVATE_MIN_MOVES` (a
|
|
602
|
+
display-quality floor). **Every one of these constants is part of a preset's
|
|
603
|
+
definition** (`checker_eval.screening()` feeds `Preset.digest()`), so changing
|
|
604
|
+
one fails `test_presets` until the affected presets get a new revision. An
|
|
605
|
+
unseeded rollout tier, cubeless and 1-ply analyzers, and any bgsage whose
|
|
530
606
|
internals have moved all fall back to `analyzer.checker_play` unchanged;
|
|
531
607
|
`GVAN_NO_SCREEN=1` forces that fallback everywhere, which is how the A/B
|
|
532
608
|
measurements above were taken.
|
|
533
609
|
|
|
610
|
+
**A rollout tier rolls out what the tier below chose, as XG's does.**
|
|
611
|
+
`game_eval` hands the second pass the screen's (or middle tier's) result as
|
|
612
|
+
`seed=`, and a rollout analyzer then rolls out only that result's best, its
|
|
613
|
+
runner-up within `ROLLOUT_RUNNER_UP_WINDOW` (0.02) at the same depth, and the
|
|
614
|
+
played move, keeping the seed's numbers for the rest. Left to itself bgsage
|
|
615
|
+
throws the seed away and picks again with a 2-ply filter (within 0.08, at most
|
|
616
|
+
5) plus a promotion loop: 3.85 rollouts a decision over 43,691 in our corpus
|
|
617
|
+
outputs, against XG World Class's 2.06 (93.9% exactly best + played; 6% add one,
|
|
618
|
+
nearly always the runner-up). Ours average 2.26 (26% take the runner-up, so the
|
|
619
|
+
window is wider than XG's; it is cheap insurance). On 11 matches `world_class`
|
|
620
|
+
went 448s -> 320s (-28%); 4 of 1,937 best moves changed, 0.029 equity of error
|
|
621
|
+
in total, PR within 0.006 mean (0.11 max). Each rolled row is bgsage's own
|
|
622
|
+
`cubeful_evaluate_board` call, so it matches bgsage exactly
|
|
623
|
+
(`test_screened_checker.check_seeded_rollout`). The second pass gets a seed
|
|
624
|
+
but no `seed_window`, so a multi-ply second pass (`fast`'s 3-ply) does not
|
|
625
|
+
deepen and is unchanged. `GVAN_ROLLOUT_SELECT=0` restores bgsage's choice.
|
|
626
|
+
|
|
534
627
|
**Because it duplicates a bgsage call site, it inherits that call's signature.**
|
|
535
628
|
bgsage 2.0 added `root_board=` to `cubeful_probs_and_equity_nply` — Stage 11's
|
|
536
629
|
snake NN is chosen once from the root position and held for the whole tree,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: gammonview
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: Backgammon match analysis — the pure-Python OGXM/GVA codec (`gvformat`) plus bgsage-powered match and position analysis (`gvanalysis`).
|
|
5
5
|
Project-URL: Homepage, https://github.com/ngvlamis/gammonview-analysis
|
|
6
6
|
Project-URL: Repository, https://github.com/ngvlamis/gammonview-analysis
|
|
@@ -202,7 +202,7 @@ own; analysis does not need them.) `--link` encodes the whole match into a
|
|
|
202
202
|
### Analysis presets
|
|
203
203
|
|
|
204
204
|
`--preset` selects an eXtreme Gammon–style scheme: a cheap first pass screens
|
|
205
|
-
every decision, and a stronger pass runs only where it is needed. The
|
|
205
|
+
every decision, and a stronger pass runs only where it is needed. The four
|
|
206
206
|
built-ins:
|
|
207
207
|
|
|
208
208
|
| Preset (aliases) | 1st pass | Middle tier | 2nd pass | XG equivalent |
|
|
@@ -210,60 +210,46 @@ built-ins:
|
|
|
210
210
|
| `very_quick` (`vq`) | `2ply` | — | — | Very quick |
|
|
211
211
|
| `fast` (`f`) | `2ply` | — | `3ply` | Fast |
|
|
212
212
|
| `deep` (`d`) | `3ply` | — | — | Deep |
|
|
213
|
-
| `world_class` (`wc`) | `3ply` | `4ply`
|
|
214
|
-
| `world_class_fast` (`wcf`) | `3ply` | `truncated2` (**cube only**) | `truncated2` | World Class (3-tier) |
|
|
213
|
+
| `world_class` (`wc`) | `3ply` | `4ply` | `truncated2` | World Class (XG Roller+) |
|
|
215
214
|
|
|
216
215
|
The second pass runs on an **error**; the middle tier runs on a **borderline**
|
|
217
216
|
decision the screen would otherwise have settled. `fast` is the default.
|
|
218
217
|
|
|
219
218
|
#### Which one to use
|
|
220
219
|
|
|
221
|
-
493 matches were analyzed by XG at World Class and by
|
|
222
|
-
|
|
220
|
+
493 matches were analyzed by XG at World Class and by `world_class`, then
|
|
221
|
+
compared decision by decision:
|
|
223
222
|
|
|
224
|
-
| | mean gap to XG's match PR |
|
|
225
|
-
|
|
226
|
-
| `world_class` | 0.
|
|
227
|
-
| `world_class_fast` | 0.431 | 90.0% | 91.3% | 166 → 1016 |
|
|
223
|
+
| | mean gap to XG's match PR | PR within 1 of XG | same cube action as XG |
|
|
224
|
+
|---|---|---|---|
|
|
225
|
+
| `world_class@2` | 0.420 | 92.0% | 96.2% |
|
|
228
226
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
study; a like-for-like check says the retune moves it by at most 0.6 points, but
|
|
234
|
-
its exact figure could not be reproduced (see
|
|
235
|
-
[the write-up](experiments/2026-09-20-corpus-rerun.md)).
|
|
227
|
+
Measured 2026-10-21. Against the September `world_class` on the same matches
|
|
228
|
+
the gap moved by −0.003, inside the noise (95% CI [−0.010, +0.004]), while the
|
|
229
|
+
run took 1.83× less time: 3 h 31 m against 6 h 25 m on a 24-core M2 Ultra, or
|
|
230
|
+
about 150 ms per ply. See [the write-up](experiments/2026-10-21-wc2-corpus.md).
|
|
236
231
|
|
|
237
232
|
**Mean gap** is the average distance between the two *ratings* of the same
|
|
238
233
|
player-match, in either direction: rate a match with `world_class` and XG rates
|
|
239
|
-
it 0.
|
|
240
|
-
|
|
241
|
-
checker play every time would post a PR of 0.333
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
`
|
|
257
|
-
|
|
258
|
-
- **`world_class` is for reproducing XG specifically**, not for being more
|
|
259
|
-
accurate. Its only real edge is on borderline cubes, and it gets there by
|
|
260
|
-
rolling them out *less* often than `world_class_fast` does — which is
|
|
261
|
-
XG-like, not demonstrably better. It costs roughly twice as much for that.
|
|
262
|
-
- **If you want cheap, drop to `deep`**, which costs an order of magnitude
|
|
263
|
-
less than either. There is nothing between them: a `balanced` preset used to
|
|
264
|
-
sit in that gap, but after the September 2026 retune it cost about what
|
|
265
|
-
`world_class_fast` costs while agreeing with XG less often, so it was
|
|
266
|
-
retired.
|
|
234
|
+
it about 0.42 PR away, typically. It is not what following the preset costs you.
|
|
235
|
+
Priced by XG's own equities, a player who made `world_class`'s recommended
|
|
236
|
+
checker play every time would post a PR of 0.333, inside what XG itself calls
|
|
237
|
+
world-class play. (That figure predates the September retune and was not
|
|
238
|
+
re-measured.)
|
|
239
|
+
|
|
240
|
+
- **`world_class` is the analysis to trust.** It routes as XG World Class
|
|
241
|
+
does: checker plays from a 3-ply screen to 4-ply on near-ties, cubes from a
|
|
242
|
+
3-ply screen to 4-ply within 0.12 of the double or take point, any wrong cube
|
|
243
|
+
action rolled out, and checker errors over 0.02 rolled out. Most of what
|
|
244
|
+
separates it from XG is the *engine*, not the search depth, and no preset can
|
|
245
|
+
spend its way past that. Resign errors, which BMAB's configuration counts,
|
|
246
|
+
are a separate switch (`--resign-errors`) that any preset can run with.
|
|
247
|
+
- **If you want cheap, drop to `deep`**, which costs about a tenth as much.
|
|
248
|
+
Nothing sits between them any more. `balanced` was retired in September 2026
|
|
249
|
+
for costing what the preset above it cost. `world_class_fast` was retired in
|
|
250
|
+
October 2026 once faster rollouts made `world_class` nearly as cheap: on 11
|
|
251
|
+
matches it took 74% of `world_class`'s time for a PR 0.13 away from it. Both
|
|
252
|
+
names now fail with a message saying so.
|
|
267
253
|
|
|
268
254
|
Every number above measures *similarity to XG*, which is not the same as being
|
|
269
255
|
right — where the two engines differ there is no arbiter here to say which is
|
|
@@ -272,9 +258,10 @@ experiment: how the presets split on checker play versus the cube, why rolling
|
|
|
272
258
|
out *costs* agreement with XG, and the evidence that the residual disagreement
|
|
273
259
|
is systematic rather than noise.
|
|
274
260
|
|
|
275
|
-
Presets live in `gvanalysis/presets.py
|
|
261
|
+
Presets live in `gvanalysis/presets.py`; every analysis records the preset's
|
|
262
|
+
version (`world_class@2`), and custom presets can be added via `presets.yaml`.
|
|
276
263
|
See **[`docs/CLI.md`](docs/CLI.md)** for the full reference — every flag, how the
|
|
277
|
-
tiers fire, parallelism, and
|
|
264
|
+
tiers fire, parallelism, preset versions and custom presets.
|
|
278
265
|
|
|
279
266
|
## Documentation
|
|
280
267
|
|