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,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the shell's Provably Fair dialog (page.js) decides without a browser:
|
|
3
|
+
* the receipt as the verdict reads it, the verdict of a recomputation against
|
|
4
|
+
* the receipt (a cashed-out ladder included), the commitment a revealed seed
|
|
5
|
+
* and a rotation are held to, the words of the result line (in the player's
|
|
6
|
+
* language, strings.js) and the revealed pairs kept across rotations. Pure,
|
|
7
|
+
* so every rule is tested here; the dialog only puts the words on screen.
|
|
8
|
+
*/
|
|
9
|
+
import { fmtFixed, fmtView, viewUnit } from '../../engine-sdk/kit/wallet-format.js';
|
|
10
|
+
import { STRINGS, counted, fill } from './strings.js';
|
|
11
|
+
|
|
12
|
+
// The states a closed round has (housegames-backend internal/models/engine.go):
|
|
13
|
+
// a table round and an auto-advance ladder settle at the bet; a cash-out
|
|
14
|
+
// ladder is cashed out at a step, busted past one, or settled at the top.
|
|
15
|
+
const STATE = /^(settled|cashed_out|busted)$/;
|
|
16
|
+
const RAW = /^(0|[1-9]\d{0,19})$/;
|
|
17
|
+
const HEX64 = /^[0-9a-f]{64}$/;
|
|
18
|
+
export const CLIENT_SEED_MAX = 64;
|
|
19
|
+
// The Seeds tab lists this many revealed pairs, newest first (the Dice modal's count).
|
|
20
|
+
export const REVEALED_PAIRS_MAX = 5;
|
|
21
|
+
|
|
22
|
+
// Characters, as the dialog and the server count a client seed (1–64), not
|
|
23
|
+
// UTF-16 units: a seed of emoji is as long as it looks.
|
|
24
|
+
export const chars = (s) => [...s].length;
|
|
25
|
+
|
|
26
|
+
/** A multiplier in basis points as the game shows it: 1.97×, 12.50×, 1.2345×. */
|
|
27
|
+
export function times(multBp) {
|
|
28
|
+
const n = Number(multBp) / 10000;
|
|
29
|
+
let s = (Number.isFinite(n) ? n : 0).toFixed(4).replace(/0+$/, '');
|
|
30
|
+
const dot = s.indexOf('.');
|
|
31
|
+
if (dot < 0) s += '.00';
|
|
32
|
+
else if (s.length - dot - 1 < 2) s = s.padEnd(dot + 3, '0');
|
|
33
|
+
return s + '×';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The receipt (GET /engine/v1/fairness/:roundId) as compare() reads it. A
|
|
38
|
+
* field of the wrong shape is dropped, never compared; one bad draw drops
|
|
39
|
+
* them all, since a half comparison would mislead — and says so
|
|
40
|
+
* (`unreadable`), since a comparison of nothing is not a verdict. The
|
|
41
|
+
* server seed hash is kept when it is one, to hold the revealed seed to.
|
|
42
|
+
*/
|
|
43
|
+
export function expectation(r) {
|
|
44
|
+
const draws = [];
|
|
45
|
+
let unreadable = !Array.isArray(r.draws);
|
|
46
|
+
for (const d of Array.isArray(r.draws) ? r.draws : []) {
|
|
47
|
+
const raw = typeof d.raw === 'string' ? d.raw : typeof d.raw === 'number' && Number.isSafeInteger(d.raw) ? String(d.raw) : null;
|
|
48
|
+
if (raw === null || !RAW.test(raw) || !Number.isInteger(d.index) || d.index < 0) {
|
|
49
|
+
draws.length = 0;
|
|
50
|
+
unreadable = true;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
draws.push({ raw, index: d.index, rejected: Number.isInteger(d.rejected) && d.rejected > 0 ? d.rejected : 0 });
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
expect: draws,
|
|
57
|
+
unreadable,
|
|
58
|
+
multBp: Number.isInteger(r.multBp) && r.multBp >= 0 ? r.multBp : null,
|
|
59
|
+
kind: r.kind === 'ladder' || r.kind === 'table' ? r.kind : null,
|
|
60
|
+
state: typeof r.state === 'string' && STATE.test(r.state) ? r.state : null,
|
|
61
|
+
step: Number.isInteger(r.step) && r.step >= 0 ? r.step : null,
|
|
62
|
+
serverSeedHash: typeof r.serverSeedHash === 'string' && HEX64.test(r.serverSeedHash) ? r.serverSeedHash : null,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The bet a recomputation pays by: the receipt's when the receipt is this
|
|
68
|
+
* mode's, the mode's only bet, else its first — and then the multiplier is
|
|
69
|
+
* not the player's and is not shown (`known` false).
|
|
70
|
+
*/
|
|
71
|
+
export function betFor(pack, modeId, receipt) {
|
|
72
|
+
const mode = pack.manifest.modes.find((m) => m.id === modeId);
|
|
73
|
+
const bets = mode && Array.isArray(mode.bets) ? mode.bets : [];
|
|
74
|
+
if (receipt && receipt.mode === modeId && bets.some((b) => b.id === receipt.bet)) return { id: receipt.bet, known: true };
|
|
75
|
+
if (bets.length === 1) return { id: bets[0].id, known: true };
|
|
76
|
+
return { id: bets.length ? bets[0].id : null, known: false };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const plain = (v) => typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean';
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* What a drawn row says, as the pack names it: the row's JSON when it is a
|
|
83
|
+
* string, a number or a boolean; an object of plain values as its one value
|
|
84
|
+
* (`{"hand":"paper"}` is `paper`) or as `key: value` pairs; the JSON text
|
|
85
|
+
* itself otherwise; and `row N` when the row carries no data.
|
|
86
|
+
*/
|
|
87
|
+
export function rowLabel(pack, draw, w = STRINGS.en) {
|
|
88
|
+
const table = pack.tables[draw.table];
|
|
89
|
+
const row = table && table.rows[draw.index];
|
|
90
|
+
const fallback = fill(w.row, { n: draw.index });
|
|
91
|
+
if (!row || row.data === null || row.data === undefined) return fallback;
|
|
92
|
+
let v;
|
|
93
|
+
try {
|
|
94
|
+
v = JSON.parse(row.data);
|
|
95
|
+
} catch {
|
|
96
|
+
return row.data;
|
|
97
|
+
}
|
|
98
|
+
if (typeof v === 'string') return v.trim() === '' ? fallback : v;
|
|
99
|
+
if (plain(v)) return String(v);
|
|
100
|
+
// A list of plain values (a slot's reel stops) reads as the values, spaced.
|
|
101
|
+
const listed = (x) => (Array.isArray(x) && x.length && x.every(plain) ? x.join(' ') : null);
|
|
102
|
+
if (listed(v) !== null) return listed(v);
|
|
103
|
+
if (v && typeof v === 'object' && !Array.isArray(v)) {
|
|
104
|
+
const entries = Object.entries(v);
|
|
105
|
+
const named = (x) => (plain(x) ? String(x) : listed(x));
|
|
106
|
+
if (entries.length && entries.every(([, x]) => named(x) !== null)) {
|
|
107
|
+
return entries.length === 1 ? named(entries[0][1]) : entries.map(([k, x]) => `${k}: ${named(x)}`).join(', ');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return row.data;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const steps = (n, w) => counted(w, n, 'step');
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The computed result, as the result line names it. A table round: its row
|
|
117
|
+
* as the pack names it, with the multiplier when the bet is known. A ladder:
|
|
118
|
+
* the rung of the step the player cashed out at (the receipt's `cashed_out`
|
|
119
|
+
* step, when the drawn path survives that far), else where the path busts,
|
|
120
|
+
* else the top rung; when the bet is not known the survival rule is not the
|
|
121
|
+
* player's, so only the number of draws is named (the draws are listed).
|
|
122
|
+
*/
|
|
123
|
+
export function outcome(pack, got, receipt, betKnown, w = STRINGS.en) {
|
|
124
|
+
if (got.kind !== 'ladder') {
|
|
125
|
+
const label = rowLabel(pack, got.draws[0], w);
|
|
126
|
+
return betKnown ? `${label} · ${times(got.multBp)}` : label;
|
|
127
|
+
}
|
|
128
|
+
const n = got.draws.length;
|
|
129
|
+
if (!betKnown) return counted(w, n, 'draw');
|
|
130
|
+
const rungs = Array.isArray(got.ladderBp) ? got.ladderBp : [];
|
|
131
|
+
const step = receipt && receipt.state === 'cashed_out' && Number.isInteger(receipt.step) ? receipt.step : null;
|
|
132
|
+
if (step !== null && step >= 1 && step <= got.survived && step <= rungs.length) return `${times(rungs[step - 1])} · ${fill(w.cashedOutAfter, { n: step, steps: steps(step, w) })}`;
|
|
133
|
+
if (got.busted) return `${times(0)} · ${fill(w.bustsAtStep, { n: got.survived + 1 })}`;
|
|
134
|
+
return `${times(rungs[n - 1] ?? 0)} · ${fill(w.survivesAll, { n, steps: steps(n, w) })}`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** The draws of a round in order, one line each, as the result lists them under the result line. */
|
|
138
|
+
export function drawLines(pack, got, w = STRINGS.en) {
|
|
139
|
+
return got.draws.map((d, k) => `#${k + 1} → ${rowLabel(pack, d, w)}`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The line above a round's result: its nonce, and what went in and came out,
|
|
144
|
+
* as money is shown everywhere else on the page (the Originals' wallet rule,
|
|
145
|
+
* kit/wallet-format.js): the coin at the wallet's precision with its code,
|
|
146
|
+
* or the fiat amount at the fiat's precision in a fiat display; never a bare
|
|
147
|
+
* number, never exponent notation. Without a wallet, eight decimals.
|
|
148
|
+
*/
|
|
149
|
+
export function roundLine(r, wallet, w = STRINGS.en) {
|
|
150
|
+
const money = (n) => (wallet ? fmtView(n, wallet) + ' ' + viewUnit(wallet) : fmtFixed(n, 8));
|
|
151
|
+
return `${fill(w.roundNonce, { n: r.nonce })} · ${money(r.amount)} → ${money(r.payout)}`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* The revealed pairs with one more: the server's `previous` on first load,
|
|
156
|
+
* then each rotation's. Newest first, never the same seed twice, at most
|
|
157
|
+
* REVEALED_PAIRS_MAX. A pair without a seed in the clear is not revealed. The
|
|
158
|
+
* maths versions its rounds were drawn on come along when the server names
|
|
159
|
+
* them (`mathHashes`, newest first), so a round of the pair verified by hand
|
|
160
|
+
* is computed against the tables it was played on.
|
|
161
|
+
*/
|
|
162
|
+
export function withRevealed(list, pair) {
|
|
163
|
+
if (!pair || typeof pair.serverSeed !== 'string' || !pair.serverSeed) return list;
|
|
164
|
+
if (list.some((p) => p.serverSeed === pair.serverSeed)) return list;
|
|
165
|
+
return [{
|
|
166
|
+
serverSeed: pair.serverSeed,
|
|
167
|
+
serverSeedHash: typeof pair.serverSeedHash === 'string' ? pair.serverSeedHash : '',
|
|
168
|
+
clientSeed: typeof pair.clientSeed === 'string' ? pair.clientSeed : '',
|
|
169
|
+
nonce: Number.isInteger(pair.nonce) && pair.nonce >= 0 ? pair.nonce : 0,
|
|
170
|
+
mathHashes: Array.isArray(pair.mathHashes) ? pair.mathHashes.filter((h) => typeof h === 'string' && HEX64.test(h)) : [],
|
|
171
|
+
}, ...list].slice(0, REVEALED_PAIRS_MAX);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* A rotation held to the commitment shown before it: `before` the pair as
|
|
176
|
+
* the dialog showed it, `after` the server's answer, `previousSeedHash` the
|
|
177
|
+
* sha256 of the seed the answer reveals. The retired pair must be the one
|
|
178
|
+
* shown (else nothing else can be held: another window rotated first, or
|
|
179
|
+
* the server retired a pair the player never saw); then the new hash must
|
|
180
|
+
* be the next hash promised, and the revealed seed must hash to the hash it
|
|
181
|
+
* was played under. The differences are word keys (strings.js); none means
|
|
182
|
+
* the house honoured what it showed. Nothing shown before: nothing to hold.
|
|
183
|
+
*/
|
|
184
|
+
export function rotationDifferences(before, after, previousSeedHash) {
|
|
185
|
+
const d = [];
|
|
186
|
+
if (!before || typeof before.serverSeedHash !== 'string' || !HEX64.test(before.serverSeedHash)) return d;
|
|
187
|
+
const prev = after && after.previous;
|
|
188
|
+
if (!prev || prev.serverSeedHash !== before.serverSeedHash) {
|
|
189
|
+
d.push('pairNotShown');
|
|
190
|
+
return d;
|
|
191
|
+
}
|
|
192
|
+
if (typeof before.nextServerSeedHash === 'string' && HEX64.test(before.nextServerSeedHash) && after.serverSeedHash !== before.nextServerSeedHash) d.push('nextHashBroken');
|
|
193
|
+
if (typeof prev.serverSeed === 'string' && previousSeedHash && previousSeedHash !== before.serverSeedHash) d.push('previousHashBroken');
|
|
194
|
+
return d;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// A ladder's multiplier against the receipt's state and step. The draws fix
|
|
198
|
+
// the hidden path (how many steps survive) but not where the player stopped:
|
|
199
|
+
// `cashed_out` pays the rung of the step cashed out at, which must be one
|
|
200
|
+
// the path survives; `busted` pays nothing and happens at the first step
|
|
201
|
+
// past the survived ones; `settled` is the top rung (or nothing, for an
|
|
202
|
+
// auto-advance ladder that busted). A receipt without a state (handed over
|
|
203
|
+
// by an older server) is held to the weaker rule: what the draws give, or
|
|
204
|
+
// any rung within the survived steps.
|
|
205
|
+
function ladderDifferences(expect, got, differences) {
|
|
206
|
+
const rungs = Array.isArray(got.ladderBp) ? got.ladderBp : [];
|
|
207
|
+
const top = got.draws.length;
|
|
208
|
+
const want = expect.multBp;
|
|
209
|
+
const step = typeof expect.step === 'number' ? expect.step : null;
|
|
210
|
+
const en = STRINGS.en;
|
|
211
|
+
const rungWithin = (bp) => bp !== 0 && rungs.indexOf(bp) >= 0 && rungs.indexOf(bp) + 1 <= got.survived;
|
|
212
|
+
if (expect.state === 'cashed_out') {
|
|
213
|
+
if (step === null) {
|
|
214
|
+
if (!rungWithin(want)) differences.push(`multiplier ${times(want)} on the receipt is not a rung within the ${steps(got.survived, en)} survived`);
|
|
215
|
+
} else if (step < 1 || step > got.survived) {
|
|
216
|
+
differences.push(`cashed out at step ${step} on the receipt, but these draws survive ${steps(got.survived, en)}`);
|
|
217
|
+
} else if (rungs[step - 1] !== want) {
|
|
218
|
+
differences.push(`rung ${step} of the ladder pays ${times(rungs[step - 1])}, the receipt says ${times(want)}`);
|
|
219
|
+
}
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (expect.state === 'busted') {
|
|
223
|
+
if (want !== 0) differences.push(`a busted ladder pays ${times(0)}, the receipt says ${times(want)}`);
|
|
224
|
+
if (!got.busted) differences.push(`these draws survive every step; the receipt says the ladder busted`);
|
|
225
|
+
else if (step !== null && step !== got.survived + 1) differences.push(`busted at step ${step} on the receipt; these draws bust at step ${got.survived + 1}`);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (expect.state === 'settled') {
|
|
229
|
+
const give = got.busted ? 0 : rungs[top - 1];
|
|
230
|
+
if (want !== give) differences.push(`multiplier ${times(give)} here, ${times(want)} on the receipt`);
|
|
231
|
+
if (step !== null && step !== top) differences.push(`settled at step ${step} on the receipt; the ladder has ${steps(top, en)}`);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (got.multBp !== want && !rungWithin(want)) {
|
|
235
|
+
differences.push(`multiplier ${times(want)} on the receipt is neither what these draws give (${times(got.multBp)}) nor a rung within the ${steps(got.survived, en)} survived`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* A recomputation against the receipt: `expect` as expectation() read it (the
|
|
241
|
+
* draws, and the multiplier, kind, state and step when the receipt named
|
|
242
|
+
* them), `got` as play() returned it, `pack` the maths pack as the dialog
|
|
243
|
+
* fetched it ({version, recomputed}: the version the receipt names and the
|
|
244
|
+
* hash of the bytes the server actually served), and `seedHash` the sha256
|
|
245
|
+
* of the server seed the draws were computed from. Every difference is
|
|
246
|
+
* named; none means the receipt is right. A pack that does not hash to the
|
|
247
|
+
* receipt's version is the first difference: the draws below it are then
|
|
248
|
+
* checked against tables nobody committed to. A seed that does not hash to
|
|
249
|
+
* the receipt's commitment is the next: the draws then follow from a seed
|
|
250
|
+
* the house never committed to. A receipt whose draws or multiplier could
|
|
251
|
+
* not be read is `unreadable`: nothing was compared, so there is no verdict
|
|
252
|
+
* either way, and the dialog says it could not verify rather than that the
|
|
253
|
+
* round does not match.
|
|
254
|
+
*/
|
|
255
|
+
export function compare(expect, got, pack, seedHash) {
|
|
256
|
+
const differences = [];
|
|
257
|
+
let unreadable = false;
|
|
258
|
+
if (pack && pack.version && pack.recomputed !== pack.version) {
|
|
259
|
+
differences.push(`the maths pack served does not hash to the version on the receipt (${pack.version}): its bytes hash to ${pack.recomputed || 'nothing'}`);
|
|
260
|
+
}
|
|
261
|
+
if (expect.serverSeedHash && seedHash && seedHash !== expect.serverSeedHash) {
|
|
262
|
+
differences.push('the revealed server seed does not hash to the commitment on the receipt');
|
|
263
|
+
}
|
|
264
|
+
if (expect.unreadable) {
|
|
265
|
+
differences.push("the receipt's draws could not be read");
|
|
266
|
+
unreadable = true;
|
|
267
|
+
}
|
|
268
|
+
const draws = expect.expect || [];
|
|
269
|
+
if (draws.length && draws.length !== got.draws.length) differences.push(`the receipt has ${draws.length} draw${draws.length === 1 ? '' : 's'}, the maths gives ${got.draws.length}`);
|
|
270
|
+
draws.forEach((w, k) => {
|
|
271
|
+
const g = got.draws[k];
|
|
272
|
+
if (!g) return;
|
|
273
|
+
if (g.raw.toString() !== w.raw) differences.push(`draw ${k + 1}: raw ${g.raw} here, ${w.raw} on the receipt`);
|
|
274
|
+
if (g.index !== w.index) differences.push(`draw ${k + 1}: row ${g.index} here, ${w.index} on the receipt`);
|
|
275
|
+
if ((g.rejected || 0) !== w.rejected) differences.push(`draw ${k + 1}: ${g.rejected || 0} rejected here, ${w.rejected} on the receipt`);
|
|
276
|
+
});
|
|
277
|
+
if (expect.multBp === null) {
|
|
278
|
+
differences.push('the receipt names no multiplier');
|
|
279
|
+
unreadable = true;
|
|
280
|
+
} else if (expect.multBp !== undefined) {
|
|
281
|
+
if (got.kind === 'ladder') ladderDifferences(expect, got, differences);
|
|
282
|
+
else if (got.multBp !== expect.multBp) differences.push(`multiplier ${times(got.multBp)} here, ${times(expect.multBp)} on the receipt`);
|
|
283
|
+
}
|
|
284
|
+
return { ok: differences.length === 0, differences, unreadable };
|
|
285
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"repo": "housegames-platform",
|
|
3
|
+
"path": "packages/dice-frontend/src/styles/00-fonts.css",
|
|
4
|
+
"commit": "80f074b6881cd6cd0ebfa29a3178031891b60589",
|
|
5
|
+
"files": {
|
|
6
|
+
"figtree-400.woff2": "d67327996a1af9b9f4f11a699e3ad0cf438ee92ba7e8413e4f76cc359036e9af",
|
|
7
|
+
"figtree-500.woff2": "4e53f9b5fedc06f18833c414eddbd25b9a4ed0205b0ba8785826bf1280b3e011",
|
|
8
|
+
"figtree-600.woff2": "d6341d9bc5546efde18285bbef8263824853fb2cd1f057e1057dbe2a9e1a05a5",
|
|
9
|
+
"red-hat-display-700.woff2": "3e118e963b677cf0f6eea25a0c06eb00a05afd975f886cd86b8f85419a345bf8"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover">
|
|
6
|
+
<title>House Engine</title>
|
|
7
|
+
<style>
|
|
8
|
+
/* The brand faces, the ones every House Original ships (dice-frontend
|
|
9
|
+
00-fonts.css, subset by the Originals' gen-assets.mjs), served from this
|
|
10
|
+
origin: the site's fonts do not cross into the shell's frame, and the
|
|
11
|
+
shell's policy allows no data: font. */
|
|
12
|
+
@font-face { font-family: 'Figtree'; font-style: normal; font-weight: 400; font-display: swap; src: url(./fonts/figtree-400.woff2) format('woff2'); }
|
|
13
|
+
@font-face { font-family: 'Figtree'; font-style: normal; font-weight: 500; font-display: swap; src: url(./fonts/figtree-500.woff2) format('woff2'); }
|
|
14
|
+
@font-face { font-family: 'Figtree'; font-style: normal; font-weight: 600; font-display: swap; src: url(./fonts/figtree-600.woff2) format('woff2'); }
|
|
15
|
+
@font-face { font-family: 'Red Hat Display'; font-style: normal; font-weight: 700; font-display: swap; src: url(./fonts/red-hat-display-700.woff2) format('woff2'); }
|
|
16
|
+
:root { color-scheme: dark; --bg:#041015; --s1:#0B1920; --s3:#0D2A38; --b:#0D2531; --b2:#113A4C; --t1:#F9F9FB; --t2:#8F96A8; --brand:#02F8F8; --brand6:#0AD4D4; --primary:#C4F4F1; --ok:#47D496; --err:#F41F50; }
|
|
17
|
+
* { box-sizing: border-box; }
|
|
18
|
+
html, body { margin: 0; height: 100%; background: var(--bg); color: var(--t1); font: 400 14px/20px Figtree, system-ui, sans-serif; touch-action: pan-x pan-y; overscroll-behavior: none; }
|
|
19
|
+
#stage { position: relative; height: 100%; min-height: 560px; }
|
|
20
|
+
#game { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; background: var(--bg); }
|
|
21
|
+
.over { position: absolute; inset: 0; display: grid; place-items: center; background: var(--bg); }
|
|
22
|
+
.over[hidden] { display: none; }
|
|
23
|
+
.loader { display: grid; gap: 14px; justify-items: center; width: min(320px, 80vw); }
|
|
24
|
+
.h { width: 44px; height: 44px; border-radius: 10px; background: var(--s1); border: 1px solid var(--b2); display: grid; place-items: center; font: 700 26px/1 'Red Hat Display', system-ui, sans-serif; color: var(--brand); animation: pulse 1.4s ease-in-out infinite; }
|
|
25
|
+
@keyframes pulse { 50% { opacity: .55; } }
|
|
26
|
+
@media (prefers-reduced-motion: reduce) { .h { animation: none; } }
|
|
27
|
+
.bar { width: 100%; height: 4px; border-radius: 999px; background: var(--s1); overflow: hidden; }
|
|
28
|
+
.bar i { display: block; height: 100%; width: 0; background: var(--brand6); transition: width .2s; }
|
|
29
|
+
.muted { color: var(--t2); font-size: 12px; line-height: 18px; }
|
|
30
|
+
.panel { background: var(--s1); border: 1px solid var(--b); border-radius: 12px; padding: 20px; max-width: 420px; display: grid; gap: 10px; }
|
|
31
|
+
.panel h2 { margin: 0; font: 700 18px/24px 'Red Hat Display', system-ui, sans-serif; }
|
|
32
|
+
.panel p { margin: 0; color: var(--t2); }
|
|
33
|
+
dialog { background: var(--s1); color: var(--t1); border: 1px solid var(--b2); border-radius: 12px; padding: 20px; width: min(480px, 92vw); max-height: 92vh; overflow: auto; }
|
|
34
|
+
dialog::backdrop { background: rgba(4,16,21,.7); }
|
|
35
|
+
dialog h2 { margin: 0 0 12px; font: 700 18px/24px 'Red Hat Display', system-ui, sans-serif; }
|
|
36
|
+
dialog h3 { margin: 0; font: 700 14px/20px 'Red Hat Display', system-ui, sans-serif; }
|
|
37
|
+
dialog p { margin: 0 0 10px; }
|
|
38
|
+
button { appearance: none; border: 0; background: var(--primary); color: #041015; font: 600 14px/20px Figtree, system-ui, sans-serif; padding: 8px 16px; border-radius: 8px; cursor: pointer; margin-top: 14px; }
|
|
39
|
+
button:disabled { opacity: .5; cursor: default; }
|
|
40
|
+
button:focus-visible, input:focus-visible, select:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
|
|
41
|
+
/* The Provably Fair dialog: the House Originals' modal, frame included
|
|
42
|
+
(dice-frontend Modal.svelte, 12-shell.css .hd-s-modal): 420 px at most
|
|
43
|
+
with 16 px gutters, a head row with the title and an X, the body
|
|
44
|
+
scrolling inside, a dimmed blurred backdrop that closes it on a press
|
|
45
|
+
and release outside. Full-width stacked fields with the label above,
|
|
46
|
+
monospace values that scroll inside their field (a 64-character seed
|
|
47
|
+
never widens the window), 48 px primary buttons, 44 px tap targets;
|
|
48
|
+
colour means something only on the result line (green matched, red
|
|
49
|
+
not). Fits a 360 px phone. */
|
|
50
|
+
#fair { width: min(420px, calc(100vw - 32px)); max-height: calc(100vh - 32px); padding: 0; overflow: hidden; display: none; flex-direction: column; border: 1px solid var(--b2); border-radius: 12px; box-shadow: 0 32px 96px rgba(0,0,0,.6); }
|
|
51
|
+
#fair[open] { display: flex; }
|
|
52
|
+
/* The dialog itself takes initial focus (tabindex=-1, as Dice's modal), so
|
|
53
|
+
no control lights its focus ring on open; the dialog shows none. */
|
|
54
|
+
#fair:focus, #fair:focus-visible { outline: none; }
|
|
55
|
+
#fair::backdrop { background: rgba(0,0,0,.6); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); }
|
|
56
|
+
.fr-head { display: flex; align-items: center; gap: 8px; padding: 14px 12px 10px 16px; flex: 0 0 auto; }
|
|
57
|
+
.fr-head h2 { flex: 1 1 auto; margin: 0; font: 700 16px/24px 'Red Hat Display', system-ui, sans-serif; }
|
|
58
|
+
.fr-x { flex: 0 0 auto; margin: 0; width: 44px; height: 44px; padding: 0; background: none; color: var(--t2); border-radius: 8px; display: grid; place-items: center; }
|
|
59
|
+
.fr-x:hover { background: var(--s3); color: var(--t1); }
|
|
60
|
+
.fr-x svg { width: 18px; height: 18px; display: block; }
|
|
61
|
+
.fr-body { padding: 0 16px 16px; overflow-y: auto; min-height: 0; }
|
|
62
|
+
.fr-tabs { display: flex; gap: 8px; border-bottom: 1px solid var(--b); margin-bottom: 14px; }
|
|
63
|
+
.fr-tab { flex: 1 1 0; position: relative; margin: 0; min-height: 44px; padding: 12px 16px; background: none; border: 0; border-radius: 0; color: var(--t2); font: 500 14px/20px Figtree, system-ui, sans-serif; }
|
|
64
|
+
.fr-tab:hover { color: var(--t1); }
|
|
65
|
+
.fr-tab[aria-selected="true"] { color: var(--t1); }
|
|
66
|
+
.fr-tab[aria-selected="true"]::after { content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px; background: var(--brand); border-radius: 1px; }
|
|
67
|
+
.fr-pane { display: flex; flex-direction: column; gap: 12px; }
|
|
68
|
+
.fr-pane[hidden] { display: none; }
|
|
69
|
+
.fr-field { display: flex; flex-direction: column; gap: 6px; }
|
|
70
|
+
.fr-field[hidden] { display: none; }
|
|
71
|
+
.fr-label { font: 600 14px/20px Figtree, system-ui, sans-serif; color: var(--t1); }
|
|
72
|
+
.fr-field input, .fr-field select { width: 100%; min-width: 0; height: 48px; padding: 0 12px; background: var(--bg); color: var(--t1); border: 1px solid var(--b2); border-radius: 8px; font: 400 13px/20px ui-monospace, Menlo, monospace; }
|
|
73
|
+
.fr-field input:read-only { color: var(--t2); }
|
|
74
|
+
.fr-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
|
|
75
|
+
.fr-nonce { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
|
|
76
|
+
.fr-nonce b { font: 700 14px/20px ui-monospace, Menlo, monospace; overflow-wrap: anywhere; }
|
|
77
|
+
.fr-primary { margin: 0; height: 48px; padding: 0 18px; flex: 0 0 auto; }
|
|
78
|
+
.fr-primary.wide { width: 100%; }
|
|
79
|
+
/* The Originals' secondary button (blackjack7 90-ds-components.css
|
|
80
|
+
.hl-btn-secondary): the one action on a pane that is not the primary. */
|
|
81
|
+
.fr-secondary { margin: 0; height: 48px; padding: 0 16px; width: 100%; background: var(--b2); border: 1px solid rgba(9,102,125,.15); border-radius: 8px; color: var(--t1); font: 700 14px/20px Figtree, system-ui, sans-serif; }
|
|
82
|
+
.fr-secondary:hover:not(:disabled) { background: var(--b); }
|
|
83
|
+
.fr-secondary:active:not(:disabled) { background: #0E202A; }
|
|
84
|
+
.fr-secondary:disabled { background: rgba(9,102,125,.15); border-color: transparent; color: var(--t2); opacity: 1; }
|
|
85
|
+
.fr-secondary[hidden] { display: none; }
|
|
86
|
+
.fr-note { margin: 0; color: var(--t2); font-size: 12px; line-height: 18px; }
|
|
87
|
+
.fr-note:empty { display: none; }
|
|
88
|
+
.fr-revealed { display: flex; flex-direction: column; gap: 8px; }
|
|
89
|
+
.fr-rev { display: flex; flex-direction: column; gap: 2px; min-height: 44px; margin: 0; padding: 10px 12px; background: var(--bg); color: var(--t1); border: 1px solid var(--b2); border-radius: 10px; text-align: left; font: inherit; }
|
|
90
|
+
.fr-rev:hover, .fr-rev:focus-visible { border-color: var(--brand); }
|
|
91
|
+
.fr-rev-seed { font: 400 13px/18px ui-monospace, Menlo, monospace; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
92
|
+
.fr-rev-meta { color: var(--t2); font-size: 12px; line-height: 18px; }
|
|
93
|
+
.fr-round { margin: 0; font: 600 14px/20px Figtree, system-ui, sans-serif; overflow-wrap: anywhere; }
|
|
94
|
+
.fr-round[hidden] { display: none; }
|
|
95
|
+
.fr-result { min-height: 20px; color: var(--t2); overflow-wrap: anywhere; }
|
|
96
|
+
.fr-result.ok { color: var(--ok); }
|
|
97
|
+
.fr-result.bad { color: var(--err); }
|
|
98
|
+
.fr-draws { margin: 0; padding: 0; list-style: none; display: grid; gap: 4px; font: 400 13px/18px ui-monospace, Menlo, monospace; overflow-wrap: anywhere; }
|
|
99
|
+
.fr-draws:empty { display: none; }
|
|
100
|
+
#fair-open { position: fixed; right: 12px; bottom: 12px; margin: 0; min-height: 44px; padding: 6px 12px; font-size: 12px; background: var(--s3); color: var(--t1); border: 1px solid var(--b2); z-index: 2; }
|
|
101
|
+
#fair-open[hidden] { display: none; }
|
|
102
|
+
/* The shell's autoplay control: rounds left and Stop, in this document over
|
|
103
|
+
the game frame, where the game can neither cover it nor press it. A pill at
|
|
104
|
+
the top, so it sits over no game's own bet row; a 44 px Stop. */
|
|
105
|
+
.auto { position: absolute; top: 10px; left: 50%; transform: translateX(-50%); z-index: 3; display: flex; align-items: center; gap: 10px; padding: 4px 4px 4px 16px; background: rgba(4,16,21,.92); border: 1px solid var(--b2); border-radius: 999px; box-shadow: 0 8px 24px rgba(0,0,0,.45); color: var(--t1); font: 600 13px/20px Figtree, system-ui, sans-serif; white-space: nowrap; }
|
|
106
|
+
.auto[hidden] { display: none; }
|
|
107
|
+
.auto-stop { margin: 0; min-height: 44px; min-width: 72px; padding: 0 16px; border-radius: 999px; background: var(--primary); color: #041015; font: 600 14px/20px Figtree, system-ui, sans-serif; }
|
|
108
|
+
/* The autoplay confirmation: the shell's own dialog, outside the game frame.
|
|
109
|
+
A session of bets is never one in-game click away — the player approves
|
|
110
|
+
exactly these terms here, on the shell's own Start, which the game cannot
|
|
111
|
+
reach, cover or pre-fill, and which is armed only a moment after the
|
|
112
|
+
dialog appears (page.js). Same modal frame as the reality check. */
|
|
113
|
+
#ap { width: min(400px, calc(100vw - 32px)); }
|
|
114
|
+
#ap:focus, #ap:focus-visible { outline: none; }
|
|
115
|
+
#ap h2 { margin: 0 0 4px; }
|
|
116
|
+
.ap-sub { margin: 0 0 14px; color: var(--t2); font-size: 13px; line-height: 18px; }
|
|
117
|
+
.ap-terms { margin: 0 0 18px; display: grid; gap: 0; }
|
|
118
|
+
.ap-terms > div { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--b); }
|
|
119
|
+
.ap-terms > div[hidden] { display: none; }
|
|
120
|
+
.ap-terms > div:last-child { border-bottom: 0; }
|
|
121
|
+
.ap-terms dt { color: var(--t2); font-size: 14px; }
|
|
122
|
+
.ap-terms dd { margin: 0; font-weight: 600; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; text-align: right; }
|
|
123
|
+
.ap-actions { display: flex; gap: 10px; }
|
|
124
|
+
.ap-actions button { flex: 1; margin: 0; min-height: 48px; }
|
|
125
|
+
.ap-secondary { background: var(--b2); color: var(--t1); }
|
|
126
|
+
.ap-secondary:hover { background: var(--b); }
|
|
127
|
+
.ap-primary { background: var(--primary); color: #041015; }
|
|
128
|
+
.sandbox-test-toggle { position: absolute; top: 10px; right: 10px; z-index: 4; width: 44px; height: 44px; margin: 0; padding: 0; border: 1px solid var(--b2); border-radius: 10px; background: rgba(4,16,21,.92); color: var(--t1); }
|
|
129
|
+
.sandbox-test-panel { position: absolute; top: 60px; right: 10px; z-index: 4; width: min(320px, calc(100vw - 20px)); max-height: calc(100vh - 70px); overflow: auto; display: grid; gap: 10px; padding: 14px; background: rgba(11,25,32,.98); border: 1px solid var(--b2); border-radius: 12px; box-shadow: 0 12px 40px rgba(0,0,0,.5); }
|
|
130
|
+
.sandbox-test-panel[hidden] { display: none; }
|
|
131
|
+
.sandbox-test-panel h2, .sandbox-test-panel h3 { margin: 0; font: 700 16px/22px 'Red Hat Display', system-ui, sans-serif; }
|
|
132
|
+
.sandbox-test-panel h3 { font-size: 13px; }
|
|
133
|
+
.sandbox-test-section { display: grid; gap: 6px; padding-top: 8px; border-top: 1px solid var(--b); }
|
|
134
|
+
.sandbox-test-panel button { margin: 0; min-height: 36px; padding: 6px 10px; font-size: 12px; }
|
|
135
|
+
.sandbox-test-tiers, .sandbox-test-fault { display: grid; gap: 6px; }
|
|
136
|
+
.sandbox-test-scenarios, .sandbox-test-scenario-group { display: grid; gap: 6px; }
|
|
137
|
+
.sandbox-test-scenario-group { padding-top: 4px; }
|
|
138
|
+
.sandbox-test-scenario-group strong { color: var(--t2); font-size: 12px; }
|
|
139
|
+
.sandbox-test-summary { margin: 4px 0 0; padding-left: 18px; color: var(--t2); font-size: 12px; }
|
|
140
|
+
.sandbox-test-fault { grid-template-columns: minmax(0, 1fr) auto auto; align-items: center; }
|
|
141
|
+
.sandbox-test-fault small { grid-column: 1 / -1; grid-row: 2; }
|
|
142
|
+
.sandbox-test-fault span { color: var(--t2); text-transform: capitalize; }
|
|
143
|
+
.sandbox-test-fault button { white-space: nowrap; }
|
|
144
|
+
.sandbox-test-panel input { width: 100%; min-width: 0; height: 38px; padding: 0 10px; background: var(--bg); color: var(--t1); border: 1px solid var(--b2); border-radius: 8px; }
|
|
145
|
+
.sandbox-test-status { margin: 0; min-height: 20px; color: var(--t2); font-size: 12px; overflow-wrap: anywhere; }
|
|
146
|
+
.sandbox-test-instruction { margin: 0; min-height: 20px; color: var(--t1); font-size: 13px; font-weight: 600; overflow-wrap: anywhere; }
|
|
147
|
+
@media (max-width: 420px) {
|
|
148
|
+
.sandbox-test-running .sandbox-test-panel { top: 60px; bottom: auto; max-height: none; }
|
|
149
|
+
.sandbox-test-running .sandbox-test-panel h2,
|
|
150
|
+
.sandbox-test-running .sandbox-test-panel .sandbox-test-scenarios-section > h3,
|
|
151
|
+
.sandbox-test-running .sandbox-test-panel .sandbox-test-section:not(.sandbox-test-scenarios-section),
|
|
152
|
+
.sandbox-test-running .sandbox-test-panel .sandbox-test-scenarios-section > .sandbox-test-scenarios,
|
|
153
|
+
.sandbox-test-running .sandbox-test-panel .sandbox-test-scenarios-section > button:first-of-type,
|
|
154
|
+
.sandbox-test-running .sandbox-test-panel .sandbox-test-summary,
|
|
155
|
+
.sandbox-test-running .sandbox-test-panel .sandbox-test-status,
|
|
156
|
+
.sandbox-test-running .sandbox-test-panel .sandbox-test-reload { display: none; }
|
|
157
|
+
.sandbox-test-running .sandbox-test-panel .sandbox-test-instruction { display: block; }
|
|
158
|
+
.sandbox-test-running .sandbox-test-panel .sandbox-test-scenarios-section { display: flex; flex-direction: row; gap: 6px; border-top: 0; padding-top: 0; }
|
|
159
|
+
.sandbox-test-running .sandbox-test-panel .sandbox-test-scenarios-section > button { flex: 1; }
|
|
160
|
+
}
|
|
161
|
+
@media (max-width: 390px) {
|
|
162
|
+
.sandbox-test-fault { gap: 4px; }
|
|
163
|
+
.sandbox-test-fault button { padding-left: 6px; padding-right: 6px; }
|
|
164
|
+
}
|
|
165
|
+
</style>
|
|
166
|
+
</head>
|
|
167
|
+
<body>
|
|
168
|
+
<div id="stage">
|
|
169
|
+
<iframe id="game" title="game" sandbox="allow-scripts allow-same-origin" allow="autoplay" referrerpolicy="no-referrer" hidden></iframe>
|
|
170
|
+
<div class="over" id="loading"><div class="loader"><div class="h">H</div><div class="bar"><i id="progress"></i></div><div class="muted" id="loading-text">Starting</div></div></div>
|
|
171
|
+
<div class="over" id="failed" hidden><div class="panel"><h2>Game unavailable</h2><p id="failed-text"></p></div></div>
|
|
172
|
+
<div class="auto" id="auto" hidden><span id="auto-text" role="status" aria-live="polite"></span><button type="button" class="auto-stop" id="auto-stop">Stop</button></div>
|
|
173
|
+
</div>
|
|
174
|
+
<dialog id="fair" aria-labelledby="fair-title" tabindex="-1">
|
|
175
|
+
<div class="fr-head">
|
|
176
|
+
<h2 id="fair-title">Provably Fair</h2>
|
|
177
|
+
<button type="button" class="fr-x" id="fair-x" aria-label="Close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg></button>
|
|
178
|
+
</div>
|
|
179
|
+
<div class="fr-body">
|
|
180
|
+
<div class="fr-tabs" role="tablist" id="fair-tabs" aria-label="Provably Fair">
|
|
181
|
+
<button type="button" class="fr-tab" id="fair-tab-seeds" role="tab" aria-selected="true" aria-controls="fair-seeds">Seeds</button>
|
|
182
|
+
<button type="button" class="fr-tab" id="fair-tab-verify" role="tab" aria-selected="false" aria-controls="fair-verify">Verify</button>
|
|
183
|
+
</div>
|
|
184
|
+
<section class="fr-pane" id="fair-seeds" role="tabpanel" aria-labelledby="fair-tab-seeds">
|
|
185
|
+
<label class="fr-field"><span class="fr-label" id="fair-l-client">Client Seed</span><input id="fair-client" maxlength="64" autocomplete="off" spellcheck="false"></label>
|
|
186
|
+
<label class="fr-field"><span class="fr-label" id="fair-l-hash">Server Seed (Hashed)</span><input id="fair-hash" readonly spellcheck="false"></label>
|
|
187
|
+
<label class="fr-field"><span class="fr-label" id="fair-l-next">Next Server Seed (Hashed)</span><input id="fair-next" readonly spellcheck="false"></label>
|
|
188
|
+
<div class="fr-row">
|
|
189
|
+
<span class="fr-nonce"><span class="fr-label" id="fair-l-nonce">Nonce</span> <b id="fair-nonce">0</b></span>
|
|
190
|
+
<button type="button" class="fr-primary" id="fair-rotate" disabled>Rotate seed pair</button>
|
|
191
|
+
</div>
|
|
192
|
+
<p class="fr-note" id="fair-seeds-note">The active server seed stays hidden until the pair rotates — its hash is the commitment.</p>
|
|
193
|
+
<p class="fr-note" id="fair-status" role="status" aria-live="polite"></p>
|
|
194
|
+
<div class="fr-revealed" id="fair-revealed"><p class="fr-note">No revealed pairs yet</p></div>
|
|
195
|
+
</section>
|
|
196
|
+
<section class="fr-pane" id="fair-verify" role="tabpanel" aria-labelledby="fair-tab-verify" hidden>
|
|
197
|
+
<p class="fr-round" id="fair-round" hidden></p>
|
|
198
|
+
<label class="fr-field"><span class="fr-label" id="fair-l-v-server">Server Seed (Revealed)</span><input id="fair-v-server" autocomplete="off" spellcheck="false"></label>
|
|
199
|
+
<label class="fr-field"><span class="fr-label" id="fair-l-v-client">Client Seed</span><input id="fair-v-client" autocomplete="off" spellcheck="false"></label>
|
|
200
|
+
<label class="fr-field"><span class="fr-label" id="fair-l-v-nonce">Nonce</span><input id="fair-v-nonce" inputmode="numeric" autocomplete="off"></label>
|
|
201
|
+
<label class="fr-field" id="fair-v-mode-field" hidden><span class="fr-label" id="fair-l-v-mode">Mode</span><select id="fair-v-mode"></select></label>
|
|
202
|
+
<button type="button" class="fr-primary wide" id="fair-verify-run">Verify</button>
|
|
203
|
+
<div class="fr-result" id="fair-result" aria-live="polite"></div>
|
|
204
|
+
<ol class="fr-draws" id="fair-draws"></ol>
|
|
205
|
+
<p class="fr-note" id="fair-v-note"></p>
|
|
206
|
+
<button type="button" class="fr-secondary" id="fair-reveal" hidden>Rotate seed pair</button>
|
|
207
|
+
</section>
|
|
208
|
+
</div>
|
|
209
|
+
</dialog>
|
|
210
|
+
<button type="button" id="fair-open" hidden>Provably Fair</button>
|
|
211
|
+
<dialog id="rc"><h2>Reality check</h2><p id="rc-text"></p><form method="dialog"><button>Continue playing</button></form></dialog>
|
|
212
|
+
<dialog id="ap" aria-labelledby="ap-title" tabindex="-1">
|
|
213
|
+
<h2 id="ap-title">Start autoplay?</h2>
|
|
214
|
+
<p class="ap-sub" id="ap-sub">The game asked to play rounds on its own. It runs only if you start it here, and you can Stop any time.</p>
|
|
215
|
+
<dl class="ap-terms">
|
|
216
|
+
<div><dt id="ap-l-bet">Bet</dt><dd id="ap-bet"></dd></div>
|
|
217
|
+
<div><dt id="ap-l-rounds">Rounds</dt><dd id="ap-rounds"></dd></div>
|
|
218
|
+
<div><dt id="ap-l-stake">Stake each round</dt><dd id="ap-stake"></dd></div>
|
|
219
|
+
<div><dt id="ap-l-total">Total staked, up to</dt><dd id="ap-total"></dd></div>
|
|
220
|
+
<div id="ap-profit-row" hidden><dt id="ap-l-profit">Stop at profit</dt><dd id="ap-profit"></dd></div>
|
|
221
|
+
<div><dt id="ap-l-loss">Stop at loss</dt><dd id="ap-loss"></dd></div>
|
|
222
|
+
</dl>
|
|
223
|
+
<div class="ap-actions">
|
|
224
|
+
<button type="button" id="ap-cancel" class="ap-secondary">Cancel</button>
|
|
225
|
+
<button type="button" id="ap-start" class="ap-primary" disabled>Start</button>
|
|
226
|
+
</div>
|
|
227
|
+
</dialog>
|
|
228
|
+
<script type="module" src="./page.js"></script>
|
|
229
|
+
</body>
|
|
230
|
+
</html>
|