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,138 @@
|
|
|
1
|
+
// Types for @housegames/engine-sdk (sdk.js, ui.js). Editors read them; nothing is built.
|
|
2
|
+
|
|
3
|
+
export type ErrorCode = 'invalid' | 'busy' | 'insufficient-balance' | 'limits' | 'pacing' | 'pending' | 'unavailable' | 'session-ended' | 'round' | 'conflict' | 'server' | 'needs-click' | 'autoplay';
|
|
4
|
+
export const ERRORS: Readonly<Record<'INVALID' | 'BUSY' | 'INSUFFICIENT' | 'LIMITS' | 'PACING' | 'PENDING' | 'UNAVAILABLE' | 'SESSION' | 'ROUND' | 'CONFLICT' | 'SERVER' | 'CLICK' | 'AUTOPLAY', ErrorCode>>;
|
|
5
|
+
|
|
6
|
+
/** A refusal from the shell or the server. */
|
|
7
|
+
export class EngineError extends Error {
|
|
8
|
+
code: ErrorCode;
|
|
9
|
+
/** For pacing and pending: when to try again. */
|
|
10
|
+
retryAfterMs?: number;
|
|
11
|
+
/** For 'round': the round as it stands; redraw it. */
|
|
12
|
+
round?: Round;
|
|
13
|
+
/** For a 'limits' refusal over the maximum win per round: the most the bet takes, a decimal
|
|
14
|
+
* string in the wallet coin as the server wrote it; set the field to it. */
|
|
15
|
+
maxStake?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface Bet { id: string; label?: string; maxMultBp: number }
|
|
19
|
+
export interface Mode {
|
|
20
|
+
id: string; label?: string; kind: 'table' | 'ladder'; bets: Bet[];
|
|
21
|
+
steps?: number; ladderBp?: number[]; cashout?: boolean; autoAdvance?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface Config {
|
|
25
|
+
game: string;
|
|
26
|
+
mathHash: string;
|
|
27
|
+
env: 'sandbox' | 'production';
|
|
28
|
+
balance?: number;
|
|
29
|
+
currency: { code: string; symbol: string; decimals: number };
|
|
30
|
+
/** In the wallet coin. `maxWin` is the most one round pays (the platform's maximum win per round,
|
|
31
|
+
* or the coin's own, whichever is less); `maxStake[mode][bet]` is the most that bet takes under it
|
|
32
|
+
* (a decimal string at the coin's precision, rounded down: stake × the bet's top multiplier stays
|
|
33
|
+
* within `maxWin`), so a high-multiplier bet has a low one. `"0"` for every bet (and `maxWin` 0)
|
|
34
|
+
* while the coin has no USD rate: nothing can be staked until it is back. Absent on an older server. */
|
|
35
|
+
limits: { min: number; max: number; maxWin: number; maxStake?: Record<string, Record<string, string>> };
|
|
36
|
+
modes: Mode[];
|
|
37
|
+
activeRound?: Round;
|
|
38
|
+
playerTag: string;
|
|
39
|
+
state: { revision: number; data: unknown };
|
|
40
|
+
locale: string;
|
|
41
|
+
/** The jurisdiction's word on autoplay. */
|
|
42
|
+
rg?: { autoplay?: { allowed: boolean } };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** The round as the game replays it. Show it as it comes back: it is the truth. */
|
|
46
|
+
export interface Round {
|
|
47
|
+
id: string; game: string; mathHash: string; mode: string; bet: string; kind: 'table' | 'ladder';
|
|
48
|
+
nonce: number; currency: string; amount: string; step: number; state: 'open' | 'closed' | string; createdAt: string;
|
|
49
|
+
draws: { index: number; raw: string; rejected: number }[];
|
|
50
|
+
/** The row data of each shown draw (what your math/*.tsv holds). */
|
|
51
|
+
data: any[];
|
|
52
|
+
payout?: string; multBp?: number; payoutMultiplier?: number; settledAt?: string;
|
|
53
|
+
survived?: number; busted?: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Money exactly as the House Originals show it, in the player's coin or fiat. Amounts are wallet units. */
|
|
57
|
+
export interface Money {
|
|
58
|
+
coin: string; isFiat: boolean; unit: string; sign: string; mark: string;
|
|
59
|
+
decimals: number; walletDecimals: number;
|
|
60
|
+
/** The smallest wallet unit (10⁻⁸ for a coin): the precision a stake is cut to. Never a bet button's increment (nor is limits.min): bet up / down walks levels(). */
|
|
61
|
+
step: number;
|
|
62
|
+
toView(amount: number): number; toWallet(value: number): number;
|
|
63
|
+
format(amount: number): string; caption(amount: number): string; parse(text: string): number;
|
|
64
|
+
payout(amount: number, mult: number): number;
|
|
65
|
+
/** Round stakes in the display unit between min and max (wallet units, engine.config.limits): offer these, never a converted limit. */
|
|
66
|
+
levels(min?: number, max?: number): BetLevel[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** A bet level: the round number the player sees, the wallet amount engine.play takes, the header's text and the button's label (€2.00, 2 USDT). */
|
|
70
|
+
export interface BetLevel { view: number; amount: number; text: string; label: string; }
|
|
71
|
+
|
|
72
|
+
export type ShellEvent = 'balance' | 'control' | 'pause' | 'resume' | 'ended' | 'autoplay';
|
|
73
|
+
/** The shell's autoplay session: sent when it starts, after each of its plays and when it ends. */
|
|
74
|
+
export interface AutoplayState {
|
|
75
|
+
active: boolean;
|
|
76
|
+
/** Plays left in the session. */
|
|
77
|
+
left: number;
|
|
78
|
+
/** Plays made in it. */
|
|
79
|
+
played: number;
|
|
80
|
+
/** The most a play in it may stake (while active). */
|
|
81
|
+
stake?: number;
|
|
82
|
+
/** Why it ended: stopped, done, profit, loss, refused, paused, ended. */
|
|
83
|
+
reason?: string;
|
|
84
|
+
}
|
|
85
|
+
export interface EventBody {
|
|
86
|
+
balance: { balance: number };
|
|
87
|
+
control: { action: 'sound' | 'settings' };
|
|
88
|
+
pause: {}; resume: {}; ended: {};
|
|
89
|
+
autoplay: AutoplayState;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** What a game asks the shell for on the player's click: at most `rounds` plays (≤ 1000) of one
|
|
93
|
+
* bet (`mode`, `bet`), none above `stake`. The shell shows the player these terms before any play. */
|
|
94
|
+
export interface AutoplayRequest {
|
|
95
|
+
mode: string;
|
|
96
|
+
bet: string;
|
|
97
|
+
rounds: number;
|
|
98
|
+
stake: number;
|
|
99
|
+
/** End the session once its net result reaches this profit / this loss (wallet units). */
|
|
100
|
+
stopOnProfit?: number;
|
|
101
|
+
stopOnLoss?: number;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface Engine {
|
|
105
|
+
readonly config: Config;
|
|
106
|
+
readonly money: Money;
|
|
107
|
+
/** The balance as the site's header shows it; null while unknown. */
|
|
108
|
+
readonly balance: number | null;
|
|
109
|
+
/** The player's saved data; neither call needs a click (a save moves no money). */
|
|
110
|
+
readonly state: { get(): Promise<{ revision: number; data: unknown }>; put(revision: number, data: unknown): Promise<{ revision: number; data: unknown }> };
|
|
111
|
+
/** Places a bet; rejects with EngineError. Call it from the player's click (or inside an
|
|
112
|
+
* autoplay session): the shell refuses a play with no activation behind it (`needs-click`). */
|
|
113
|
+
play(bet: { mode: string; bet: string; amount: number }): Promise<{ round: Round; balance?: number }>;
|
|
114
|
+
/** Moves an open ladder round. `advance` and `cashout` both need the player's click as a play does
|
|
115
|
+
* (nothing moves without the player; a round left open waits for them, never for the game), or the session's own round. */
|
|
116
|
+
action(round: Round, action: 'advance' | 'cashout'): Promise<{ round: Round; balance?: number }>;
|
|
117
|
+
/** Autoplay is the shell's: ask for a session from the player's click (`needs-click` otherwise) —
|
|
118
|
+
* the shell shows the player the capped terms and resolves this on their Start (rejects with
|
|
119
|
+
* `autoplay` on Cancel, for 10 s after a Cancel, or when their rules forbid it) — play inside it
|
|
120
|
+
* (no click needed, on the bet granted), follow the `autoplay` event; the shell shows rounds left
|
|
121
|
+
* and Stop over the game. */
|
|
122
|
+
readonly autoplay: {
|
|
123
|
+
start(request: AutoplayRequest): Promise<{ session: AutoplayRequest }>;
|
|
124
|
+
stop(): Promise<{ stopped: true }>;
|
|
125
|
+
};
|
|
126
|
+
/** The settled round as the game may see it, without its seeds; the shell's dialog (openFairness) holds the receipt. */
|
|
127
|
+
round(roundId: string): Promise<unknown>;
|
|
128
|
+
on<T extends ShellEvent>(type: T, fn: (body: EventBody[T]) => void): () => void;
|
|
129
|
+
/** The first frame is on screen. */
|
|
130
|
+
ready(): void;
|
|
131
|
+
loading(group: string, loaded: number, total: number): void;
|
|
132
|
+
/** The round's outcome is on screen: the header balance follows now. */
|
|
133
|
+
revealed(roundId: string): void;
|
|
134
|
+
openFairness(roundId: string): void;
|
|
135
|
+
reportError(message: string): void;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function connect(options?: { timeoutMs?: number }): Promise<Engine>;
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @housegames/engine-sdk — what a studio game imports.
|
|
3
|
+
*
|
|
4
|
+
* import { connect } from '@housegames/engine-sdk';
|
|
5
|
+
*
|
|
6
|
+
* const engine = await connect();
|
|
7
|
+
* engine.config; // game, modes, currency, limits, balance, state…
|
|
8
|
+
* engine.money; // the player's currency, as the Originals show it
|
|
9
|
+
* const field = document.querySelector('hb-bet-amount'); // bet-field.js
|
|
10
|
+
* field.connect(engine);
|
|
11
|
+
* const { round, balance } = await engine.play({ mode: 'coin', bet: 'heads', amount: field.amount });
|
|
12
|
+
* await engine.action(round, 'cashout');
|
|
13
|
+
* engine.on('balance', ({ balance }) => …);
|
|
14
|
+
* engine.ready(); // first frame is on screen
|
|
15
|
+
*
|
|
16
|
+
* The game never talks to the server. It asks the shell around it, and the
|
|
17
|
+
* shell answers with what the server decided. Show `round` and `balance` as
|
|
18
|
+
* they come back: they are the truth, and nothing the game computes is.
|
|
19
|
+
*
|
|
20
|
+
* A bet is the player's act: call play(), and a ladder's action (advance
|
|
21
|
+
* and cashout alike), from the handler of the player's click or key press,
|
|
22
|
+
* or the shell refuses it with `needs-click`. Autoplay is the shell's: ask
|
|
23
|
+
* for a session (engine.autoplay.start, below, from the player's click too)
|
|
24
|
+
* — the shell shows the player the terms and starts it on their Start,
|
|
25
|
+
* answering you then — play inside it, and follow its `autoplay` events; the
|
|
26
|
+
* shell shows its own Stop over the game.
|
|
27
|
+
*/
|
|
28
|
+
import {
|
|
29
|
+
ERRORS,
|
|
30
|
+
GAME_EVENTS,
|
|
31
|
+
REQUESTS,
|
|
32
|
+
SHELL_EVENTS,
|
|
33
|
+
event,
|
|
34
|
+
maxStakeFor,
|
|
35
|
+
readShellMessage,
|
|
36
|
+
request,
|
|
37
|
+
} from './protocol.js';
|
|
38
|
+
import { moneyFor } from './money.js';
|
|
39
|
+
|
|
40
|
+
/** A refusal from the shell or the server. `code` is one of ERRORS. */
|
|
41
|
+
export class EngineError extends Error {
|
|
42
|
+
constructor(code, message, retryAfterMs, round, maxStake) {
|
|
43
|
+
super(message || code);
|
|
44
|
+
this.name = 'EngineError';
|
|
45
|
+
this.code = code;
|
|
46
|
+
if (retryAfterMs > 0) this.retryAfterMs = retryAfterMs;
|
|
47
|
+
// A refusal about a round carries it as it stands now.
|
|
48
|
+
if (round && typeof round === 'object') this.round = round;
|
|
49
|
+
// A `limits` refusal over the maximum win per round carries the most
|
|
50
|
+
// the bet takes (a decimal string in the wallet coin, as the server
|
|
51
|
+
// wrote it): set the field to it.
|
|
52
|
+
if (typeof maxStake === 'string' && maxStake !== '') this.maxStake = maxStake;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export { ERRORS };
|
|
57
|
+
|
|
58
|
+
const CONNECT_TIMEOUT_MS = 15_000;
|
|
59
|
+
const HELLO_EVERY_MS = 400;
|
|
60
|
+
// Windows whose uncaught errors are already reported: a second connect() on the same
|
|
61
|
+
// page (a game that reconnects) must not count each error twice.
|
|
62
|
+
const hooked = new WeakSet();
|
|
63
|
+
// The shell always answers: it has its own deadline on the server and turns
|
|
64
|
+
// a timeout into a refusal. This is only a backstop for a shell that is gone.
|
|
65
|
+
const ANSWER_TIMEOUT_MS = 60_000;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Waits for the shell's `init` and returns the engine handle. Rejects when no
|
|
69
|
+
* shell answers in time (the game was opened outside the shell).
|
|
70
|
+
*/
|
|
71
|
+
export function connect({ host = globalThis.parent, self = globalThis, timeoutMs = CONNECT_TIMEOUT_MS } = {}) {
|
|
72
|
+
return new Promise((resolve, reject) => {
|
|
73
|
+
if (!host || host === self) {
|
|
74
|
+
reject(new EngineError(ERRORS.SESSION, 'not inside the engine shell'));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
// The game says it is here and asks for its config, again every
|
|
78
|
+
// HELLO_EVERY_MS until the shell answers: however long the page took
|
|
79
|
+
// to load, and whenever the shell started listening.
|
|
80
|
+
const hello = () => host.postMessage(event(GAME_EVENTS.HELLO, {}), '*');
|
|
81
|
+
const again = setInterval(hello, HELLO_EVERY_MS);
|
|
82
|
+
const timer = setTimeout(() => {
|
|
83
|
+
clearInterval(again);
|
|
84
|
+
self.removeEventListener('message', onInit);
|
|
85
|
+
reject(new EngineError(ERRORS.SESSION, 'the engine shell did not answer'));
|
|
86
|
+
}, timeoutMs);
|
|
87
|
+
function onInit(e) {
|
|
88
|
+
if (e.source !== host) return;
|
|
89
|
+
const msg = readShellMessage(e.data);
|
|
90
|
+
if (!msg || msg.kind !== 'evt' || msg.type !== SHELL_EVENTS.INIT) return;
|
|
91
|
+
clearTimeout(timer);
|
|
92
|
+
clearInterval(again);
|
|
93
|
+
self.removeEventListener('message', onInit);
|
|
94
|
+
resolve(new Engine(host, self, msg.body));
|
|
95
|
+
}
|
|
96
|
+
self.addEventListener('message', onInit);
|
|
97
|
+
hello();
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
class Engine {
|
|
102
|
+
#host;
|
|
103
|
+
#pending = new Map(); // id -> { resolve, reject, timer }
|
|
104
|
+
#listeners = new Map(); // shell event type -> Set<fn>
|
|
105
|
+
#seq = 0;
|
|
106
|
+
#moneyInFlight = false;
|
|
107
|
+
#balance = null;
|
|
108
|
+
#reveal = null; // { roundId, balance }: the settled round whose result waits for revealed()
|
|
109
|
+
|
|
110
|
+
constructor(host, self, config) {
|
|
111
|
+
this.#host = host;
|
|
112
|
+
this.config = Object.freeze(config);
|
|
113
|
+
// An old shell sends no wallet: the plain coin at its precision.
|
|
114
|
+
const c = config.currency || {};
|
|
115
|
+
this.money = moneyFor(config.wallet || { symbol: c.symbol, name: c.symbol, isFiat: false, rate: 1, betPrecision: c.decimals });
|
|
116
|
+
if (typeof config.balance === 'number') this.#balance = config.balance;
|
|
117
|
+
self.addEventListener('message', (e) => {
|
|
118
|
+
if (e.source !== host) return;
|
|
119
|
+
const msg = readShellMessage(e.data);
|
|
120
|
+
if (!msg) return;
|
|
121
|
+
if (msg.kind === 'res') this.#settle(msg);
|
|
122
|
+
else {
|
|
123
|
+
// The shell's word on the balance outranks a reveal still waiting.
|
|
124
|
+
if (msg.type === SHELL_EVENTS.BALANCE && typeof msg.body.balance === 'number') { this.#balance = msg.body.balance; this.#reveal = null; }
|
|
125
|
+
this.#emit(msg.type, msg.body);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
this.state = Object.freeze({
|
|
129
|
+
get: () => this.#ask(REQUESTS.STATE_GET, {}),
|
|
130
|
+
put: (revision, data) => this.#ask(REQUESTS.STATE_PUT, { revision, data }),
|
|
131
|
+
});
|
|
132
|
+
// Autoplay is a session the shell grants and runs the count of:
|
|
133
|
+
// start({ mode, bet, rounds, stake, stopOnProfit?, stopOnLoss? }) → { session }, on the player's click;
|
|
134
|
+
// rounds ≤ 1000, every play in it on that bet and none above `stake`;
|
|
135
|
+
// a stop ends it at that net result
|
|
136
|
+
// stop() → { stopped: true }, always taken
|
|
137
|
+
// Plays inside it need no click; the shell's `autoplay` event says
|
|
138
|
+
// {active, left, played, reason?} on every change. The shell shows
|
|
139
|
+
// rounds left and Stop over the game itself.
|
|
140
|
+
this.autoplay = Object.freeze({
|
|
141
|
+
start: ({ mode, bet, rounds, stake, stopOnProfit, stopOnLoss } = {}) => {
|
|
142
|
+
const body = { mode, bet, rounds, stake };
|
|
143
|
+
if (stopOnProfit > 0) body.stopOnProfit = stopOnProfit;
|
|
144
|
+
if (stopOnLoss > 0) body.stopOnLoss = stopOnLoss;
|
|
145
|
+
return this.#ask(REQUESTS.AUTOPLAY_START, body);
|
|
146
|
+
},
|
|
147
|
+
stop: () => this.#ask(REQUESTS.AUTOPLAY_STOP, {}),
|
|
148
|
+
});
|
|
149
|
+
// An error the page never caught (a render loop, a click handler, a promise) is
|
|
150
|
+
// reported like reportError: the shell logs it, the engine counts one error, and
|
|
151
|
+
// the studio's Reports show the count. The game's own code sees nothing of this.
|
|
152
|
+
if (!hooked.has(self)) {
|
|
153
|
+
hooked.add(self);
|
|
154
|
+
self.addEventListener('error', (e) => this.reportError(e && e.message ? e.message : 'uncaught error'));
|
|
155
|
+
self.addEventListener('unhandledrejection', (e) => {
|
|
156
|
+
const r = e ? e.reason : undefined;
|
|
157
|
+
this.reportError(r && r.message ? r.message : r === undefined ? 'unhandled rejection' : r);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** The player's balance in the wallet currency, as the site's header
|
|
163
|
+
* shows it: the stake leaves when the bet is placed, the result lands
|
|
164
|
+
* when the game calls revealed(). null while unknown. */
|
|
165
|
+
get balance() {
|
|
166
|
+
return this.#balance;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Places a bet. Resolves { round, balance? }; rejects with EngineError.
|
|
170
|
+
* The stake is a positive amount: there is no free round (a round that
|
|
171
|
+
* costs nothing would still consume a nonce of the player's seed pair),
|
|
172
|
+
* so 0 is refused here with `limits`, as the server refuses it, before
|
|
173
|
+
* anything is sent. So is a stake over the most this bet may take under
|
|
174
|
+
* the maximum win per round (config.limits.maxStake[mode][bet]): the
|
|
175
|
+
* server would refuse it before the bet, and the refusal names the
|
|
176
|
+
* highest stake the bet takes. A figure of 0 (the coin has no rate yet)
|
|
177
|
+
* is refused as `unavailable`, which is what the server would answer. */
|
|
178
|
+
play({ mode, bet, amount }) {
|
|
179
|
+
if (!(typeof amount === 'number' && Number.isFinite(amount) && amount > 0)) {
|
|
180
|
+
return Promise.reject(new EngineError(ERRORS.LIMITS, 'a stake is required: a round is never played for free'));
|
|
181
|
+
}
|
|
182
|
+
const most = maxStakeFor(this.config.limits, mode, bet);
|
|
183
|
+
if (most === 0) {
|
|
184
|
+
return Promise.reject(new EngineError(ERRORS.UNAVAILABLE, 'nothing can be staked on this bet right now: the coin has no rate to price the maximum win in; try again shortly'));
|
|
185
|
+
}
|
|
186
|
+
// Compared at the wallet's precision, so a float's noise past it never
|
|
187
|
+
// refuses a stake the field shows as exactly the cap.
|
|
188
|
+
if (most !== null && Number(amount.toFixed(this.money.walletDecimals)) > most) {
|
|
189
|
+
const named = this.config.limits.maxStake[mode][bet]; // as the server wrote it (a stand may hand a number)
|
|
190
|
+
return Promise.reject(new EngineError(ERRORS.LIMITS, `the most this bet takes is ${this.money.format(most)} ${this.money.unit}: one round pays no more than the maximum win`, 0, undefined, String(named)));
|
|
191
|
+
}
|
|
192
|
+
return this.#money(REQUESTS.PLAY, { mode, bet, amount }).then((answer) => {
|
|
193
|
+
this.#afterMoney(answer, amount);
|
|
194
|
+
return answer;
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** Moves an open ladder round: 'advance' or 'cashout' — either from the player's click
|
|
199
|
+
* (the shell refuses both with `needs-click` otherwise). Pass the round as
|
|
200
|
+
* you last had it: its step goes with the request, so a stale action
|
|
201
|
+
* (another tab, a retry) is refused with the round as it stands. */
|
|
202
|
+
action(round, action) {
|
|
203
|
+
return this.#money(REQUESTS.ACTION, { roundId: round.id, step: round.step, action }).then((answer) => {
|
|
204
|
+
this.#afterMoney(answer, 0);
|
|
205
|
+
return answer;
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** Reads one of the player's settled rounds as the game may see it,
|
|
210
|
+
* without its seeds; the shell's dialog (openFairness) holds the receipt. */
|
|
211
|
+
round(roundId) {
|
|
212
|
+
return this.#ask(REQUESTS.ROUND, { roundId });
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Subscribes to a shell event (balance, control, pause, resume, ended, autoplay). */
|
|
216
|
+
on(type, fn) {
|
|
217
|
+
if (!Object.values(SHELL_EVENTS).includes(type) || type === SHELL_EVENTS.INIT) {
|
|
218
|
+
throw new TypeError(`unknown engine event ${type}`);
|
|
219
|
+
}
|
|
220
|
+
if (!this.#listeners.has(type)) this.#listeners.set(type, new Set());
|
|
221
|
+
this.#listeners.get(type).add(fn);
|
|
222
|
+
return () => this.#listeners.get(type).delete(fn);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** The first frame is on screen: the shell drops its loader. */
|
|
226
|
+
ready() {
|
|
227
|
+
this.#tell(GAME_EVENTS.READY, {});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** Progress of an asset group, for the shell's loader and telemetry. */
|
|
231
|
+
loading(group, loaded, total) {
|
|
232
|
+
this.#tell(GAME_EVENTS.LOADING, { group, loaded, total });
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** The round's outcome is on screen: the site's header balance follows
|
|
236
|
+
* now. Call it when your reveal animation lands; if you never do, the
|
|
237
|
+
* shell moves the header at its own deadline. */
|
|
238
|
+
revealed(roundId) {
|
|
239
|
+
if (this.#reveal && this.#reveal.roundId === roundId) {
|
|
240
|
+
this.#setBalance(this.#reveal.balance);
|
|
241
|
+
this.#reveal = null;
|
|
242
|
+
}
|
|
243
|
+
this.#tell(GAME_EVENTS.REVEALED, { roundId });
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** Opens the shell's fairness dialog on a round. */
|
|
247
|
+
openFairness(roundId) {
|
|
248
|
+
this.#tell(GAME_EVENTS.FAIRNESS, { roundId });
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** Reports a game error: the shell logs it in the player's console and the engine counts
|
|
252
|
+
* one error for the game's day. The count reaches the studio; the text never does. */
|
|
253
|
+
reportError(message) {
|
|
254
|
+
this.#tell(GAME_EVENTS.ERROR, { message: String(message).slice(0, 500) });
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// The header's two steps: the stake leaves now; a settled round's result
|
|
258
|
+
// waits for its reveal, an open round's balance is already the truth.
|
|
259
|
+
#afterMoney(answer, stake) {
|
|
260
|
+
const known = answer && typeof answer.balance === 'number' && answer.balance >= 0 ? answer.balance : null;
|
|
261
|
+
const round = answer && answer.round;
|
|
262
|
+
if (known === null) return;
|
|
263
|
+
// A reveal the game never reported is overtaken by the next round.
|
|
264
|
+
if (this.#reveal) this.#setBalance(this.#reveal.balance);
|
|
265
|
+
this.#reveal = null;
|
|
266
|
+
if (round && round.state !== 'open' && typeof round.id === 'string') {
|
|
267
|
+
this.#reveal = { roundId: round.id, balance: known };
|
|
268
|
+
const dp = this.money.walletDecimals;
|
|
269
|
+
if (this.#balance !== null && stake > 0) this.#setBalance(Math.max(0, Number((this.#balance - stake).toFixed(dp))));
|
|
270
|
+
} else {
|
|
271
|
+
this.#setBalance(known);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
#setBalance(balance) {
|
|
276
|
+
if (balance === this.#balance) return;
|
|
277
|
+
this.#balance = balance;
|
|
278
|
+
this.#emit(SHELL_EVENTS.BALANCE, { balance });
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// One bet or action at a time: the server would refuse the second one
|
|
282
|
+
// anyway (it holds the player's lock), and saying so here is clearer.
|
|
283
|
+
#money(type, body) {
|
|
284
|
+
if (this.#moneyInFlight) {
|
|
285
|
+
return Promise.reject(new EngineError(ERRORS.BUSY, 'another bet is still with the server'));
|
|
286
|
+
}
|
|
287
|
+
this.#moneyInFlight = true;
|
|
288
|
+
return this.#ask(type, body).finally(() => {
|
|
289
|
+
this.#moneyInFlight = false;
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
#ask(type, body) {
|
|
294
|
+
const id = `r${++this.#seq}`;
|
|
295
|
+
return new Promise((resolve, reject) => {
|
|
296
|
+
const timer = setTimeout(() => {
|
|
297
|
+
this.#pending.delete(id);
|
|
298
|
+
reject(new EngineError(ERRORS.SERVER, 'the engine shell did not answer'));
|
|
299
|
+
}, ANSWER_TIMEOUT_MS);
|
|
300
|
+
this.#pending.set(id, { resolve, reject, timer });
|
|
301
|
+
this.#host.postMessage(request(id, type, body), '*');
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
#settle(msg) {
|
|
306
|
+
const p = this.#pending.get(msg.id);
|
|
307
|
+
if (!p) return;
|
|
308
|
+
this.#pending.delete(msg.id);
|
|
309
|
+
clearTimeout(p.timer);
|
|
310
|
+
if (msg.ok) p.resolve(msg.body);
|
|
311
|
+
else p.reject(new EngineError(msg.error.code, msg.error.message, msg.error.retryAfterMs, msg.error.round, msg.error.maxStake));
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
#emit(type, body) {
|
|
315
|
+
for (const fn of this.#listeners.get(type) ?? []) {
|
|
316
|
+
try {
|
|
317
|
+
fn(body);
|
|
318
|
+
} catch (err) {
|
|
319
|
+
this.reportError(err && err.message ? err.message : err);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// The shell's window is the only reader. '*' is the target because the
|
|
325
|
+
// game cannot name the shell's origin from inside an opaque sandbox; the
|
|
326
|
+
// message carries nothing secret.
|
|
327
|
+
#tell(type, body) {
|
|
328
|
+
this.#host.postMessage(event(type, body), '*');
|
|
329
|
+
}
|
|
330
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Why a stake cannot be played now, as the bet field (bet-field.js) and the
|
|
3
|
+
* engine panel (ui.js) decide it before anything reaches the shell. One pure
|
|
4
|
+
* rule, so it is tested without a browser:
|
|
5
|
+
*
|
|
6
|
+
* 'balance' over the balance the shell last reported
|
|
7
|
+
* 'min' no stake at all, or under the coin's minimum — an engine round
|
|
8
|
+
* is never played for free (SPEC §2 item 5): a round costs a
|
|
9
|
+
* nonce of the player's seed pair, so the server refuses a stake
|
|
10
|
+
* of 0 and the field never offers one
|
|
11
|
+
* 'max' over the coin's maximum
|
|
12
|
+
* 'maxWin' over the most this bet may take under the maximum win per
|
|
13
|
+
* round (SPEC §3.1): stake × the bet's top multiplier would pass
|
|
14
|
+
* the cap, so the server would refuse the bet before it starts.
|
|
15
|
+
* A ceiling of 0 (the server names it while the coin has no USD
|
|
16
|
+
* rate) refuses every stake, the empty field included: nothing
|
|
17
|
+
* can be staked on the bet until the rate is back
|
|
18
|
+
* null playable
|
|
19
|
+
*
|
|
20
|
+
* Of the coin's maximum and the bet's own ceiling, the lower is the one named:
|
|
21
|
+
* the server's `maxStake` never passes the coin's max bet, so a player who
|
|
22
|
+
* types more than both hears the figure the bet can take, not one it never
|
|
23
|
+
* could. `limits` is engine.config.limits ({min, max} in wallet units; either
|
|
24
|
+
* may be missing, as in the sandbox); `balance` is engine.balance (null while
|
|
25
|
+
* unknown); `maxStake` is the selected bet's own ceiling in wallet units
|
|
26
|
+
* (protocol.js maxStakeFor), or null when the session names none for it.
|
|
27
|
+
*/
|
|
28
|
+
export function stakeReason(amount, limits, balance, maxStake = null) {
|
|
29
|
+
const a = Number(amount);
|
|
30
|
+
const { min, max } = limits || {};
|
|
31
|
+
const coinMax = typeof max === 'number' && max > 0 ? max : null;
|
|
32
|
+
const betMax = typeof maxStake === 'number' && maxStake >= 0 ? maxStake : null;
|
|
33
|
+
if (betMax === 0) return 'maxWin';
|
|
34
|
+
if (!(a > 0) || !Number.isFinite(a)) return 'min';
|
|
35
|
+
if (balance !== null && balance !== undefined && a > balance) return 'balance';
|
|
36
|
+
if (typeof min === 'number' && a < min) return 'min';
|
|
37
|
+
// The lower ceiling first; a bet's ceiling above the coin's (a stand) is
|
|
38
|
+
// never reached before the coin's is.
|
|
39
|
+
if (betMax !== null && (coinMax === null || betMax <= coinMax) && a > betMax) return 'maxWin';
|
|
40
|
+
if (coinMax !== null && a > coinMax) return 'max';
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The most the field offers (its max chip): the coin's maximum, the selected
|
|
46
|
+
* bet's own ceiling under the maximum win and the balance, whichever is
|
|
47
|
+
* least; Infinity when none is known. A ceiling of 0 is 0: nothing to offer.
|
|
48
|
+
*/
|
|
49
|
+
export function stakeCeiling(limits, balance, maxStake = null) {
|
|
50
|
+
const { max } = limits || {};
|
|
51
|
+
return Math.min(
|
|
52
|
+
typeof max === 'number' && max > 0 ? max : Infinity,
|
|
53
|
+
typeof maxStake === 'number' && maxStake >= 0 ? maxStake : Infinity,
|
|
54
|
+
balance ?? Infinity,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The least a player can stake: the coin's minimum when the server named
|
|
60
|
+
* one, else one unit of the wallet's precision — what the "minimum bet"
|
|
61
|
+
* message says when the field is empty.
|
|
62
|
+
*/
|
|
63
|
+
export function leastStake(limits, walletDecimals) {
|
|
64
|
+
const { min } = limits || {};
|
|
65
|
+
if (typeof min === 'number' && min > 0) return min;
|
|
66
|
+
const dp = Number.isInteger(walletDecimals) && walletDecimals >= 0 ? walletDecimals : 8;
|
|
67
|
+
return 10 ** -dp;
|
|
68
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Engine, Round } from './sdk.js';
|
|
2
|
+
|
|
3
|
+
export interface Game {
|
|
4
|
+
readonly engine: Engine;
|
|
5
|
+
/** The stake field (<hb-bet-amount>). `reason` says why the stake cannot be played: over the balance,
|
|
6
|
+
* under the coin's minimum, over its maximum, or over the most the selected bet takes under the
|
|
7
|
+
* maximum win per round (`maxWin`); the panel keeps `bet` on the selection. */
|
|
8
|
+
readonly field: HTMLElement & {
|
|
9
|
+
amount: number;
|
|
10
|
+
reason: { code: 'balance' | 'min' | 'max' | 'maxWin'; message: string } | null;
|
|
11
|
+
disabled: boolean;
|
|
12
|
+
bet: { mode: string; bet: string } | null;
|
|
13
|
+
/** The most the selected bet takes, in wallet units; null when the session names none;
|
|
14
|
+
* 0 when nothing can be staked for now (the coin has no rate). */
|
|
15
|
+
readonly maxStake: number | null;
|
|
16
|
+
};
|
|
17
|
+
mode: string;
|
|
18
|
+
bet: string;
|
|
19
|
+
/** The ladder round in play, if any. */
|
|
20
|
+
readonly open: Round | null;
|
|
21
|
+
/** Climb or cash out the open ladder round, as the panel's buttons do (a zone that drives its own ladder). */
|
|
22
|
+
advance(): Promise<Round | null>;
|
|
23
|
+
cashout(): Promise<Round | null>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** The engine panel around your zone: modes, bets, stake, Bet, Auto, Next / Cash out. */
|
|
27
|
+
export function mount(el: HTMLElement, hooks: {
|
|
28
|
+
/** A table round is drawn: show it; resolve when it is on screen. */
|
|
29
|
+
reveal?: (round: Round, zone: HTMLElement) => Promise<void> | void;
|
|
30
|
+
/** A ladder round moved (bet, step, cash-out, bust). */
|
|
31
|
+
step?: (round: Round, zone: HTMLElement) => Promise<void> | void;
|
|
32
|
+
/** false: no Next button; the zone climbs with game.advance(). */
|
|
33
|
+
next?: boolean;
|
|
34
|
+
/** Mode and bet names by language, then id: { es: { base: 'Tirada' } } (else the math's labels). */
|
|
35
|
+
labels?: Record<string, Record<string, string>>;
|
|
36
|
+
}): Promise<Game>;
|