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