hbengine 0.2.3
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.
- package/README.md +20 -0
- package/engine-cli/connect.mjs +73 -0
- package/engine-cli/hbengine.mjs +565 -0
- package/engine-cli/package.json +1 -0
- package/engine-cli/templates.mjs +14 -0
- package/engine-conformance/analyse-worker.mjs +12 -0
- package/engine-conformance/analyse.mjs +4458 -0
- package/engine-conformance/bundle.mjs +208 -0
- package/engine-conformance/clock.mjs +294 -0
- package/engine-conformance/csp.go +56 -0
- package/engine-conformance/csp.json +6 -0
- package/engine-conformance/csp.mjs +20 -0
- package/engine-conformance/gzip-sizes.mjs +9 -0
- package/engine-conformance/package.json +1 -0
- package/engine-conformance/picture.mjs +55 -0
- package/engine-conformance/report.go +349 -0
- package/engine-conformance/run.mjs +1217 -0
- package/engine-conformance/scan.mjs +476 -0
- package/engine-conformance/testdata/clock-overlay/bundle/assets.json +1 -0
- package/engine-conformance/testdata/clock-overlay/bundle/game.css +17 -0
- package/engine-conformance/testdata/clock-overlay/bundle/game.js +83 -0
- package/engine-conformance/testdata/clock-overlay/bundle/hbtest.json +4 -0
- package/engine-conformance/testdata/clock-overlay/bundle/index.html +34 -0
- package/engine-conformance/testdata/clock-probe/bundle/assets.json +1 -0
- package/engine-conformance/testdata/clock-probe/bundle/game.css +17 -0
- package/engine-conformance/testdata/clock-probe/bundle/game.js +65 -0
- package/engine-conformance/testdata/clock-probe/bundle/hbtest.json +4 -0
- package/engine-conformance/testdata/clock-probe/bundle/index.html +35 -0
- package/engine-conformance/testdata/clock-probe/bundle/probe-worker.js +46 -0
- package/engine-conformance/testdata/clock-probe/bundle/probe.js +210 -0
- package/engine-conformance/testdata/clock-probe/math/dice.tsv +100 -0
- package/engine-conformance/testdata/clock-probe/math/manifest.json +2925 -0
- package/engine-conformance/testdata/clock-worker/bundle/assets.json +1 -0
- package/engine-conformance/testdata/clock-worker/bundle/clock-worker.js +14 -0
- package/engine-conformance/testdata/clock-worker/bundle/game.css +17 -0
- package/engine-conformance/testdata/clock-worker/bundle/game.js +61 -0
- package/engine-conformance/testdata/clock-worker/bundle/hbtest.json +4 -0
- package/engine-conformance/testdata/clock-worker/bundle/index.html +35 -0
- package/engine-conformance/testdata/clock-worker/math/dice.tsv +100 -0
- package/engine-conformance/testdata/clock-worker/math/manifest.json +2925 -0
- package/engine-conformance/travel.mjs +1209 -0
- package/engine-conformance/vendor/acorn/LICENSE +21 -0
- package/engine-conformance/vendor/acorn/acorn.mjs +6266 -0
- package/engine-conformance/wasm-releases.json +168 -0
- package/engine-examples/README.md +23 -0
- package/engine-examples/coin/AGENTS.md +3 -0
- package/engine-examples/coin/README.md +7 -0
- package/engine-examples/coin/bundle/assets.json +1 -0
- package/engine-examples/coin/bundle/game.css +5 -0
- package/engine-examples/coin/bundle/game.js +19 -0
- package/engine-examples/coin/bundle/hbtest.json +5 -0
- package/engine-examples/coin/bundle/index.html +16 -0
- package/engine-examples/coin/math/coin.tsv +2 -0
- package/engine-examples/coin/math/manifest.json +17 -0
- package/engine-examples/crash/AGENTS.md +3 -0
- package/engine-examples/crash/README.md +14 -0
- package/engine-examples/crash/bundle/assets.json +1 -0
- package/engine-examples/crash/bundle/game.css +8 -0
- package/engine-examples/crash/bundle/game.js +90 -0
- package/engine-examples/crash/bundle/hbtest.json +5 -0
- package/engine-examples/crash/bundle/index.html +20 -0
- package/engine-examples/crash/math/crash-01.tsv +2 -0
- package/engine-examples/crash/math/crash-02.tsv +2 -0
- package/engine-examples/crash/math/crash-03.tsv +2 -0
- package/engine-examples/crash/math/crash-04.tsv +2 -0
- package/engine-examples/crash/math/crash-05.tsv +2 -0
- package/engine-examples/crash/math/crash-06.tsv +2 -0
- package/engine-examples/crash/math/crash-07.tsv +2 -0
- package/engine-examples/crash/math/crash-08.tsv +2 -0
- package/engine-examples/crash/math/crash-09.tsv +2 -0
- package/engine-examples/crash/math/crash-10.tsv +2 -0
- package/engine-examples/crash/math/crash-11.tsv +2 -0
- package/engine-examples/crash/math/crash-12.tsv +2 -0
- package/engine-examples/crash/math/crash-13.tsv +2 -0
- package/engine-examples/crash/math/crash-14.tsv +2 -0
- package/engine-examples/crash/math/crash-15.tsv +2 -0
- package/engine-examples/crash/math/crash-16.tsv +2 -0
- package/engine-examples/crash/math/crash-17.tsv +2 -0
- package/engine-examples/crash/math/crash-18.tsv +2 -0
- package/engine-examples/crash/math/crash-19.tsv +2 -0
- package/engine-examples/crash/math/crash-20.tsv +2 -0
- package/engine-examples/crash/math/crash-21.tsv +2 -0
- package/engine-examples/crash/math/crash-22.tsv +2 -0
- package/engine-examples/crash/math/crash-23.tsv +2 -0
- package/engine-examples/crash/math/crash-24.tsv +2 -0
- package/engine-examples/crash/math/crash-25.tsv +2 -0
- package/engine-examples/crash/math/crash-26.tsv +2 -0
- package/engine-examples/crash/math/crash-27.tsv +2 -0
- package/engine-examples/crash/math/crash-28.tsv +2 -0
- package/engine-examples/crash/math/crash-29.tsv +2 -0
- package/engine-examples/crash/math/crash-30.tsv +2 -0
- package/engine-examples/crash/math/crash-31.tsv +2 -0
- package/engine-examples/crash/math/crash-32.tsv +2 -0
- package/engine-examples/crash/math/crash-33.tsv +2 -0
- package/engine-examples/crash/math/crash-34.tsv +2 -0
- package/engine-examples/crash/math/crash-35.tsv +2 -0
- package/engine-examples/crash/math/crash-36.tsv +2 -0
- package/engine-examples/crash/math/crash-37.tsv +2 -0
- package/engine-examples/crash/math/crash-38.tsv +2 -0
- package/engine-examples/crash/math/crash-39.tsv +2 -0
- package/engine-examples/crash/math/crash-40.tsv +2 -0
- package/engine-examples/crash/math/crash-41.tsv +2 -0
- package/engine-examples/crash/math/crash-42.tsv +2 -0
- package/engine-examples/crash/math/crash-43.tsv +2 -0
- package/engine-examples/crash/math/crash-44.tsv +2 -0
- package/engine-examples/crash/math/crash-45.tsv +2 -0
- package/engine-examples/crash/math/crash-46.tsv +2 -0
- package/engine-examples/crash/math/crash-47.tsv +2 -0
- package/engine-examples/crash/math/crash-48.tsv +2 -0
- package/engine-examples/crash/math/crash-49.tsv +2 -0
- package/engine-examples/crash/math/crash-50.tsv +2 -0
- package/engine-examples/crash/math/crash-51.tsv +2 -0
- package/engine-examples/crash/math/crash-52.tsv +2 -0
- package/engine-examples/crash/math/crash-53.tsv +2 -0
- package/engine-examples/crash/math/crash-54.tsv +2 -0
- package/engine-examples/crash/math/crash-55.tsv +2 -0
- package/engine-examples/crash/math/crash-56.tsv +2 -0
- package/engine-examples/crash/math/crash-57.tsv +2 -0
- package/engine-examples/crash/math/crash-58.tsv +2 -0
- package/engine-examples/crash/math/crash-59.tsv +2 -0
- package/engine-examples/crash/math/crash-60.tsv +2 -0
- package/engine-examples/crash/math/crash-61.tsv +2 -0
- package/engine-examples/crash/math/crash-62.tsv +2 -0
- package/engine-examples/crash/math/crash-63.tsv +2 -0
- package/engine-examples/crash/math/crash-64.tsv +2 -0
- package/engine-examples/crash/math/gen.mjs +31 -0
- package/engine-examples/crash/math/manifest.json +1 -0
- package/engine-examples/dice/AGENTS.md +3 -0
- package/engine-examples/dice/README.md +25 -0
- package/engine-examples/dice/bundle/assets.json +1 -0
- package/engine-examples/dice/bundle/game.css +17 -0
- package/engine-examples/dice/bundle/game.js +61 -0
- package/engine-examples/dice/bundle/hbtest.json +4 -0
- package/engine-examples/dice/bundle/index.html +34 -0
- package/engine-examples/dice/math/dice.tsv +100 -0
- package/engine-examples/dice/math/manifest.json +2925 -0
- package/engine-examples/keno/AGENTS.md +3 -0
- package/engine-examples/keno/README.md +11 -0
- package/engine-examples/keno/bundle/assets.json +1 -0
- package/engine-examples/keno/bundle/game.css +14 -0
- package/engine-examples/keno/bundle/game.js +86 -0
- package/engine-examples/keno/bundle/hbtest.json +4 -0
- package/engine-examples/keno/bundle/i18n.json +342 -0
- package/engine-examples/keno/bundle/index.html +20 -0
- package/engine-examples/keno/bundle/model.json +1 -0
- package/engine-examples/keno/math/gen.mjs +35 -0
- package/engine-examples/keno/math/keno-picks-1.tsv +2 -0
- package/engine-examples/keno/math/keno-picks-10.tsv +11 -0
- package/engine-examples/keno/math/keno-picks-2.tsv +3 -0
- package/engine-examples/keno/math/keno-picks-3.tsv +4 -0
- package/engine-examples/keno/math/keno-picks-4.tsv +5 -0
- package/engine-examples/keno/math/keno-picks-5.tsv +6 -0
- package/engine-examples/keno/math/keno-picks-6.tsv +7 -0
- package/engine-examples/keno/math/keno-picks-7.tsv +8 -0
- package/engine-examples/keno/math/keno-picks-8.tsv +9 -0
- package/engine-examples/keno/math/keno-picks-9.tsv +10 -0
- package/engine-examples/keno/math/manifest.json +1 -0
- package/engine-examples/keno/math-kit/pack.mjs +165 -0
- package/engine-examples/keno/math-kit/package.json +8 -0
- package/engine-examples/keno/math-kit/slot.mjs +94 -0
- package/engine-examples/keno/math-kit/tumble.mjs +94 -0
- package/engine-examples/lines/AGENTS.md +3 -0
- package/engine-examples/lines/README.md +20 -0
- package/engine-examples/lines/bundle/assets.json +1 -0
- package/engine-examples/lines/bundle/game.css +16 -0
- package/engine-examples/lines/bundle/game.js +125 -0
- package/engine-examples/lines/bundle/hbtest.json +4 -0
- package/engine-examples/lines/bundle/i18n.json +242 -0
- package/engine-examples/lines/bundle/index.html +23 -0
- package/engine-examples/lines/bundle/model.json +1 -0
- package/engine-examples/lines/math/gen.mjs +54 -0
- package/engine-examples/lines/math-kit/pack.mjs +165 -0
- package/engine-examples/lines/math-kit/package.json +8 -0
- package/engine-examples/lines/math-kit/slot.mjs +94 -0
- package/engine-examples/lines/math-kit/tumble.mjs +94 -0
- package/engine-examples/mines/AGENTS.md +3 -0
- package/engine-examples/mines/README.md +13 -0
- package/engine-examples/mines/bundle/assets.json +1 -0
- package/engine-examples/mines/bundle/game.css +12 -0
- package/engine-examples/mines/bundle/game.js +58 -0
- package/engine-examples/mines/bundle/hbtest.json +8 -0
- package/engine-examples/mines/bundle/index.html +16 -0
- package/engine-examples/mines/math/gen.mjs +40 -0
- package/engine-examples/mines/math/manifest.json +1 -0
- package/engine-examples/mines/math/mines-1-1.tsv +2 -0
- package/engine-examples/mines/math/mines-1-2.tsv +2 -0
- package/engine-examples/mines/math/mines-1-22.tsv +2 -0
- package/engine-examples/mines/math/mines-1-23.tsv +2 -0
- package/engine-examples/mines/math/mines-1-24.tsv +2 -0
- package/engine-examples/mines/math/mines-1-3.tsv +2 -0
- package/engine-examples/mines/math/mines-10-1.tsv +2 -0
- package/engine-examples/mines/math/mines-10-10.tsv +2 -0
- package/engine-examples/mines/math/mines-10-11.tsv +2 -0
- package/engine-examples/mines/math/mines-10-12.tsv +2 -0
- package/engine-examples/mines/math/mines-10-13.tsv +2 -0
- package/engine-examples/mines/math/mines-10-14.tsv +2 -0
- package/engine-examples/mines/math/mines-10-15.tsv +2 -0
- package/engine-examples/mines/math/mines-10-2.tsv +2 -0
- package/engine-examples/mines/math/mines-10-3.tsv +2 -0
- package/engine-examples/mines/math/mines-10-4.tsv +2 -0
- package/engine-examples/mines/math/mines-10-5.tsv +2 -0
- package/engine-examples/mines/math/mines-10-6.tsv +2 -0
- package/engine-examples/mines/math/mines-10-7.tsv +2 -0
- package/engine-examples/mines/math/mines-10-8.tsv +2 -0
- package/engine-examples/mines/math/mines-10-9.tsv +2 -0
- package/engine-examples/mines/math/mines-11-1.tsv +2 -0
- package/engine-examples/mines/math/mines-11-10.tsv +2 -0
- package/engine-examples/mines/math/mines-11-11.tsv +2 -0
- package/engine-examples/mines/math/mines-11-12.tsv +2 -0
- package/engine-examples/mines/math/mines-11-13.tsv +2 -0
- package/engine-examples/mines/math/mines-11-14.tsv +2 -0
- package/engine-examples/mines/math/mines-11-2.tsv +2 -0
- package/engine-examples/mines/math/mines-11-3.tsv +2 -0
- package/engine-examples/mines/math/mines-11-4.tsv +2 -0
- package/engine-examples/mines/math/mines-11-5.tsv +2 -0
- package/engine-examples/mines/math/mines-11-6.tsv +2 -0
- package/engine-examples/mines/math/mines-11-7.tsv +2 -0
- package/engine-examples/mines/math/mines-11-8.tsv +2 -0
- package/engine-examples/mines/math/mines-11-9.tsv +2 -0
- package/engine-examples/mines/math/mines-12-1.tsv +2 -0
- package/engine-examples/mines/math/mines-12-10.tsv +2 -0
- package/engine-examples/mines/math/mines-12-11.tsv +2 -0
- package/engine-examples/mines/math/mines-12-12.tsv +2 -0
- package/engine-examples/mines/math/mines-12-13.tsv +2 -0
- package/engine-examples/mines/math/mines-12-2.tsv +2 -0
- package/engine-examples/mines/math/mines-12-3.tsv +2 -0
- package/engine-examples/mines/math/mines-12-4.tsv +2 -0
- package/engine-examples/mines/math/mines-12-5.tsv +2 -0
- package/engine-examples/mines/math/mines-12-6.tsv +2 -0
- package/engine-examples/mines/math/mines-12-7.tsv +2 -0
- package/engine-examples/mines/math/mines-12-8.tsv +2 -0
- package/engine-examples/mines/math/mines-12-9.tsv +2 -0
- package/engine-examples/mines/math/mines-13-1.tsv +2 -0
- package/engine-examples/mines/math/mines-13-10.tsv +2 -0
- package/engine-examples/mines/math/mines-13-11.tsv +2 -0
- package/engine-examples/mines/math/mines-13-12.tsv +2 -0
- package/engine-examples/mines/math/mines-13-2.tsv +2 -0
- package/engine-examples/mines/math/mines-13-3.tsv +2 -0
- package/engine-examples/mines/math/mines-13-4.tsv +2 -0
- package/engine-examples/mines/math/mines-13-5.tsv +2 -0
- package/engine-examples/mines/math/mines-13-6.tsv +2 -0
- package/engine-examples/mines/math/mines-13-7.tsv +2 -0
- package/engine-examples/mines/math/mines-13-8.tsv +2 -0
- package/engine-examples/mines/math/mines-13-9.tsv +2 -0
- package/engine-examples/mines/math/mines-14-1.tsv +2 -0
- package/engine-examples/mines/math/mines-14-10.tsv +2 -0
- package/engine-examples/mines/math/mines-14-11.tsv +2 -0
- package/engine-examples/mines/math/mines-14-2.tsv +2 -0
- package/engine-examples/mines/math/mines-14-3.tsv +2 -0
- package/engine-examples/mines/math/mines-14-4.tsv +2 -0
- package/engine-examples/mines/math/mines-14-5.tsv +2 -0
- package/engine-examples/mines/math/mines-14-6.tsv +2 -0
- package/engine-examples/mines/math/mines-14-7.tsv +2 -0
- package/engine-examples/mines/math/mines-14-8.tsv +2 -0
- package/engine-examples/mines/math/mines-14-9.tsv +2 -0
- package/engine-examples/mines/math/mines-15-1.tsv +2 -0
- package/engine-examples/mines/math/mines-15-10.tsv +2 -0
- package/engine-examples/mines/math/mines-15-2.tsv +2 -0
- package/engine-examples/mines/math/mines-15-3.tsv +2 -0
- package/engine-examples/mines/math/mines-15-4.tsv +2 -0
- package/engine-examples/mines/math/mines-15-5.tsv +2 -0
- package/engine-examples/mines/math/mines-15-6.tsv +2 -0
- package/engine-examples/mines/math/mines-15-7.tsv +2 -0
- package/engine-examples/mines/math/mines-15-8.tsv +2 -0
- package/engine-examples/mines/math/mines-15-9.tsv +2 -0
- package/engine-examples/mines/math/mines-16-1.tsv +2 -0
- package/engine-examples/mines/math/mines-16-2.tsv +2 -0
- package/engine-examples/mines/math/mines-16-3.tsv +2 -0
- package/engine-examples/mines/math/mines-16-4.tsv +2 -0
- package/engine-examples/mines/math/mines-16-5.tsv +2 -0
- package/engine-examples/mines/math/mines-16-6.tsv +2 -0
- package/engine-examples/mines/math/mines-16-7.tsv +2 -0
- package/engine-examples/mines/math/mines-16-8.tsv +2 -0
- package/engine-examples/mines/math/mines-16-9.tsv +2 -0
- package/engine-examples/mines/math/mines-17-1.tsv +2 -0
- package/engine-examples/mines/math/mines-17-2.tsv +2 -0
- package/engine-examples/mines/math/mines-17-3.tsv +2 -0
- package/engine-examples/mines/math/mines-17-4.tsv +2 -0
- package/engine-examples/mines/math/mines-17-5.tsv +2 -0
- package/engine-examples/mines/math/mines-17-6.tsv +2 -0
- package/engine-examples/mines/math/mines-17-7.tsv +2 -0
- package/engine-examples/mines/math/mines-17-8.tsv +2 -0
- package/engine-examples/mines/math/mines-18-1.tsv +2 -0
- package/engine-examples/mines/math/mines-18-2.tsv +2 -0
- package/engine-examples/mines/math/mines-18-3.tsv +2 -0
- package/engine-examples/mines/math/mines-18-4.tsv +2 -0
- package/engine-examples/mines/math/mines-18-5.tsv +2 -0
- package/engine-examples/mines/math/mines-18-6.tsv +2 -0
- package/engine-examples/mines/math/mines-18-7.tsv +2 -0
- package/engine-examples/mines/math/mines-19-1.tsv +2 -0
- package/engine-examples/mines/math/mines-19-2.tsv +2 -0
- package/engine-examples/mines/math/mines-19-3.tsv +2 -0
- package/engine-examples/mines/math/mines-19-4.tsv +2 -0
- package/engine-examples/mines/math/mines-19-5.tsv +2 -0
- package/engine-examples/mines/math/mines-19-6.tsv +2 -0
- package/engine-examples/mines/math/mines-2-1.tsv +2 -0
- package/engine-examples/mines/math/mines-2-19.tsv +2 -0
- package/engine-examples/mines/math/mines-2-2.tsv +2 -0
- package/engine-examples/mines/math/mines-2-20.tsv +2 -0
- package/engine-examples/mines/math/mines-2-21.tsv +2 -0
- package/engine-examples/mines/math/mines-2-22.tsv +2 -0
- package/engine-examples/mines/math/mines-2-23.tsv +2 -0
- package/engine-examples/mines/math/mines-2-3.tsv +2 -0
- package/engine-examples/mines/math/mines-2-4.tsv +2 -0
- package/engine-examples/mines/math/mines-2-5.tsv +2 -0
- package/engine-examples/mines/math/mines-20-1.tsv +2 -0
- package/engine-examples/mines/math/mines-20-2.tsv +2 -0
- package/engine-examples/mines/math/mines-20-3.tsv +2 -0
- package/engine-examples/mines/math/mines-20-4.tsv +2 -0
- package/engine-examples/mines/math/mines-20-5.tsv +2 -0
- package/engine-examples/mines/math/mines-21-1.tsv +2 -0
- package/engine-examples/mines/math/mines-21-2.tsv +2 -0
- package/engine-examples/mines/math/mines-21-3.tsv +2 -0
- package/engine-examples/mines/math/mines-21-4.tsv +2 -0
- package/engine-examples/mines/math/mines-22-1.tsv +2 -0
- package/engine-examples/mines/math/mines-22-2.tsv +2 -0
- package/engine-examples/mines/math/mines-22-3.tsv +2 -0
- package/engine-examples/mines/math/mines-23-1.tsv +2 -0
- package/engine-examples/mines/math/mines-23-2.tsv +2 -0
- package/engine-examples/mines/math/mines-24-1.tsv +2 -0
- package/engine-examples/mines/math/mines-3-1.tsv +2 -0
- package/engine-examples/mines/math/mines-3-17.tsv +2 -0
- package/engine-examples/mines/math/mines-3-18.tsv +2 -0
- package/engine-examples/mines/math/mines-3-19.tsv +2 -0
- package/engine-examples/mines/math/mines-3-2.tsv +2 -0
- package/engine-examples/mines/math/mines-3-20.tsv +2 -0
- package/engine-examples/mines/math/mines-3-21.tsv +2 -0
- package/engine-examples/mines/math/mines-3-22.tsv +2 -0
- package/engine-examples/mines/math/mines-3-3.tsv +2 -0
- package/engine-examples/mines/math/mines-3-4.tsv +2 -0
- package/engine-examples/mines/math/mines-3-5.tsv +2 -0
- package/engine-examples/mines/math/mines-3-6.tsv +2 -0
- package/engine-examples/mines/math/mines-4-1.tsv +2 -0
- package/engine-examples/mines/math/mines-4-13.tsv +2 -0
- package/engine-examples/mines/math/mines-4-14.tsv +2 -0
- package/engine-examples/mines/math/mines-4-15.tsv +2 -0
- package/engine-examples/mines/math/mines-4-16.tsv +2 -0
- package/engine-examples/mines/math/mines-4-17.tsv +2 -0
- package/engine-examples/mines/math/mines-4-18.tsv +2 -0
- package/engine-examples/mines/math/mines-4-19.tsv +2 -0
- package/engine-examples/mines/math/mines-4-2.tsv +2 -0
- package/engine-examples/mines/math/mines-4-20.tsv +2 -0
- package/engine-examples/mines/math/mines-4-21.tsv +2 -0
- package/engine-examples/mines/math/mines-4-3.tsv +2 -0
- package/engine-examples/mines/math/mines-4-4.tsv +2 -0
- package/engine-examples/mines/math/mines-4-5.tsv +2 -0
- package/engine-examples/mines/math/mines-4-6.tsv +2 -0
- package/engine-examples/mines/math/mines-4-7.tsv +2 -0
- package/engine-examples/mines/math/mines-4-8.tsv +2 -0
- package/engine-examples/mines/math/mines-4-9.tsv +2 -0
- package/engine-examples/mines/math/mines-5-1.tsv +2 -0
- package/engine-examples/mines/math/mines-5-10.tsv +2 -0
- package/engine-examples/mines/math/mines-5-11.tsv +2 -0
- package/engine-examples/mines/math/mines-5-12.tsv +2 -0
- package/engine-examples/mines/math/mines-5-13.tsv +2 -0
- package/engine-examples/mines/math/mines-5-14.tsv +2 -0
- package/engine-examples/mines/math/mines-5-15.tsv +2 -0
- package/engine-examples/mines/math/mines-5-16.tsv +2 -0
- package/engine-examples/mines/math/mines-5-17.tsv +2 -0
- package/engine-examples/mines/math/mines-5-18.tsv +2 -0
- package/engine-examples/mines/math/mines-5-19.tsv +2 -0
- package/engine-examples/mines/math/mines-5-2.tsv +2 -0
- package/engine-examples/mines/math/mines-5-20.tsv +2 -0
- package/engine-examples/mines/math/mines-5-3.tsv +2 -0
- package/engine-examples/mines/math/mines-5-4.tsv +2 -0
- package/engine-examples/mines/math/mines-5-5.tsv +2 -0
- package/engine-examples/mines/math/mines-5-6.tsv +2 -0
- package/engine-examples/mines/math/mines-5-7.tsv +2 -0
- package/engine-examples/mines/math/mines-5-8.tsv +2 -0
- package/engine-examples/mines/math/mines-5-9.tsv +2 -0
- package/engine-examples/mines/math/mines-6-1.tsv +2 -0
- package/engine-examples/mines/math/mines-6-10.tsv +2 -0
- package/engine-examples/mines/math/mines-6-11.tsv +2 -0
- package/engine-examples/mines/math/mines-6-12.tsv +2 -0
- package/engine-examples/mines/math/mines-6-13.tsv +2 -0
- package/engine-examples/mines/math/mines-6-14.tsv +2 -0
- package/engine-examples/mines/math/mines-6-15.tsv +2 -0
- package/engine-examples/mines/math/mines-6-16.tsv +2 -0
- package/engine-examples/mines/math/mines-6-17.tsv +2 -0
- package/engine-examples/mines/math/mines-6-18.tsv +2 -0
- package/engine-examples/mines/math/mines-6-19.tsv +2 -0
- package/engine-examples/mines/math/mines-6-2.tsv +2 -0
- package/engine-examples/mines/math/mines-6-3.tsv +2 -0
- package/engine-examples/mines/math/mines-6-4.tsv +2 -0
- package/engine-examples/mines/math/mines-6-5.tsv +2 -0
- package/engine-examples/mines/math/mines-6-6.tsv +2 -0
- package/engine-examples/mines/math/mines-6-7.tsv +2 -0
- package/engine-examples/mines/math/mines-6-8.tsv +2 -0
- package/engine-examples/mines/math/mines-6-9.tsv +2 -0
- package/engine-examples/mines/math/mines-7-1.tsv +2 -0
- package/engine-examples/mines/math/mines-7-10.tsv +2 -0
- package/engine-examples/mines/math/mines-7-11.tsv +2 -0
- package/engine-examples/mines/math/mines-7-12.tsv +2 -0
- package/engine-examples/mines/math/mines-7-13.tsv +2 -0
- package/engine-examples/mines/math/mines-7-14.tsv +2 -0
- package/engine-examples/mines/math/mines-7-15.tsv +2 -0
- package/engine-examples/mines/math/mines-7-16.tsv +2 -0
- package/engine-examples/mines/math/mines-7-17.tsv +2 -0
- package/engine-examples/mines/math/mines-7-18.tsv +2 -0
- package/engine-examples/mines/math/mines-7-2.tsv +2 -0
- package/engine-examples/mines/math/mines-7-3.tsv +2 -0
- package/engine-examples/mines/math/mines-7-4.tsv +2 -0
- package/engine-examples/mines/math/mines-7-5.tsv +2 -0
- package/engine-examples/mines/math/mines-7-6.tsv +2 -0
- package/engine-examples/mines/math/mines-7-7.tsv +2 -0
- package/engine-examples/mines/math/mines-7-8.tsv +2 -0
- package/engine-examples/mines/math/mines-7-9.tsv +2 -0
- package/engine-examples/mines/math/mines-8-1.tsv +2 -0
- package/engine-examples/mines/math/mines-8-10.tsv +2 -0
- package/engine-examples/mines/math/mines-8-11.tsv +2 -0
- package/engine-examples/mines/math/mines-8-12.tsv +2 -0
- package/engine-examples/mines/math/mines-8-13.tsv +2 -0
- package/engine-examples/mines/math/mines-8-14.tsv +2 -0
- package/engine-examples/mines/math/mines-8-15.tsv +2 -0
- package/engine-examples/mines/math/mines-8-16.tsv +2 -0
- package/engine-examples/mines/math/mines-8-17.tsv +2 -0
- package/engine-examples/mines/math/mines-8-2.tsv +2 -0
- package/engine-examples/mines/math/mines-8-3.tsv +2 -0
- package/engine-examples/mines/math/mines-8-4.tsv +2 -0
- package/engine-examples/mines/math/mines-8-5.tsv +2 -0
- package/engine-examples/mines/math/mines-8-6.tsv +2 -0
- package/engine-examples/mines/math/mines-8-7.tsv +2 -0
- package/engine-examples/mines/math/mines-8-8.tsv +2 -0
- package/engine-examples/mines/math/mines-8-9.tsv +2 -0
- package/engine-examples/mines/math/mines-9-1.tsv +2 -0
- package/engine-examples/mines/math/mines-9-10.tsv +2 -0
- package/engine-examples/mines/math/mines-9-11.tsv +2 -0
- package/engine-examples/mines/math/mines-9-12.tsv +2 -0
- package/engine-examples/mines/math/mines-9-13.tsv +2 -0
- package/engine-examples/mines/math/mines-9-14.tsv +2 -0
- package/engine-examples/mines/math/mines-9-15.tsv +2 -0
- package/engine-examples/mines/math/mines-9-16.tsv +2 -0
- package/engine-examples/mines/math/mines-9-2.tsv +2 -0
- package/engine-examples/mines/math/mines-9-3.tsv +2 -0
- package/engine-examples/mines/math/mines-9-4.tsv +2 -0
- package/engine-examples/mines/math/mines-9-5.tsv +2 -0
- package/engine-examples/mines/math/mines-9-6.tsv +2 -0
- package/engine-examples/mines/math/mines-9-7.tsv +2 -0
- package/engine-examples/mines/math/mines-9-8.tsv +2 -0
- package/engine-examples/mines/math/mines-9-9.tsv +2 -0
- package/engine-examples/plinko/AGENTS.md +3 -0
- package/engine-examples/plinko/README.md +9 -0
- package/engine-examples/plinko/bundle/assets.json +1 -0
- package/engine-examples/plinko/bundle/game.css +5 -0
- package/engine-examples/plinko/bundle/game.js +92 -0
- package/engine-examples/plinko/bundle/hbtest.json +4 -0
- package/engine-examples/plinko/bundle/index.html +16 -0
- package/engine-examples/plinko/bundle/model.json +1 -0
- package/engine-examples/plinko/math/gen.mjs +40 -0
- package/engine-examples/plinko/math/manifest.json +1 -0
- package/engine-examples/plinko/math/plinko-12-high.tsv +13 -0
- package/engine-examples/plinko/math/plinko-12-low.tsv +13 -0
- package/engine-examples/plinko/math/plinko-12-medium.tsv +13 -0
- package/engine-examples/plinko/math/plinko-16-high.tsv +17 -0
- package/engine-examples/plinko/math/plinko-16-low.tsv +17 -0
- package/engine-examples/plinko/math/plinko-16-medium.tsv +17 -0
- package/engine-examples/plinko/math/plinko-8-high.tsv +9 -0
- package/engine-examples/plinko/math/plinko-8-low.tsv +9 -0
- package/engine-examples/plinko/math/plinko-8-medium.tsv +9 -0
- package/engine-examples/plinko/math-kit/pack.mjs +165 -0
- package/engine-examples/plinko/math-kit/package.json +8 -0
- package/engine-examples/plinko/math-kit/slot.mjs +94 -0
- package/engine-examples/plinko/math-kit/tumble.mjs +94 -0
- package/engine-examples/slot/AGENTS.md +3 -0
- package/engine-examples/slot/README.md +26 -0
- package/engine-examples/slot/bundle/assets.json +1 -0
- package/engine-examples/slot/bundle/game.css +19 -0
- package/engine-examples/slot/bundle/game.js +42 -0
- package/engine-examples/slot/bundle/hbtest.json +4 -0
- package/engine-examples/slot/bundle/index.html +23 -0
- package/engine-examples/slot/math/gen.mjs +79 -0
- package/engine-examples/slot/math/manifest.json +1 -0
- package/engine-examples/slot/math/slot.tsv +110592 -0
- package/engine-examples/tower/AGENTS.md +3 -0
- package/engine-examples/tower/README.md +21 -0
- package/engine-examples/tower/bundle/assets.json +1 -0
- package/engine-examples/tower/bundle/game.css +8 -0
- package/engine-examples/tower/bundle/game.js +37 -0
- package/engine-examples/tower/bundle/hbtest.json +5 -0
- package/engine-examples/tower/bundle/index.html +16 -0
- package/engine-examples/tower/math/floor.tsv +4 -0
- package/engine-examples/tower/math/manifest.json +61 -0
- package/engine-examples/tumble/AGENTS.md +3 -0
- package/engine-examples/tumble/README.md +18 -0
- package/engine-examples/tumble/bundle/assets.json +1 -0
- package/engine-examples/tumble/bundle/game.css +13 -0
- package/engine-examples/tumble/bundle/game.js +119 -0
- package/engine-examples/tumble/bundle/hbtest.json +4 -0
- package/engine-examples/tumble/bundle/i18n.json +122 -0
- package/engine-examples/tumble/bundle/index.html +20 -0
- package/engine-examples/tumble/bundle/model.json +1 -0
- package/engine-examples/tumble/math/gen.mjs +38 -0
- package/engine-examples/tumble/math-kit/pack.mjs +165 -0
- package/engine-examples/tumble/math-kit/package.json +8 -0
- package/engine-examples/tumble/math-kit/slot.mjs +94 -0
- package/engine-examples/tumble/math-kit/tumble.mjs +94 -0
- package/engine-examples/wheel/README.md +14 -0
- package/engine-examples/wheel/bundle/assets.json +1 -0
- package/engine-examples/wheel/bundle/game.css +7 -0
- package/engine-examples/wheel/bundle/game.js +51 -0
- package/engine-examples/wheel/bundle/hbtest.json +4 -0
- package/engine-examples/wheel/bundle/index.html +19 -0
- package/engine-examples/wheel/math/high.tsv +24 -0
- package/engine-examples/wheel/math/low.tsv +24 -0
- package/engine-examples/wheel/math/manifest.json +48 -0
- package/engine-examples/wheel/math/medium.tsv +24 -0
- package/engine-math/pack.mjs +165 -0
- package/engine-math/package.json +1 -0
- package/engine-math/slot.mjs +94 -0
- package/engine-math/tumble.mjs +94 -0
- package/engine-mcp/package.json +1 -0
- package/engine-mcp/server.mjs +151 -0
- package/engine-sdk/GUIDE.md +855 -0
- package/engine-sdk/assets.js +92 -0
- package/engine-sdk/autoplay.js +82 -0
- package/engine-sdk/bet-field.js +391 -0
- package/engine-sdk/kit/fiat-coins.js +51 -0
- package/engine-sdk/kit/numeric-input.js +116 -0
- package/engine-sdk/kit/wallet-format.js +423 -0
- package/engine-sdk/marks.js +22 -0
- package/engine-sdk/messages.js +289 -0
- package/engine-sdk/money.js +89 -0
- package/engine-sdk/package.json +1 -0
- package/engine-sdk/protocol.js +263 -0
- package/engine-sdk/sdk.d.ts +138 -0
- package/engine-sdk/sdk.js +330 -0
- package/engine-sdk/stake.js +68 -0
- package/engine-sdk/ui.d.ts +36 -0
- package/engine-sdk/ui.js +340 -0
- package/engine-shell/core/faults.js +127 -0
- package/engine-shell/core/rgs.js +193 -0
- package/engine-shell/core/router.js +917 -0
- package/engine-shell/host/boot.js +408 -0
- package/engine-shell/host/engine-v1.js +438 -0
- package/engine-shell/host/fair.js +285 -0
- package/engine-shell/host/fonts/SOURCE.json +11 -0
- package/engine-shell/host/fonts/figtree-400.woff2 +0 -0
- package/engine-shell/host/fonts/figtree-500.woff2 +0 -0
- package/engine-shell/host/fonts/figtree-600.woff2 +0 -0
- package/engine-shell/host/fonts/red-hat-display-700.woff2 +0 -0
- package/engine-shell/host/index.html +230 -0
- package/engine-shell/host/page.js +809 -0
- package/engine-shell/host/sandbox-panel.js +681 -0
- package/engine-shell/host/sandbox-scenarios.js +554 -0
- package/engine-shell/host/site-bridge.js +87 -0
- package/engine-shell/host/strings.js +916 -0
- package/engine-shell/package.json +1 -0
- package/engine-shell/verifiers/badcat/coin.tsv +2 -0
- package/engine-shell/verifiers/badcat/dice-2.tsv +11 -0
- package/engine-shell/verifiers/badcat/dice-5.tsv +26 -0
- package/engine-shell/verifiers/badcat/dice-9.tsv +46 -0
- package/engine-shell/verifiers/badcat/manifest.json +3344 -0
- package/engine-shell/verifiers/badcat/rps.tsv +3 -0
- package/engine-shell/verifiers/badcat/triple.tsv +66 -0
- package/engine-shell/verifiers/engine-verify.js +358 -0
- package/engine-shell/verifiers/package.json +5 -0
- package/engine-shell/verifiers/parity/manifest.json +27 -0
- package/engine-shell/verifiers/parity/wide.tsv +5 -0
- package/engine-shell/verifiers/parity-vectors.json +244 -0
- package/engine-shell/verifiers/vectors.json +100 -0
- package/package.json +10 -0
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `engine-v1` in the browser: the pack hash of SPEC.md §1.3 and the derivation
|
|
3
|
+
* of §2, as the Verify tab of the shell's Provably Fair dialog (page.js) runs
|
|
4
|
+
* them for a player. Web Crypto for HMAC-SHA256 and SHA-256, BigInt for the
|
|
5
|
+
* 64-bit draws, nothing imported — the page's policy is `script-src 'self'`
|
|
6
|
+
* and a verifier that pulls code from elsewhere is not one the player can
|
|
7
|
+
* trust.
|
|
8
|
+
*
|
|
9
|
+
* The reference is the backend's CommonJS verifier, kept byte for byte at
|
|
10
|
+
* ../verifiers/engine-verify.js (housegames-backend
|
|
11
|
+
* internal/engine/verifiers/engine-verify.js, whose sha256 engine-v1.test.js
|
|
12
|
+
* pins). Node's `crypto` is synchronous and the browser's is not, and the
|
|
13
|
+
* reference reads files where this reads the public /math document, so this
|
|
14
|
+
* is a port, not a copy: engine-v1.test.js holds it to the reference on both
|
|
15
|
+
* golden packs (BAD CAT, and the parity pack whose rows carry every escape
|
|
16
|
+
* the canonical form has a rule for), on random seeds, nonces, modes and
|
|
17
|
+
* bets, on a byte order mark, on a document that is not UTF-8 and on every
|
|
18
|
+
* pack the reference refuses, so the two cannot drift apart unnoticed.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const subtle = globalThis.crypto.subtle;
|
|
22
|
+
const utf8 = new TextEncoder();
|
|
23
|
+
|
|
24
|
+
// ---- bytes → text, as the Go reference reads them ----------------------------
|
|
25
|
+
// A pack is UTF-8. The /math document's manifest is the studio's submitted
|
|
26
|
+
// bytes as they are — hashed, served and kept on record as they are — so a
|
|
27
|
+
// manifest that is not well-formed UTF-8 is refused at upload
|
|
28
|
+
// (mathpack.Load), by the reference (readManifestText) and by the retention
|
|
29
|
+
// that keeps the document, never admitted as U+FFFD into a document no text
|
|
30
|
+
// column could hold. A table's bytes are canonicalised on the way in (one
|
|
31
|
+
// U+FFFD per byte that is not well-formed, as Go decodes), so the rows the
|
|
32
|
+
// document carries are UTF-8 already. The document is therefore UTF-8 end to
|
|
33
|
+
// end, and one that is not is not the server's: it is refused here as the
|
|
34
|
+
// reference refuses the manifest. `fatal` refuses; `ignoreBOM` keeps a byte
|
|
35
|
+
// order mark as the character it is to Go and to the reference, wherever it
|
|
36
|
+
// stands — a decoder that stripped one at the start would hash a manifest
|
|
37
|
+
// the server never hashed.
|
|
38
|
+
const utf8Decoder = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true });
|
|
39
|
+
|
|
40
|
+
/** The text of `bytes` (Uint8Array or ArrayBuffer); an Error when they are not well-formed UTF-8. */
|
|
41
|
+
export function textFromBytes(bytes) {
|
|
42
|
+
try {
|
|
43
|
+
return utf8Decoder.decode(bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes));
|
|
44
|
+
} catch (e) {
|
|
45
|
+
throw new Error('the document is not UTF-8');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ---- canonical JSON: keys sorted, no whitespace, numbers as written ---------
|
|
50
|
+
// A tiny parser that keeps each number's source text, so `2.50` stays `2.50`
|
|
51
|
+
// exactly as the Go reference does. Strings are escaped the way Go's
|
|
52
|
+
// encoding/json escapes them: `"` `\` and the short escapes \b \f \n \r \t,
|
|
53
|
+
// every other control character as \u00xx, `<` `>` `&` U+2028 U+2029 as
|
|
54
|
+
// \uXXXX, everything else as itself. An escaped surrogate pair is one code
|
|
55
|
+
// point; a lone surrogate is U+FFFD. Keys sort by code point.
|
|
56
|
+
|
|
57
|
+
export function canonicalJSON(text) {
|
|
58
|
+
let i = 0;
|
|
59
|
+
const ws = () => { while (i < text.length && ' \t\r\n'.includes(text[i])) i++; };
|
|
60
|
+
const fail = (m) => { throw new Error(`canonical json: ${m} at ${i}`); };
|
|
61
|
+
function value() {
|
|
62
|
+
ws();
|
|
63
|
+
const c = text[i];
|
|
64
|
+
if (c === '{') return object();
|
|
65
|
+
if (c === '[') return array();
|
|
66
|
+
if (c === '"') return string();
|
|
67
|
+
if (c === 't' && text.startsWith('true', i)) { i += 4; return 'true'; }
|
|
68
|
+
if (c === 'f' && text.startsWith('false', i)) { i += 5; return 'false'; }
|
|
69
|
+
if (c === 'n' && text.startsWith('null', i)) { i += 4; return 'null'; }
|
|
70
|
+
const m = /^-?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?/.exec(text.slice(i));
|
|
71
|
+
if (!m) fail('unexpected token');
|
|
72
|
+
i += m[0].length;
|
|
73
|
+
return m[0];
|
|
74
|
+
}
|
|
75
|
+
function object() {
|
|
76
|
+
i++; ws();
|
|
77
|
+
const entries = [];
|
|
78
|
+
if (text[i] === '}') { i++; return '{}'; }
|
|
79
|
+
for (;;) {
|
|
80
|
+
ws();
|
|
81
|
+
if (text[i] !== '"') fail('expected key');
|
|
82
|
+
const k = rawString();
|
|
83
|
+
ws();
|
|
84
|
+
if (text[i] !== ':') fail('expected :');
|
|
85
|
+
i++;
|
|
86
|
+
entries.push([k, value()]);
|
|
87
|
+
ws();
|
|
88
|
+
if (text[i] === ',') { i++; continue; }
|
|
89
|
+
if (text[i] === '}') { i++; break; }
|
|
90
|
+
fail('expected , or }');
|
|
91
|
+
}
|
|
92
|
+
if (new Set(entries.map(([k]) => k)).size !== entries.length) fail('duplicate key');
|
|
93
|
+
entries.sort((a, b) => compareCodePoints(a[0], b[0]));
|
|
94
|
+
return '{' + entries.map(([k, v]) => escapeString(k) + ':' + v).join(',') + '}';
|
|
95
|
+
}
|
|
96
|
+
function array() {
|
|
97
|
+
i++; ws();
|
|
98
|
+
const items = [];
|
|
99
|
+
if (text[i] === ']') { i++; return '[]'; }
|
|
100
|
+
for (;;) {
|
|
101
|
+
items.push(value());
|
|
102
|
+
ws();
|
|
103
|
+
if (text[i] === ',') { i++; continue; }
|
|
104
|
+
if (text[i] === ']') { i++; break; }
|
|
105
|
+
fail('expected , or ]');
|
|
106
|
+
}
|
|
107
|
+
return '[' + items.join(',') + ']';
|
|
108
|
+
}
|
|
109
|
+
function rawString() {
|
|
110
|
+
// returns the decoded string
|
|
111
|
+
i++;
|
|
112
|
+
let out = '';
|
|
113
|
+
for (;;) {
|
|
114
|
+
if (i >= text.length) fail('unterminated string');
|
|
115
|
+
const c = text[i++];
|
|
116
|
+
if (c === '"') return out;
|
|
117
|
+
// A raw control character inside a string is not JSON; Go's scanner
|
|
118
|
+
// refuses it, so a verifier that escaped it would hash a pack the
|
|
119
|
+
// gate never takes.
|
|
120
|
+
if (c.charCodeAt(0) < 0x20) fail('control character in string');
|
|
121
|
+
if (c !== '\\') { out += c; continue; }
|
|
122
|
+
const e = text[i++];
|
|
123
|
+
if (e === 'u') { out += String.fromCodePoint(unicodeEscape()); continue; }
|
|
124
|
+
const short = { '"': '"', '\\': '\\', '/': '/', b: '\b', f: '\f', n: '\n', r: '\r', t: '\t' }[e];
|
|
125
|
+
if (short === undefined) fail('bad escape');
|
|
126
|
+
out += short;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// unicodeEscape reads the XXXX of a \uXXXX (i is just past the u). A high
|
|
130
|
+
// surrogate followed by an escaped low surrogate is one code point. A
|
|
131
|
+
// surrogate on its own is U+FFFD, as Go's decoder has it, and whatever
|
|
132
|
+
// escape follows a lone high surrogate is read on its own.
|
|
133
|
+
function unicodeEscape() {
|
|
134
|
+
const cp = hex4(i);
|
|
135
|
+
i += 4;
|
|
136
|
+
if (cp < 0xd800 || cp > 0xdfff) return cp;
|
|
137
|
+
if (cp <= 0xdbff && text[i] === '\\' && text[i + 1] === 'u') {
|
|
138
|
+
const low = hex4(i + 2);
|
|
139
|
+
if (low >= 0xdc00 && low <= 0xdfff) {
|
|
140
|
+
i += 6;
|
|
141
|
+
return 0x10000 + ((cp - 0xd800) << 10) + (low - 0xdc00);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return 0xfffd;
|
|
145
|
+
}
|
|
146
|
+
function hex4(at) {
|
|
147
|
+
const h = text.slice(at, at + 4);
|
|
148
|
+
if (!/^[0-9a-fA-F]{4}$/.test(h)) fail('bad \\u escape');
|
|
149
|
+
return parseInt(h, 16);
|
|
150
|
+
}
|
|
151
|
+
function string() { return escapeString(rawString()); }
|
|
152
|
+
const out = value();
|
|
153
|
+
ws();
|
|
154
|
+
if (i !== text.length) fail('trailing data');
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// compareCodePoints orders keys as the Go reference does: by code point,
|
|
159
|
+
// which is UTF-8 byte order. JavaScript's own `<` compares UTF-16 code
|
|
160
|
+
// units, which would put an emoji before U+E000..U+FFFF.
|
|
161
|
+
export function compareCodePoints(a, b) {
|
|
162
|
+
let i = 0, j = 0;
|
|
163
|
+
while (i < a.length && j < b.length) {
|
|
164
|
+
const x = a.codePointAt(i), y = b.codePointAt(j);
|
|
165
|
+
if (x !== y) return x - y;
|
|
166
|
+
i += x > 0xffff ? 2 : 1;
|
|
167
|
+
j += y > 0xffff ? 2 : 1;
|
|
168
|
+
}
|
|
169
|
+
return (a.length - i) - (b.length - j);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function escapeString(s) {
|
|
173
|
+
let out = '"';
|
|
174
|
+
for (const ch of s) {
|
|
175
|
+
const cp = ch.codePointAt(0);
|
|
176
|
+
if (ch === '"') out += '\\"';
|
|
177
|
+
else if (ch === '\\') out += '\\\\';
|
|
178
|
+
else if (ch === '\b') out += '\\b';
|
|
179
|
+
else if (ch === '\f') out += '\\f';
|
|
180
|
+
else if (ch === '\n') out += '\\n';
|
|
181
|
+
else if (ch === '\r') out += '\\r';
|
|
182
|
+
else if (ch === '\t') out += '\\t';
|
|
183
|
+
else if (cp < 0x20 || ch === '<' || ch === '>' || ch === '&' || cp === 0x2028 || cp === 0x2029) {
|
|
184
|
+
out += '\\u' + cp.toString(16).padStart(4, '0');
|
|
185
|
+
} else out += ch;
|
|
186
|
+
}
|
|
187
|
+
return out + '"';
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ---- pack ------------------------------------------------------------------------
|
|
191
|
+
|
|
192
|
+
// A weight is what Go's strconv.ParseUint(s, 10, 64) takes: ASCII digits and
|
|
193
|
+
// nothing else. BigInt() alone would also take `0x10`, `+5` and ` 5`, and a
|
|
194
|
+
// verifier must not hash a table the gate refuses.
|
|
195
|
+
const WEIGHT = /^[0-9]+$/;
|
|
196
|
+
|
|
197
|
+
export function parseTable(name, text) {
|
|
198
|
+
const rows = [];
|
|
199
|
+
const lines = text.split('\n');
|
|
200
|
+
if (lines[lines.length - 1] === '') lines.pop();
|
|
201
|
+
for (const line of lines) {
|
|
202
|
+
if (line.startsWith('#') && rows.length === 0) continue;
|
|
203
|
+
const tab = line.indexOf('\t');
|
|
204
|
+
const field = tab < 0 ? line : line.slice(0, tab);
|
|
205
|
+
if (!WEIGHT.test(field)) throw new Error(`${name}: weight ${JSON.stringify(field)} is not a decimal integer`);
|
|
206
|
+
const w = BigInt(field);
|
|
207
|
+
if (w <= 0n) throw new Error(`${name}: weight must be > 0`);
|
|
208
|
+
let data = null;
|
|
209
|
+
if (tab >= 0) {
|
|
210
|
+
// The data column is one JSON value: never empty, never holding a
|
|
211
|
+
// second tab or a carriage return (a CRLF file is refused, as in Go).
|
|
212
|
+
const rest = line.slice(tab + 1);
|
|
213
|
+
if (rest === '' || /[\t\r]/.test(rest)) throw new Error(`${name}: malformed data column`);
|
|
214
|
+
data = canonicalJSON(rest);
|
|
215
|
+
}
|
|
216
|
+
rows.push({ weight: w, data });
|
|
217
|
+
}
|
|
218
|
+
if (rows.length < 2) throw new Error(`${name}: needs at least 2 rows`);
|
|
219
|
+
const cum = [];
|
|
220
|
+
let sum = 0n;
|
|
221
|
+
for (const r of rows) { sum += r.weight; cum.push(sum); }
|
|
222
|
+
if (sum > (1n << 63n)) throw new Error(`${name}: weights sum past 2^63`);
|
|
223
|
+
return { name, rows, cum, sum };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function tableBytes(t) {
|
|
227
|
+
return t.rows.map((r) => r.weight.toString() + (r.data === null ? '' : '\t' + r.data) + '\n').join('');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* The raw text of one member of the JSON object `text`, exactly as written:
|
|
232
|
+
* what the hash commits to is the manifest's own bytes, canonicalised, and a
|
|
233
|
+
* JSON.parse → JSON.stringify round trip would rewrite them.
|
|
234
|
+
*/
|
|
235
|
+
export function rawMember(text, key) {
|
|
236
|
+
let i = 0;
|
|
237
|
+
const ws = () => { while (i < text.length && ' \t\r\n'.includes(text[i])) i++; };
|
|
238
|
+
const fail = (m) => { throw new Error(`json: ${m} at ${i}`); };
|
|
239
|
+
function skipString() {
|
|
240
|
+
i++;
|
|
241
|
+
for (;;) {
|
|
242
|
+
if (i >= text.length) fail('unterminated string');
|
|
243
|
+
const c = text[i++];
|
|
244
|
+
if (c === '"') return;
|
|
245
|
+
if (c === '\\') i++;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function skipValue() {
|
|
249
|
+
ws();
|
|
250
|
+
const c = text[i];
|
|
251
|
+
if (c === '"') return skipString();
|
|
252
|
+
if (c === '{' || c === '[') {
|
|
253
|
+
const close = c === '{' ? '}' : ']';
|
|
254
|
+
i++; ws();
|
|
255
|
+
if (text[i] === close) { i++; return; }
|
|
256
|
+
for (;;) {
|
|
257
|
+
if (c === '{') { ws(); if (text[i] !== '"') fail('expected key'); skipString(); ws(); if (text[i] !== ':') fail('expected :'); i++; }
|
|
258
|
+
skipValue();
|
|
259
|
+
ws();
|
|
260
|
+
if (text[i] === ',') { i++; continue; }
|
|
261
|
+
if (text[i] === close) { i++; return; }
|
|
262
|
+
fail('expected , or ' + close);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const m = /^(true|false|null|-?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?)/.exec(text.slice(i));
|
|
266
|
+
if (!m) fail('unexpected token');
|
|
267
|
+
i += m[0].length;
|
|
268
|
+
}
|
|
269
|
+
ws();
|
|
270
|
+
if (text[i] !== '{') fail('expected an object');
|
|
271
|
+
i++; ws();
|
|
272
|
+
if (text[i] === '}') return null;
|
|
273
|
+
for (;;) {
|
|
274
|
+
ws();
|
|
275
|
+
if (text[i] !== '"') fail('expected key');
|
|
276
|
+
const start = i;
|
|
277
|
+
skipString();
|
|
278
|
+
const k = JSON.parse(text.slice(start, i));
|
|
279
|
+
ws();
|
|
280
|
+
if (text[i] !== ':') fail('expected :');
|
|
281
|
+
i++; ws();
|
|
282
|
+
const from = i;
|
|
283
|
+
skipValue();
|
|
284
|
+
if (k === key) return text.slice(from, i);
|
|
285
|
+
ws();
|
|
286
|
+
if (text[i] === ',') { i++; continue; }
|
|
287
|
+
if (text[i] === '}') return null;
|
|
288
|
+
fail('expected , or }');
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export const FORMAT = 'house-engine-math/1';
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* A pack from the public document GET /engine/v1/math/<slug>?version=<hash>
|
|
296
|
+
* answers: `{format, hash, manifest, tables{name: tsv}, report}`. Takes the
|
|
297
|
+
* response bytes (or its text), never the parsed object, so the manifest's
|
|
298
|
+
* bytes are the server's own; the manifest is canonicalised here, so a
|
|
299
|
+
* document the reference refuses (a repeated key, a control character, bytes
|
|
300
|
+
* that are not UTF-8) is refused before anything is drawn from it.
|
|
301
|
+
*/
|
|
302
|
+
export function packFromDocument(source) {
|
|
303
|
+
const text = typeof source === 'string' ? source : textFromBytes(source);
|
|
304
|
+
const doc = JSON.parse(text);
|
|
305
|
+
if (!doc || typeof doc !== 'object' || doc.format !== FORMAT) throw new Error(`not a ${FORMAT} document`);
|
|
306
|
+
const manifestText = rawMember(text, 'manifest');
|
|
307
|
+
if (!manifestText || !doc.manifest || !Array.isArray(doc.manifest.modes)) throw new Error('the document has no manifest');
|
|
308
|
+
if (!doc.tables || typeof doc.tables !== 'object') throw new Error('the document has no tables');
|
|
309
|
+
const manifestCanonical = canonicalJSON(manifestText);
|
|
310
|
+
const tables = {};
|
|
311
|
+
for (const m of doc.manifest.modes) {
|
|
312
|
+
const names = m.kind === 'table' ? [m.table] : (m.steps || []).map((s) => s.table);
|
|
313
|
+
for (const n of names) {
|
|
314
|
+
if (tables[n]) continue;
|
|
315
|
+
if (typeof doc.tables[n] !== 'string') throw new Error(`the document lacks table ${n}`);
|
|
316
|
+
tables[n] = parseTable(n, doc.tables[n]);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return { manifest: doc.manifest, manifestText, manifestCanonical, tables, hash: typeof doc.hash === 'string' ? doc.hash : '', report: doc.report };
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Loads and verifies one public maths document for the shell. The Provably Fair
|
|
324
|
+
* dialog and the sandbox test panel share this owner so they ask for the same
|
|
325
|
+
* version and hash the exact bytes the round server served.
|
|
326
|
+
*/
|
|
327
|
+
export async function loadPack(api, game, version, fetcher = globalThis.fetch) {
|
|
328
|
+
if (!/^[0-9a-f]{64}$/.test(version)) throw new Error('version');
|
|
329
|
+
const url = `${api}/engine/v1/math/${encodeURIComponent(game)}?version=${version}`;
|
|
330
|
+
const res = await fetcher(url, { credentials: 'omit' });
|
|
331
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
332
|
+
const pack = packFromDocument(new Uint8Array(await res.arrayBuffer()));
|
|
333
|
+
pack.recomputed = await mathHash(pack);
|
|
334
|
+
return pack;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export const hex = (bytes) => Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
|
|
338
|
+
|
|
339
|
+
export async function sha256Hex(text) {
|
|
340
|
+
return hex(new Uint8Array(await subtle.digest('SHA-256', utf8.encode(text))));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export async function mathHash(pack) {
|
|
344
|
+
let s = 'house-engine-math/1\n';
|
|
345
|
+
s += 'manifest\n' + (pack.manifestCanonical || canonicalJSON(pack.manifestText)) + '\n';
|
|
346
|
+
for (const name of Object.keys(pack.tables).sort(compareCodePoints)) s += 'table ' + name + '\n' + tableBytes(pack.tables[name]);
|
|
347
|
+
return sha256Hex(s);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// ---- engine-v1 stream and draw ----------------------------------------------------
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* The stream of §2: block_i = HMAC_SHA256(key = serverSeed, msg =
|
|
354
|
+
* "clientSeed:nonce:i"), consumed 8 bytes at a time, big-endian. `blocks`
|
|
355
|
+
* keeps every block computed (message and digest) for anyone walking a draw
|
|
356
|
+
* by hand; the draw itself reads nothing from it.
|
|
357
|
+
*/
|
|
358
|
+
export function stream(serverSeed, clientSeed, nonce) {
|
|
359
|
+
let key = null, block = 0, buf = new Uint8Array(0), off = 0;
|
|
360
|
+
const blocks = [];
|
|
361
|
+
const byte = async () => {
|
|
362
|
+
if (off >= buf.length) {
|
|
363
|
+
if (!key) key = await subtle.importKey('raw', utf8.encode(serverSeed), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
364
|
+
const message = `${clientSeed}:${nonce}:${block}`;
|
|
365
|
+
buf = new Uint8Array(await subtle.sign('HMAC', key, utf8.encode(message)));
|
|
366
|
+
blocks.push({ block, message, digest: hex(buf) });
|
|
367
|
+
block++; off = 0;
|
|
368
|
+
}
|
|
369
|
+
return buf[off++];
|
|
370
|
+
};
|
|
371
|
+
return {
|
|
372
|
+
async u64() { let v = 0n; for (let k = 0; k < 8; k++) v = (v << 8n) | BigInt(await byte()); return v; },
|
|
373
|
+
blocks,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
export const TWO64 = 1n << 64n;
|
|
378
|
+
|
|
379
|
+
/** One row of `table` from the stream `s`, with the rejected count and, for the page, the arithmetic. */
|
|
380
|
+
export async function pick(table, s) {
|
|
381
|
+
const rem = TWO64 % table.sum;
|
|
382
|
+
const limit = TWO64 - rem;
|
|
383
|
+
let rejected = 0;
|
|
384
|
+
for (;;) {
|
|
385
|
+
const v = await s.u64();
|
|
386
|
+
if (v < limit) {
|
|
387
|
+
const r = v % table.sum;
|
|
388
|
+
let lo = 0, hi = table.cum.length - 1;
|
|
389
|
+
while (lo < hi) { const mid = (lo + hi) >> 1; if (table.cum[mid] > r) hi = mid; else lo = mid + 1; }
|
|
390
|
+
return { raw: v, rejected, index: lo, table: table.name, sum: table.sum, limit, r };
|
|
391
|
+
}
|
|
392
|
+
rejected++;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const inRanges = (rs, i) => rs.some(([lo, hi]) => i >= lo && i <= hi);
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* A round re-derived: the same draws, multiplier and ladder outcome the server
|
|
400
|
+
* computed for (serverSeed, clientSeed, nonce) under `pack`, SPEC §2. A ladder
|
|
401
|
+
* is drawn every step at the bet; a cash-out after k steps pays ladderBp[k-1],
|
|
402
|
+
* which the receipt's `multBp` names.
|
|
403
|
+
*/
|
|
404
|
+
export async function play(pack, modeId, betId, serverSeed, clientSeed, nonce) {
|
|
405
|
+
const mode = pack.manifest.modes.find((m) => m.id === modeId);
|
|
406
|
+
if (!mode) throw new Error(`no mode ${modeId}`);
|
|
407
|
+
const bet = mode.bets.find((b) => b.id === betId);
|
|
408
|
+
if (!bet) throw new Error(`no bet ${betId}`);
|
|
409
|
+
const s = stream(serverSeed, clientSeed, nonce);
|
|
410
|
+
if (mode.kind === 'table') {
|
|
411
|
+
const d = await pick(pack.tables[mode.table], s);
|
|
412
|
+
const tier = bet.tiers.find((t) => inRanges(t.outcomes, d.index));
|
|
413
|
+
return { mode: modeId, bet: betId, kind: 'table', draws: [d], multBp: tier ? tier.multBp : 0, blocks: s.blocks };
|
|
414
|
+
}
|
|
415
|
+
const draws = []; let survived = 0, busted = false;
|
|
416
|
+
for (const step of mode.steps) {
|
|
417
|
+
const d = await pick(pack.tables[step.table], s);
|
|
418
|
+
draws.push(d);
|
|
419
|
+
if (!busted && inRanges(bet.survive, d.index)) survived++; else busted = true;
|
|
420
|
+
}
|
|
421
|
+
busted = survived < mode.steps.length;
|
|
422
|
+
const multBp = mode.autoAdvance && !busted ? mode.ladderBp[mode.steps.length - 1] : 0;
|
|
423
|
+
return { mode: modeId, bet: betId, kind: 'ladder', draws, multBp, survived, busted, ladderBp: mode.ladderBp, blocks: s.blocks };
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* What a stake pays at a multiplier, as the server computes it (housegames-backend
|
|
428
|
+
* internal/services/engine.go enginePayout → moneymath.TruncateProduct): both
|
|
429
|
+
* sides in whole 1e-8 units, the product truncated to a unit, never rounded up.
|
|
430
|
+
*/
|
|
431
|
+
export function payoutOf(amount, multBp) {
|
|
432
|
+
const a = Number(amount), m = Number(multBp);
|
|
433
|
+
if (!(a > 0) || !(m > 0) || !Number.isFinite(a) || !Number.isSafeInteger(m)) return 0;
|
|
434
|
+
const units = BigInt(Math.round(a * 1e8));
|
|
435
|
+
const mult = BigInt(Math.round((m / 10000) * 1e8));
|
|
436
|
+
if (units >= (1n << 62n) || mult >= (1n << 62n)) return 0;
|
|
437
|
+
return Number((units * mult) / 100000000n) / 1e8;
|
|
438
|
+
}
|