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.
Files changed (561) hide show
  1. package/README.md +20 -0
  2. package/engine-cli/connect.mjs +73 -0
  3. package/engine-cli/hbengine.mjs +565 -0
  4. package/engine-cli/package.json +1 -0
  5. package/engine-cli/templates.mjs +14 -0
  6. package/engine-conformance/analyse-worker.mjs +12 -0
  7. package/engine-conformance/analyse.mjs +4458 -0
  8. package/engine-conformance/bundle.mjs +208 -0
  9. package/engine-conformance/clock.mjs +294 -0
  10. package/engine-conformance/csp.go +56 -0
  11. package/engine-conformance/csp.json +6 -0
  12. package/engine-conformance/csp.mjs +20 -0
  13. package/engine-conformance/gzip-sizes.mjs +9 -0
  14. package/engine-conformance/package.json +1 -0
  15. package/engine-conformance/picture.mjs +55 -0
  16. package/engine-conformance/report.go +349 -0
  17. package/engine-conformance/run.mjs +1217 -0
  18. package/engine-conformance/scan.mjs +476 -0
  19. package/engine-conformance/testdata/clock-overlay/bundle/assets.json +1 -0
  20. package/engine-conformance/testdata/clock-overlay/bundle/game.css +17 -0
  21. package/engine-conformance/testdata/clock-overlay/bundle/game.js +83 -0
  22. package/engine-conformance/testdata/clock-overlay/bundle/hbtest.json +4 -0
  23. package/engine-conformance/testdata/clock-overlay/bundle/index.html +34 -0
  24. package/engine-conformance/testdata/clock-probe/bundle/assets.json +1 -0
  25. package/engine-conformance/testdata/clock-probe/bundle/game.css +17 -0
  26. package/engine-conformance/testdata/clock-probe/bundle/game.js +65 -0
  27. package/engine-conformance/testdata/clock-probe/bundle/hbtest.json +4 -0
  28. package/engine-conformance/testdata/clock-probe/bundle/index.html +35 -0
  29. package/engine-conformance/testdata/clock-probe/bundle/probe-worker.js +46 -0
  30. package/engine-conformance/testdata/clock-probe/bundle/probe.js +210 -0
  31. package/engine-conformance/testdata/clock-probe/math/dice.tsv +100 -0
  32. package/engine-conformance/testdata/clock-probe/math/manifest.json +2925 -0
  33. package/engine-conformance/testdata/clock-worker/bundle/assets.json +1 -0
  34. package/engine-conformance/testdata/clock-worker/bundle/clock-worker.js +14 -0
  35. package/engine-conformance/testdata/clock-worker/bundle/game.css +17 -0
  36. package/engine-conformance/testdata/clock-worker/bundle/game.js +61 -0
  37. package/engine-conformance/testdata/clock-worker/bundle/hbtest.json +4 -0
  38. package/engine-conformance/testdata/clock-worker/bundle/index.html +35 -0
  39. package/engine-conformance/testdata/clock-worker/math/dice.tsv +100 -0
  40. package/engine-conformance/testdata/clock-worker/math/manifest.json +2925 -0
  41. package/engine-conformance/travel.mjs +1209 -0
  42. package/engine-conformance/vendor/acorn/LICENSE +21 -0
  43. package/engine-conformance/vendor/acorn/acorn.mjs +6266 -0
  44. package/engine-conformance/wasm-releases.json +168 -0
  45. package/engine-examples/README.md +23 -0
  46. package/engine-examples/coin/AGENTS.md +3 -0
  47. package/engine-examples/coin/README.md +7 -0
  48. package/engine-examples/coin/bundle/assets.json +1 -0
  49. package/engine-examples/coin/bundle/game.css +5 -0
  50. package/engine-examples/coin/bundle/game.js +19 -0
  51. package/engine-examples/coin/bundle/hbtest.json +5 -0
  52. package/engine-examples/coin/bundle/index.html +16 -0
  53. package/engine-examples/coin/math/coin.tsv +2 -0
  54. package/engine-examples/coin/math/manifest.json +17 -0
  55. package/engine-examples/crash/AGENTS.md +3 -0
  56. package/engine-examples/crash/README.md +14 -0
  57. package/engine-examples/crash/bundle/assets.json +1 -0
  58. package/engine-examples/crash/bundle/game.css +8 -0
  59. package/engine-examples/crash/bundle/game.js +90 -0
  60. package/engine-examples/crash/bundle/hbtest.json +5 -0
  61. package/engine-examples/crash/bundle/index.html +20 -0
  62. package/engine-examples/crash/math/crash-01.tsv +2 -0
  63. package/engine-examples/crash/math/crash-02.tsv +2 -0
  64. package/engine-examples/crash/math/crash-03.tsv +2 -0
  65. package/engine-examples/crash/math/crash-04.tsv +2 -0
  66. package/engine-examples/crash/math/crash-05.tsv +2 -0
  67. package/engine-examples/crash/math/crash-06.tsv +2 -0
  68. package/engine-examples/crash/math/crash-07.tsv +2 -0
  69. package/engine-examples/crash/math/crash-08.tsv +2 -0
  70. package/engine-examples/crash/math/crash-09.tsv +2 -0
  71. package/engine-examples/crash/math/crash-10.tsv +2 -0
  72. package/engine-examples/crash/math/crash-11.tsv +2 -0
  73. package/engine-examples/crash/math/crash-12.tsv +2 -0
  74. package/engine-examples/crash/math/crash-13.tsv +2 -0
  75. package/engine-examples/crash/math/crash-14.tsv +2 -0
  76. package/engine-examples/crash/math/crash-15.tsv +2 -0
  77. package/engine-examples/crash/math/crash-16.tsv +2 -0
  78. package/engine-examples/crash/math/crash-17.tsv +2 -0
  79. package/engine-examples/crash/math/crash-18.tsv +2 -0
  80. package/engine-examples/crash/math/crash-19.tsv +2 -0
  81. package/engine-examples/crash/math/crash-20.tsv +2 -0
  82. package/engine-examples/crash/math/crash-21.tsv +2 -0
  83. package/engine-examples/crash/math/crash-22.tsv +2 -0
  84. package/engine-examples/crash/math/crash-23.tsv +2 -0
  85. package/engine-examples/crash/math/crash-24.tsv +2 -0
  86. package/engine-examples/crash/math/crash-25.tsv +2 -0
  87. package/engine-examples/crash/math/crash-26.tsv +2 -0
  88. package/engine-examples/crash/math/crash-27.tsv +2 -0
  89. package/engine-examples/crash/math/crash-28.tsv +2 -0
  90. package/engine-examples/crash/math/crash-29.tsv +2 -0
  91. package/engine-examples/crash/math/crash-30.tsv +2 -0
  92. package/engine-examples/crash/math/crash-31.tsv +2 -0
  93. package/engine-examples/crash/math/crash-32.tsv +2 -0
  94. package/engine-examples/crash/math/crash-33.tsv +2 -0
  95. package/engine-examples/crash/math/crash-34.tsv +2 -0
  96. package/engine-examples/crash/math/crash-35.tsv +2 -0
  97. package/engine-examples/crash/math/crash-36.tsv +2 -0
  98. package/engine-examples/crash/math/crash-37.tsv +2 -0
  99. package/engine-examples/crash/math/crash-38.tsv +2 -0
  100. package/engine-examples/crash/math/crash-39.tsv +2 -0
  101. package/engine-examples/crash/math/crash-40.tsv +2 -0
  102. package/engine-examples/crash/math/crash-41.tsv +2 -0
  103. package/engine-examples/crash/math/crash-42.tsv +2 -0
  104. package/engine-examples/crash/math/crash-43.tsv +2 -0
  105. package/engine-examples/crash/math/crash-44.tsv +2 -0
  106. package/engine-examples/crash/math/crash-45.tsv +2 -0
  107. package/engine-examples/crash/math/crash-46.tsv +2 -0
  108. package/engine-examples/crash/math/crash-47.tsv +2 -0
  109. package/engine-examples/crash/math/crash-48.tsv +2 -0
  110. package/engine-examples/crash/math/crash-49.tsv +2 -0
  111. package/engine-examples/crash/math/crash-50.tsv +2 -0
  112. package/engine-examples/crash/math/crash-51.tsv +2 -0
  113. package/engine-examples/crash/math/crash-52.tsv +2 -0
  114. package/engine-examples/crash/math/crash-53.tsv +2 -0
  115. package/engine-examples/crash/math/crash-54.tsv +2 -0
  116. package/engine-examples/crash/math/crash-55.tsv +2 -0
  117. package/engine-examples/crash/math/crash-56.tsv +2 -0
  118. package/engine-examples/crash/math/crash-57.tsv +2 -0
  119. package/engine-examples/crash/math/crash-58.tsv +2 -0
  120. package/engine-examples/crash/math/crash-59.tsv +2 -0
  121. package/engine-examples/crash/math/crash-60.tsv +2 -0
  122. package/engine-examples/crash/math/crash-61.tsv +2 -0
  123. package/engine-examples/crash/math/crash-62.tsv +2 -0
  124. package/engine-examples/crash/math/crash-63.tsv +2 -0
  125. package/engine-examples/crash/math/crash-64.tsv +2 -0
  126. package/engine-examples/crash/math/gen.mjs +31 -0
  127. package/engine-examples/crash/math/manifest.json +1 -0
  128. package/engine-examples/dice/AGENTS.md +3 -0
  129. package/engine-examples/dice/README.md +25 -0
  130. package/engine-examples/dice/bundle/assets.json +1 -0
  131. package/engine-examples/dice/bundle/game.css +17 -0
  132. package/engine-examples/dice/bundle/game.js +61 -0
  133. package/engine-examples/dice/bundle/hbtest.json +4 -0
  134. package/engine-examples/dice/bundle/index.html +34 -0
  135. package/engine-examples/dice/math/dice.tsv +100 -0
  136. package/engine-examples/dice/math/manifest.json +2925 -0
  137. package/engine-examples/keno/AGENTS.md +3 -0
  138. package/engine-examples/keno/README.md +11 -0
  139. package/engine-examples/keno/bundle/assets.json +1 -0
  140. package/engine-examples/keno/bundle/game.css +14 -0
  141. package/engine-examples/keno/bundle/game.js +86 -0
  142. package/engine-examples/keno/bundle/hbtest.json +4 -0
  143. package/engine-examples/keno/bundle/i18n.json +342 -0
  144. package/engine-examples/keno/bundle/index.html +20 -0
  145. package/engine-examples/keno/bundle/model.json +1 -0
  146. package/engine-examples/keno/math/gen.mjs +35 -0
  147. package/engine-examples/keno/math/keno-picks-1.tsv +2 -0
  148. package/engine-examples/keno/math/keno-picks-10.tsv +11 -0
  149. package/engine-examples/keno/math/keno-picks-2.tsv +3 -0
  150. package/engine-examples/keno/math/keno-picks-3.tsv +4 -0
  151. package/engine-examples/keno/math/keno-picks-4.tsv +5 -0
  152. package/engine-examples/keno/math/keno-picks-5.tsv +6 -0
  153. package/engine-examples/keno/math/keno-picks-6.tsv +7 -0
  154. package/engine-examples/keno/math/keno-picks-7.tsv +8 -0
  155. package/engine-examples/keno/math/keno-picks-8.tsv +9 -0
  156. package/engine-examples/keno/math/keno-picks-9.tsv +10 -0
  157. package/engine-examples/keno/math/manifest.json +1 -0
  158. package/engine-examples/keno/math-kit/pack.mjs +165 -0
  159. package/engine-examples/keno/math-kit/package.json +8 -0
  160. package/engine-examples/keno/math-kit/slot.mjs +94 -0
  161. package/engine-examples/keno/math-kit/tumble.mjs +94 -0
  162. package/engine-examples/lines/AGENTS.md +3 -0
  163. package/engine-examples/lines/README.md +20 -0
  164. package/engine-examples/lines/bundle/assets.json +1 -0
  165. package/engine-examples/lines/bundle/game.css +16 -0
  166. package/engine-examples/lines/bundle/game.js +125 -0
  167. package/engine-examples/lines/bundle/hbtest.json +4 -0
  168. package/engine-examples/lines/bundle/i18n.json +242 -0
  169. package/engine-examples/lines/bundle/index.html +23 -0
  170. package/engine-examples/lines/bundle/model.json +1 -0
  171. package/engine-examples/lines/math/gen.mjs +54 -0
  172. package/engine-examples/lines/math-kit/pack.mjs +165 -0
  173. package/engine-examples/lines/math-kit/package.json +8 -0
  174. package/engine-examples/lines/math-kit/slot.mjs +94 -0
  175. package/engine-examples/lines/math-kit/tumble.mjs +94 -0
  176. package/engine-examples/mines/AGENTS.md +3 -0
  177. package/engine-examples/mines/README.md +13 -0
  178. package/engine-examples/mines/bundle/assets.json +1 -0
  179. package/engine-examples/mines/bundle/game.css +12 -0
  180. package/engine-examples/mines/bundle/game.js +58 -0
  181. package/engine-examples/mines/bundle/hbtest.json +8 -0
  182. package/engine-examples/mines/bundle/index.html +16 -0
  183. package/engine-examples/mines/math/gen.mjs +40 -0
  184. package/engine-examples/mines/math/manifest.json +1 -0
  185. package/engine-examples/mines/math/mines-1-1.tsv +2 -0
  186. package/engine-examples/mines/math/mines-1-2.tsv +2 -0
  187. package/engine-examples/mines/math/mines-1-22.tsv +2 -0
  188. package/engine-examples/mines/math/mines-1-23.tsv +2 -0
  189. package/engine-examples/mines/math/mines-1-24.tsv +2 -0
  190. package/engine-examples/mines/math/mines-1-3.tsv +2 -0
  191. package/engine-examples/mines/math/mines-10-1.tsv +2 -0
  192. package/engine-examples/mines/math/mines-10-10.tsv +2 -0
  193. package/engine-examples/mines/math/mines-10-11.tsv +2 -0
  194. package/engine-examples/mines/math/mines-10-12.tsv +2 -0
  195. package/engine-examples/mines/math/mines-10-13.tsv +2 -0
  196. package/engine-examples/mines/math/mines-10-14.tsv +2 -0
  197. package/engine-examples/mines/math/mines-10-15.tsv +2 -0
  198. package/engine-examples/mines/math/mines-10-2.tsv +2 -0
  199. package/engine-examples/mines/math/mines-10-3.tsv +2 -0
  200. package/engine-examples/mines/math/mines-10-4.tsv +2 -0
  201. package/engine-examples/mines/math/mines-10-5.tsv +2 -0
  202. package/engine-examples/mines/math/mines-10-6.tsv +2 -0
  203. package/engine-examples/mines/math/mines-10-7.tsv +2 -0
  204. package/engine-examples/mines/math/mines-10-8.tsv +2 -0
  205. package/engine-examples/mines/math/mines-10-9.tsv +2 -0
  206. package/engine-examples/mines/math/mines-11-1.tsv +2 -0
  207. package/engine-examples/mines/math/mines-11-10.tsv +2 -0
  208. package/engine-examples/mines/math/mines-11-11.tsv +2 -0
  209. package/engine-examples/mines/math/mines-11-12.tsv +2 -0
  210. package/engine-examples/mines/math/mines-11-13.tsv +2 -0
  211. package/engine-examples/mines/math/mines-11-14.tsv +2 -0
  212. package/engine-examples/mines/math/mines-11-2.tsv +2 -0
  213. package/engine-examples/mines/math/mines-11-3.tsv +2 -0
  214. package/engine-examples/mines/math/mines-11-4.tsv +2 -0
  215. package/engine-examples/mines/math/mines-11-5.tsv +2 -0
  216. package/engine-examples/mines/math/mines-11-6.tsv +2 -0
  217. package/engine-examples/mines/math/mines-11-7.tsv +2 -0
  218. package/engine-examples/mines/math/mines-11-8.tsv +2 -0
  219. package/engine-examples/mines/math/mines-11-9.tsv +2 -0
  220. package/engine-examples/mines/math/mines-12-1.tsv +2 -0
  221. package/engine-examples/mines/math/mines-12-10.tsv +2 -0
  222. package/engine-examples/mines/math/mines-12-11.tsv +2 -0
  223. package/engine-examples/mines/math/mines-12-12.tsv +2 -0
  224. package/engine-examples/mines/math/mines-12-13.tsv +2 -0
  225. package/engine-examples/mines/math/mines-12-2.tsv +2 -0
  226. package/engine-examples/mines/math/mines-12-3.tsv +2 -0
  227. package/engine-examples/mines/math/mines-12-4.tsv +2 -0
  228. package/engine-examples/mines/math/mines-12-5.tsv +2 -0
  229. package/engine-examples/mines/math/mines-12-6.tsv +2 -0
  230. package/engine-examples/mines/math/mines-12-7.tsv +2 -0
  231. package/engine-examples/mines/math/mines-12-8.tsv +2 -0
  232. package/engine-examples/mines/math/mines-12-9.tsv +2 -0
  233. package/engine-examples/mines/math/mines-13-1.tsv +2 -0
  234. package/engine-examples/mines/math/mines-13-10.tsv +2 -0
  235. package/engine-examples/mines/math/mines-13-11.tsv +2 -0
  236. package/engine-examples/mines/math/mines-13-12.tsv +2 -0
  237. package/engine-examples/mines/math/mines-13-2.tsv +2 -0
  238. package/engine-examples/mines/math/mines-13-3.tsv +2 -0
  239. package/engine-examples/mines/math/mines-13-4.tsv +2 -0
  240. package/engine-examples/mines/math/mines-13-5.tsv +2 -0
  241. package/engine-examples/mines/math/mines-13-6.tsv +2 -0
  242. package/engine-examples/mines/math/mines-13-7.tsv +2 -0
  243. package/engine-examples/mines/math/mines-13-8.tsv +2 -0
  244. package/engine-examples/mines/math/mines-13-9.tsv +2 -0
  245. package/engine-examples/mines/math/mines-14-1.tsv +2 -0
  246. package/engine-examples/mines/math/mines-14-10.tsv +2 -0
  247. package/engine-examples/mines/math/mines-14-11.tsv +2 -0
  248. package/engine-examples/mines/math/mines-14-2.tsv +2 -0
  249. package/engine-examples/mines/math/mines-14-3.tsv +2 -0
  250. package/engine-examples/mines/math/mines-14-4.tsv +2 -0
  251. package/engine-examples/mines/math/mines-14-5.tsv +2 -0
  252. package/engine-examples/mines/math/mines-14-6.tsv +2 -0
  253. package/engine-examples/mines/math/mines-14-7.tsv +2 -0
  254. package/engine-examples/mines/math/mines-14-8.tsv +2 -0
  255. package/engine-examples/mines/math/mines-14-9.tsv +2 -0
  256. package/engine-examples/mines/math/mines-15-1.tsv +2 -0
  257. package/engine-examples/mines/math/mines-15-10.tsv +2 -0
  258. package/engine-examples/mines/math/mines-15-2.tsv +2 -0
  259. package/engine-examples/mines/math/mines-15-3.tsv +2 -0
  260. package/engine-examples/mines/math/mines-15-4.tsv +2 -0
  261. package/engine-examples/mines/math/mines-15-5.tsv +2 -0
  262. package/engine-examples/mines/math/mines-15-6.tsv +2 -0
  263. package/engine-examples/mines/math/mines-15-7.tsv +2 -0
  264. package/engine-examples/mines/math/mines-15-8.tsv +2 -0
  265. package/engine-examples/mines/math/mines-15-9.tsv +2 -0
  266. package/engine-examples/mines/math/mines-16-1.tsv +2 -0
  267. package/engine-examples/mines/math/mines-16-2.tsv +2 -0
  268. package/engine-examples/mines/math/mines-16-3.tsv +2 -0
  269. package/engine-examples/mines/math/mines-16-4.tsv +2 -0
  270. package/engine-examples/mines/math/mines-16-5.tsv +2 -0
  271. package/engine-examples/mines/math/mines-16-6.tsv +2 -0
  272. package/engine-examples/mines/math/mines-16-7.tsv +2 -0
  273. package/engine-examples/mines/math/mines-16-8.tsv +2 -0
  274. package/engine-examples/mines/math/mines-16-9.tsv +2 -0
  275. package/engine-examples/mines/math/mines-17-1.tsv +2 -0
  276. package/engine-examples/mines/math/mines-17-2.tsv +2 -0
  277. package/engine-examples/mines/math/mines-17-3.tsv +2 -0
  278. package/engine-examples/mines/math/mines-17-4.tsv +2 -0
  279. package/engine-examples/mines/math/mines-17-5.tsv +2 -0
  280. package/engine-examples/mines/math/mines-17-6.tsv +2 -0
  281. package/engine-examples/mines/math/mines-17-7.tsv +2 -0
  282. package/engine-examples/mines/math/mines-17-8.tsv +2 -0
  283. package/engine-examples/mines/math/mines-18-1.tsv +2 -0
  284. package/engine-examples/mines/math/mines-18-2.tsv +2 -0
  285. package/engine-examples/mines/math/mines-18-3.tsv +2 -0
  286. package/engine-examples/mines/math/mines-18-4.tsv +2 -0
  287. package/engine-examples/mines/math/mines-18-5.tsv +2 -0
  288. package/engine-examples/mines/math/mines-18-6.tsv +2 -0
  289. package/engine-examples/mines/math/mines-18-7.tsv +2 -0
  290. package/engine-examples/mines/math/mines-19-1.tsv +2 -0
  291. package/engine-examples/mines/math/mines-19-2.tsv +2 -0
  292. package/engine-examples/mines/math/mines-19-3.tsv +2 -0
  293. package/engine-examples/mines/math/mines-19-4.tsv +2 -0
  294. package/engine-examples/mines/math/mines-19-5.tsv +2 -0
  295. package/engine-examples/mines/math/mines-19-6.tsv +2 -0
  296. package/engine-examples/mines/math/mines-2-1.tsv +2 -0
  297. package/engine-examples/mines/math/mines-2-19.tsv +2 -0
  298. package/engine-examples/mines/math/mines-2-2.tsv +2 -0
  299. package/engine-examples/mines/math/mines-2-20.tsv +2 -0
  300. package/engine-examples/mines/math/mines-2-21.tsv +2 -0
  301. package/engine-examples/mines/math/mines-2-22.tsv +2 -0
  302. package/engine-examples/mines/math/mines-2-23.tsv +2 -0
  303. package/engine-examples/mines/math/mines-2-3.tsv +2 -0
  304. package/engine-examples/mines/math/mines-2-4.tsv +2 -0
  305. package/engine-examples/mines/math/mines-2-5.tsv +2 -0
  306. package/engine-examples/mines/math/mines-20-1.tsv +2 -0
  307. package/engine-examples/mines/math/mines-20-2.tsv +2 -0
  308. package/engine-examples/mines/math/mines-20-3.tsv +2 -0
  309. package/engine-examples/mines/math/mines-20-4.tsv +2 -0
  310. package/engine-examples/mines/math/mines-20-5.tsv +2 -0
  311. package/engine-examples/mines/math/mines-21-1.tsv +2 -0
  312. package/engine-examples/mines/math/mines-21-2.tsv +2 -0
  313. package/engine-examples/mines/math/mines-21-3.tsv +2 -0
  314. package/engine-examples/mines/math/mines-21-4.tsv +2 -0
  315. package/engine-examples/mines/math/mines-22-1.tsv +2 -0
  316. package/engine-examples/mines/math/mines-22-2.tsv +2 -0
  317. package/engine-examples/mines/math/mines-22-3.tsv +2 -0
  318. package/engine-examples/mines/math/mines-23-1.tsv +2 -0
  319. package/engine-examples/mines/math/mines-23-2.tsv +2 -0
  320. package/engine-examples/mines/math/mines-24-1.tsv +2 -0
  321. package/engine-examples/mines/math/mines-3-1.tsv +2 -0
  322. package/engine-examples/mines/math/mines-3-17.tsv +2 -0
  323. package/engine-examples/mines/math/mines-3-18.tsv +2 -0
  324. package/engine-examples/mines/math/mines-3-19.tsv +2 -0
  325. package/engine-examples/mines/math/mines-3-2.tsv +2 -0
  326. package/engine-examples/mines/math/mines-3-20.tsv +2 -0
  327. package/engine-examples/mines/math/mines-3-21.tsv +2 -0
  328. package/engine-examples/mines/math/mines-3-22.tsv +2 -0
  329. package/engine-examples/mines/math/mines-3-3.tsv +2 -0
  330. package/engine-examples/mines/math/mines-3-4.tsv +2 -0
  331. package/engine-examples/mines/math/mines-3-5.tsv +2 -0
  332. package/engine-examples/mines/math/mines-3-6.tsv +2 -0
  333. package/engine-examples/mines/math/mines-4-1.tsv +2 -0
  334. package/engine-examples/mines/math/mines-4-13.tsv +2 -0
  335. package/engine-examples/mines/math/mines-4-14.tsv +2 -0
  336. package/engine-examples/mines/math/mines-4-15.tsv +2 -0
  337. package/engine-examples/mines/math/mines-4-16.tsv +2 -0
  338. package/engine-examples/mines/math/mines-4-17.tsv +2 -0
  339. package/engine-examples/mines/math/mines-4-18.tsv +2 -0
  340. package/engine-examples/mines/math/mines-4-19.tsv +2 -0
  341. package/engine-examples/mines/math/mines-4-2.tsv +2 -0
  342. package/engine-examples/mines/math/mines-4-20.tsv +2 -0
  343. package/engine-examples/mines/math/mines-4-21.tsv +2 -0
  344. package/engine-examples/mines/math/mines-4-3.tsv +2 -0
  345. package/engine-examples/mines/math/mines-4-4.tsv +2 -0
  346. package/engine-examples/mines/math/mines-4-5.tsv +2 -0
  347. package/engine-examples/mines/math/mines-4-6.tsv +2 -0
  348. package/engine-examples/mines/math/mines-4-7.tsv +2 -0
  349. package/engine-examples/mines/math/mines-4-8.tsv +2 -0
  350. package/engine-examples/mines/math/mines-4-9.tsv +2 -0
  351. package/engine-examples/mines/math/mines-5-1.tsv +2 -0
  352. package/engine-examples/mines/math/mines-5-10.tsv +2 -0
  353. package/engine-examples/mines/math/mines-5-11.tsv +2 -0
  354. package/engine-examples/mines/math/mines-5-12.tsv +2 -0
  355. package/engine-examples/mines/math/mines-5-13.tsv +2 -0
  356. package/engine-examples/mines/math/mines-5-14.tsv +2 -0
  357. package/engine-examples/mines/math/mines-5-15.tsv +2 -0
  358. package/engine-examples/mines/math/mines-5-16.tsv +2 -0
  359. package/engine-examples/mines/math/mines-5-17.tsv +2 -0
  360. package/engine-examples/mines/math/mines-5-18.tsv +2 -0
  361. package/engine-examples/mines/math/mines-5-19.tsv +2 -0
  362. package/engine-examples/mines/math/mines-5-2.tsv +2 -0
  363. package/engine-examples/mines/math/mines-5-20.tsv +2 -0
  364. package/engine-examples/mines/math/mines-5-3.tsv +2 -0
  365. package/engine-examples/mines/math/mines-5-4.tsv +2 -0
  366. package/engine-examples/mines/math/mines-5-5.tsv +2 -0
  367. package/engine-examples/mines/math/mines-5-6.tsv +2 -0
  368. package/engine-examples/mines/math/mines-5-7.tsv +2 -0
  369. package/engine-examples/mines/math/mines-5-8.tsv +2 -0
  370. package/engine-examples/mines/math/mines-5-9.tsv +2 -0
  371. package/engine-examples/mines/math/mines-6-1.tsv +2 -0
  372. package/engine-examples/mines/math/mines-6-10.tsv +2 -0
  373. package/engine-examples/mines/math/mines-6-11.tsv +2 -0
  374. package/engine-examples/mines/math/mines-6-12.tsv +2 -0
  375. package/engine-examples/mines/math/mines-6-13.tsv +2 -0
  376. package/engine-examples/mines/math/mines-6-14.tsv +2 -0
  377. package/engine-examples/mines/math/mines-6-15.tsv +2 -0
  378. package/engine-examples/mines/math/mines-6-16.tsv +2 -0
  379. package/engine-examples/mines/math/mines-6-17.tsv +2 -0
  380. package/engine-examples/mines/math/mines-6-18.tsv +2 -0
  381. package/engine-examples/mines/math/mines-6-19.tsv +2 -0
  382. package/engine-examples/mines/math/mines-6-2.tsv +2 -0
  383. package/engine-examples/mines/math/mines-6-3.tsv +2 -0
  384. package/engine-examples/mines/math/mines-6-4.tsv +2 -0
  385. package/engine-examples/mines/math/mines-6-5.tsv +2 -0
  386. package/engine-examples/mines/math/mines-6-6.tsv +2 -0
  387. package/engine-examples/mines/math/mines-6-7.tsv +2 -0
  388. package/engine-examples/mines/math/mines-6-8.tsv +2 -0
  389. package/engine-examples/mines/math/mines-6-9.tsv +2 -0
  390. package/engine-examples/mines/math/mines-7-1.tsv +2 -0
  391. package/engine-examples/mines/math/mines-7-10.tsv +2 -0
  392. package/engine-examples/mines/math/mines-7-11.tsv +2 -0
  393. package/engine-examples/mines/math/mines-7-12.tsv +2 -0
  394. package/engine-examples/mines/math/mines-7-13.tsv +2 -0
  395. package/engine-examples/mines/math/mines-7-14.tsv +2 -0
  396. package/engine-examples/mines/math/mines-7-15.tsv +2 -0
  397. package/engine-examples/mines/math/mines-7-16.tsv +2 -0
  398. package/engine-examples/mines/math/mines-7-17.tsv +2 -0
  399. package/engine-examples/mines/math/mines-7-18.tsv +2 -0
  400. package/engine-examples/mines/math/mines-7-2.tsv +2 -0
  401. package/engine-examples/mines/math/mines-7-3.tsv +2 -0
  402. package/engine-examples/mines/math/mines-7-4.tsv +2 -0
  403. package/engine-examples/mines/math/mines-7-5.tsv +2 -0
  404. package/engine-examples/mines/math/mines-7-6.tsv +2 -0
  405. package/engine-examples/mines/math/mines-7-7.tsv +2 -0
  406. package/engine-examples/mines/math/mines-7-8.tsv +2 -0
  407. package/engine-examples/mines/math/mines-7-9.tsv +2 -0
  408. package/engine-examples/mines/math/mines-8-1.tsv +2 -0
  409. package/engine-examples/mines/math/mines-8-10.tsv +2 -0
  410. package/engine-examples/mines/math/mines-8-11.tsv +2 -0
  411. package/engine-examples/mines/math/mines-8-12.tsv +2 -0
  412. package/engine-examples/mines/math/mines-8-13.tsv +2 -0
  413. package/engine-examples/mines/math/mines-8-14.tsv +2 -0
  414. package/engine-examples/mines/math/mines-8-15.tsv +2 -0
  415. package/engine-examples/mines/math/mines-8-16.tsv +2 -0
  416. package/engine-examples/mines/math/mines-8-17.tsv +2 -0
  417. package/engine-examples/mines/math/mines-8-2.tsv +2 -0
  418. package/engine-examples/mines/math/mines-8-3.tsv +2 -0
  419. package/engine-examples/mines/math/mines-8-4.tsv +2 -0
  420. package/engine-examples/mines/math/mines-8-5.tsv +2 -0
  421. package/engine-examples/mines/math/mines-8-6.tsv +2 -0
  422. package/engine-examples/mines/math/mines-8-7.tsv +2 -0
  423. package/engine-examples/mines/math/mines-8-8.tsv +2 -0
  424. package/engine-examples/mines/math/mines-8-9.tsv +2 -0
  425. package/engine-examples/mines/math/mines-9-1.tsv +2 -0
  426. package/engine-examples/mines/math/mines-9-10.tsv +2 -0
  427. package/engine-examples/mines/math/mines-9-11.tsv +2 -0
  428. package/engine-examples/mines/math/mines-9-12.tsv +2 -0
  429. package/engine-examples/mines/math/mines-9-13.tsv +2 -0
  430. package/engine-examples/mines/math/mines-9-14.tsv +2 -0
  431. package/engine-examples/mines/math/mines-9-15.tsv +2 -0
  432. package/engine-examples/mines/math/mines-9-16.tsv +2 -0
  433. package/engine-examples/mines/math/mines-9-2.tsv +2 -0
  434. package/engine-examples/mines/math/mines-9-3.tsv +2 -0
  435. package/engine-examples/mines/math/mines-9-4.tsv +2 -0
  436. package/engine-examples/mines/math/mines-9-5.tsv +2 -0
  437. package/engine-examples/mines/math/mines-9-6.tsv +2 -0
  438. package/engine-examples/mines/math/mines-9-7.tsv +2 -0
  439. package/engine-examples/mines/math/mines-9-8.tsv +2 -0
  440. package/engine-examples/mines/math/mines-9-9.tsv +2 -0
  441. package/engine-examples/plinko/AGENTS.md +3 -0
  442. package/engine-examples/plinko/README.md +9 -0
  443. package/engine-examples/plinko/bundle/assets.json +1 -0
  444. package/engine-examples/plinko/bundle/game.css +5 -0
  445. package/engine-examples/plinko/bundle/game.js +92 -0
  446. package/engine-examples/plinko/bundle/hbtest.json +4 -0
  447. package/engine-examples/plinko/bundle/index.html +16 -0
  448. package/engine-examples/plinko/bundle/model.json +1 -0
  449. package/engine-examples/plinko/math/gen.mjs +40 -0
  450. package/engine-examples/plinko/math/manifest.json +1 -0
  451. package/engine-examples/plinko/math/plinko-12-high.tsv +13 -0
  452. package/engine-examples/plinko/math/plinko-12-low.tsv +13 -0
  453. package/engine-examples/plinko/math/plinko-12-medium.tsv +13 -0
  454. package/engine-examples/plinko/math/plinko-16-high.tsv +17 -0
  455. package/engine-examples/plinko/math/plinko-16-low.tsv +17 -0
  456. package/engine-examples/plinko/math/plinko-16-medium.tsv +17 -0
  457. package/engine-examples/plinko/math/plinko-8-high.tsv +9 -0
  458. package/engine-examples/plinko/math/plinko-8-low.tsv +9 -0
  459. package/engine-examples/plinko/math/plinko-8-medium.tsv +9 -0
  460. package/engine-examples/plinko/math-kit/pack.mjs +165 -0
  461. package/engine-examples/plinko/math-kit/package.json +8 -0
  462. package/engine-examples/plinko/math-kit/slot.mjs +94 -0
  463. package/engine-examples/plinko/math-kit/tumble.mjs +94 -0
  464. package/engine-examples/slot/AGENTS.md +3 -0
  465. package/engine-examples/slot/README.md +26 -0
  466. package/engine-examples/slot/bundle/assets.json +1 -0
  467. package/engine-examples/slot/bundle/game.css +19 -0
  468. package/engine-examples/slot/bundle/game.js +42 -0
  469. package/engine-examples/slot/bundle/hbtest.json +4 -0
  470. package/engine-examples/slot/bundle/index.html +23 -0
  471. package/engine-examples/slot/math/gen.mjs +79 -0
  472. package/engine-examples/slot/math/manifest.json +1 -0
  473. package/engine-examples/slot/math/slot.tsv +110592 -0
  474. package/engine-examples/tower/AGENTS.md +3 -0
  475. package/engine-examples/tower/README.md +21 -0
  476. package/engine-examples/tower/bundle/assets.json +1 -0
  477. package/engine-examples/tower/bundle/game.css +8 -0
  478. package/engine-examples/tower/bundle/game.js +37 -0
  479. package/engine-examples/tower/bundle/hbtest.json +5 -0
  480. package/engine-examples/tower/bundle/index.html +16 -0
  481. package/engine-examples/tower/math/floor.tsv +4 -0
  482. package/engine-examples/tower/math/manifest.json +61 -0
  483. package/engine-examples/tumble/AGENTS.md +3 -0
  484. package/engine-examples/tumble/README.md +18 -0
  485. package/engine-examples/tumble/bundle/assets.json +1 -0
  486. package/engine-examples/tumble/bundle/game.css +13 -0
  487. package/engine-examples/tumble/bundle/game.js +119 -0
  488. package/engine-examples/tumble/bundle/hbtest.json +4 -0
  489. package/engine-examples/tumble/bundle/i18n.json +122 -0
  490. package/engine-examples/tumble/bundle/index.html +20 -0
  491. package/engine-examples/tumble/bundle/model.json +1 -0
  492. package/engine-examples/tumble/math/gen.mjs +38 -0
  493. package/engine-examples/tumble/math-kit/pack.mjs +165 -0
  494. package/engine-examples/tumble/math-kit/package.json +8 -0
  495. package/engine-examples/tumble/math-kit/slot.mjs +94 -0
  496. package/engine-examples/tumble/math-kit/tumble.mjs +94 -0
  497. package/engine-examples/wheel/README.md +14 -0
  498. package/engine-examples/wheel/bundle/assets.json +1 -0
  499. package/engine-examples/wheel/bundle/game.css +7 -0
  500. package/engine-examples/wheel/bundle/game.js +51 -0
  501. package/engine-examples/wheel/bundle/hbtest.json +4 -0
  502. package/engine-examples/wheel/bundle/index.html +19 -0
  503. package/engine-examples/wheel/math/high.tsv +24 -0
  504. package/engine-examples/wheel/math/low.tsv +24 -0
  505. package/engine-examples/wheel/math/manifest.json +48 -0
  506. package/engine-examples/wheel/math/medium.tsv +24 -0
  507. package/engine-math/pack.mjs +165 -0
  508. package/engine-math/package.json +1 -0
  509. package/engine-math/slot.mjs +94 -0
  510. package/engine-math/tumble.mjs +94 -0
  511. package/engine-mcp/package.json +1 -0
  512. package/engine-mcp/server.mjs +151 -0
  513. package/engine-sdk/GUIDE.md +855 -0
  514. package/engine-sdk/assets.js +92 -0
  515. package/engine-sdk/autoplay.js +82 -0
  516. package/engine-sdk/bet-field.js +391 -0
  517. package/engine-sdk/kit/fiat-coins.js +51 -0
  518. package/engine-sdk/kit/numeric-input.js +116 -0
  519. package/engine-sdk/kit/wallet-format.js +423 -0
  520. package/engine-sdk/marks.js +22 -0
  521. package/engine-sdk/messages.js +289 -0
  522. package/engine-sdk/money.js +89 -0
  523. package/engine-sdk/package.json +1 -0
  524. package/engine-sdk/protocol.js +263 -0
  525. package/engine-sdk/sdk.d.ts +138 -0
  526. package/engine-sdk/sdk.js +330 -0
  527. package/engine-sdk/stake.js +68 -0
  528. package/engine-sdk/ui.d.ts +36 -0
  529. package/engine-sdk/ui.js +340 -0
  530. package/engine-shell/core/faults.js +127 -0
  531. package/engine-shell/core/rgs.js +193 -0
  532. package/engine-shell/core/router.js +917 -0
  533. package/engine-shell/host/boot.js +408 -0
  534. package/engine-shell/host/engine-v1.js +438 -0
  535. package/engine-shell/host/fair.js +285 -0
  536. package/engine-shell/host/fonts/SOURCE.json +11 -0
  537. package/engine-shell/host/fonts/figtree-400.woff2 +0 -0
  538. package/engine-shell/host/fonts/figtree-500.woff2 +0 -0
  539. package/engine-shell/host/fonts/figtree-600.woff2 +0 -0
  540. package/engine-shell/host/fonts/red-hat-display-700.woff2 +0 -0
  541. package/engine-shell/host/index.html +230 -0
  542. package/engine-shell/host/page.js +809 -0
  543. package/engine-shell/host/sandbox-panel.js +681 -0
  544. package/engine-shell/host/sandbox-scenarios.js +554 -0
  545. package/engine-shell/host/site-bridge.js +87 -0
  546. package/engine-shell/host/strings.js +916 -0
  547. package/engine-shell/package.json +1 -0
  548. package/engine-shell/verifiers/badcat/coin.tsv +2 -0
  549. package/engine-shell/verifiers/badcat/dice-2.tsv +11 -0
  550. package/engine-shell/verifiers/badcat/dice-5.tsv +26 -0
  551. package/engine-shell/verifiers/badcat/dice-9.tsv +46 -0
  552. package/engine-shell/verifiers/badcat/manifest.json +3344 -0
  553. package/engine-shell/verifiers/badcat/rps.tsv +3 -0
  554. package/engine-shell/verifiers/badcat/triple.tsv +66 -0
  555. package/engine-shell/verifiers/engine-verify.js +358 -0
  556. package/engine-shell/verifiers/package.json +5 -0
  557. package/engine-shell/verifiers/parity/manifest.json +27 -0
  558. package/engine-shell/verifiers/parity/wide.tsv +5 -0
  559. package/engine-shell/verifiers/parity-vectors.json +244 -0
  560. package/engine-shell/verifiers/vectors.json +100 -0
  561. package/package.json +10 -0
@@ -0,0 +1,3 @@
1
+ # For agents
2
+
3
+ This is a House Engine game (Housebets). Read the guide first: `engine_guide` (MCP) or `engine-sdk/GUIDE.md`. Then `hbengine verify`, `hbengine dev`, `hbengine upload` (or the `engine_*` tools). The math is `math/`, the frontend `bundle/`; never compute money or outcomes in the frontend.
@@ -0,0 +1,21 @@
1
+ # Tower — a ladder with cash-out
2
+
3
+ Eight floors; four tiles each, one is a trap. Every floor survived multiplies
4
+ the pot by about 4/3; the player cashes out after any floor, or loses the
5
+ stake on a trap. Max 9.89×.
6
+
7
+ Cumulative payout after k survived floors, exactly (`ladderBp`):
8
+
9
+ 13200 · 17600 · 23466 · 31288 · 41718 · 55624 · 74166 · 98888 bp
10
+
11
+ RTP of stopping after floor k = ladderBp[k-1] · (3/4)^k, every stop ≤ 99 %:
12
+
13
+ 9900 · 9900 · 9899.72 · 9899.72 · 9899.88 · 9899.88 · 9899.97 · 9899.97 bp
14
+
15
+ The best stop bounds every strategy (the player learns only "survived so
16
+ far"), so the bet's RTP is 99.00 %. The panel (`/engine-sdk/ui.js`) draws Bet,
17
+ Next and Cash out; this bundle's `game.js` renders the tower and reveals each
18
+ floor the server drew.
19
+
20
+ npx hbengine verify . exact RTP per stop, the mathHash
21
+ npx hbengine init my-tower --template tower --slug my-tower # make it yours, then: verify, dev, upload
@@ -0,0 +1 @@
1
+ { "groups": { "boot": { "files": ["index.html", "game.js", "game.css"] } } }
@@ -0,0 +1,8 @@
1
+ body { margin: 0; background: #08141a; }
2
+ .tower { display: grid; grid-template-rows: repeat(8, 44px); gap: 6px; padding: 20px; width: min(360px, 100%); box-sizing: border-box; margin: 0 auto; }
3
+ .floor { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; opacity: .5; transition: opacity 160ms ease; }
4
+ .floor.revealed { opacity: 1; }
5
+ .tile { height: 44px; border-radius: 8px; background: #0d2a38; box-shadow: inset 0 0 0 1px rgba(40,243,243,.1); transition: background 180ms ease, box-shadow 180ms ease, transform 180ms ease; }
6
+ .floor.revealed .tile.safe { background: #0b3b2a; box-shadow: inset 0 0 0 1px #3ad67a; }
7
+ .floor.revealed .tile.trap { background: #3b0f14; box-shadow: inset 0 0 0 1px #ff5a6a; }
8
+ .floor.bust .tile.trap { background: #ff5a6a; transform: scale(1.04); }
@@ -0,0 +1,37 @@
1
+ import { mount } from '/engine-sdk/ui.js';
2
+
3
+ const FLOORS = 8;
4
+ const TILES = 4;
5
+ const zone = document.getElementById('tower');
6
+
7
+ for (let i = FLOORS; i >= 1; i--) {
8
+ const row = document.createElement('div');
9
+ row.className = 'floor';
10
+ row.dataset.floor = i;
11
+ for (let t = 0; t < TILES; t++) {
12
+ const tile = document.createElement('div');
13
+ tile.className = 'tile';
14
+ tile.dataset.tile = t;
15
+ row.append(tile);
16
+ }
17
+ zone.append(row);
18
+ }
19
+
20
+ function render(round) {
21
+ const bust = round.state === 'busted'; // round.busted is the drawn path's, also after a cash-out
22
+ for (let i = 1; i <= FLOORS; i++) {
23
+ const row = zone.querySelector(`[data-floor="${i}"]`);
24
+ const data = round.data[i - 1];
25
+ row.classList.toggle('revealed', !!data);
26
+ row.classList.toggle('bust', bust && i === round.step);
27
+ for (const tile of row.children) {
28
+ const t = Number(tile.dataset.tile);
29
+ tile.classList.toggle('trap', !!data && t === data.trap);
30
+ tile.classList.toggle('safe', !!data && t !== data.trap);
31
+ }
32
+ }
33
+ }
34
+
35
+ await mount(document.getElementById('game'), {
36
+ step: (round) => render(round),
37
+ });
@@ -0,0 +1,5 @@
1
+ {
2
+ "stake": { "page": "index.html", "selector": "hb-game hb-bet-amount input", "value": "1.00" },
3
+ "bet": { "page": "index.html", "selector": "hb-game [data-main]" },
4
+ "then": [{ "page": "index.html", "selector": "hb-game [data-next]" }, { "page": "index.html", "selector": "hb-game [data-cashout]" }]
5
+ }
@@ -0,0 +1,16 @@
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>Tower</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="tower" id="tower" aria-live="polite"></div>
14
+ </hb-game>
15
+ </body>
16
+ </html>
@@ -0,0 +1,4 @@
1
+ 1 {"trap":0}
2
+ 1 {"trap":1}
3
+ 1 {"trap":2}
4
+ 1 {"trap":3}
@@ -0,0 +1,61 @@
1
+ {
2
+ "format": "house-engine-math/1",
3
+ "game": "tower",
4
+ "rtpBp": 9900,
5
+ "modes": [
6
+ {
7
+ "id": "climb",
8
+ "label": "Climb",
9
+ "kind": "ladder",
10
+ "steps": [
11
+ {
12
+ "table": "floor.tsv"
13
+ },
14
+ {
15
+ "table": "floor.tsv"
16
+ },
17
+ {
18
+ "table": "floor.tsv"
19
+ },
20
+ {
21
+ "table": "floor.tsv"
22
+ },
23
+ {
24
+ "table": "floor.tsv"
25
+ },
26
+ {
27
+ "table": "floor.tsv"
28
+ },
29
+ {
30
+ "table": "floor.tsv"
31
+ },
32
+ {
33
+ "table": "floor.tsv"
34
+ }
35
+ ],
36
+ "cashout": true,
37
+ "ladderBp": [
38
+ 13200,
39
+ 17600,
40
+ 23466,
41
+ 31288,
42
+ 41718,
43
+ 55624,
44
+ 74166,
45
+ 98888
46
+ ],
47
+ "bets": [
48
+ {
49
+ "id": "climb",
50
+ "label": "Climb",
51
+ "survive": [
52
+ [
53
+ 0,
54
+ 2
55
+ ]
56
+ ]
57
+ }
58
+ ]
59
+ }
60
+ ]
61
+ }
@@ -0,0 +1,3 @@
1
+ # For agents
2
+
3
+ This is a House Engine game (Housebets). Read the guide first: `engine_guide` (MCP) or `engine-sdk/GUIDE.md`. Then `hbengine verify`, `hbengine dev`, `hbengine upload` (or the `engine_*` tools). The math is `math/`, the frontend `bundle/`; never compute money or outcomes in the frontend.
@@ -0,0 +1,18 @@
1
+ # Tumble — a scatter-pays slot with tumbles, from the maths kit
2
+
3
+ A 6 × 5 grid: 8 or more of a symbol anywhere pays; the winners vanish and
4
+ new symbols drop in until nothing pays. Four scatters pay and start ten free
5
+ spins where multiplier symbols (2× to 100×) add up and multiply each winning
6
+ spin; three scatters in free spins add five (up to 30). 5,000× cap. RTP
7
+ exactly 96.00%, hit rate 22.7%.
8
+
9
+ | path | what |
10
+ |---|---|
11
+ | `math/gen.mjs` | the model, 300,000 rounds played into rows, `fit` to exactly 96%; writes `bundle/model.json` |
12
+ | `math-kit/tumble.mjs` | the tumble model (grid, scatter pays, tumbles, free spins, multipliers) |
13
+ | `bundle/game.js` | drops the grid, plays each tumble and the free spins the row holds; a tap skips the free spins |
14
+
15
+ A row: `{"b":[grid, [[winners, winBp, fill per column…]…], multipliers?], "f":[spin…]}`;
16
+ the grid is column by column, top to bottom, one character per symbol.
17
+
18
+ node math/gen.mjs && npx hbengine verify .
@@ -0,0 +1 @@
1
+ {"groups": {"boot": {"files": ["index.html", "game.js", "game.css", "model.json", "i18n.json"]}}}
@@ -0,0 +1,13 @@
1
+ body { margin: 0; background: #08141a; }
2
+ .tumble { container-type: inline-size; display: flex; flex-direction: column; align-items: center; gap: 12px; width: 100%; padding: 14px 12px; box-sizing: border-box; }
3
+ .banner, .win { height: 22px; font: 700 15px/22px 'Red Hat Display', 'Figtree', system-ui, sans-serif; font-variant-numeric: tabular-nums; }
4
+ .banner { color: #8F96A8; font-size: 13px; }
5
+ .win { color: #FDDC73; }
6
+ .grid { --c: min(84px, (100cqi - 40px) / 6); position: relative; display: grid; grid-template-columns: repeat(6, var(--c)); gap: 4px; padding: 8px; border-radius: 14px; background: #061218; box-shadow: inset 0 0 0 1px #0D2A38; }
7
+ .col { position: relative; height: calc(var(--c) * 5); overflow: hidden; border-radius: 8px; background: #0b1f28; }
8
+ .cell { position: absolute; left: 0; top: 0; width: var(--c); height: var(--c); display: grid; place-items: center; transition: transform 260ms cubic-bezier(.3, .7, .3, 1), opacity 200ms; }
9
+ .cell svg { width: 70%; height: 70%; }
10
+ .cell.won { background: rgba(253, 220, 115, .14); border-radius: 8px; }
11
+ .cell.gone { opacity: 0; }
12
+ .cell i { position: absolute; font: 800 calc(var(--c) * .26)/1 'Red Hat Display', system-ui, sans-serif; color: #041015; font-style: normal; }
13
+ @media (prefers-reduced-motion: reduce) { .cell { transition: none; } }
@@ -0,0 +1,119 @@
1
+ // Tumble: a 6 × 5 grid that pays 8 or more of a symbol anywhere. The panel
2
+ // is the engine's (engine-sdk/ui.js); this file plays back the row the
3
+ // server drew: the first grid, each tumble (what paid, what drops in), and
4
+ // the free spins with their multipliers. model.json comes from math/gen.mjs.
5
+ import { mount } from '/engine-sdk/ui.js';
6
+
7
+ const model = await (await fetch('./model.json')).json();
8
+ // The page's own words in the player's language (bundle/i18n.json, English otherwise).
9
+ const I18N = await (await fetch('./i18n.json')).json();
10
+ let W = I18N.en;
11
+ const t = (k, v = {}) => W[k].replace(/\{(\w+)\}/g, (_, x) => v[x]);
12
+ const ROWS = model.rows, COLS = model.cols;
13
+ const gridEl = document.getElementById('grid'), banner = document.getElementById('banner'), winEl = document.getElementById('win');
14
+ const wait = (ms) => new Promise((r) => setTimeout(r, ms));
15
+ const shape = (d, fill, stroke) => `<svg viewBox="0 0 40 40"><path d="${d}" fill="${fill}" stroke="${stroke}" stroke-width="2.2" stroke-linejoin="round"/></svg>`;
16
+ // Deep fills, bright strokes: the big shapes stay muted.
17
+ const ART = {
18
+ R: shape('M20 34L7 21a7.5 7.5 0 0113-9 7.5 7.5 0 0113 9z', '#7e1f3a', '#ff6b81'),
19
+ P: shape('M20 5l13 10-5 17H12L7 15z', '#4a2a6b', '#c89bff'),
20
+ G: shape('M20 5l14 15-14 15L6 20z', '#1f5a3a', '#7cf2b0'),
21
+ B: shape('M8 14l6-8h12l6 8-12 20z', '#0d5f6e', '#5cf6f6'),
22
+ a: shape('M20 7a13 13 0 110 26 13 13 0 010-26z', '#7d5410', '#FDDC73'),
23
+ b: shape('M9 9h22v22H9z', '#2b2f6b', '#a6adff'),
24
+ c: shape('M20 6l15 26H5z', '#5a2a2a', '#ff9f80'),
25
+ d: shape('M12 7h16l7 13-7 13H12L5 20z', '#264a52', '#9fe0ea'),
26
+ e: shape('M20 6l4 10h10l-8 7 3 11-9-6-9 6 3-11-8-7h10z', '#3b4a1f', '#d4f27c'),
27
+ S: shape('M20 4l4.5 10.5L36 16l-8.5 7.5L30 35l-10-6-10 6 2.5-11.5L4 16l11.5-1.5z', '#7d5410', '#FDDC73'),
28
+ M: shape('M20 6a14 14 0 110 28 14 14 0 010-28z', '#F9F9FB', '#FDDC73'),
29
+ };
30
+
31
+ let c = 80;
32
+ const cols = Array.from({ length: COLS }, () => { const e = document.createElement('div'); e.className = 'col'; gridEl.append(e); return { el: e, cells: [] }; });
33
+ const place = (cell, y) => { cell.el.style.transform = `translateY(${y * c}px)`; };
34
+ const make = (sym, y) => {
35
+ const el = document.createElement('div');
36
+ el.className = 'cell';
37
+ el.innerHTML = ART[sym] || sym;
38
+ el.style.transition = 'none';
39
+ el.style.transform = `translateY(${y * c}px)`;
40
+ return { sym, el };
41
+ };
42
+
43
+ // A new grid falls in from above.
44
+ async function drop(grid, quick) {
45
+ c = cols[0].el.getBoundingClientRect().height / ROWS || c;
46
+ cols.forEach((col, x) => {
47
+ col.el.replaceChildren();
48
+ col.cells = [...grid.slice(x * ROWS, x * ROWS + ROWS)].map((s, y) => make(s, y - ROWS - 1));
49
+ col.cells.forEach((cell) => col.el.append(cell.el));
50
+ });
51
+ void gridEl.offsetWidth;
52
+ cols.forEach((col, x) => col.cells.forEach((cell, y) => { cell.el.style.transition = ''; cell.el.style.transitionDelay = quick ? '0ms' : `${x * 40}ms`; place(cell, y); }));
53
+ await wait(quick ? 200 : 520);
54
+ }
55
+
56
+ // One tumble: the winners light up and vanish, the rest fall, new ones drop in.
57
+ async function tumble([paid, , ...fills], quick) {
58
+ cols.forEach((col) => col.cells.forEach((cell) => cell.el.classList.toggle('won', paid.includes(cell.sym))));
59
+ await wait(quick ? 120 : 520);
60
+ cols.forEach((col) => col.cells.forEach((cell) => { if (paid.includes(cell.sym)) cell.el.classList.add('gone'); }));
61
+ await wait(quick ? 60 : 200);
62
+ cols.forEach((col, x) => {
63
+ const kept = col.cells.filter((cell) => !paid.includes(cell.sym));
64
+ col.cells.filter((cell) => paid.includes(cell.sym)).forEach((cell) => cell.el.remove());
65
+ const add = [...fills[x]].map((s, i) => make(s, i - fills[x].length - 1));
66
+ add.forEach((cell) => col.el.append(cell.el));
67
+ col.cells = [...add, ...kept];
68
+ void col.el.offsetWidth;
69
+ col.cells.forEach((cell, y) => { cell.el.style.transition = ''; cell.el.style.transitionDelay = '0ms'; place(cell, y); });
70
+ });
71
+ await wait(quick ? 160 : 360);
72
+ }
73
+
74
+ const times = (bp) => (bp / 10000).toFixed(2) + '×';
75
+
76
+ // One spin of the row: the grid, its tumbles, its multipliers. Returns its win.
77
+ async function play([grid, tumbles, bombs], quick, already) {
78
+ await drop(grid, quick);
79
+ let win = 0;
80
+ for (const t of tumbles) {
81
+ await tumble(t, quick);
82
+ win += t[1];
83
+ winEl.textContent = times(already + win);
84
+ }
85
+ if (bombs) {
86
+ let i = 0;
87
+ cols.forEach((col) => col.cells.forEach((cell) => { if (cell.sym === 'M') cell.el.insertAdjacentHTML('beforeend', `<i>${bombs[i++]}×</i>`); }));
88
+ if (win) { win *= bombs.reduce((a, b) => a + b, 0); winEl.textContent = times(already + win); await wait(quick ? 150 : 600); }
89
+ }
90
+ return win;
91
+ }
92
+
93
+ drop(Array.from({ length: COLS * ROWS }, (_, i) => 'edcbaBGPR'[i % 9]), true);
94
+
95
+ const game = await mount(document.getElementById('game'), {
96
+ async reveal(round) {
97
+ const d = round.data[0];
98
+ winEl.textContent = ''; banner.textContent = '';
99
+ let total = await play(d.b, false, 0);
100
+ const scatters = cols.flatMap((col) => col.cells).filter((cell) => cell.sym === 'S').length; // on the last grid, as the maths counts them
101
+ if (model.scatter.pays[Math.min(scatters, 6)]) total += model.scatter.pays[Math.min(scatters, 6)];
102
+ if (total) winEl.textContent = times(total);
103
+ if (d.f) {
104
+ let skip = false;
105
+ const tap = () => { skip = true; };
106
+ gridEl.addEventListener('pointerdown', tap);
107
+ await wait(600);
108
+ for (let i = 0; i < d.f.length; i++) {
109
+ banner.textContent = t('freeSpins', { i: i + 1, n: d.f.length }) + (skip ? '' : ` · ${t('tapToSkip')}`);
110
+ total += await play(d.f[i], skip, total);
111
+ }
112
+ gridEl.removeEventListener('pointerdown', tap);
113
+ banner.textContent = t('freeOver');
114
+ }
115
+ if (d.cap) winEl.textContent = `${times(model.maxWinBp)} · ${t('maxWin')}`;
116
+ else if (total) winEl.textContent = times(total);
117
+ },
118
+ });
119
+ W = I18N[(game.engine.config.locale || 'en').slice(0, 2)] || I18N.en;
@@ -0,0 +1,4 @@
1
+ {
2
+ "stake": { "page": "index.html", "selector": "hb-game hb-bet-amount input", "value": "1.00" },
3
+ "bet": { "page": "index.html", "selector": "hb-game [data-main]" }
4
+ }
@@ -0,0 +1,122 @@
1
+ {
2
+ "en": {
3
+ "freeSpins": "Free spins {i} of {n}",
4
+ "tapToSkip": "tap to skip",
5
+ "freeOver": "Free spins over",
6
+ "maxWin": "max win"
7
+ },
8
+ "ar": {
9
+ "freeSpins": "دورات مجانية {i} من {n}",
10
+ "tapToSkip": "اضغط للتخطي",
11
+ "freeOver": "انتهت الدورات المجانية",
12
+ "maxWin": "أقصى ربح"
13
+ },
14
+ "da": {
15
+ "freeSpins": "Gratis spins {i} af {n}",
16
+ "tapToSkip": "tryk for at springe over",
17
+ "freeOver": "Gratis spins slut",
18
+ "maxWin": "maks. gevinst"
19
+ },
20
+ "de": {
21
+ "freeSpins": "Freispiele {i} von {n}",
22
+ "tapToSkip": "zum Überspringen tippen",
23
+ "freeOver": "Freispiele beendet",
24
+ "maxWin": "Maximalgewinn"
25
+ },
26
+ "es": {
27
+ "freeSpins": "Giros gratis {i} de {n}",
28
+ "tapToSkip": "toca para saltar",
29
+ "freeOver": "Giros gratis terminados",
30
+ "maxWin": "premio máximo"
31
+ },
32
+ "fi": {
33
+ "freeSpins": "Ilmaiskierrokset {i}/{n}",
34
+ "tapToSkip": "ohita napauttamalla",
35
+ "freeOver": "Ilmaiskierrokset päättyivät",
36
+ "maxWin": "maksimivoitto"
37
+ },
38
+ "fr": {
39
+ "freeSpins": "Tours gratuits {i} sur {n}",
40
+ "tapToSkip": "touchez pour passer",
41
+ "freeOver": "Tours gratuits terminés",
42
+ "maxWin": "gain maximal"
43
+ },
44
+ "hi": {
45
+ "freeSpins": "फ्री स्पिन {i} / {n}",
46
+ "tapToSkip": "छोड़ने के लिए टैप करें",
47
+ "freeOver": "फ्री स्पिन खत्म",
48
+ "maxWin": "अधिकतम जीत"
49
+ },
50
+ "hu": {
51
+ "freeSpins": "Ingyenes pörgetés {i}/{n}",
52
+ "tapToSkip": "koppints az átugráshoz",
53
+ "freeOver": "Vége az ingyenes pörgetéseknek",
54
+ "maxWin": "max. nyeremény"
55
+ },
56
+ "id": {
57
+ "freeSpins": "Putaran gratis {i} dari {n}",
58
+ "tapToSkip": "ketuk untuk lewati",
59
+ "freeOver": "Putaran gratis selesai",
60
+ "maxWin": "kemenangan maks"
61
+ },
62
+ "it": {
63
+ "freeSpins": "Giri gratis {i} di {n}",
64
+ "tapToSkip": "tocca per saltare",
65
+ "freeOver": "Giri gratis finiti",
66
+ "maxWin": "vincita massima"
67
+ },
68
+ "ja": {
69
+ "freeSpins": "フリースピン {i}/{n}",
70
+ "tapToSkip": "タップでスキップ",
71
+ "freeOver": "フリースピン終了",
72
+ "maxWin": "最大配当"
73
+ },
74
+ "ko": {
75
+ "freeSpins": "프리 스핀 {i}/{n}",
76
+ "tapToSkip": "탭하여 건너뛰기",
77
+ "freeOver": "프리 스핀 종료",
78
+ "maxWin": "최대 당첨"
79
+ },
80
+ "pl": {
81
+ "freeSpins": "Darmowe spiny {i} z {n}",
82
+ "tapToSkip": "dotknij, aby pominąć",
83
+ "freeOver": "Koniec darmowych spinów",
84
+ "maxWin": "maks. wygrana"
85
+ },
86
+ "pt": {
87
+ "freeSpins": "Giros grátis {i} de {n}",
88
+ "tapToSkip": "toque para pular",
89
+ "freeOver": "Giros grátis terminados",
90
+ "maxWin": "ganho máximo"
91
+ },
92
+ "ru": {
93
+ "freeSpins": "Бесплатные вращения {i} из {n}",
94
+ "tapToSkip": "нажмите, чтобы пропустить",
95
+ "freeOver": "Бесплатные вращения закончились",
96
+ "maxWin": "максимальный выигрыш"
97
+ },
98
+ "th": {
99
+ "freeSpins": "ฟรีสปิน {i} จาก {n}",
100
+ "tapToSkip": "แตะเพื่อข้าม",
101
+ "freeOver": "ฟรีสปินจบแล้ว",
102
+ "maxWin": "รางวัลสูงสุด"
103
+ },
104
+ "tr": {
105
+ "freeSpins": "Bedava dönüş {i}/{n}",
106
+ "tapToSkip": "geçmek için dokun",
107
+ "freeOver": "Bedava dönüşler bitti",
108
+ "maxWin": "maksimum kazanç"
109
+ },
110
+ "vi": {
111
+ "freeSpins": "Lượt quay miễn phí {i}/{n}",
112
+ "tapToSkip": "chạm để bỏ qua",
113
+ "freeOver": "Hết lượt quay miễn phí",
114
+ "maxWin": "thắng tối đa"
115
+ },
116
+ "zh": {
117
+ "freeSpins": "免费旋转 {i}/{n}",
118
+ "tapToSkip": "点击跳过",
119
+ "freeOver": "免费旋转结束",
120
+ "maxWin": "最高奖金"
121
+ }
122
+ }
@@ -0,0 +1,20 @@
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>Tumble</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="tumble">
14
+ <div class="banner" id="banner" aria-live="polite"></div>
15
+ <div class="grid" id="grid"></div>
16
+ <div class="win" id="win" aria-live="polite"></div>
17
+ </div>
18
+ </hb-game>
19
+ </body>
20
+ </html>
@@ -0,0 +1 @@
1
+ {"cols":6,"rows":5,"weights":{"e":16,"d":15,"c":14,"b":13,"a":12,"B":9,"G":8,"P":7,"R":6,"S":3},"freeWeights":{"e":16,"d":15,"c":14,"b":13,"a":12,"B":9,"G":8,"P":7,"R":6,"S":3,"M":3},"pays":{"R":[[8,250000],[10,600000],[12,1200000]],"P":[[8,60000],[10,250000],[12,600000]],"G":[[8,50000],[10,120000],[12,400000]],"B":[[8,40000],[10,50000],[12,300000]],"a":[[8,25000],[10,40000],[12,250000]],"b":[[8,20000],[10,30000],[12,200000]],"c":[[8,12000],[10,25000],[12,120000]],"d":[[8,10000],[10,22000],[12,100000]],"e":[[8,6000],[10,18000],[12,50000]]},"scatter":{"symbol":"S","pays":{"4":30000,"5":50000,"6":1000000},"spins":{"4":10,"5":10,"6":10},"retrigger":{"count":3,"spins":5},"cap":30},"bomb":{"symbol":"M","values":[[2,40],[3,25],[4,15],[5,10],[10,6],[25,3],[50,1],[100,0.5]]},"maxWinBp":50000000}
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env node
2
+ // The tumble slot's maths: a 6 × 5 grid that pays 8 or more of a symbol
3
+ // anywhere; winners vanish and new symbols drop in until nothing pays. Four
4
+ // scatters start ten free spins where multiplier symbols (2× to 100×) add up
5
+ // and multiply each winning spin; three scatters in free spins add five.
6
+ // 5,000× cap. Played into whole-round rows and fitted to exactly 96.00%;
7
+ // the model goes to the bundle too.
8
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
9
+ import { dirname, join } from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+ import { fit, rng, writePack } from '../math-kit/pack.mjs';
12
+ import { books } from '../math-kit/slot.mjs';
13
+ import { tumble } from '../math-kit/tumble.mjs';
14
+
15
+ const DIR = dirname(fileURLToPath(import.meta.url));
16
+ const project = join(DIR, '..', 'hbengine.json');
17
+ const GAME = process.argv[2] || (existsSync(project) ? JSON.parse(readFileSync(project, 'utf8')).slug : 'tumble');
18
+ const RTP = 9600, ROUNDS = Number(process.env.ROUNDS || 300_000), SEED = 20261015;
19
+
20
+ const weights = { e: 16, d: 15, c: 14, b: 13, a: 12, B: 9, G: 8, P: 7, R: 6, S: 3 };
21
+ const model = {
22
+ cols: 6, rows: 5, weights, freeWeights: { ...weights, M: 3 },
23
+ // × the stake for 8–9, 10–11, 12 or more anywhere (basis points below)
24
+ pays: Object.fromEntries(Object.entries({
25
+ R: [25, 60, 120], P: [6, 25, 60], G: [5, 12, 40], B: [4, 5, 30],
26
+ a: [2.5, 4, 25], b: [2, 3, 20], c: [1.2, 2.5, 12], d: [1, 2.2, 10], e: [0.6, 1.8, 5],
27
+ }).map(([s, [x, y, z]]) => [s, [[8, x * 10000], [10, y * 10000], [12, z * 10000]]])),
28
+ scatter: { symbol: 'S', pays: { 4: 30000, 5: 50000, 6: 1000000 }, spins: { 4: 10, 5: 10, 6: 10 }, retrigger: { count: 3, spins: 5 }, cap: 30 },
29
+ bomb: { symbol: 'M', values: [[2, 40], [3, 25], [4, 15], [5, 10], [10, 6], [25, 3], [50, 1], [100, 0.5]] },
30
+ maxWinBp: 50_000_000,
31
+ };
32
+
33
+ const rows = books(tumble(model), rng(SEED), ROUNDS);
34
+ const raw = rows.reduce((a, r) => [a[0] + r.weight * BigInt(r.pay.spin), a[1] + r.weight], [0n, 0n]);
35
+ const hits = rows.reduce((a, r) => a + (r.pay.spin > 0 ? r.weight : 0n), 0n);
36
+ console.log(`played ${ROUNDS}: before the fit RTP ${(Number(raw[0] * 1000000n / raw[1]) / 1e6 / 100).toFixed(2)}%, hit rate ${(Number(hits * 10000n / BigInt(ROUNDS)) / 100).toFixed(2)}%`);
37
+ writePack(DIR, { game: GAME, rtpBp: RTP, modes: [{ id: 'base', label: 'Spin', rows: fit(rows, RTP), bets: [{ id: 'spin', label: 'Spin' }] }] });
38
+ writeFileSync(join(DIR, '..', 'bundle', 'model.json'), JSON.stringify(model) + '\n');