@cubingcenter/scrambler 0.6.3 → 0.8.0

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 (191) hide show
  1. package/README.md +17 -4
  2. package/dist/core/api.d.ts.map +1 -1
  3. package/dist/core/api.js +10 -2
  4. package/dist/puzzles/cube2/solver/browser-loader.d.ts +4 -7
  5. package/dist/puzzles/cube2/solver/browser-loader.d.ts.map +1 -1
  6. package/dist/puzzles/cube2/solver/browser-loader.js +42 -14
  7. package/dist/puzzles/cube2/solver/generated-assets.d.ts.map +1 -1
  8. package/dist/puzzles/cube2/solver/generated-assets.js +8 -3
  9. package/dist/puzzles/cube2/solver/search.d.ts.map +1 -1
  10. package/dist/puzzles/cube2/solver/search.js +3 -3
  11. package/dist/puzzles/cube2/solver/tables.d.ts +6 -0
  12. package/dist/puzzles/cube2/solver/tables.d.ts.map +1 -1
  13. package/dist/puzzles/cube2/solver/tables.js +16 -2
  14. package/dist/puzzles/cube3/index.d.ts +1 -0
  15. package/dist/puzzles/cube3/index.d.ts.map +1 -1
  16. package/dist/puzzles/cube3/moves.d.ts.map +1 -1
  17. package/dist/puzzles/cube3/moves.js +104 -18
  18. package/dist/puzzles/cube3/scrambler.d.ts +16 -1
  19. package/dist/puzzles/cube3/scrambler.d.ts.map +1 -1
  20. package/dist/puzzles/cube3/scrambler.js +1 -1
  21. package/dist/puzzles/cube3/solver/browser-loader.d.ts.map +1 -1
  22. package/dist/puzzles/cube3/solver/browser-loader.js +48 -22
  23. package/dist/puzzles/cube3/solver/generated-assets.d.ts.map +1 -1
  24. package/dist/puzzles/cube3/solver/generated-assets.js +9 -3
  25. package/dist/puzzles/cube3/solver/index.d.ts +5 -3
  26. package/dist/puzzles/cube3/solver/index.d.ts.map +1 -1
  27. package/dist/puzzles/cube3/solver/index.js +2 -1
  28. package/dist/puzzles/cube3/solver/node-loader.js +9 -2
  29. package/dist/puzzles/cube3/solver/phase1.d.ts +37 -0
  30. package/dist/puzzles/cube3/solver/phase1.d.ts.map +1 -1
  31. package/dist/puzzles/cube3/solver/phase1.js +156 -15
  32. package/dist/puzzles/cube3/solver/phase2.d.ts +75 -0
  33. package/dist/puzzles/cube3/solver/phase2.d.ts.map +1 -1
  34. package/dist/puzzles/cube3/solver/phase2.js +279 -68
  35. package/dist/puzzles/cube3/solver/search-utils.d.ts +1 -1
  36. package/dist/puzzles/cube3/solver/search-utils.d.ts.map +1 -1
  37. package/dist/puzzles/cube3/solver/symmetry.d.ts +24 -0
  38. package/dist/puzzles/cube3/solver/symmetry.d.ts.map +1 -0
  39. package/dist/puzzles/cube3/solver/symmetry.js +74 -0
  40. package/dist/puzzles/cube3/solver/tables.d.ts +6 -1
  41. package/dist/puzzles/cube3/solver/tables.d.ts.map +1 -1
  42. package/dist/puzzles/cube3/solver/tables.js +14 -12
  43. package/dist/puzzles/cube3/validation.d.ts.map +1 -1
  44. package/dist/puzzles/cube3/validation.js +15 -7
  45. package/dist/puzzles/cube4/browser-worker-pool.d.ts +82 -0
  46. package/dist/puzzles/cube4/browser-worker-pool.d.ts.map +1 -0
  47. package/dist/puzzles/cube4/browser-worker-pool.js +205 -0
  48. package/dist/puzzles/cube4/browser-worker.d.ts +2 -0
  49. package/dist/puzzles/cube4/browser-worker.d.ts.map +1 -0
  50. package/dist/puzzles/cube4/browser-worker.js +31 -0
  51. package/dist/puzzles/cube4/index.d.ts +10 -2
  52. package/dist/puzzles/cube4/index.d.ts.map +1 -1
  53. package/dist/puzzles/cube4/index.js +6 -2
  54. package/dist/puzzles/cube4/moves.d.ts +5 -0
  55. package/dist/puzzles/cube4/moves.d.ts.map +1 -1
  56. package/dist/puzzles/cube4/moves.js +212 -11
  57. package/dist/puzzles/cube4/reduction/browser-loader.d.ts +29 -1
  58. package/dist/puzzles/cube4/reduction/browser-loader.d.ts.map +1 -1
  59. package/dist/puzzles/cube4/reduction/browser-loader.js +227 -32
  60. package/dist/puzzles/cube4/reduction/center2.d.ts +44 -3
  61. package/dist/puzzles/cube4/reduction/center2.d.ts.map +1 -1
  62. package/dist/puzzles/cube4/reduction/center2.js +152 -10
  63. package/dist/puzzles/cube4/reduction/edge3.d.ts +4 -0
  64. package/dist/puzzles/cube4/reduction/edge3.d.ts.map +1 -1
  65. package/dist/puzzles/cube4/reduction/edge3.js +23 -3
  66. package/dist/puzzles/cube4/reduction/finish3.d.ts +53 -0
  67. package/dist/puzzles/cube4/reduction/finish3.d.ts.map +1 -0
  68. package/dist/puzzles/cube4/reduction/finish3.js +393 -0
  69. package/dist/puzzles/cube4/reduction/generated/c3_pair_dist.bin +1 -0
  70. package/dist/puzzles/cube4/reduction/generated/center2-manifest.json +36 -0
  71. package/dist/puzzles/cube4/reduction/generated/center2-pruning-wide.bin +1 -0
  72. package/dist/puzzles/cube4/reduction/generated/hc-distances.bin +1 -0
  73. package/dist/puzzles/cube4/reduction/generated/hc-manifest.json +44 -0
  74. package/dist/puzzles/cube4/reduction/generated/hc-move.bin +0 -0
  75. package/dist/puzzles/cube4/reduction/hc-pdb.d.ts +52 -0
  76. package/dist/puzzles/cube4/reduction/hc-pdb.d.ts.map +1 -0
  77. package/dist/puzzles/cube4/reduction/hc-pdb.js +220 -0
  78. package/dist/puzzles/cube4/reduction/node-loader.d.ts +15 -1
  79. package/dist/puzzles/cube4/reduction/node-loader.d.ts.map +1 -1
  80. package/dist/puzzles/cube4/reduction/node-loader.js +199 -25
  81. package/dist/puzzles/cube4/reduction/pair-transversal.d.ts +28 -0
  82. package/dist/puzzles/cube4/reduction/pair-transversal.d.ts.map +1 -0
  83. package/dist/puzzles/cube4/reduction/pair-transversal.js +107 -0
  84. package/dist/puzzles/cube4/reduction/pairing-coordinate.js +5 -5
  85. package/dist/puzzles/cube4/reduction/phase3-search.d.ts +116 -7
  86. package/dist/puzzles/cube4/reduction/phase3-search.d.ts.map +1 -1
  87. package/dist/puzzles/cube4/reduction/phase3-search.js +458 -32
  88. package/dist/puzzles/cube4/reduction/rotation-fix.d.ts +28 -0
  89. package/dist/puzzles/cube4/reduction/rotation-fix.d.ts.map +1 -0
  90. package/dist/puzzles/cube4/reduction/rotation-fix.js +212 -0
  91. package/dist/puzzles/cube4/reduction/solve.d.ts +172 -2
  92. package/dist/puzzles/cube4/reduction/solve.d.ts.map +1 -1
  93. package/dist/puzzles/cube4/reduction/solve.js +1201 -180
  94. package/dist/puzzles/cube4/scramble-cache.d.ts +93 -0
  95. package/dist/puzzles/cube4/scramble-cache.d.ts.map +1 -0
  96. package/dist/puzzles/cube4/scramble-cache.js +150 -0
  97. package/dist/puzzles/cube4/scrambler.d.ts +201 -6
  98. package/dist/puzzles/cube4/scrambler.d.ts.map +1 -1
  99. package/dist/puzzles/cube4/scrambler.js +414 -176
  100. package/dist/puzzles/cube4/state.d.ts.map +1 -1
  101. package/dist/puzzles/cube4/state.js +4 -1
  102. package/dist/puzzles/cube4/worker-pool.d.ts +139 -0
  103. package/dist/puzzles/cube4/worker-pool.d.ts.map +1 -0
  104. package/dist/puzzles/cube4/worker-pool.js +405 -0
  105. package/dist/puzzles/cube4/worker-protocol.d.ts +58 -0
  106. package/dist/puzzles/cube4/worker-protocol.d.ts.map +1 -0
  107. package/dist/puzzles/cube4/worker-protocol.js +58 -0
  108. package/dist/puzzles/cube4/worker.d.ts +26 -0
  109. package/dist/puzzles/cube4/worker.d.ts.map +1 -0
  110. package/dist/puzzles/cube4/worker.js +49 -0
  111. package/dist/puzzles/pyraminx/solver/browser-loader.d.ts +4 -8
  112. package/dist/puzzles/pyraminx/solver/browser-loader.d.ts.map +1 -1
  113. package/dist/puzzles/pyraminx/solver/browser-loader.js +44 -15
  114. package/dist/puzzles/pyraminx/solver/generated-assets.d.ts.map +1 -1
  115. package/dist/puzzles/pyraminx/solver/generated-assets.js +8 -3
  116. package/dist/puzzles/pyraminx/solver/search.d.ts.map +1 -1
  117. package/dist/puzzles/pyraminx/solver/search.js +3 -6
  118. package/dist/puzzles/pyraminx/solver/tables.d.ts +6 -0
  119. package/dist/puzzles/pyraminx/solver/tables.d.ts.map +1 -1
  120. package/dist/puzzles/pyraminx/solver/tables.js +17 -0
  121. package/dist/puzzles/shared/compressed-fetch.d.ts +37 -1
  122. package/dist/puzzles/shared/compressed-fetch.d.ts.map +1 -1
  123. package/dist/puzzles/shared/compressed-fetch.js +226 -31
  124. package/dist/puzzles/shared/load-once.d.ts +16 -0
  125. package/dist/puzzles/shared/load-once.d.ts.map +1 -0
  126. package/dist/puzzles/shared/load-once.js +11 -0
  127. package/dist/puzzles/skewb/solver/browser-loader.d.ts +4 -11
  128. package/dist/puzzles/skewb/solver/browser-loader.d.ts.map +1 -1
  129. package/dist/puzzles/skewb/solver/browser-loader.js +44 -16
  130. package/dist/puzzles/skewb/solver/generated-assets.d.ts.map +1 -1
  131. package/dist/puzzles/skewb/solver/generated-assets.js +8 -3
  132. package/dist/puzzles/skewb/solver/search.js +3 -3
  133. package/dist/puzzles/skewb/solver/tables.d.ts +6 -0
  134. package/dist/puzzles/skewb/solver/tables.d.ts.map +1 -1
  135. package/dist/puzzles/skewb/solver/tables.js +17 -0
  136. package/dist/puzzles/sq1/solver/browser-loader.d.ts.map +1 -1
  137. package/dist/puzzles/sq1/solver/browser-loader.js +58 -26
  138. package/dist-cjs/core/api.js +10 -2
  139. package/dist-cjs/puzzles/cube2/solver/browser-loader.js +42 -14
  140. package/dist-cjs/puzzles/cube2/solver/generated-assets.js +8 -3
  141. package/dist-cjs/puzzles/cube2/solver/search.js +2 -2
  142. package/dist-cjs/puzzles/cube2/solver/tables.js +17 -3
  143. package/dist-cjs/puzzles/cube3/moves.js +104 -18
  144. package/dist-cjs/puzzles/cube3/scrambler.js +1 -1
  145. package/dist-cjs/puzzles/cube3/solver/browser-loader.js +48 -22
  146. package/dist-cjs/puzzles/cube3/solver/generated-assets.js +9 -3
  147. package/dist-cjs/puzzles/cube3/solver/index.js +8 -1
  148. package/dist-cjs/puzzles/cube3/solver/node-loader.js +9 -2
  149. package/dist-cjs/puzzles/cube3/solver/phase1.js +157 -15
  150. package/dist-cjs/puzzles/cube3/solver/phase2.js +276 -65
  151. package/dist-cjs/puzzles/cube3/solver/symmetry.js +80 -0
  152. package/dist-cjs/puzzles/cube3/solver/tables.js +14 -12
  153. package/dist-cjs/puzzles/cube3/validation.js +15 -7
  154. package/dist-cjs/puzzles/cube4/browser-worker-pool.js +211 -0
  155. package/dist-cjs/puzzles/cube4/index.js +23 -1
  156. package/dist-cjs/puzzles/cube4/moves.js +213 -11
  157. package/dist-cjs/puzzles/cube4/reduction/browser-loader.js +227 -32
  158. package/dist-cjs/puzzles/cube4/reduction/center2.js +155 -10
  159. package/dist-cjs/puzzles/cube4/reduction/edge3.js +24 -3
  160. package/dist-cjs/puzzles/cube4/reduction/finish3.js +398 -0
  161. package/dist-cjs/puzzles/cube4/reduction/generated/c3_pair_dist.bin +1 -0
  162. package/dist-cjs/puzzles/cube4/reduction/generated/center2-manifest.json +36 -0
  163. package/dist-cjs/puzzles/cube4/reduction/generated/center2-pruning-wide.bin +1 -0
  164. package/dist-cjs/puzzles/cube4/reduction/generated/hc-distances.bin +1 -0
  165. package/dist-cjs/puzzles/cube4/reduction/generated/hc-manifest.json +44 -0
  166. package/dist-cjs/puzzles/cube4/reduction/generated/hc-move.bin +0 -0
  167. package/dist-cjs/puzzles/cube4/reduction/hc-pdb.js +232 -0
  168. package/dist-cjs/puzzles/cube4/reduction/node-loader.js +200 -25
  169. package/dist-cjs/puzzles/cube4/reduction/pair-transversal.js +112 -0
  170. package/dist-cjs/puzzles/cube4/reduction/pairing-coordinate.js +5 -5
  171. package/dist-cjs/puzzles/cube4/reduction/phase3-search.js +460 -33
  172. package/dist-cjs/puzzles/cube4/reduction/rotation-fix.js +216 -0
  173. package/dist-cjs/puzzles/cube4/reduction/solve.js +1203 -172
  174. package/dist-cjs/puzzles/cube4/scramble-cache.js +156 -0
  175. package/dist-cjs/puzzles/cube4/scrambler.js +416 -174
  176. package/dist-cjs/puzzles/cube4/state.js +4 -1
  177. package/dist-cjs/puzzles/cube4/worker-pool.js +447 -0
  178. package/dist-cjs/puzzles/cube4/worker-protocol.js +62 -0
  179. package/dist-cjs/puzzles/pyraminx/solver/browser-loader.js +44 -15
  180. package/dist-cjs/puzzles/pyraminx/solver/generated-assets.js +8 -3
  181. package/dist-cjs/puzzles/pyraminx/solver/search.js +2 -5
  182. package/dist-cjs/puzzles/pyraminx/solver/tables.js +18 -1
  183. package/dist-cjs/puzzles/shared/compressed-fetch.js +229 -31
  184. package/dist-cjs/puzzles/shared/load-once.js +14 -0
  185. package/dist-cjs/puzzles/skewb/solver/browser-loader.js +44 -16
  186. package/dist-cjs/puzzles/skewb/solver/generated-assets.js +8 -3
  187. package/dist-cjs/puzzles/skewb/solver/search.js +2 -2
  188. package/dist-cjs/puzzles/skewb/solver/tables.js +18 -1
  189. package/dist-cjs/puzzles/sq1/solver/browser-loader.js +58 -26
  190. package/docs/development.md +19 -7
  191. package/package.json +17 -7
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Message protocol shared by the 4x4 Node worker (`worker.ts`), its pool
3
+ * (`worker-pool.ts`), and the browser equivalents (`browser-worker.ts`,
4
+ * `browser-worker-pool.ts`).
5
+ *
6
+ * Design notes:
7
+ * - Tasks carry the FULL `generateCube4Scramble` options object. The
8
+ * scrambler is a pure function of (seed, complete options), so forwarding
9
+ * every option verbatim is what makes the pool output bit-identical to
10
+ * the inline path for the same inputs.
11
+ * - Errors cross the thread boundary as data (`Cube4SerializedError`):
12
+ * `name`, `message`, `code` and `stack` survive, so typed solver errors
13
+ * (e.g. `Cube4TargetMismatchError` with `code: "cube4-target-mismatch"`)
14
+ * remain discriminated on the consumer side by name/code — the same
15
+ * classification keys `scrambler.ts` itself uses. Classes cannot be
16
+ * structured-cloned, so `instanceof` does not cross the boundary
17
+ * (documented contract).
18
+ *
19
+ * This module is deliberately free of runtime imports (type-only imports
20
+ * are erased at emit) so it can be bundled for the browser worker without
21
+ * dragging Node-only code into the graph.
22
+ */
23
+ /**
24
+ * Flattens an unknown thrown value into the structured-clone-safe shape
25
+ * above. Non-Error values are stringified so nothing is lost silently.
26
+ */
27
+ export function serializeCube4Error(error) {
28
+ if (error instanceof Error) {
29
+ const code = error.code;
30
+ const primitiveCode = typeof code === "string" || typeof code === "number" ? code : undefined;
31
+ const serialized = {
32
+ name: error.name,
33
+ message: error.message
34
+ };
35
+ if (primitiveCode !== undefined)
36
+ serialized.code = primitiveCode;
37
+ if (typeof error.stack === "string")
38
+ serialized.stack = error.stack;
39
+ return serialized;
40
+ }
41
+ return { name: "Error", message: String(error) };
42
+ }
43
+ /**
44
+ * Rebuilds an Error from the wire shape, restoring `name`, `code` and
45
+ * `stack` (stack kept only as a debugging aid — its file locations point
46
+ * into the worker thread).
47
+ */
48
+ export function deserializeCube4Error(serialized) {
49
+ const error = new Error(serialized.message);
50
+ error.name = serialized.name;
51
+ if (serialized.code !== undefined) {
52
+ error.code = serialized.code;
53
+ }
54
+ if (serialized.stack !== undefined) {
55
+ error.stack = serialized.stack;
56
+ }
57
+ return error;
58
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Node.js worker_thread entry for 4x4 scramble generation.
3
+ *
4
+ * Lifecycle / lazy init: the thread boots without doing any work; the
5
+ * reduction + 3x3 tables (≈37 MB) are loaded on the FIRST task and then
6
+ * reused for every later task, because `scrambler.ts` deduplicates table
7
+ * loads per thread via its promise cache. Spawning is driven lazily by
8
+ * `worker-pool.ts` (one thread per queued job, up to the pool size), so a
9
+ * small batch never pays for idle threads.
10
+ *
11
+ * Protocol: see `worker-protocol.ts`. One task in flight per thread (the
12
+ * pool only re-dispatches after a response), but the handler is written to
13
+ * cope with concurrent messages just in case.
14
+ *
15
+ * Source-vs-dist loading: this file is excluded from the CommonJS build,
16
+ * so `import.meta` is safe here. When running from source the thread is
17
+ * booted with `execArgv: ["--import", "tsx"]` by the pool, but that flag
18
+ * alone does NOT register tsx's resolve hooks inside a worker thread
19
+ * (measured: `./x.js` → `./x.ts` mapping missing). Therefore, before any
20
+ * value import of the repo's `.js`-suffixed specifiers, we register the
21
+ * tsx hooks explicitly — `tsx/esm/api` resolves because source mode only
22
+ * happens inside a checkout with node_modules. From `dist/` no tsx is
23
+ * involved: the `.js` files are real.
24
+ */
25
+ export {};
26
+ //# sourceMappingURL=worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube4/worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Node.js worker_thread entry for 4x4 scramble generation.
3
+ *
4
+ * Lifecycle / lazy init: the thread boots without doing any work; the
5
+ * reduction + 3x3 tables (≈37 MB) are loaded on the FIRST task and then
6
+ * reused for every later task, because `scrambler.ts` deduplicates table
7
+ * loads per thread via its promise cache. Spawning is driven lazily by
8
+ * `worker-pool.ts` (one thread per queued job, up to the pool size), so a
9
+ * small batch never pays for idle threads.
10
+ *
11
+ * Protocol: see `worker-protocol.ts`. One task in flight per thread (the
12
+ * pool only re-dispatches after a response), but the handler is written to
13
+ * cope with concurrent messages just in case.
14
+ *
15
+ * Source-vs-dist loading: this file is excluded from the CommonJS build,
16
+ * so `import.meta` is safe here. When running from source the thread is
17
+ * booted with `execArgv: ["--import", "tsx"]` by the pool, but that flag
18
+ * alone does NOT register tsx's resolve hooks inside a worker thread
19
+ * (measured: `./x.js` → `./x.ts` mapping missing). Therefore, before any
20
+ * value import of the repo's `.js`-suffixed specifiers, we register the
21
+ * tsx hooks explicitly — `tsx/esm/api` resolves because source mode only
22
+ * happens inside a checkout with node_modules. From `dist/` no tsx is
23
+ * involved: the `.js` files are real.
24
+ */
25
+ import { parentPort } from "node:worker_threads";
26
+ if (!parentPort) {
27
+ throw new Error("[cube4] worker.ts must be started as a worker_thread (use Cube4WorkerPool).");
28
+ }
29
+ const port = parentPort;
30
+ if (import.meta.url.endsWith(".ts")) {
31
+ const tsxApi = await import("tsx/esm/api");
32
+ tsxApi.register();
33
+ }
34
+ const { generateCube4Scramble } = await import("./scrambler.js");
35
+ const { serializeCube4Error } = await import("./worker-protocol.js");
36
+ port.on("message", (task) => {
37
+ void (async () => {
38
+ const response = await (async () => {
39
+ try {
40
+ const result = await generateCube4Scramble(task.options);
41
+ return { id: task.id, ok: true, result };
42
+ }
43
+ catch (error) {
44
+ return { id: task.id, ok: false, error: serializeCube4Error(error) };
45
+ }
46
+ })();
47
+ port.postMessage(response);
48
+ })();
49
+ });
@@ -1,14 +1,10 @@
1
- export declare function getCachedBrowserTables(): {
1
+ export interface PyraminxBrowserTables {
2
2
  centersTable: Uint8Array;
3
3
  permutationTable: Uint16Array;
4
4
  orientationTable: Uint8Array;
5
5
  policyTable: Uint8Array;
6
- } | undefined;
6
+ }
7
+ export declare function getCachedBrowserTables(): PyraminxBrowserTables | undefined;
7
8
  export declare function clearCachedBrowserTables(): void;
8
- export declare function loadBrowserTables(baseUrl?: string): Promise<{
9
- centersTable: Uint8Array;
10
- permutationTable: Uint16Array;
11
- orientationTable: Uint8Array;
12
- policyTable: Uint8Array;
13
- }>;
9
+ export declare function loadBrowserTables(baseUrl?: string): Promise<PyraminxBrowserTables>;
14
10
  //# sourceMappingURL=browser-loader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"browser-loader.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/pyraminx/solver/browser-loader.ts"],"names":[],"mappings":"AAUA,wBAAgB,sBAAsB;kBANtB,UAAU;sBACN,WAAW;sBACX,UAAU;iBACf,UAAU;cAKxB;AACD,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAED,wBAAsB,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACjE,YAAY,EAAE,UAAU,CAAC;IACzB,gBAAgB,EAAE,WAAW,CAAC;IAC9B,gBAAgB,EAAE,UAAU,CAAC;IAC7B,WAAW,EAAE,UAAU,CAAC;CACzB,CAAC,CAYD"}
1
+ {"version":3,"file":"browser-loader.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/pyraminx/solver/browser-loader.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,UAAU,CAAC;IACzB,gBAAgB,EAAE,WAAW,CAAC;IAC9B,gBAAgB,EAAE,UAAU,CAAC;IAC7B,WAAW,EAAE,UAAU,CAAC;CACzB;AAKD,wBAAgB,sBAAsB,IAAI,qBAAqB,GAAG,SAAS,CAE1E;AAED,wBAAgB,wBAAwB,IAAI,IAAI,CAG/C;AAED,wBAAsB,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAkBxF"}
@@ -1,22 +1,42 @@
1
1
  import { PYRAMINX_SOLVER_TABLE_METADATA } from "./table-metadata.js";
2
2
  import { fetchTableBuffer as fetchCompressedTableBuffer } from "../../shared/compressed-fetch.js";
3
+ const TABLES_DIR = "puzzles/pyraminx/solver/generated";
4
+ const CACHE_NAME = "cubingcenter-scrambler-tables-v1";
3
5
  let cachedBrowserTables;
6
+ let cachedBrowserTablesPromise;
4
7
  export function getCachedBrowserTables() {
5
8
  return cachedBrowserTables;
6
9
  }
7
10
  export function clearCachedBrowserTables() {
8
11
  cachedBrowserTables = undefined;
12
+ cachedBrowserTablesPromise = undefined;
9
13
  }
10
14
  export async function loadBrowserTables(baseUrl) {
11
15
  if (cachedBrowserTables)
12
16
  return cachedBrowserTables;
17
+ if (cachedBrowserTablesPromise) {
18
+ // One shared in-flight promise: concurrent calls trigger exactly one
19
+ // fetch pass (dedup).
20
+ return cachedBrowserTablesPromise;
21
+ }
22
+ const promise = fetchTables(baseUrl);
23
+ cachedBrowserTablesPromise = promise;
24
+ promise.then(() => { }, () => {
25
+ // A failed load must not poison the dedup slot: purge the rejected
26
+ // promise so a later call retries from scratch.
27
+ if (cachedBrowserTablesPromise === promise)
28
+ cachedBrowserTablesPromise = undefined;
29
+ });
30
+ return promise;
31
+ }
32
+ async function fetchTables(baseUrl) {
13
33
  const resolved = resolveBaseUrl(baseUrl);
14
34
  const manifest = await fetchManifest(resolved);
15
35
  const [centersTable, permutationTable, orientationTable, policyTable] = await Promise.all([
16
- fetchUint8Table(resolved, manifest.files.centersTable, PYRAMINX_SOLVER_TABLE_METADATA.centersTable.entries),
17
- fetchUint16Table(resolved, manifest.files.permutationTable, PYRAMINX_SOLVER_TABLE_METADATA.permutationTable.entries),
18
- fetchUint8Table(resolved, manifest.files.orientationTable, PYRAMINX_SOLVER_TABLE_METADATA.orientationTable.entries),
19
- fetchUint8Table(resolved, manifest.files.policyTable, PYRAMINX_SOLVER_TABLE_METADATA.policyTable.entries)
36
+ fetchUint8Table(resolved, manifest.files.centersTable, PYRAMINX_SOLVER_TABLE_METADATA.centersTable),
37
+ fetchUint16Table(resolved, manifest.files.permutationTable, PYRAMINX_SOLVER_TABLE_METADATA.permutationTable),
38
+ fetchUint8Table(resolved, manifest.files.orientationTable, PYRAMINX_SOLVER_TABLE_METADATA.orientationTable),
39
+ fetchUint8Table(resolved, manifest.files.policyTable, PYRAMINX_SOLVER_TABLE_METADATA.policyTable)
20
40
  ]);
21
41
  cachedBrowserTables = { centersTable, permutationTable, orientationTable, policyTable };
22
42
  return cachedBrowserTables;
@@ -43,19 +63,28 @@ async function fetchManifest(baseUrl) {
43
63
  throw new Error(`Unsupported pyraminx solver table manifest version: ${manifest.version}`);
44
64
  return manifest;
45
65
  }
46
- const TABLES_DIR = "puzzles/pyraminx/solver/generated";
47
- const CACHE_NAME = "cubingcenter-scrambler-tables-v1";
48
- async function fetchUint8Table(baseUrl, entry, expectedEntries) {
49
- const buffer = await fetchCompressedTableBuffer(baseUrl, TABLES_DIR, entry.file, { cacheName: CACHE_NAME, compressed: entry.compressed !== false });
50
- if (buffer.byteLength !== expectedEntries)
51
- throw new Error(`Invalid byte size for ${entry.file}`);
66
+ /**
67
+ * Fetch + verify with the SAME checks as node-loader.ts: byte length from
68
+ * the manifest entries, FNV-1a checksum against the table-metadata
69
+ * constants (the values node asserts against).
70
+ */
71
+ async function fetchUint8Table(baseUrl, entry, meta) {
72
+ const buffer = await fetchCompressedTableBuffer(baseUrl, TABLES_DIR, entry.file, {
73
+ cacheName: CACHE_NAME,
74
+ compressed: entry.compressed !== false,
75
+ expectedBytes: entry.entries,
76
+ expectedFnv1a: { value: meta.checksum, elementSize: 1 }
77
+ });
52
78
  return new Uint8Array(buffer);
53
79
  }
54
- async function fetchUint16Table(baseUrl, entry, expectedEntries) {
55
- const buffer = await fetchCompressedTableBuffer(baseUrl, TABLES_DIR, entry.file, { cacheName: CACHE_NAME, compressed: entry.compressed !== false });
56
- if (buffer.byteLength !== expectedEntries * 2)
57
- throw new Error(`Invalid byte size for ${entry.file}`);
58
- const values = new Uint16Array(expectedEntries);
80
+ async function fetchUint16Table(baseUrl, entry, meta) {
81
+ const buffer = await fetchCompressedTableBuffer(baseUrl, TABLES_DIR, entry.file, {
82
+ cacheName: CACHE_NAME,
83
+ compressed: entry.compressed !== false,
84
+ expectedBytes: entry.entries * 2,
85
+ expectedFnv1a: { value: meta.checksum, elementSize: 2 }
86
+ });
87
+ const values = new Uint16Array(entry.entries);
59
88
  const view = new DataView(buffer);
60
89
  for (let i = 0; i < values.length; i++)
61
90
  values[i] = view.getUint16(i * 2, true);
@@ -1 +1 @@
1
- {"version":3,"file":"generated-assets.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/pyraminx/solver/generated-assets.ts"],"names":[],"mappings":"AAaA,wBAAsB,iCAAiC,IAAI,OAAO,CAAC;IACjE,YAAY,EAAE,UAAU,CAAC;IACzB,gBAAgB,EAAE,WAAW,CAAC;IAC9B,gBAAgB,EAAE,UAAU,CAAC;IAC7B,WAAW,EAAE,UAAU,CAAC;CACzB,CAAC,CAYD"}
1
+ {"version":3,"file":"generated-assets.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/pyraminx/solver/generated-assets.ts"],"names":[],"mappings":"AAmBA,wBAAsB,iCAAiC,IAAI,OAAO,CAAC;IACjE,YAAY,EAAE,UAAU,CAAC;IACzB,gBAAgB,EAAE,WAAW,CAAC;IAC9B,gBAAgB,EAAE,UAAU,CAAC;IAC7B,WAAW,EAAE,UAAU,CAAC;CACzB,CAAC,CAWD"}
@@ -1,5 +1,11 @@
1
1
  import { getCachedBrowserTables } from "./browser-loader.js";
2
- let cachedTables;
2
+ import { loadOnce } from "../../shared/load-once.js";
3
+ // One shared in-flight promise for the node fs load (same pattern as
4
+ // cube3/solver/generated-assets.ts): the previous `cached ??= await …` raced —
5
+ // concurrent callers interleaving on the await both ran `loadNodeTables()`
6
+ // (two filesystem reads, last assignment wins). On rejection the slot is
7
+ // purged so the next call can retry.
8
+ const nodeTablesSlot = { promise: undefined };
3
9
  function isBrowser() {
4
10
  return typeof self !== "undefined";
5
11
  }
@@ -12,8 +18,7 @@ export async function loadGeneratedPyraminxSolverTables() {
12
18
  "In browser environments, call `initializeScrambler(baseUrl)` " +
13
19
  "before generating scrambles.");
14
20
  }
15
- cachedTables ??= await loadNodeTables();
16
- return cachedTables;
21
+ return loadOnce(nodeTablesSlot, loadNodeTables);
17
22
  }
18
23
  async function loadNodeTables() {
19
24
  const loader = await import(
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/pyraminx/solver/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAe/D,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAGjF;AAED,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAG3F;AAMD,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAGlF;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,EAAE,CAGvE;AAuCD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,YAAY,EAAE,CAGtE;AAaD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,YAAY,EAAE,GAAG,OAAO,CAsCzG;AAKD,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B,OAAO,CAmCT"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/pyraminx/solver/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAgB/D,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAGjF;AAED,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAG3F;AAMD,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAGlF;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,EAAE,CAGvE;AAoCD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,YAAY,EAAE,CAGtE;AAaD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,YAAY,EAAE,GAAG,OAAO,CAsCzG;AAKD,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B,OAAO,CAmCT"}
@@ -1,4 +1,4 @@
1
- import { POLICY_TABLE, CENTERS_TABLE, PERMUTATION_TABLE, ORIENTATION_TABLE, PYRAMINX_MOVES, MOVE_COUNT, PERMUTATION_COUNT, getCentersIndex, getOrientationIndex, getPermutationCompactIndex, ensureTablesLoaded } from "./tables.js";
1
+ import { POLICY_TABLE, POLICY_TABLE_EXPANDED, CENTERS_TABLE, PERMUTATION_TABLE, ORIENTATION_TABLE, PYRAMINX_MOVES, MOVE_COUNT, PERMUTATION_COUNT, getCentersIndex, getOrientationIndex, getPermutationCompactIndex, ensureTablesLoaded } from "./tables.js";
2
2
  export async function solvePyraminx(state) {
3
3
  await ensureTablesLoaded();
4
4
  return solveWithTables(state).moves;
@@ -29,7 +29,7 @@ function solveWithTablesIndices(state) {
29
29
  const centersTable = CENTERS_TABLE;
30
30
  const permTable = PERMUTATION_TABLE;
31
31
  const orientTable = ORIENTATION_TABLE;
32
- const policy = POLICY_TABLE;
32
+ const policy = POLICY_TABLE_EXPANDED;
33
33
  let c = getCentersIndex(state.centers);
34
34
  let p = getPermutationCompactIndex(state.edgesPerm);
35
35
  let o = getOrientationIndex(state.edgesOrient);
@@ -38,10 +38,7 @@ function solveWithTablesIndices(state) {
38
38
  return [];
39
39
  const moves = [];
40
40
  while (stateIndex !== 0) {
41
- const bits = stateIndex * 3;
42
- const byteIndex = bits >> 3;
43
- const offset = bits & 7;
44
- const m = ((policy[byteIndex] | ((policy[byteIndex + 1] ?? 0) << 8)) >> offset) & 0x07;
41
+ const m = policy[stateIndex];
45
42
  moves.push(m);
46
43
  c = centersTable[c * MOVE_COUNT + m];
47
44
  p = permTable[p * MOVE_COUNT + m];
@@ -34,6 +34,12 @@ export declare let CENTERS_TABLE: Uint8Array | null;
34
34
  export declare let PERMUTATION_TABLE: Uint16Array | null;
35
35
  export declare let ORIENTATION_TABLE: Uint8Array | null;
36
36
  export declare let POLICY_TABLE: Uint8Array | null;
37
+ /**
38
+ * Byte-expanded policy (one optimal move per state, no bit-unpacking in
39
+ * the hot walk). Built at load from the 3-bit packed table: +0.9 MB RAM,
40
+ * zero wire/format change.
41
+ */
42
+ export declare let POLICY_TABLE_EXPANDED: Uint8Array | null;
37
43
  export declare function ensureTablesLoaded(): Promise<void>;
38
44
  export declare function arePyraminxTablesLoaded(): boolean;
39
45
  export declare function getPolicyMove(stateIndex: number): number;
@@ -1 +1 @@
1
- {"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/pyraminx/solver/tables.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/pyraminx/solver/tables.ts"],"names":[],"mappings":"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"}
@@ -261,6 +261,12 @@ export let CENTERS_TABLE = null;
261
261
  export let PERMUTATION_TABLE = null;
262
262
  export let ORIENTATION_TABLE = null;
263
263
  export let POLICY_TABLE = null;
264
+ /**
265
+ * Byte-expanded policy (one optimal move per state, no bit-unpacking in
266
+ * the hot walk). Built at load from the 3-bit packed table: +0.9 MB RAM,
267
+ * zero wire/format change.
268
+ */
269
+ export let POLICY_TABLE_EXPANDED = null;
264
270
  export async function ensureTablesLoaded() {
265
271
  if (tablesLoaded)
266
272
  return;
@@ -269,8 +275,18 @@ export async function ensureTablesLoaded() {
269
275
  PERMUTATION_TABLE = tables.permutationTable;
270
276
  ORIENTATION_TABLE = tables.orientationTable;
271
277
  POLICY_TABLE = tables.policyTable;
278
+ POLICY_TABLE_EXPANDED = expandPolicy3Bit(tables.policyTable, STATE_COUNT);
272
279
  tablesLoaded = true;
273
280
  }
281
+ function expandPolicy3Bit(packed, count) {
282
+ const expanded = new Uint8Array(count);
283
+ for (let index = 0; index < count; index += 1) {
284
+ const bits = index * 3;
285
+ const byteIndex = bits >> 3;
286
+ expanded[index] = (((packed[byteIndex] | ((packed[byteIndex + 1] ?? 0) << 8)) >> (bits & 7)) & 0x07);
287
+ }
288
+ return expanded;
289
+ }
274
290
  export function arePyraminxTablesLoaded() {
275
291
  return tablesLoaded;
276
292
  }
@@ -300,4 +316,5 @@ export function resetTablesForTests() {
300
316
  PERMUTATION_TABLE = null;
301
317
  ORIENTATION_TABLE = null;
302
318
  POLICY_TABLE = null;
319
+ POLICY_TABLE_EXPANDED = null;
303
320
  }
@@ -1,5 +1,19 @@
1
+ /** FNV-1a expectations from the legacy (non-sha256) manifests. */
2
+ export interface ExpectedFnv1a {
3
+ /** FNV-1a 32-bit checksum (`>>> 0`) over the decoded numeric sequence. */
4
+ value: number;
5
+ /**
6
+ * Wire size of each hashed element: 1 = byte, 2 = uint16 LE, 4 = int32 LE.
7
+ * Mirrors how each node loader decodes values before hashing (`4` also
8
+ * covers the 3x3 int32 move tables: their hashed uint16 values are the
9
+ * same numbers, and XOR/`Math.imul` are sign-insensitive there).
10
+ */
11
+ elementSize: 1 | 2 | 4;
12
+ /** Hash 1-byte elements as signed int8 (Square-1's int8 tables). */
13
+ signed?: boolean;
14
+ }
1
15
  export interface FetchTableBufferOptions {
2
- /** Cache API cache name to store responses in (used by cube3). */
16
+ /** Cache API cache name to store responses in (used by all table loaders). */
3
17
  cacheName?: string;
4
18
  /**
5
19
  * Whether the manifest advertises precompressed variants for this file.
@@ -7,6 +21,28 @@ export interface FetchTableBufferOptions {
7
21
  * set to false to skip .br/.gz attempts entirely.
8
22
  */
9
23
  compressed?: boolean;
24
+ /**
25
+ * sha256 hex digest of the UNCOMPRESSED file (from its manifest entry).
26
+ * Verified after decompression; a mismatch rejects with the file URL.
27
+ */
28
+ expectedSha256?: string;
29
+ /** Exact byte length of the uncompressed file (from its manifest entry). */
30
+ expectedBytes?: number;
31
+ /**
32
+ * FNV-1a checksum from legacy manifests (cube2/cube3/pyraminx/skewb/sq1).
33
+ * Verified after the length check; a mismatch rejects with the file URL.
34
+ */
35
+ expectedFnv1a?: ExpectedFnv1a;
10
36
  }
11
37
  export declare function fetchTableBuffer(baseUrl: string, dir: string, file: string, options?: FetchTableBufferOptions): Promise<ArrayBuffer>;
38
+ /**
39
+ * FNV-1a (32-bit) over the elements decoded from a wire buffer: the browser
40
+ * twin of the node loaders' `checksum(values)` (imul multiply, `>>> 0`
41
+ * result). `elementSize` mirrors each loader's decoding of the manifest
42
+ * checksum — see `ExpectedFnv1a`.
43
+ */
44
+ export declare function fnv1a32(buffer: ArrayBuffer, elementSize: 1 | 2 | 4, signed?: boolean): number;
45
+ /** sha256 hex of a buffer; WebCrypto when available, portable JS fallback otherwise. */
46
+ export declare function sha256Hex(buffer: ArrayBuffer): Promise<string>;
47
+ export declare function sha256HexJs(buffer: ArrayBuffer): string;
12
48
  //# sourceMappingURL=compressed-fetch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"compressed-fetch.d.ts","sourceRoot":"","sources":["../../../src/puzzles/shared/compressed-fetch.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,uBAAuB;IACtC,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAkBD,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,WAAW,CAAC,CAuBtB"}
1
+ {"version":3,"file":"compressed-fetch.d.ts","sourceRoot":"","sources":["../../../src/puzzles/shared/compressed-fetch.ts"],"names":[],"mappings":"AAeA,kEAAkE;AAClE,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,WAAW,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACvB,oEAAoE;IACpE,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,uBAAuB;IACtC,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4EAA4E;IAC5E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAkBD,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,WAAW,CAAC,CAyDtB;AAmCD;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,MAAM,UAAQ,GAAG,MAAM,CAgB3F;AAED,wFAAwF;AACxF,wBAAsB,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAYpE;AAcD,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAmEvD"}