gammonview 1.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-1.0.0/.gitattributes +21 -0
- gammonview-1.0.0/.github/workflows/ci.yml +127 -0
- gammonview-1.0.0/.github/workflows/engine.yml +70 -0
- gammonview-1.0.0/.github/workflows/publish.yml +93 -0
- gammonview-1.0.0/.gitignore +40 -0
- gammonview-1.0.0/.python-version +1 -0
- gammonview-1.0.0/CHANGELOG.md +451 -0
- gammonview-1.0.0/CLAUDE.md +367 -0
- gammonview-1.0.0/CONTRIBUTING.md +160 -0
- gammonview-1.0.0/LICENSE +21 -0
- gammonview-1.0.0/PKG-INFO +319 -0
- gammonview-1.0.0/README.md +290 -0
- gammonview-1.0.0/THIRD-PARTY-NOTICES.md +80 -0
- gammonview-1.0.0/assets/logo.png +0 -0
- gammonview-1.0.0/docs/CLI.md +311 -0
- gammonview-1.0.0/docs/METHODOLOGY.md +111 -0
- gammonview-1.0.0/docs/OGXM_COMPUTED_FIELDS.md +275 -0
- gammonview-1.0.0/docs/OGXM_FORMAT_SPEC_GAMMONVIEW.md +703 -0
- gammonview-1.0.0/docs/OGXM_JSON_SPEC_GAMMONVIEW.md +851 -0
- gammonview-1.0.0/docs/PRESET_ACCURACY.md +229 -0
- gammonview-1.0.0/docs/upstream-bgsage.md +258 -0
- gammonview-1.0.0/gvanalysis/__init__.py +118 -0
- gammonview-1.0.0/gvanalysis/batch.py +232 -0
- gammonview-1.0.0/gvanalysis/checker_eval.py +378 -0
- gammonview-1.0.0/gvanalysis/compare_gva.py +323 -0
- gammonview-1.0.0/gvanalysis/game_eval.py +1010 -0
- gammonview-1.0.0/gvanalysis/loader.py +67 -0
- gammonview-1.0.0/gvanalysis/luck.py +143 -0
- gammonview-1.0.0/gvanalysis/match.py +832 -0
- gammonview-1.0.0/gvanalysis/ogxm_reconstructor.py +225 -0
- gammonview-1.0.0/gvanalysis/position.py +474 -0
- gammonview-1.0.0/gvanalysis/presets.py +344 -0
- gammonview-1.0.0/gvanalysis/progress.py +98 -0
- gammonview-1.0.0/gvanalysis/reconstruct_mat.py +306 -0
- gammonview-1.0.0/gvanalysis/share.py +30 -0
- gammonview-1.0.0/gvformat/__init__.py +65 -0
- gammonview-1.0.0/gvformat/basefill.py +300 -0
- gammonview-1.0.0/gvformat/bgf.py +1569 -0
- gammonview-1.0.0/gvformat/binary.py +956 -0
- gammonview-1.0.0/gvformat/export.py +1351 -0
- gammonview-1.0.0/gvformat/game_reconstructor.py +311 -0
- gammonview-1.0.0/gvformat/legality.py +243 -0
- gammonview-1.0.0/gvformat/mat.py +144 -0
- gammonview-1.0.0/gvformat/mat_parser.py +323 -0
- gammonview-1.0.0/gvformat/merge.py +133 -0
- gammonview-1.0.0/gvformat/met.py +196 -0
- gammonview-1.0.0/gvformat/notation.py +212 -0
- gammonview-1.0.0/gvformat/ogid.py +849 -0
- gammonview-1.0.0/gvformat/place.py +54 -0
- gammonview-1.0.0/gvformat/reader.py +1061 -0
- gammonview-1.0.0/gvformat/share.py +50 -0
- gammonview-1.0.0/gvformat/stats.py +450 -0
- gammonview-1.0.0/gvformat/xg.py +1413 -0
- gammonview-1.0.0/gvformat-js/.gitignore +1 -0
- gammonview-1.0.0/gvformat-js/LICENSE +21 -0
- gammonview-1.0.0/gvformat-js/README.md +108 -0
- gammonview-1.0.0/gvformat-js/THIRD-PARTY-NOTICES.md +80 -0
- gammonview-1.0.0/gvformat-js/cli/README.md +27 -0
- gammonview-1.0.0/gvformat-js/cli/bgf2gva.js +27 -0
- gammonview-1.0.0/gvformat-js/cli/mat2gva.js +27 -0
- gammonview-1.0.0/gvformat-js/cli/xg2gva.js +27 -0
- gammonview-1.0.0/gvformat-js/package-lock.json +35 -0
- gammonview-1.0.0/gvformat-js/package.json +39 -0
- gammonview-1.0.0/gvformat-js/src/basefill.js +263 -0
- gammonview-1.0.0/gvformat-js/src/bgf2gva.js +1337 -0
- gammonview-1.0.0/gvformat-js/src/binary.js +999 -0
- gammonview-1.0.0/gvformat-js/src/constants.js +92 -0
- gammonview-1.0.0/gvformat-js/src/export.js +1028 -0
- gammonview-1.0.0/gvformat-js/src/index.js +27 -0
- gammonview-1.0.0/gvformat-js/src/legality.js +220 -0
- gammonview-1.0.0/gvformat-js/src/mat2gva.js +737 -0
- gammonview-1.0.0/gvformat-js/src/merge.js +203 -0
- gammonview-1.0.0/gvformat-js/src/met.js +149 -0
- gammonview-1.0.0/gvformat-js/src/notation.js +212 -0
- gammonview-1.0.0/gvformat-js/src/og2gva.js +410 -0
- gammonview-1.0.0/gvformat-js/src/ogid.js +615 -0
- gammonview-1.0.0/gvformat-js/src/place.js +54 -0
- gammonview-1.0.0/gvformat-js/src/reader.js +1082 -0
- gammonview-1.0.0/gvformat-js/src/share.js +58 -0
- gammonview-1.0.0/gvformat-js/src/stats.js +359 -0
- gammonview-1.0.0/gvformat-js/src/xg2gva.js +1175 -0
- gammonview-1.0.0/gvformat-js/test/fixtures/mat/3WNK_g1Z-PLsh_HyvQ5j4a.json +1 -0
- gammonview-1.0.0/gvformat-js/test/fixtures/mat/5nqfGw9bWG3deTaU.json +1 -0
- gammonview-1.0.0/gvformat-js/test/fixtures/mat/B4_SrGcsKAQmoTyHlgJCbM.json +1 -0
- gammonview-1.0.0/gvformat-js/test/fixtures/mat/C07OQCKyw4Pe-CnjFiAMYO.json +1 -0
- gammonview-1.0.0/gvformat-js/test/fixtures/mat/MYdvw1qGuyaRUH__.json +1 -0
- gammonview-1.0.0/gvformat-js/test/fixtures/mat/Sz-2PgKnvb6aFls69-qFXg.json +1 -0
- gammonview-1.0.0/gvformat-js/test/fixtures/mat/UL-CV6F9jMM-E1Pu.json +1 -0
- gammonview-1.0.0/gvformat-js/test/fixtures/mat/XCu3RJ0UvDg_Tldl.json +1 -0
- gammonview-1.0.0/gvformat-js/test/fixtures/mat/baR-U643iUDpvzTC.json +1 -0
- gammonview-1.0.0/gvformat-js/test/fixtures/mat/eXBNG5wZHS5Alu3P.json +1 -0
- gammonview-1.0.0/gvformat-js/test/fixtures/mat/uwvEbd63UvdX7SDin5o_Iw.json +1 -0
- gammonview-1.0.0/gvformat-js/test/test-alternative-move-steps.js +145 -0
- gammonview-1.0.0/gvformat-js/test/test-basefill.js +268 -0
- gammonview-1.0.0/gvformat-js/test/test-bgf-alternatives.js +73 -0
- gammonview-1.0.0/gvformat-js/test/test-bgf-embedded-cube.js +93 -0
- gammonview-1.0.0/gvformat-js/test/test-bgf-move-steps.js +124 -0
- gammonview-1.0.0/gvformat-js/test/test-chunk-passthrough.js +161 -0
- gammonview-1.0.0/gvformat-js/test/test-equity-loss.js +207 -0
- gammonview-1.0.0/gvformat-js/test/test-illegal-move.js +123 -0
- gammonview-1.0.0/gvformat-js/test/test-illegal-play-steps.js +122 -0
- gammonview-1.0.0/gvformat-js/test/test-legality.js +145 -0
- gammonview-1.0.0/gvformat-js/test/test-mat.js +270 -0
- gammonview-1.0.0/gvformat-js/test/test-merge.js +330 -0
- gammonview-1.0.0/gvformat-js/test/test-met.js +76 -0
- gammonview-1.0.0/gvformat-js/test/test-og.js +495 -0
- gammonview-1.0.0/gvformat-js/test/test-ogid.js +258 -0
- gammonview-1.0.0/gvformat-js/test/test-place.js +121 -0
- gammonview-1.0.0/gvformat-js/test/test-robustness.js +221 -0
- gammonview-1.0.0/gvformat-js/test/test-roundtrip.js +409 -0
- gammonview-1.0.0/gvformat-js/test/test-share.js +110 -0
- gammonview-1.0.0/gvformat-js/test/test-stats.js +227 -0
- gammonview-1.0.0/gvformat-js/test/test-xg-cube.js +67 -0
- gammonview-1.0.0/gvformat-js/test/test-xg-eval-levels.js +106 -0
- gammonview-1.0.0/gvformat-js/test/test-xg-move-steps.js +117 -0
- gammonview-1.0.0/gvformat-js/test/test-xg-zero-win-eval.js +91 -0
- gammonview-1.0.0/pyproject.toml +95 -0
- gammonview-1.0.0/samples/README.md +245 -0
- gammonview-1.0.0/samples/bgf/3WNK_g1Z-PLsh_HyvQ5j4a.bgf +0 -0
- gammonview-1.0.0/samples/bgf/5nqfGw9bWG3deTaU.bgf +0 -0
- gammonview-1.0.0/samples/bgf/B4_SrGcsKAQmoTyHlgJCbM.bgf +0 -0
- gammonview-1.0.0/samples/bgf/C07OQCKyw4Pe-CnjFiAMYO.bgf +0 -0
- gammonview-1.0.0/samples/bgf/MYdvw1qGuyaRUH__.bgf +0 -0
- gammonview-1.0.0/samples/bgf/Sz-2PgKnvb6aFls69-qFXg.bgf +0 -0
- gammonview-1.0.0/samples/bgf/UL-CV6F9jMM-E1Pu.bgf +0 -0
- gammonview-1.0.0/samples/bgf/XCu3RJ0UvDg_Tldl.bgf +0 -0
- gammonview-1.0.0/samples/bgf/baR-U643iUDpvzTC.bgf +0 -0
- gammonview-1.0.0/samples/bgf/eXBNG5wZHS5Alu3P.bgf +0 -0
- gammonview-1.0.0/samples/bgf/uwvEbd63UvdX7SDin5o_Iw.bgf +0 -0
- gammonview-1.0.0/samples/gv/3WNK_g1Z-PLsh_HyvQ5j4a.gvab +0 -0
- gammonview-1.0.0/samples/gv/5nqfGw9bWG3deTaU.gvab +0 -0
- gammonview-1.0.0/samples/gv/B4_SrGcsKAQmoTyHlgJCbM.gva +1 -0
- gammonview-1.0.0/samples/gv/B4_SrGcsKAQmoTyHlgJCbM.gvab +0 -0
- gammonview-1.0.0/samples/gv/C07OQCKyw4Pe-CnjFiAMYO.gvab +0 -0
- gammonview-1.0.0/samples/gv/MYdvw1qGuyaRUH__.gvab +0 -0
- gammonview-1.0.0/samples/gv/Sz-2PgKnvb6aFls69-qFXg.gvab +0 -0
- gammonview-1.0.0/samples/gv/UL-CV6F9jMM-E1Pu.gvab +0 -0
- gammonview-1.0.0/samples/gv/XCu3RJ0UvDg_Tldl.gvab +0 -0
- gammonview-1.0.0/samples/gv/baR-U643iUDpvzTC.gvab +0 -0
- gammonview-1.0.0/samples/gv/eXBNG5wZHS5Alu3P.gvab +0 -0
- gammonview-1.0.0/samples/gv/uwvEbd63UvdX7SDin5o_Iw.gvab +0 -0
- gammonview-1.0.0/samples/mat/3WNK_g1Z-PLsh_HyvQ5j4a.mat +179 -0
- gammonview-1.0.0/samples/mat/5nqfGw9bWG3deTaU.mat +157 -0
- gammonview-1.0.0/samples/mat/B4_SrGcsKAQmoTyHlgJCbM.mat +58 -0
- gammonview-1.0.0/samples/mat/C07OQCKyw4Pe-CnjFiAMYO.mat +188 -0
- gammonview-1.0.0/samples/mat/MYdvw1qGuyaRUH__.mat +91 -0
- gammonview-1.0.0/samples/mat/Sz-2PgKnvb6aFls69-qFXg.mat +161 -0
- gammonview-1.0.0/samples/mat/UL-CV6F9jMM-E1Pu.mat +138 -0
- gammonview-1.0.0/samples/mat/XCu3RJ0UvDg_Tldl.mat +76 -0
- gammonview-1.0.0/samples/mat/baR-U643iUDpvzTC.mat +75 -0
- gammonview-1.0.0/samples/mat/eXBNG5wZHS5Alu3P.mat +130 -0
- gammonview-1.0.0/samples/mat/uwvEbd63UvdX7SDin5o_Iw.mat +165 -0
- gammonview-1.0.0/samples/xg/3WNK_g1Z-PLsh_HyvQ5j4a.xg +0 -0
- gammonview-1.0.0/samples/xg/5nqfGw9bWG3deTaU.xg +0 -0
- gammonview-1.0.0/samples/xg/B4_SrGcsKAQmoTyHlgJCbM.xg +0 -0
- gammonview-1.0.0/samples/xg/C07OQCKyw4Pe-CnjFiAMYO.xg +0 -0
- gammonview-1.0.0/samples/xg/MYdvw1qGuyaRUH__.xg +0 -0
- gammonview-1.0.0/samples/xg/Sz-2PgKnvb6aFls69-qFXg.xg +0 -0
- gammonview-1.0.0/samples/xg/UL-CV6F9jMM-E1Pu.xg +0 -0
- gammonview-1.0.0/samples/xg/XCu3RJ0UvDg_Tldl.xg +0 -0
- gammonview-1.0.0/samples/xg/baR-U643iUDpvzTC.xg +0 -0
- gammonview-1.0.0/samples/xg/eXBNG5wZHS5Alu3P.xg +0 -0
- gammonview-1.0.0/samples/xg/uwvEbd63UvdX7SDin5o_Iw.xg +0 -0
- gammonview-1.0.0/tests/audit_corpus.py +256 -0
- gammonview-1.0.0/tests/fixtures.py +137 -0
- gammonview-1.0.0/tests/golden/3WNK_g1Z-PLsh_HyvQ5j4a.fast.gva +1 -0
- gammonview-1.0.0/tests/golden/3WNK_g1Z-PLsh_HyvQ5j4a.fast.gvab +0 -0
- gammonview-1.0.0/tests/golden/5nqfGw9bWG3deTaU.fast.gva +1 -0
- gammonview-1.0.0/tests/golden/5nqfGw9bWG3deTaU.fast.gvab +0 -0
- gammonview-1.0.0/tests/golden/B4_SrGcsKAQmoTyHlgJCbM.fast.gva +1 -0
- gammonview-1.0.0/tests/golden/B4_SrGcsKAQmoTyHlgJCbM.fast.gvab +0 -0
- gammonview-1.0.0/tests/golden/MYdvw1qGuyaRUH__.fast.gva +1 -0
- gammonview-1.0.0/tests/golden/MYdvw1qGuyaRUH__.fast.gvab +0 -0
- gammonview-1.0.0/tests/golden/PROVENANCE.json +7 -0
- gammonview-1.0.0/tests/golden/baR-U643iUDpvzTC.fast.gva +1 -0
- gammonview-1.0.0/tests/golden/baR-U643iUDpvzTC.fast.gvab +0 -0
- gammonview-1.0.0/tests/regen_golden.py +129 -0
- gammonview-1.0.0/tests/run_all.py +147 -0
- gammonview-1.0.0/tests/test_alternative_move_steps.py +153 -0
- gammonview-1.0.0/tests/test_analyze_position.py +237 -0
- gammonview-1.0.0/tests/test_basefill.py +310 -0
- gammonview-1.0.0/tests/test_bgf_alternatives.py +106 -0
- gammonview-1.0.0/tests/test_bgf_cube_response.py +148 -0
- gammonview-1.0.0/tests/test_bgf_move_steps.py +133 -0
- gammonview-1.0.0/tests/test_chunk_passthrough.py +231 -0
- gammonview-1.0.0/tests/test_count_illegal.py +130 -0
- gammonview-1.0.0/tests/test_cube_tiers.py +168 -0
- gammonview-1.0.0/tests/test_docs_presets.py +135 -0
- gammonview-1.0.0/tests/test_equity_loss.py +177 -0
- gammonview-1.0.0/tests/test_gvab_writer.py +666 -0
- gammonview-1.0.0/tests/test_illegal_move.py +149 -0
- gammonview-1.0.0/tests/test_illegal_play_steps.py +180 -0
- gammonview-1.0.0/tests/test_legality.py +183 -0
- gammonview-1.0.0/tests/test_luck_fill.py +138 -0
- gammonview-1.0.0/tests/test_met.py +104 -0
- gammonview-1.0.0/tests/test_ogxm_conformance.py +377 -0
- gammonview-1.0.0/tests/test_ogxm_export.py +650 -0
- gammonview-1.0.0/tests/test_ogxm_pipeline.py +224 -0
- gammonview-1.0.0/tests/test_ogxm_stats.py +209 -0
- gammonview-1.0.0/tests/test_position_id.py +194 -0
- gammonview-1.0.0/tests/test_read_gvab.py +564 -0
- gammonview-1.0.0/tests/test_reconstruct_mat.py +187 -0
- gammonview-1.0.0/tests/test_resign_actor.py +107 -0
- gammonview-1.0.0/tests/test_screened_checker.py +371 -0
- gammonview-1.0.0/tests/test_share_link.py +141 -0
- gammonview-1.0.0/tests/test_xg_cube.py +87 -0
- gammonview-1.0.0/tests/test_xg_eval_levels.py +108 -0
- gammonview-1.0.0/tests/test_xg_move_steps.py +124 -0
- gammonview-1.0.0/tests/test_xg_zero_win_eval.py +106 -0
- gammonview-1.0.0/uv.lock +357 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Normalise line endings to LF. Nothing tracked here contains a CRLF today, so
|
|
2
|
+
# this is a no-op on the current tree; it exists so a clone with
|
|
3
|
+
# core.autocrlf=true cannot start rewriting one, in a repo whose tests compare
|
|
4
|
+
# fixtures exactly.
|
|
5
|
+
* text=auto eol=lf
|
|
6
|
+
|
|
7
|
+
# Binary fixtures, declared rather than detected. Git's heuristic would catch
|
|
8
|
+
# every one of these (all carry NULs in the first bytes -- checked), but "would
|
|
9
|
+
# detect" is the wrong guarantee for files a test compares as raw bytes: the
|
|
10
|
+
# .gvab goldens that pin the format, and the .xg/.bgf sources the converters
|
|
11
|
+
# are asserted against ply by ply. Declaring it also stops these showing up as
|
|
12
|
+
# diff noise.
|
|
13
|
+
*.gvab binary
|
|
14
|
+
*.xg binary
|
|
15
|
+
*.bgf binary
|
|
16
|
+
*.png binary
|
|
17
|
+
|
|
18
|
+
# Generated lockfiles: collapsed in diffs on GitHub, and never worth reviewing
|
|
19
|
+
# line by line. They are still tracked, and still reviewed as a version bump.
|
|
20
|
+
uv.lock linguist-generated=true -diff
|
|
21
|
+
gvformat-js/package-lock.json linguist-generated=true -diff
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# The cheap half of the suite, on every push and pull request.
|
|
2
|
+
#
|
|
3
|
+
# `tests/run_all.py --fast` skips the thirteen names in that file's ENGINE_BACKED
|
|
4
|
+
# set -- the tests that need the `[engine]` extra and minutes of CPU. Those run
|
|
5
|
+
# in engine.yml on a slower cadence. Splitting them is what keeps this workflow
|
|
6
|
+
# a few seconds per row, so the matrix below can afford to be wide.
|
|
7
|
+
name: CI
|
|
8
|
+
|
|
9
|
+
on:
|
|
10
|
+
push:
|
|
11
|
+
branches: [main]
|
|
12
|
+
pull_request:
|
|
13
|
+
workflow_dispatch:
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
|
|
18
|
+
# A newer push to the same ref makes the older run's verdict worthless.
|
|
19
|
+
concurrency:
|
|
20
|
+
group: ci-${{ github.ref }}
|
|
21
|
+
cancel-in-progress: true
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
codec:
|
|
25
|
+
name: codec (${{ matrix.os }}, py${{ matrix.python }})
|
|
26
|
+
runs-on: ${{ matrix.os }}
|
|
27
|
+
strategy:
|
|
28
|
+
# One red row should not hide the state of the others -- the usual reason
|
|
29
|
+
# to look at this matrix is to find out *which* versions broke.
|
|
30
|
+
fail-fast: false
|
|
31
|
+
matrix:
|
|
32
|
+
os: [ubuntu-latest]
|
|
33
|
+
python: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
34
|
+
include:
|
|
35
|
+
# Windows earns a row on its own merits: the suite compares .gvab
|
|
36
|
+
# goldens as raw bytes and reads .gva back with read_text(), which is
|
|
37
|
+
# precisely the sort of thing a line-ending or default-encoding
|
|
38
|
+
# difference breaks. .gitattributes is the other half of that defence.
|
|
39
|
+
- os: windows-latest
|
|
40
|
+
python: "3.13"
|
|
41
|
+
# macOS is the author's platform, so it is the configuration every
|
|
42
|
+
# change is written against; a row here says so out loud.
|
|
43
|
+
- os: macos-latest
|
|
44
|
+
python: "3.13"
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@v4
|
|
47
|
+
|
|
48
|
+
- uses: astral-sh/setup-uv@v6
|
|
49
|
+
with:
|
|
50
|
+
enable-cache: true
|
|
51
|
+
|
|
52
|
+
# Two flags, each load-bearing:
|
|
53
|
+
#
|
|
54
|
+
# `--python` is not optional. A tracked .python-version pins 3.13 and uv
|
|
55
|
+
# honours it, so without an explicit override every row of this matrix
|
|
56
|
+
# would quietly run 3.13 and the 3.10 row would prove nothing at all.
|
|
57
|
+
#
|
|
58
|
+
# `--no-dev` is the assertion, not a saving. The dev group carries bgsage
|
|
59
|
+
# and pyyaml; leaving it out means this job installs the project with zero
|
|
60
|
+
# third-party dependencies, which is the README's central claim about
|
|
61
|
+
# `gvformat`. If an import creeps in from the engine side, this goes red.
|
|
62
|
+
- name: fast suite, no engine
|
|
63
|
+
run: uv run --python ${{ matrix.python }} --no-dev python tests/run_all.py --fast
|
|
64
|
+
|
|
65
|
+
js:
|
|
66
|
+
name: gvformat-js (node ${{ matrix.node }})
|
|
67
|
+
runs-on: ubuntu-latest
|
|
68
|
+
strategy:
|
|
69
|
+
fail-fast: false
|
|
70
|
+
# package.json declares `engines: node >=18`. Test the floor it promises
|
|
71
|
+
# and the current release; the versions between them are not where an ESM
|
|
72
|
+
# or TypedArray difference is going to appear.
|
|
73
|
+
matrix:
|
|
74
|
+
node: ["18", "24"]
|
|
75
|
+
steps:
|
|
76
|
+
- uses: actions/checkout@v4
|
|
77
|
+
|
|
78
|
+
- uses: actions/setup-node@v4
|
|
79
|
+
with:
|
|
80
|
+
node-version: ${{ matrix.node }}
|
|
81
|
+
cache: npm
|
|
82
|
+
cache-dependency-path: gvformat-js/package-lock.json
|
|
83
|
+
|
|
84
|
+
# `npm ci`, not `npm install`: the one runtime dependency (pako) is
|
|
85
|
+
# pinned in the lockfile, and the lockfile is what the published tarball
|
|
86
|
+
# is built against. The only publishConfig here is `access: public`,
|
|
87
|
+
# which scoped packages need at publish time and which affects nothing
|
|
88
|
+
# about this install.
|
|
89
|
+
- run: npm ci
|
|
90
|
+
working-directory: gvformat-js
|
|
91
|
+
|
|
92
|
+
- run: npm test
|
|
93
|
+
working-directory: gvformat-js
|
|
94
|
+
|
|
95
|
+
build:
|
|
96
|
+
name: build (sdist + wheel)
|
|
97
|
+
runs-on: ubuntu-latest
|
|
98
|
+
steps:
|
|
99
|
+
- uses: actions/checkout@v4
|
|
100
|
+
with:
|
|
101
|
+
# hatch-vcs derives the version from git tags. A shallow clone has no
|
|
102
|
+
# tags and the build does not fail -- it quietly stamps 0.1.dev1+g...
|
|
103
|
+
# on the artifacts. Fetch everything, then print what it decided.
|
|
104
|
+
fetch-depth: 0
|
|
105
|
+
|
|
106
|
+
- uses: astral-sh/setup-uv@v6
|
|
107
|
+
with:
|
|
108
|
+
enable-cache: true
|
|
109
|
+
|
|
110
|
+
- run: uv build
|
|
111
|
+
|
|
112
|
+
- name: report the derived version
|
|
113
|
+
run: ls -l dist/
|
|
114
|
+
|
|
115
|
+
# The wheel's real test is whether the base install works on its own. No
|
|
116
|
+
# project, no extras, nothing from the lockfile: just the wheel and a
|
|
117
|
+
# bare interpreter. This is what `pip install gammonview` gets.
|
|
118
|
+
#
|
|
119
|
+
# It runs from outside the checkout on purpose. With the repo as cwd,
|
|
120
|
+
# `import gvformat` finds the source tree sitting right there and the
|
|
121
|
+
# check passes without the wheel being involved at all -- so the assert
|
|
122
|
+
# insists the module actually came out of a site-packages.
|
|
123
|
+
- name: the wheel imports with no extras
|
|
124
|
+
working-directory: ${{ runner.temp }}
|
|
125
|
+
run: |
|
|
126
|
+
uv run --isolated --no-project --with "$GITHUB_WORKSPACE"/dist/*.whl \
|
|
127
|
+
python -c 'import gvformat, pathlib; p = pathlib.Path(gvformat.__file__); print(p); assert "site-packages" in p.parts, p'
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# The engine-backed half of the suite: the thirteen names in run_all.py's
|
|
2
|
+
# ENGINE_BACKED set. They need the `[engine]` extra -- bgsage, a large compiled
|
|
3
|
+
# wheel, plus pyyaml -- and they analyze a whole match, so they cost minutes
|
|
4
|
+
# where ci.yml costs seconds. That is the only reason they are split out; it is
|
|
5
|
+
# a cadence decision, not a statement about how much they matter.
|
|
6
|
+
#
|
|
7
|
+
# One thing to know before reading this job's output: three of these tests
|
|
8
|
+
# consume tests/golden/, and two compare engine output to it *exactly*.
|
|
9
|
+
# test_ogxm_pipeline re-analyzes the corpus and requires the .gvab to match
|
|
10
|
+
# byte-for-byte; test_luck_fill re-measures luck and requires identical floats,
|
|
11
|
+
# on purpose ("not close, identical"). Those goldens were generated on macOS
|
|
12
|
+
# arm64, and **bgsage does not reproduce them here**: measured 2026-09-13 on
|
|
13
|
+
# this runner with the same bgsage build, one best move of 521 changed, one cube
|
|
14
|
+
# verdict of 296 changed, and one match's PR moved 6.448 -> 6.246. It is not
|
|
15
|
+
# rounding, and it is not a bug -- it is a different CPU.
|
|
16
|
+
#
|
|
17
|
+
# So those two comparisons are platform-gated: they skip by name wherever the
|
|
18
|
+
# machine does not match tests/golden/PROVENANCE.json, and every structural
|
|
19
|
+
# check in the same two files still runs. Expect a handful of SKIP lines here
|
|
20
|
+
# and nothing red. The gate is in tests/fixtures.py:goldens_are_native; the
|
|
21
|
+
# provenance file is stamped by regen_golden.py, so regenerating on a different
|
|
22
|
+
# machine moves the gate rather than needing this comment edited.
|
|
23
|
+
#
|
|
24
|
+
# tests/regen_golden.py is genuinely not run: regenerating is a maintainer action
|
|
25
|
+
# on the machine that owns the goldens, and CLAUDE.md explains why the diff wants
|
|
26
|
+
# reading rather than accepting.
|
|
27
|
+
name: engine
|
|
28
|
+
|
|
29
|
+
on:
|
|
30
|
+
push:
|
|
31
|
+
branches: [main]
|
|
32
|
+
schedule:
|
|
33
|
+
# Mondays, 06:00 UTC. This is the run that catches a bgsage release moving
|
|
34
|
+
# the numbers underneath us without anyone here touching a file.
|
|
35
|
+
- cron: "0 6 * * 1"
|
|
36
|
+
workflow_dispatch:
|
|
37
|
+
|
|
38
|
+
permissions:
|
|
39
|
+
contents: read
|
|
40
|
+
|
|
41
|
+
concurrency:
|
|
42
|
+
group: engine-${{ github.ref }}
|
|
43
|
+
cancel-in-progress: true
|
|
44
|
+
|
|
45
|
+
jobs:
|
|
46
|
+
full-suite:
|
|
47
|
+
name: full suite (py${{ matrix.python }})
|
|
48
|
+
# ubuntu-latest is x86_64 on purpose. bgsage publishes manylinux wheels for
|
|
49
|
+
# x86_64 only, so an arm64 runner would fall back to the sdist and spend the
|
|
50
|
+
# job compiling an engine instead of testing against one.
|
|
51
|
+
runs-on: ubuntu-latest
|
|
52
|
+
strategy:
|
|
53
|
+
fail-fast: false
|
|
54
|
+
# The floor and the version this repo develops on. The middle of the
|
|
55
|
+
# range is covered by ci.yml, which exercises the same source without
|
|
56
|
+
# the engine.
|
|
57
|
+
matrix:
|
|
58
|
+
python: ["3.10", "3.13"]
|
|
59
|
+
steps:
|
|
60
|
+
- uses: actions/checkout@v4
|
|
61
|
+
|
|
62
|
+
- uses: astral-sh/setup-uv@v6
|
|
63
|
+
with:
|
|
64
|
+
enable-cache: true
|
|
65
|
+
|
|
66
|
+
# No --no-dev here: the dev group is exactly what these tests need. The
|
|
67
|
+
# runner reports skips separately from passes, so a suite that quietly
|
|
68
|
+
# stopped finding its fixtures does not read as green.
|
|
69
|
+
- name: full suite
|
|
70
|
+
run: uv run --python ${{ matrix.python }} python tests/run_all.py
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Publish to PyPI via trusted publishing (OIDC). No API token exists anywhere:
|
|
2
|
+
# PyPI is configured to trust *this workflow in this repository*, and mints a
|
|
3
|
+
# short-lived credential for the job at run time. Nothing to store, leak, or
|
|
4
|
+
# rotate, and a stolen repo secret cannot publish because there is no secret.
|
|
5
|
+
#
|
|
6
|
+
# One-time setup on PyPI (https://pypi.org/manage/account/publishing/), which
|
|
7
|
+
# must be done before the first run, because the project does not exist yet:
|
|
8
|
+
#
|
|
9
|
+
# PyPI project name : gammonview
|
|
10
|
+
# Owner : ngvlamis
|
|
11
|
+
# Repository name : gammonview-analysis
|
|
12
|
+
# Workflow name : publish.yml
|
|
13
|
+
# Environment name : pypi
|
|
14
|
+
#
|
|
15
|
+
# That registers a "pending publisher"; the project is created by the first
|
|
16
|
+
# successful upload.
|
|
17
|
+
name: publish
|
|
18
|
+
|
|
19
|
+
on:
|
|
20
|
+
# A tag is the release signal, matching how the version is derived: hatch-vcs
|
|
21
|
+
# reads it from git, so the tag *is* the version. Pushing v1.2.3 publishes
|
|
22
|
+
# 1.2.3 -- there is no number to bump anywhere else, and no way for the tag
|
|
23
|
+
# and the artifact to disagree.
|
|
24
|
+
push:
|
|
25
|
+
tags: ["v*"]
|
|
26
|
+
# Manual trigger, for a tag that already exists (as v1.0.0 did when this
|
|
27
|
+
# workflow was written) or to retry a failed upload.
|
|
28
|
+
workflow_dispatch:
|
|
29
|
+
|
|
30
|
+
permissions:
|
|
31
|
+
contents: read
|
|
32
|
+
|
|
33
|
+
jobs:
|
|
34
|
+
build:
|
|
35
|
+
name: build sdist + wheel
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/checkout@v4
|
|
39
|
+
with:
|
|
40
|
+
# hatch-vcs derives the version from git tags, and a shallow clone has
|
|
41
|
+
# none. It does not fail -- it quietly stamps 0.1.dev1+g<sha> on the
|
|
42
|
+
# artifacts, which is the sort of thing you discover on PyPI.
|
|
43
|
+
fetch-depth: 0
|
|
44
|
+
|
|
45
|
+
- uses: astral-sh/setup-uv@v6
|
|
46
|
+
with:
|
|
47
|
+
enable-cache: true
|
|
48
|
+
|
|
49
|
+
- run: uv build
|
|
50
|
+
|
|
51
|
+
# Refuse to publish a version that git did not name. Without this, a run
|
|
52
|
+
# from an untagged commit uploads a dev version to PyPI, where it cannot
|
|
53
|
+
# be taken back.
|
|
54
|
+
- name: the artifacts carry a real release version
|
|
55
|
+
run: |
|
|
56
|
+
ls -l dist/
|
|
57
|
+
version=$(ls dist/*.whl | sed -E 's|.*/gammonview-([^-]+)-py3.*|\1|')
|
|
58
|
+
echo "derived version: $version"
|
|
59
|
+
case "$version" in
|
|
60
|
+
*dev*|*+*)
|
|
61
|
+
echo "::error::refusing to publish $version -- hatch-vcs found no tag on this commit"
|
|
62
|
+
exit 1 ;;
|
|
63
|
+
esac
|
|
64
|
+
|
|
65
|
+
- name: metadata renders on PyPI
|
|
66
|
+
run: uv run --isolated --no-project --with twine python -m twine check dist/*
|
|
67
|
+
|
|
68
|
+
- uses: actions/upload-artifact@v4
|
|
69
|
+
with:
|
|
70
|
+
name: dist
|
|
71
|
+
path: dist/
|
|
72
|
+
|
|
73
|
+
pypi:
|
|
74
|
+
name: upload to PyPI
|
|
75
|
+
needs: build
|
|
76
|
+
runs-on: ubuntu-latest
|
|
77
|
+
# The environment is the approval gate: GitHub can require a manual review
|
|
78
|
+
# before the job runs, and PyPI's trusted publisher is bound to this name,
|
|
79
|
+
# so a workflow that does not declare it cannot publish.
|
|
80
|
+
environment:
|
|
81
|
+
name: pypi
|
|
82
|
+
url: https://pypi.org/p/gammonview
|
|
83
|
+
permissions:
|
|
84
|
+
# The OIDC token this mints is what PyPI trades for an upload credential.
|
|
85
|
+
# Without it the action fails with a confusing 403.
|
|
86
|
+
id-token: write
|
|
87
|
+
steps:
|
|
88
|
+
- uses: actions/download-artifact@v4
|
|
89
|
+
with:
|
|
90
|
+
name: dist
|
|
91
|
+
path: dist/
|
|
92
|
+
|
|
93
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Python-generated files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[oc]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info
|
|
8
|
+
|
|
9
|
+
# Test-runner scratch. The suite here is standalone scripts run by
|
|
10
|
+
# tests/run_all.py, not pytest -- but contributors reach for pytest anyway, and
|
|
11
|
+
# it leaves this behind.
|
|
12
|
+
.pytest_cache/
|
|
13
|
+
|
|
14
|
+
# Virtual environments
|
|
15
|
+
.venv
|
|
16
|
+
|
|
17
|
+
# JS mirror dependencies (installed by `npm install` in gvformat-js/)
|
|
18
|
+
node_modules/
|
|
19
|
+
|
|
20
|
+
# Agent instruction data -- covered by a global gitignore on the author's
|
|
21
|
+
# machine, which a public clone obviously cannot rely on.
|
|
22
|
+
.claude/
|
|
23
|
+
|
|
24
|
+
# User-created preset overrides (built-in defaults live in presets.py)
|
|
25
|
+
presets.yaml
|
|
26
|
+
|
|
27
|
+
# Live OpenGammon API data for test-og.js's parity section: pulled from a real
|
|
28
|
+
# account, so it carries real handles. Everything else under samples/ is
|
|
29
|
+
# committed and anonymized -- this one directory is not.
|
|
30
|
+
samples/og/
|
|
31
|
+
|
|
32
|
+
# Instructions for non-Claude agents. Not part of the project.
|
|
33
|
+
AGENTS.md
|
|
34
|
+
|
|
35
|
+
# Local maintainer notes (NOTES.local.md and friends): superseded workflows,
|
|
36
|
+
# host paths -- true and useful, but not public-repo material.
|
|
37
|
+
*.local.md
|
|
38
|
+
|
|
39
|
+
# Mac System files
|
|
40
|
+
.DS_Store
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|