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,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The maths kit: a game's outcomes as weighted rows, its RTP exactly, and the
|
|
3
|
+
* pack the engine verifies (SPEC §1). A row is { weight, data, pay } where
|
|
4
|
+
* pay is { betId: multBp } (10000 = 1×, the total stake back) and data is
|
|
5
|
+
* what the bundle receives to show the round.
|
|
6
|
+
*
|
|
7
|
+
* import { writePack, fit } from './pack.mjs';
|
|
8
|
+
* const rows = fit(simulated, 9600); // exact 96.00% (balance() when no row pays nothing)
|
|
9
|
+
* writePack('math', { game: 'my-slot', rtpBp: 9600, modes: [{ id: 'base', rows, bets: ['spin'] }] });
|
|
10
|
+
*
|
|
11
|
+
* Every number here is an integer: weights are BigInt, RTP is a fraction.
|
|
12
|
+
*/
|
|
13
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
14
|
+
import { join } from 'node:path';
|
|
15
|
+
|
|
16
|
+
const MAX_SUM = 1n << 63n;
|
|
17
|
+
|
|
18
|
+
// A small seeded generator for building tables (never for play: the engine draws).
|
|
19
|
+
export function rng(seed) {
|
|
20
|
+
let a = seed >>> 0;
|
|
21
|
+
const next = () => {
|
|
22
|
+
a = (a + 0x6d2b79f5) >>> 0;
|
|
23
|
+
let t = a;
|
|
24
|
+
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
25
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
26
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
27
|
+
};
|
|
28
|
+
next.int = (n) => Math.floor(next() * n);
|
|
29
|
+
return next;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function binomial(n, k) {
|
|
33
|
+
let r = 1n;
|
|
34
|
+
for (let i = 1n; i <= BigInt(k); i++) r = (r * BigInt(n - k) + r * i) / i;
|
|
35
|
+
return r;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const gcd = (a, b) => { while (b) [a, b] = [b, a % b]; return a < 0n ? -a : a; };
|
|
39
|
+
|
|
40
|
+
/** RTP of one bet over rows, exactly: { num, den } in basis points, and a 4-decimal string. */
|
|
41
|
+
export function rtp(rows, bet) {
|
|
42
|
+
let num = 0n, den = 0n;
|
|
43
|
+
for (const r of rows) { den += r.weight; num += r.weight * BigInt(r.pay[bet] || 0); }
|
|
44
|
+
return { num, den, bp: bp4(num, den) };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const bp4 = (num, den) => {
|
|
48
|
+
const x = (num * 10000n) / den; // bp × 10^4, truncated as the verifier reports it
|
|
49
|
+
return `${x / 10000n}.${String(x % 10000n).padStart(4, '0')}`;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Weights so that one bet's RTP is exactly targetBp: the paying rows keep
|
|
54
|
+
* their proportions (all multiplied by one integer), the rows that pay
|
|
55
|
+
* nothing share what is left in their own proportions. Needs rows that pay
|
|
56
|
+
* nothing, and paying rows that alone return more than the target.
|
|
57
|
+
*/
|
|
58
|
+
export function fit(rows, targetBp, bet = Object.keys(rows[0].pay)[0]) {
|
|
59
|
+
const T = BigInt(targetBp);
|
|
60
|
+
const wins = rows.filter((r) => r.pay[bet] > 0), zeros = rows.filter((r) => !(r.pay[bet] > 0));
|
|
61
|
+
if (!zeros.length) throw new Error('fit: no row pays nothing');
|
|
62
|
+
const S = wins.reduce((s, r) => s + r.weight * (BigInt(r.pay[bet]) - T), 0n); // what the wins return above T
|
|
63
|
+
if (S <= 0n) throw new Error(`fit: the paying rows alone return less than ${targetBp / 100}%`);
|
|
64
|
+
const k = T / gcd(S, T); // so that k·S is a multiple of T
|
|
65
|
+
const Z = (k * S) / T; // the weight the zero rows share
|
|
66
|
+
const zw = zeros.reduce((s, r) => s + r.weight, 0n);
|
|
67
|
+
let given = 0n;
|
|
68
|
+
const out = rows.map((r) => {
|
|
69
|
+
if (r.pay[bet] > 0) return { ...r, weight: r.weight * k };
|
|
70
|
+
const w = (Z * r.weight) / zw;
|
|
71
|
+
given += w;
|
|
72
|
+
return { ...r, weight: w };
|
|
73
|
+
});
|
|
74
|
+
// The rounding remainder goes to the first zero row; a zero weight is dropped.
|
|
75
|
+
const first = out.find((r) => !(r.pay[bet] > 0));
|
|
76
|
+
first.weight += Z - given;
|
|
77
|
+
const kept = out.filter((r) => r.weight > 0n);
|
|
78
|
+
const check = rtp(kept, bet);
|
|
79
|
+
if (check.num !== T * check.den) throw new Error('fit: not exact (a bug)');
|
|
80
|
+
return kept;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Weights so that one bet's RTP is exactly targetBp when every row pays
|
|
85
|
+
* something (a bonus buy, plinko): rows paying more than the target are
|
|
86
|
+
* all multiplied by one integer, rows paying less by another, so each
|
|
87
|
+
* group keeps its own proportions and the two balance at the target.
|
|
88
|
+
*/
|
|
89
|
+
export function balance(rows, targetBp, bet = Object.keys(rows[0].pay)[0]) {
|
|
90
|
+
const T = BigInt(targetBp);
|
|
91
|
+
const over = rows.reduce((s, r) => s + (BigInt(r.pay[bet] || 0) > T ? r.weight * (BigInt(r.pay[bet]) - T) : 0n), 0n);
|
|
92
|
+
const under = rows.reduce((s, r) => s + (BigInt(r.pay[bet] || 0) < T ? r.weight * (T - BigInt(r.pay[bet] || 0)) : 0n), 0n);
|
|
93
|
+
if (!over || !under) throw new Error('balance: needs rows on both sides of the target');
|
|
94
|
+
const g = gcd(over, under);
|
|
95
|
+
const out = rows.map((r) => ({ ...r, weight: r.weight * (BigInt(r.pay[bet] || 0) > T ? under / g : BigInt(r.pay[bet] || 0) < T ? over / g : 1n) }));
|
|
96
|
+
const check = rtp(out, bet);
|
|
97
|
+
if (check.num !== T * check.den) throw new Error('balance: not exact (a bug)');
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Rows with the same data are one row with the sum of their weights. */
|
|
102
|
+
export function merge(rows) {
|
|
103
|
+
const by = new Map();
|
|
104
|
+
for (const r of rows) {
|
|
105
|
+
const key = JSON.stringify(r.data);
|
|
106
|
+
const x = by.get(key);
|
|
107
|
+
if (x) x.weight += r.weight;
|
|
108
|
+
else by.set(key, { ...r, weight: r.weight });
|
|
109
|
+
}
|
|
110
|
+
return [...by.values()];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Inclusive [lo, hi] ranges of sorted indexes.
|
|
114
|
+
function ranges(ids) {
|
|
115
|
+
const out = [];
|
|
116
|
+
for (const i of ids) {
|
|
117
|
+
const last = out.at(-1);
|
|
118
|
+
if (last && last[1] === i - 1) last[1] = i;
|
|
119
|
+
else out.push([i, i]);
|
|
120
|
+
}
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Writes manifest.json and one table per mode. A mode is { id, label?, rows,
|
|
126
|
+
* bets: [id | {id, label}] }; its rows are sorted by what the first bet pays,
|
|
127
|
+
* so each pay tier is one range. Prints every bet's exact RTP, hit rate and
|
|
128
|
+
* largest multiplier; returns them.
|
|
129
|
+
*/
|
|
130
|
+
export function writePack(dir, { game, rtpBp, modes }) {
|
|
131
|
+
mkdirSync(dir, { recursive: true });
|
|
132
|
+
const manifest = { format: 'house-engine-math/1', game, rtpBp, modes: [] };
|
|
133
|
+
const report = [];
|
|
134
|
+
for (const mode of modes) {
|
|
135
|
+
const bets = mode.bets.map((b) => (typeof b === 'string' ? { id: b } : b));
|
|
136
|
+
const first = bets[0].id;
|
|
137
|
+
const rows = [...mode.rows].sort((a, b) => (a.pay[first] || 0) - (b.pay[first] || 0));
|
|
138
|
+
if (rows.length < 2 || rows.length > 1_000_000) throw new Error(`${mode.id}: 2 to 1,000,000 rows, not ${rows.length}`);
|
|
139
|
+
const sum = rows.reduce((s, r) => s + r.weight, 0n);
|
|
140
|
+
if (sum > MAX_SUM) throw new Error(`${mode.id}: weights sum to more than 2^63`);
|
|
141
|
+
const lines = rows.map((r) => {
|
|
142
|
+
if (r.weight <= 0n) throw new Error(`${mode.id}: a weight is not positive`);
|
|
143
|
+
const json = r.data === undefined ? '' : JSON.stringify(r.data);
|
|
144
|
+
if (json.length > 4096) throw new Error(`${mode.id}: a row's data is over 4096 bytes`);
|
|
145
|
+
return json ? `${r.weight}\t${json}` : String(r.weight);
|
|
146
|
+
});
|
|
147
|
+
const table = `${game}-${mode.id}.tsv`;
|
|
148
|
+
writeFileSync(join(dir, table), lines.join('\n') + '\n');
|
|
149
|
+
manifest.modes.push({
|
|
150
|
+
id: mode.id, ...(mode.label ? { label: mode.label } : {}), kind: 'table', table,
|
|
151
|
+
bets: bets.map((b) => {
|
|
152
|
+
const tiers = new Map();
|
|
153
|
+
rows.forEach((r, i) => { const m = r.pay[b.id] || 0; if (m > 0) (tiers.get(m) || tiers.set(m, []).get(m)).push(i); });
|
|
154
|
+
const { num, den, bp } = rtp(rows, b.id);
|
|
155
|
+
const hit = rows.reduce((s, r) => s + (r.pay[b.id] > 0 ? r.weight : 0n), 0n);
|
|
156
|
+
const max = Math.max(...tiers.keys());
|
|
157
|
+
report.push({ mode: mode.id, bet: b.id, rtp: bp, hit: bp4(hit * 10000n, den), max: max / 10000, rows: rows.length, exact: num % den === 0n });
|
|
158
|
+
return { id: b.id, ...(b.label ? { label: b.label } : {}), tiers: [...tiers].map(([multBp, ids]) => ({ outcomes: ranges(ids), multBp })) };
|
|
159
|
+
}),
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
writeFileSync(join(dir, 'manifest.json'), JSON.stringify(manifest) + '\n');
|
|
163
|
+
for (const r of report) console.log(`${r.mode}/${r.bet}: RTP ${r.rtp} bp hit ${r.hit} bp max ${r.max}× rows ${r.rows}`);
|
|
164
|
+
return report;
|
|
165
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@housegames/engine-math",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "The maths kit: outcomes as weighted rows, exact RTP, an exact fit to a target, the pack the engine verifies; a video slot model (reels, lines, wilds, scatters, free spins) and a tumble model (scatter pays, tumbles, multipliers).",
|
|
7
|
+
"exports": { "./pack.mjs": "./pack.mjs", "./slot.mjs": "./slot.mjs", "./tumble.mjs": "./tumble.mjs" }
|
|
8
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A video slot for the maths kit: reels, paylines, a wild, a scatter that
|
|
3
|
+
* starts free spins (with a multiplier, retriggers), and a win cap. It plays
|
|
4
|
+
* rounds with a seeded generator to make the table's rows ("books"): every
|
|
5
|
+
* row is a whole round, base spin and free spins, with what it pays. fit()
|
|
6
|
+
* then makes the table's RTP exact; the table is the game.
|
|
7
|
+
*
|
|
8
|
+
* Pays are in basis points of the total stake (10000 = 1×) for 3, 4, 5 of a
|
|
9
|
+
* kind on a line, left to right; scatters pay anywhere, times the stake.
|
|
10
|
+
*
|
|
11
|
+
* A row's data, for the bundle: { b: spin, f: [spin…], x: free multiplier }
|
|
12
|
+
* where a spin is [stop0…stopN, winBp, lineMask, scatterCount].
|
|
13
|
+
*/
|
|
14
|
+
export function slot(cfg) {
|
|
15
|
+
const { reels, freeReels = reels, rows, lines, pays, wild, scatter, maxWinBp } = cfg;
|
|
16
|
+
const n = reels.length;
|
|
17
|
+
|
|
18
|
+
const grid = (strips, stops) => stops.map((s, r) => Array.from({ length: rows }, (_, y) => strips[r][(s + y) % strips[r].length]));
|
|
19
|
+
|
|
20
|
+
// One spin's wins: each line pays its best run from the left (wilds stand in).
|
|
21
|
+
function evaluate(strips, stops) {
|
|
22
|
+
const g = grid(strips, stops);
|
|
23
|
+
let win = 0, mask = 0;
|
|
24
|
+
lines.forEach((line, li) => {
|
|
25
|
+
const cells = line.map((y, r) => g[r][y]);
|
|
26
|
+
const sym = cells.find((c) => c !== wild) ?? wild;
|
|
27
|
+
if (sym === scatter.symbol) return;
|
|
28
|
+
let count = 0;
|
|
29
|
+
while (count < n && (cells[count] === sym || cells[count] === wild)) count++;
|
|
30
|
+
const pay = (pays[sym] || {})[count] || 0;
|
|
31
|
+
// A run of wilds alone pays as wilds when that is more.
|
|
32
|
+
let wc = 0;
|
|
33
|
+
while (wc < n && cells[wc] === wild) wc++;
|
|
34
|
+
const best = Math.max(pay, (pays[wild] || {})[wc] || 0);
|
|
35
|
+
if (best > 0) { win += best; mask |= 1 << li; }
|
|
36
|
+
});
|
|
37
|
+
const scatters = g.flat().filter((c) => c === scatter.symbol).length;
|
|
38
|
+
win += (scatter.pays[scatters] || 0);
|
|
39
|
+
return { win, mask, scatters };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const spin = (strips, next) => strips.map((s) => next.int(s.length));
|
|
43
|
+
|
|
44
|
+
/** One whole round: the row's data and what it pays (bp of the stake).
|
|
45
|
+
* A bought round is the free spins alone, as three scatters start them. */
|
|
46
|
+
function round(next, buy = false) {
|
|
47
|
+
const stops = buy ? null : spin(reels, next);
|
|
48
|
+
const base = buy ? { win: 0, scatters: 3 } : evaluate(reels, stops);
|
|
49
|
+
const data = buy ? { buy: 1 } : { b: [...stops, base.win, base.mask, base.scatters] };
|
|
50
|
+
let total = base.win;
|
|
51
|
+
let left = scatter.spins[base.scatters] || 0;
|
|
52
|
+
if (left) {
|
|
53
|
+
data.f = [];
|
|
54
|
+
data.x = scatter.multiplier;
|
|
55
|
+
while (left > 0 && data.f.length < scatter.cap) {
|
|
56
|
+
left--;
|
|
57
|
+
const s = spin(freeReels, next);
|
|
58
|
+
const e = evaluate(freeReels, s);
|
|
59
|
+
data.f.push([...s, e.win * scatter.multiplier, e.mask, e.scatters]);
|
|
60
|
+
total += e.win * scatter.multiplier;
|
|
61
|
+
left += scatter.retrigger[e.scatters] || 0;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (total > maxWinBp) { total = maxWinBp; data.cap = 1; }
|
|
65
|
+
return { data, pay: total };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return { evaluate, round, grid };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Plays `count` rounds (bought ones with `buy`) and keeps them as rows: every paying round, and up to
|
|
73
|
+
* `losing` different losing rounds that share the weight of every losing
|
|
74
|
+
* round played (so the rows keep the played RTP and hit rate before fit).
|
|
75
|
+
*/
|
|
76
|
+
export function books(game, next, count, losing = 4000, buy = false) {
|
|
77
|
+
const wins = new Map(), lose = new Map();
|
|
78
|
+
let lost = 0n;
|
|
79
|
+
for (let i = 0; i < count; i++) {
|
|
80
|
+
const { data, pay } = game.round(next, buy);
|
|
81
|
+
if (pay <= 0) {
|
|
82
|
+
lost++;
|
|
83
|
+
if (lose.size < losing) lose.set(JSON.stringify(data), { weight: 0n, data, pay: { spin: 0 } });
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const key = JSON.stringify(data);
|
|
87
|
+
const r = wins.get(key);
|
|
88
|
+
if (r) r.weight += 1n;
|
|
89
|
+
else wins.set(key, { weight: 1n, data, pay: { spin: pay } });
|
|
90
|
+
}
|
|
91
|
+
const zeros = [...lose.values()];
|
|
92
|
+
zeros.forEach((r, i) => { r.weight = lost / BigInt(zeros.length) + (BigInt(i) < lost % BigInt(zeros.length) ? 1n : 0n); });
|
|
93
|
+
return [...zeros.filter((r) => r.weight > 0n), ...wins.values()];
|
|
94
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A tumbling, scatter-pays slot for the maths kit: a cols × rows grid, every
|
|
3
|
+
* cell drawn by symbol weight; a symbol pays when enough of it is anywhere
|
|
4
|
+
* on the grid; winners vanish, the rest fall, new symbols drop in, until
|
|
5
|
+
* nothing pays. Scatters start free spins, where multiplier symbols land
|
|
6
|
+
* and multiply the spin's win. Like slot.mjs, it plays whole rounds into
|
|
7
|
+
* rows; fit() then makes the RTP exact.
|
|
8
|
+
*
|
|
9
|
+
* Symbols are single characters. Pays are basis points of the stake by
|
|
10
|
+
* count: { A: [[8, 2000], [10, 5000], [12, 10000]] } pays the highest band
|
|
11
|
+
* the count reaches.
|
|
12
|
+
*
|
|
13
|
+
* A row's data, for the page:
|
|
14
|
+
* { b: spin, f: [spin…] } spin = [grid, [tumble…], multipliers?]
|
|
15
|
+
* grid the first grid, column by column, top to bottom ("ABCDE…")
|
|
16
|
+
* tumble [winners, winBp, fill0, fill1, …]: the symbols that paid, what
|
|
17
|
+
* they paid, and per column the new symbols that drop in (top first)
|
|
18
|
+
* multipliers free spins only: the values of the multiplier symbols on the
|
|
19
|
+
* last grid, summed onto the spin's win when it won
|
|
20
|
+
*/
|
|
21
|
+
export function tumble(cfg) {
|
|
22
|
+
const { cols, rows, weights, freeWeights = weights, pays, scatter, bomb, maxWinBp } = cfg;
|
|
23
|
+
const pick = (w, next) => {
|
|
24
|
+
const total = w.reduce((s, [, n]) => s + n, 0);
|
|
25
|
+
let x = next() * total;
|
|
26
|
+
for (const [s, n] of w) if ((x -= n) < 0) return s;
|
|
27
|
+
return w[w.length - 1][0];
|
|
28
|
+
};
|
|
29
|
+
const table = (o) => Object.entries(o);
|
|
30
|
+
const base = table(weights), free = table(freeWeights);
|
|
31
|
+
const counts = Object.keys(scatter.spins).map(Number), least = Math.min(...counts), most = Math.max(...counts);
|
|
32
|
+
|
|
33
|
+
// One spin: the grid, its tumbles, and (free spins) its multipliers.
|
|
34
|
+
function spin(next, inFree) {
|
|
35
|
+
const w = inFree ? free : base;
|
|
36
|
+
const draw = () => {
|
|
37
|
+
const s = pick(w, next);
|
|
38
|
+
return s === bomb?.symbol && !inFree ? pick(base.filter(([x]) => x !== bomb.symbol), next) : s;
|
|
39
|
+
};
|
|
40
|
+
let grid = Array.from({ length: cols }, () => Array.from({ length: rows }, draw));
|
|
41
|
+
const first = grid.map((c) => c.join('')).join('');
|
|
42
|
+
const tumbles = [];
|
|
43
|
+
let win = 0;
|
|
44
|
+
for (;;) {
|
|
45
|
+
const counts = {};
|
|
46
|
+
for (const c of grid) for (const s of c) counts[s] = (counts[s] || 0) + 1;
|
|
47
|
+
let paid = '', bp = 0;
|
|
48
|
+
for (const [s, bands] of Object.entries(pays)) {
|
|
49
|
+
const band = bands.filter(([n]) => counts[s] >= n).at(-1);
|
|
50
|
+
if (band) { paid += s; bp += band[1]; }
|
|
51
|
+
}
|
|
52
|
+
if (!paid) break;
|
|
53
|
+
win += bp;
|
|
54
|
+
const fills = grid.map((c) => {
|
|
55
|
+
const kept = c.filter((s) => !paid.includes(s));
|
|
56
|
+
const add = Array.from({ length: rows - kept.length }, draw);
|
|
57
|
+
c.splice(0, rows, ...add, ...kept); // new symbols on top
|
|
58
|
+
return add.join('');
|
|
59
|
+
});
|
|
60
|
+
tumbles.push([paid, bp, ...fills]);
|
|
61
|
+
}
|
|
62
|
+
const out = [first, tumbles];
|
|
63
|
+
const scatters = grid.flat().filter((s) => s === scatter.symbol).length; // on the last grid
|
|
64
|
+
let total = win;
|
|
65
|
+
if (inFree && bomb) {
|
|
66
|
+
const bombs = grid.flat().filter((s) => s === bomb.symbol).map(() => pick(bomb.values, next));
|
|
67
|
+
if (bombs.length) out.push(bombs);
|
|
68
|
+
if (win && bombs.length) total = win * bombs.reduce((a, b) => a + b, 0);
|
|
69
|
+
}
|
|
70
|
+
return { out, total, scatters };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** One whole round: the row's data and what it pays (bp of the stake). */
|
|
74
|
+
function round(next) {
|
|
75
|
+
const b = spin(next, false);
|
|
76
|
+
const data = { b: b.out };
|
|
77
|
+
let total = b.total + (scatter.pays[Math.min(b.scatters, most)] || 0);
|
|
78
|
+
let left = b.scatters >= least ? scatter.spins[Math.min(b.scatters, most)] : 0;
|
|
79
|
+
if (left) {
|
|
80
|
+
data.f = [];
|
|
81
|
+
while (left > 0 && data.f.length < scatter.cap) {
|
|
82
|
+
left--;
|
|
83
|
+
const f = spin(next, true);
|
|
84
|
+
data.f.push(f.out);
|
|
85
|
+
total += f.total;
|
|
86
|
+
if (f.scatters >= scatter.retrigger.count) left += scatter.retrigger.spins;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (total > maxWinBp) { total = maxWinBp; data.cap = 1; }
|
|
90
|
+
return { data, pay: total };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return { round };
|
|
94
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Wheel — three risk modes on one 24-segment wheel
|
|
2
|
+
|
|
3
|
+
Low, Medium, High: three tables, one `spin` bet each, one draw. The panel
|
|
4
|
+
is the engine's (`/engine-sdk/ui.js`); `bundle/game.js` draws the wheel and
|
|
5
|
+
lands it on `round.data[0].seg`.
|
|
6
|
+
|
|
7
|
+
Exact RTP (Σ multBp over 24 segments / 24):
|
|
8
|
+
|
|
9
|
+
low 1×2.00 4×1.50 13×1.20 6×0 → 236000 / 24 = 9833.33 bp (98.3333%)
|
|
10
|
+
medium 1×5.00 2×2.00 4×1.50 7×1.20 10×0 → 234000 / 24 = 9750.00 bp (97.5000%)
|
|
11
|
+
high 1×15.00 1×2.00 2×1.50 3×1.20 17×0 → 236000 / 24 = 9833.33 bp (98.3333%)
|
|
12
|
+
|
|
13
|
+
npx hbengine verify . exact RTP per bet, the mathHash
|
|
14
|
+
npx hbengine init my-wheel --template wheel --slug my-wheel # make it yours, then: verify, dev, upload
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "groups": { "boot": { "files": ["index.html", "game.js", "game.css"] } } }
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
body { margin: 0; background: #08141a; }
|
|
2
|
+
.stage { position: relative; display: grid; place-items: center; padding: 32px 16px; }
|
|
3
|
+
.wheel { width: min(360px, 86vw); aspect-ratio: 1; }
|
|
4
|
+
.wheel svg { width: 100%; height: 100%; display: block; filter: drop-shadow(0 12px 40px rgba(0, 0, 0, .55)); }
|
|
5
|
+
.wheel .disc { transform-origin: 100px 100px; }
|
|
6
|
+
.face { position: absolute; inset: 0; display: grid; place-items: center; font: 700 36px/1 'Figtree', system-ui, sans-serif; color: #F9F9FB; text-shadow: 0 2px 8px #041015; pointer-events: none; letter-spacing: -0.5px; }
|
|
7
|
+
.face.lose { color: #8F96A8; }
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Wheel: 24 segments, three risk modes, one spin. The panel is the engine's
|
|
2
|
+
// (/engine-sdk/ui.js); this file draws the wheel and lands it on the drawn seg.
|
|
3
|
+
import { mount } from '/engine-sdk/ui.js';
|
|
4
|
+
|
|
5
|
+
const SEG = 24;
|
|
6
|
+
const SLICE = 360 / SEG;
|
|
7
|
+
const COLOR = { 0: '#263040', 12000: '#28F3F3', 15000: '#9F7AEA', 20000: '#F6D469', 50000: '#F28149', 150000: '#F052A3' };
|
|
8
|
+
// Visual layout (multBp per segment, table order) in lockstep with math/<mode>.tsv.
|
|
9
|
+
const LAYOUT = {
|
|
10
|
+
low: [20000, 15000, 15000, 15000, 15000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 0, 0, 0, 0, 0, 0],
|
|
11
|
+
medium: [50000, 20000, 20000, 15000, 15000, 15000, 15000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
12
|
+
high: [150000, 20000, 15000, 15000, 12000, 12000, 12000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
13
|
+
};
|
|
14
|
+
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
15
|
+
const el = document.getElementById('game');
|
|
16
|
+
const wheel = document.getElementById('wheel');
|
|
17
|
+
const face = document.getElementById('face');
|
|
18
|
+
let rot = 0;
|
|
19
|
+
|
|
20
|
+
function slice(i, color) {
|
|
21
|
+
const t1 = (i * SLICE - 90) * Math.PI / 180;
|
|
22
|
+
const t2 = ((i + 1) * SLICE - 90) * Math.PI / 180;
|
|
23
|
+
const x1 = (100 + 90 * Math.cos(t1)).toFixed(3), y1 = (100 + 90 * Math.sin(t1)).toFixed(3);
|
|
24
|
+
const x2 = (100 + 90 * Math.cos(t2)).toFixed(3), y2 = (100 + 90 * Math.sin(t2)).toFixed(3);
|
|
25
|
+
return `<path d="M100 100 L ${x1} ${y1} A 90 90 0 0 1 ${x2} ${y2} Z" fill="${color}"></path>`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function build(modeId) {
|
|
29
|
+
wheel.innerHTML = `<svg viewBox="0 0 200 200" aria-hidden="true"><g class="disc" style="transform:rotate(${rot}deg)">${LAYOUT[modeId].map((v, i) => slice(i, COLOR[v])).join('')}<circle cx="100" cy="100" r="22" fill="#0b1f28" stroke="#0D2A38" stroke-width="2"></circle></g><polygon points="100,2 92,22 108,22" fill="#F9F9FB" stroke="#041015" stroke-width="1"></polygon></svg>`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const game = await mount(el, {
|
|
33
|
+
async reveal(round) {
|
|
34
|
+
face.textContent = '';
|
|
35
|
+
face.classList.remove('lose');
|
|
36
|
+
const disc = wheel.querySelector('.disc');
|
|
37
|
+
disc.style.transition = 'none';
|
|
38
|
+
void disc.offsetWidth;
|
|
39
|
+
const desired = (360 - (round.data[0].seg * SLICE + SLICE / 2) + 360) % 360;
|
|
40
|
+
rot += 360 * 5 + ((desired - (rot % 360) + 360) % 360);
|
|
41
|
+
disc.style.transition = 'transform 2.6s cubic-bezier(.17,.67,.08,1)';
|
|
42
|
+
disc.style.transform = `rotate(${rot}deg)`;
|
|
43
|
+
await wait(2700);
|
|
44
|
+
face.textContent = round.data[0].mult + '×';
|
|
45
|
+
face.classList.toggle('lose', round.payout === 0);
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
let built = game.mode;
|
|
50
|
+
build(built);
|
|
51
|
+
el.addEventListener('select', (e) => { if (e.detail.mode !== built) { built = e.detail.mode; build(built); } });
|
|
@@ -0,0 +1,19 @@
|
|
|
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, viewport-fit=cover">
|
|
6
|
+
<title>Wheel</title>
|
|
7
|
+
<link rel="stylesheet" href="./game.css">
|
|
8
|
+
<script type="module" src="/engine-sdk/bet-field.js"></script>
|
|
9
|
+
<script type="module" src="./game.js"></script>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<hb-game id="game">
|
|
13
|
+
<div slot="zone" class="stage" aria-live="polite">
|
|
14
|
+
<div class="wheel" id="wheel"></div>
|
|
15
|
+
<div class="face" id="face"></div>
|
|
16
|
+
</div>
|
|
17
|
+
</hb-game>
|
|
18
|
+
</body>
|
|
19
|
+
</html>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
1 {"mult":"15.00","seg":0}
|
|
2
|
+
1 {"mult":"2.00","seg":1}
|
|
3
|
+
1 {"mult":"1.50","seg":2}
|
|
4
|
+
1 {"mult":"1.50","seg":3}
|
|
5
|
+
1 {"mult":"1.20","seg":4}
|
|
6
|
+
1 {"mult":"1.20","seg":5}
|
|
7
|
+
1 {"mult":"1.20","seg":6}
|
|
8
|
+
1 {"mult":"0.00","seg":7}
|
|
9
|
+
1 {"mult":"0.00","seg":8}
|
|
10
|
+
1 {"mult":"0.00","seg":9}
|
|
11
|
+
1 {"mult":"0.00","seg":10}
|
|
12
|
+
1 {"mult":"0.00","seg":11}
|
|
13
|
+
1 {"mult":"0.00","seg":12}
|
|
14
|
+
1 {"mult":"0.00","seg":13}
|
|
15
|
+
1 {"mult":"0.00","seg":14}
|
|
16
|
+
1 {"mult":"0.00","seg":15}
|
|
17
|
+
1 {"mult":"0.00","seg":16}
|
|
18
|
+
1 {"mult":"0.00","seg":17}
|
|
19
|
+
1 {"mult":"0.00","seg":18}
|
|
20
|
+
1 {"mult":"0.00","seg":19}
|
|
21
|
+
1 {"mult":"0.00","seg":20}
|
|
22
|
+
1 {"mult":"0.00","seg":21}
|
|
23
|
+
1 {"mult":"0.00","seg":22}
|
|
24
|
+
1 {"mult":"0.00","seg":23}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
1 {"mult":"2.00","seg":0}
|
|
2
|
+
1 {"mult":"1.50","seg":1}
|
|
3
|
+
1 {"mult":"1.50","seg":2}
|
|
4
|
+
1 {"mult":"1.50","seg":3}
|
|
5
|
+
1 {"mult":"1.50","seg":4}
|
|
6
|
+
1 {"mult":"1.20","seg":5}
|
|
7
|
+
1 {"mult":"1.20","seg":6}
|
|
8
|
+
1 {"mult":"1.20","seg":7}
|
|
9
|
+
1 {"mult":"1.20","seg":8}
|
|
10
|
+
1 {"mult":"1.20","seg":9}
|
|
11
|
+
1 {"mult":"1.20","seg":10}
|
|
12
|
+
1 {"mult":"1.20","seg":11}
|
|
13
|
+
1 {"mult":"1.20","seg":12}
|
|
14
|
+
1 {"mult":"1.20","seg":13}
|
|
15
|
+
1 {"mult":"1.20","seg":14}
|
|
16
|
+
1 {"mult":"1.20","seg":15}
|
|
17
|
+
1 {"mult":"1.20","seg":16}
|
|
18
|
+
1 {"mult":"1.20","seg":17}
|
|
19
|
+
1 {"mult":"0.00","seg":18}
|
|
20
|
+
1 {"mult":"0.00","seg":19}
|
|
21
|
+
1 {"mult":"0.00","seg":20}
|
|
22
|
+
1 {"mult":"0.00","seg":21}
|
|
23
|
+
1 {"mult":"0.00","seg":22}
|
|
24
|
+
1 {"mult":"0.00","seg":23}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"format": "house-engine-math/1",
|
|
3
|
+
"game": "wheel",
|
|
4
|
+
"rtpBp": 9900,
|
|
5
|
+
"modes": [
|
|
6
|
+
{
|
|
7
|
+
"id": "low",
|
|
8
|
+
"label": "Low",
|
|
9
|
+
"kind": "table",
|
|
10
|
+
"table": "low.tsv",
|
|
11
|
+
"bets": [
|
|
12
|
+
{ "id": "spin", "label": "Spin", "tiers": [
|
|
13
|
+
{ "outcomes": [[0, 0]], "multBp": 20000 },
|
|
14
|
+
{ "outcomes": [[1, 4]], "multBp": 15000 },
|
|
15
|
+
{ "outcomes": [[5, 17]], "multBp": 12000 }
|
|
16
|
+
] }
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": "medium",
|
|
21
|
+
"label": "Medium",
|
|
22
|
+
"kind": "table",
|
|
23
|
+
"table": "medium.tsv",
|
|
24
|
+
"bets": [
|
|
25
|
+
{ "id": "spin", "label": "Spin", "tiers": [
|
|
26
|
+
{ "outcomes": [[0, 0]], "multBp": 50000 },
|
|
27
|
+
{ "outcomes": [[1, 2]], "multBp": 20000 },
|
|
28
|
+
{ "outcomes": [[3, 6]], "multBp": 15000 },
|
|
29
|
+
{ "outcomes": [[7, 13]], "multBp": 12000 }
|
|
30
|
+
] }
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "high",
|
|
35
|
+
"label": "High",
|
|
36
|
+
"kind": "table",
|
|
37
|
+
"table": "high.tsv",
|
|
38
|
+
"bets": [
|
|
39
|
+
{ "id": "spin", "label": "Spin", "tiers": [
|
|
40
|
+
{ "outcomes": [[0, 0]], "multBp": 150000 },
|
|
41
|
+
{ "outcomes": [[1, 1]], "multBp": 20000 },
|
|
42
|
+
{ "outcomes": [[2, 3]], "multBp": 15000 },
|
|
43
|
+
{ "outcomes": [[4, 6]], "multBp": 12000 }
|
|
44
|
+
] }
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
1 {"mult":"5.00","seg":0}
|
|
2
|
+
1 {"mult":"2.00","seg":1}
|
|
3
|
+
1 {"mult":"2.00","seg":2}
|
|
4
|
+
1 {"mult":"1.50","seg":3}
|
|
5
|
+
1 {"mult":"1.50","seg":4}
|
|
6
|
+
1 {"mult":"1.50","seg":5}
|
|
7
|
+
1 {"mult":"1.50","seg":6}
|
|
8
|
+
1 {"mult":"1.20","seg":7}
|
|
9
|
+
1 {"mult":"1.20","seg":8}
|
|
10
|
+
1 {"mult":"1.20","seg":9}
|
|
11
|
+
1 {"mult":"1.20","seg":10}
|
|
12
|
+
1 {"mult":"1.20","seg":11}
|
|
13
|
+
1 {"mult":"1.20","seg":12}
|
|
14
|
+
1 {"mult":"1.20","seg":13}
|
|
15
|
+
1 {"mult":"0.00","seg":14}
|
|
16
|
+
1 {"mult":"0.00","seg":15}
|
|
17
|
+
1 {"mult":"0.00","seg":16}
|
|
18
|
+
1 {"mult":"0.00","seg":17}
|
|
19
|
+
1 {"mult":"0.00","seg":18}
|
|
20
|
+
1 {"mult":"0.00","seg":19}
|
|
21
|
+
1 {"mult":"0.00","seg":20}
|
|
22
|
+
1 {"mult":"0.00","seg":21}
|
|
23
|
+
1 {"mult":"0.00","seg":22}
|
|
24
|
+
1 {"mult":"0.00","seg":23}
|