@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,211 @@
1
+ "use strict";
2
+ /**
3
+ * Web Worker pool for 4x4 random-state scrambles — the browser counterpart
4
+ * of `worker-pool.ts` with the same protocol, lazy-spawn lifecycle, FIFO
5
+ * queue, typed error propagation and per-thread table reuse.
6
+ *
7
+ * Unlike the Node pool (which locates its own worker file), the browser
8
+ * pool REQUIRES the caller to pass `workerUrl`, because only the caller's
9
+ * file can host the static `new URL("./browser-worker.js", import.meta.url)`
10
+ * expression that bundlers (Vite, Webpack 5, Parcel) statically analyse to
11
+ * emit a separate worker chunk:
12
+ *
13
+ * ```ts
14
+ * const pool = new Cube4BrowserWorkerPool({
15
+ * workerUrl: new URL("./browser-worker.js", import.meta.url)
16
+ * });
17
+ * const batch = await pool.generateBatch(8, { seed: "demo" });
18
+ * await pool.terminate();
19
+ * ```
20
+ *
21
+ * This file contains no `import.meta` and no static Node imports, so it
22
+ * also compiles in the CommonJS build (unlike a hard-coded
23
+ * `new URL(..., import.meta.url)` default).
24
+ *
25
+ * Errors crossing the worker boundary arrive as `Error` instances with the
26
+ * original `name`, `message` and `code` preserved (`worker-protocol.ts`).
27
+ */
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.Cube4BrowserWorkerPool = exports.Cube4BrowserWorkerPoolClosedError = void 0;
30
+ exports.deriveBrowserBatchCube4Options = deriveBrowserBatchCube4Options;
31
+ const worker_protocol_js_1 = require("./worker-protocol.js");
32
+ /** Typed rejection used for post-terminate operations and shutdown itself. */
33
+ class Cube4BrowserWorkerPoolClosedError extends Error {
34
+ code = "cube4-browser-pool-closed";
35
+ constructor(action = "operation") {
36
+ super(`[cube4] browser worker pool is closed (${action} rejected).`);
37
+ this.name = "Cube4BrowserWorkerPoolClosedError";
38
+ }
39
+ }
40
+ exports.Cube4BrowserWorkerPoolClosedError = Cube4BrowserWorkerPoolClosedError;
41
+ /**
42
+ * Batch seed derivation — identical to core `generateScrambles` and to
43
+ * `deriveBatchCube4Options` in the Node pool.
44
+ */
45
+ function deriveBrowserBatchCube4Options(options, index, count) {
46
+ if (count === 1 || options.seed === undefined) {
47
+ return options;
48
+ }
49
+ return { ...options, seed: `${options.seed}:${index}` };
50
+ }
51
+ function defaultBrowserSize() {
52
+ if (typeof navigator !== "undefined" && navigator.hardwareConcurrency) {
53
+ return Math.max(1, navigator.hardwareConcurrency - 1);
54
+ }
55
+ return 4;
56
+ }
57
+ class Cube4BrowserWorkerPool {
58
+ workers = [];
59
+ queue = [];
60
+ workerUrl;
61
+ maxSize;
62
+ pumping = false;
63
+ terminatePromise;
64
+ nextId = 0;
65
+ terminated = false;
66
+ constructor(options) {
67
+ if (typeof Worker === "undefined") {
68
+ throw new Error("[cube4] Web Workers are not available in this environment; " +
69
+ "use generateCube4Scramble() directly for single-threaded generation.");
70
+ }
71
+ if (options.size !== undefined) {
72
+ if (!Number.isInteger(options.size) || options.size < 1) {
73
+ throw new RangeError(`[cube4] pool size must be an integer >= 1 (got ${options.size}).`);
74
+ }
75
+ }
76
+ this.workerUrl =
77
+ typeof options.workerUrl === "string" ? options.workerUrl : options.workerUrl.href;
78
+ this.maxSize = options.size ?? defaultBrowserSize();
79
+ }
80
+ /** Threads spawned so far (lazily grown; 0 before the first job). */
81
+ get workerCount() {
82
+ return this.workers.length;
83
+ }
84
+ get queuedCount() {
85
+ return this.queue.length;
86
+ }
87
+ get busyCount() {
88
+ return this.workers.reduce((n, rec) => n + (rec.task !== null ? 1 : 0), 0);
89
+ }
90
+ get closed() {
91
+ return this.terminated;
92
+ }
93
+ generate(options = {}) {
94
+ if (this.terminated) {
95
+ return Promise.reject(new Cube4BrowserWorkerPoolClosedError("generate"));
96
+ }
97
+ const id = this.nextId++;
98
+ return new Promise((resolve, reject) => {
99
+ this.queue.push({ id, options, resolve, reject });
100
+ this.pump();
101
+ });
102
+ }
103
+ generateBatch(count, options = {}) {
104
+ if (!Number.isInteger(count) || count < 1) {
105
+ return Promise.reject(new RangeError(`count must be a positive integer (got ${count}).`));
106
+ }
107
+ return Promise.all(Array.from({ length: count }, (_, index) => this.generate(deriveBrowserBatchCube4Options(options, index, count))));
108
+ }
109
+ /**
110
+ * Prompt shutdown: rejects queued/in-flight tasks and terminates every
111
+ * Web Worker synchronously (no threads to join in the browser).
112
+ * Idempotent.
113
+ */
114
+ terminate() {
115
+ if (this.terminatePromise !== undefined)
116
+ return;
117
+ this.terminated = true;
118
+ const closed = () => new Cube4BrowserWorkerPoolClosedError();
119
+ for (const task of this.queue.splice(0))
120
+ task.reject(closed());
121
+ const records = this.workers.splice(0);
122
+ for (const rec of records) {
123
+ if (rec.task !== null) {
124
+ rec.task.reject(closed());
125
+ rec.task = null;
126
+ }
127
+ rec.worker.terminate();
128
+ }
129
+ this.terminatePromise = Promise.resolve();
130
+ }
131
+ // ── internals (same shape as the Node pool) ──────────────────────────
132
+ pump() {
133
+ if (this.pumping || this.terminated)
134
+ return;
135
+ this.pumping = true;
136
+ void this.runPump()
137
+ .catch(() => undefined)
138
+ .finally(() => {
139
+ this.pumping = false;
140
+ });
141
+ }
142
+ async runPump() {
143
+ while (!this.terminated && this.queue.length > 0) {
144
+ const idle = this.workers.find((rec) => rec.task === null);
145
+ if (idle !== undefined) {
146
+ this.dispatch(idle, this.queue.shift());
147
+ continue;
148
+ }
149
+ if (this.workers.length < this.maxSize) {
150
+ this.spawnWorker();
151
+ continue;
152
+ }
153
+ break;
154
+ }
155
+ }
156
+ spawnWorker() {
157
+ const worker = new Worker(this.workerUrl, { type: "module", name: "cubingcenter-444" });
158
+ const rec = { worker, task: null };
159
+ worker.onmessage = (event) => {
160
+ this.handleResponse(rec, event.data);
161
+ };
162
+ worker.onerror = (event) => {
163
+ this.handleWorkerGone(rec, event.error instanceof Error
164
+ ? event.error
165
+ : new Error(event.message || "[cube4] browser worker failed."));
166
+ };
167
+ this.workers.push(rec);
168
+ return rec;
169
+ }
170
+ dispatch(rec, task) {
171
+ rec.task = task;
172
+ const message = { id: task.id, options: task.options };
173
+ try {
174
+ rec.worker.postMessage(message);
175
+ }
176
+ catch (error) {
177
+ rec.task = null;
178
+ task.reject(error);
179
+ this.pump();
180
+ }
181
+ }
182
+ handleResponse(rec, response) {
183
+ const task = rec.task;
184
+ if (task === null || task.id !== response.id)
185
+ return;
186
+ rec.task = null;
187
+ if (this.terminated)
188
+ return;
189
+ if (response.ok)
190
+ task.resolve(response.result);
191
+ else
192
+ task.reject((0, worker_protocol_js_1.deserializeCube4Error)(response.error));
193
+ const next = this.queue.shift();
194
+ if (next !== undefined)
195
+ this.dispatch(rec, next);
196
+ this.pump();
197
+ }
198
+ handleWorkerGone(rec, error) {
199
+ const index = this.workers.indexOf(rec);
200
+ if (index === -1)
201
+ return;
202
+ this.workers.splice(index, 1);
203
+ const task = rec.task;
204
+ rec.task = null;
205
+ if (task !== null && !this.terminated)
206
+ task.reject(error);
207
+ if (!this.terminated)
208
+ this.pump();
209
+ }
210
+ }
211
+ exports.Cube4BrowserWorkerPool = Cube4BrowserWorkerPool;
@@ -3,7 +3,8 @@
3
3
  // cube4 barrel exports
4
4
  // ---------------------------------------------------------------------------
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.initializeScrambler = exports.getSupportedEvents = exports.getEventInfo = exports.generateScrambles = exports.generateScramble = exports.getCube4FaceOrder = exports.formatCube4Facelets = exports.toCube4Facelets = exports.isAllowedRandomCube4State = exports.isTrivialCube4State = exports.isOneMoveFromSolvedCube4State = exports.validateCube4State = exports.generateRandomCube4State = exports.FACE_MOVE_EFFECTS = exports.parseMove = exports.parseCube4Move = exports.parseCube4Algorithm = exports.formatCube4Algorithm = exports.invertCube4Algorithm = exports.invertCube4Move = exports.applyCube4Algorithm = exports.applyCube4Move = exports.generateCube4Scramble = exports.cube4ScrambleGenerator = exports.areWingsPaired = exports.areCentersReduced = exports.isSolvedCube4State = exports.cube4StatesEqual = exports.cloneCube4State = exports.createSolvedCube4State = exports.CUBE4_OUTER_MOVES = exports.CUBE4_ALL_MOVES = exports.CUBE4_FACE_MOVES = exports.B = exports.L = exports.D = exports.F = exports.R = exports.U = exports.CENTERS_PER_FACE = exports.FACE_COUNT = exports.CENTER_COUNT = exports.WING_COUNT = exports.CORNER_COUNT = void 0;
6
+ exports.validateCube4State = exports.generateRandomCube4State = exports.FACE_MOVE_EFFECTS = exports.parseMove = exports.parseCube4Move = exports.parseCube4Algorithm = exports.formatCube4Algorithm = exports.simplifyCube4Algorithm = exports.invertCube4Algorithm = exports.invertCube4Move = exports.applyCube4Algorithm = exports.applyCube4Move = exports.deriveCube4StreamOptions = exports.createCube4ScrambleCache = exports.Cube4ScrambleCache = exports.deriveBrowserBatchCube4Options = exports.Cube4BrowserWorkerPoolClosedError = exports.Cube4BrowserWorkerPool = exports.closeDefaultCube4WorkerPool = exports.getDefaultCube4WorkerPool = exports.generateCube4ScrambleBatch = exports.deriveBatchCube4Options = exports.Cube4WorkerPoolClosedError = exports.Cube4WorkerPool = exports.generateCube4Attempt = exports.generateCube4Scramble = exports.cube4ScrambleGenerator = exports.Cube4Phase3CapError = exports.Cube4Phase2CapError = exports.Cube4FinishCapError = exports.areWingsPaired = exports.areCentersReduced = exports.isSolvedCube4State = exports.cube4StatesEqual = exports.cloneCube4State = exports.createSolvedCube4State = exports.CUBE4_OUTER_MOVES = exports.CUBE4_ALL_MOVES = exports.CUBE4_FACE_MOVES = exports.B = exports.L = exports.D = exports.F = exports.R = exports.U = exports.CENTERS_PER_FACE = exports.FACE_COUNT = exports.CENTER_COUNT = exports.WING_COUNT = exports.CORNER_COUNT = void 0;
7
+ exports.initializeScrambler = exports.getSupportedEvents = exports.getEventInfo = exports.generateScrambles = exports.generateScramble = exports.getCube4FaceOrder = exports.formatCube4Facelets = exports.toCube4Facelets = exports.isAllowedRandomCube4State = exports.isTrivialCube4State = exports.isOneMoveFromSolvedCube4State = void 0;
7
8
  const index_js_1 = require("../../core/index.js");
8
9
  const scrambler_js_1 = require("./scrambler.js");
9
10
  (0, index_js_1.registerScrambleGenerator)(scrambler_js_1.cube4ScrambleGenerator);
@@ -29,14 +30,35 @@ Object.defineProperty(exports, "cube4StatesEqual", { enumerable: true, get: func
29
30
  Object.defineProperty(exports, "isSolvedCube4State", { enumerable: true, get: function () { return state_js_1.isSolvedCube4State; } });
30
31
  Object.defineProperty(exports, "areCentersReduced", { enumerable: true, get: function () { return state_js_1.areCentersReduced; } });
31
32
  Object.defineProperty(exports, "areWingsPaired", { enumerable: true, get: function () { return state_js_1.areWingsPaired; } });
33
+ var solve_js_1 = require("./reduction/solve.js");
34
+ Object.defineProperty(exports, "Cube4FinishCapError", { enumerable: true, get: function () { return solve_js_1.Cube4FinishCapError; } });
35
+ Object.defineProperty(exports, "Cube4Phase2CapError", { enumerable: true, get: function () { return solve_js_1.Cube4Phase2CapError; } });
36
+ Object.defineProperty(exports, "Cube4Phase3CapError", { enumerable: true, get: function () { return solve_js_1.Cube4Phase3CapError; } });
32
37
  var scrambler_js_2 = require("./scrambler.js");
33
38
  Object.defineProperty(exports, "cube4ScrambleGenerator", { enumerable: true, get: function () { return scrambler_js_2.cube4ScrambleGenerator; } });
34
39
  Object.defineProperty(exports, "generateCube4Scramble", { enumerable: true, get: function () { return scrambler_js_2.generateCube4Scramble; } });
40
+ Object.defineProperty(exports, "generateCube4Attempt", { enumerable: true, get: function () { return scrambler_js_2.generateCube4Attempt; } });
41
+ var worker_pool_js_1 = require("./worker-pool.js");
42
+ Object.defineProperty(exports, "Cube4WorkerPool", { enumerable: true, get: function () { return worker_pool_js_1.Cube4WorkerPool; } });
43
+ Object.defineProperty(exports, "Cube4WorkerPoolClosedError", { enumerable: true, get: function () { return worker_pool_js_1.Cube4WorkerPoolClosedError; } });
44
+ Object.defineProperty(exports, "deriveBatchCube4Options", { enumerable: true, get: function () { return worker_pool_js_1.deriveBatchCube4Options; } });
45
+ Object.defineProperty(exports, "generateCube4ScrambleBatch", { enumerable: true, get: function () { return worker_pool_js_1.generateCube4ScrambleBatch; } });
46
+ Object.defineProperty(exports, "getDefaultCube4WorkerPool", { enumerable: true, get: function () { return worker_pool_js_1.getDefaultCube4WorkerPool; } });
47
+ Object.defineProperty(exports, "closeDefaultCube4WorkerPool", { enumerable: true, get: function () { return worker_pool_js_1.closeDefaultCube4WorkerPool; } });
48
+ var browser_worker_pool_js_1 = require("./browser-worker-pool.js");
49
+ Object.defineProperty(exports, "Cube4BrowserWorkerPool", { enumerable: true, get: function () { return browser_worker_pool_js_1.Cube4BrowserWorkerPool; } });
50
+ Object.defineProperty(exports, "Cube4BrowserWorkerPoolClosedError", { enumerable: true, get: function () { return browser_worker_pool_js_1.Cube4BrowserWorkerPoolClosedError; } });
51
+ Object.defineProperty(exports, "deriveBrowserBatchCube4Options", { enumerable: true, get: function () { return browser_worker_pool_js_1.deriveBrowserBatchCube4Options; } });
52
+ var scramble_cache_js_1 = require("./scramble-cache.js");
53
+ Object.defineProperty(exports, "Cube4ScrambleCache", { enumerable: true, get: function () { return scramble_cache_js_1.Cube4ScrambleCache; } });
54
+ Object.defineProperty(exports, "createCube4ScrambleCache", { enumerable: true, get: function () { return scramble_cache_js_1.createCube4ScrambleCache; } });
55
+ Object.defineProperty(exports, "deriveCube4StreamOptions", { enumerable: true, get: function () { return scramble_cache_js_1.deriveCube4StreamOptions; } });
35
56
  var moves_js_1 = require("./moves.js");
36
57
  Object.defineProperty(exports, "applyCube4Move", { enumerable: true, get: function () { return moves_js_1.applyCube4Move; } });
37
58
  Object.defineProperty(exports, "applyCube4Algorithm", { enumerable: true, get: function () { return moves_js_1.applyCube4Algorithm; } });
38
59
  Object.defineProperty(exports, "invertCube4Move", { enumerable: true, get: function () { return moves_js_1.invertCube4Move; } });
39
60
  Object.defineProperty(exports, "invertCube4Algorithm", { enumerable: true, get: function () { return moves_js_1.invertCube4Algorithm; } });
61
+ Object.defineProperty(exports, "simplifyCube4Algorithm", { enumerable: true, get: function () { return moves_js_1.simplifyCube4Algorithm; } });
40
62
  Object.defineProperty(exports, "formatCube4Algorithm", { enumerable: true, get: function () { return moves_js_1.formatCube4Algorithm; } });
41
63
  Object.defineProperty(exports, "parseCube4Algorithm", { enumerable: true, get: function () { return moves_js_1.parseCube4Algorithm; } });
42
64
  Object.defineProperty(exports, "parseCube4Move", { enumerable: true, get: function () { return moves_js_1.parseCube4Move; } });
@@ -20,6 +20,7 @@ exports.invertCube4Algorithm = invertCube4Algorithm;
20
20
  exports.formatCube4Algorithm = formatCube4Algorithm;
21
21
  exports.parseCube4Algorithm = parseCube4Algorithm;
22
22
  exports.parseCube4Move = parseCube4Move;
23
+ exports.simplifyCube4Algorithm = simplifyCube4Algorithm;
23
24
  const types_js_1 = require("./types.js");
24
25
  // ---------------------------------------------------------------------------
25
26
  // Identity arrays
@@ -170,6 +171,32 @@ const FACE_MOVE_EFFECTS = {
170
171
  };
171
172
  exports.FACE_MOVE_EFFECTS = FACE_MOVE_EFFECTS;
172
173
  // ---------------------------------------------------------------------------
174
+ // Precomposed total effect per move string ("R'" = R x3, "U2" = U x2),
175
+ // built once at module load. applyCube4Algorithm then costs one lookup +
176
+ // one pass per move: no per-move string parsing, no repeated
177
+ // quarter-turn passes (average ~2 passes collapse into exactly 1).
178
+ // ---------------------------------------------------------------------------
179
+ function composePower(face, turns) {
180
+ const quarter = FACE_MOVE_EFFECTS[face];
181
+ if (turns === 2)
182
+ return composeEffects(quarter, quarter);
183
+ if (turns === 3)
184
+ return composeEffects(composeEffects(quarter, quarter), quarter);
185
+ return quarter;
186
+ }
187
+ const COMPOSED_EFFECTS = Object.fromEntries(types_js_1.CUBE4_ALL_MOVES.map((move) => {
188
+ const { face, turns } = parseMove(move);
189
+ return [move, composePower(face, turns)];
190
+ }));
191
+ /** Total effect for one move string (precomposed fast path + parse fallback). */
192
+ function effectForMove(move) {
193
+ const hit = COMPOSED_EFFECTS[move];
194
+ if (hit !== undefined)
195
+ return hit;
196
+ const { face, turns } = parseMove(move);
197
+ return composePower(face, turns);
198
+ }
199
+ // ---------------------------------------------------------------------------
173
200
  // Apply a single move to a state
174
201
  // ---------------------------------------------------------------------------
175
202
  function applyEffect(state, effect) {
@@ -204,20 +231,52 @@ function parseMove(move) {
204
231
  return { face: face, suffix, turns };
205
232
  }
206
233
  function applyCube4Move(state, move) {
207
- const { face, turns } = parseMove(move);
208
- const effect = FACE_MOVE_EFFECTS[face];
209
- let current = state;
210
- for (let i = 0; i < turns; i++) {
211
- current = applyEffect(current, effect);
212
- }
213
- return current;
234
+ // Single-pass fast path through the precomposed total effect: one lookup
235
+ // + one pass, no string re-parsing, no repeated quarter-turn passes, no
236
+ // intermediate states.
237
+ return applyEffect(state, effectForMove(move));
214
238
  }
215
239
  function applyCube4Algorithm(state, moves) {
216
- let current = state;
217
- for (const move of moves) {
218
- current = applyCube4Move(current, move);
240
+ if (moves.length === 0)
241
+ return state;
242
+ const bufA = {
243
+ cp: new Uint8Array(state.cp),
244
+ co: new Uint8Array(state.co),
245
+ wp: new Uint8Array(state.wp),
246
+ centers: new Uint8Array(state.centers)
247
+ };
248
+ const bufB = {
249
+ cp: new Uint8Array(8),
250
+ co: new Uint8Array(8),
251
+ wp: new Uint8Array(24),
252
+ centers: new Uint8Array(24)
253
+ };
254
+ let cur = bufA;
255
+ let nxt = bufB;
256
+ for (let mIdx = 0; mIdx < moves.length; mIdx++) {
257
+ const move = moves[mIdx];
258
+ const eff = effectForMove(move);
259
+ for (let i = 0; i < 8; i++) {
260
+ const from = eff.cperm[i];
261
+ nxt.cp[i] = cur.cp[from];
262
+ nxt.co[i] = (cur.co[from] + eff.corient[i]) % 3;
263
+ }
264
+ for (let i = 0; i < 24; i++) {
265
+ nxt.wp[i] = cur.wp[eff.wperm[i]];
266
+ }
267
+ for (let i = 0; i < 24; i++) {
268
+ nxt.centers[i] = cur.centers[eff.ctperm[i]];
269
+ }
270
+ const tmp = cur;
271
+ cur = nxt;
272
+ nxt = tmp;
219
273
  }
220
- return current;
274
+ return {
275
+ cp: cur.cp,
276
+ co: cur.co,
277
+ wp: cur.wp,
278
+ centers: cur.centers
279
+ };
221
280
  }
222
281
  // ---------------------------------------------------------------------------
223
282
  // Move inversion
@@ -249,3 +308,146 @@ function parseCube4Move(value) {
249
308
  }
250
309
  throw new Error(`Invalid 4x4 move: ${value}`);
251
310
  }
311
+ function parse4Move(m) {
312
+ const match = m.match(/^([UDFBLR]w?)(2|')?$/);
313
+ if (!match)
314
+ throw new Error(`Invalid move format: ${m}`);
315
+ const base = match[1];
316
+ const suffix = match[2] ?? "";
317
+ const amount = suffix === "2" ? 2 : suffix === "'" ? 3 : 1;
318
+ const axis = (base[0] === "U" || base[0] === "D") ? 0 :
319
+ (base[0] === "R" || base[0] === "L") ? 1 : 2;
320
+ return { base, axis, amount };
321
+ }
322
+ const AXIS_FACES = [
323
+ ["U", "D"],
324
+ ["R", "L"],
325
+ ["F", "B"]
326
+ ];
327
+ const AXIS_TURN_OPTIONS = [
328
+ ["2", 2],
329
+ ["", 1],
330
+ ["'", 3]
331
+ ];
332
+ function axisVectorKey(vector) {
333
+ return vector[0] | (vector[1] << 2) | (vector[2] << 4) | (vector[3] << 6);
334
+ }
335
+ function applyMoveToAxisVector(vector, base, turns, out) {
336
+ out[0] = vector[0];
337
+ out[1] = vector[1];
338
+ out[2] = vector[2];
339
+ out[3] = vector[3];
340
+ const axis = base[0];
341
+ const [positive, negative] = AXIS_FACES.find(([p, n]) => p === axis || n === axis);
342
+ const isPositive = axis === positive;
343
+ const signedTurns = isPositive ? turns : 4 - turns;
344
+ const wide = base.endsWith("w");
345
+ if (wide) {
346
+ const firstLayer = isPositive ? 0 : 2;
347
+ out[firstLayer] = (out[firstLayer] + signedTurns) & 3;
348
+ out[firstLayer + 1] = (out[firstLayer + 1] + signedTurns) & 3;
349
+ }
350
+ else {
351
+ const layer = isPositive ? 0 : 3;
352
+ out[layer] = (out[layer] + signedTurns) & 3;
353
+ }
354
+ }
355
+ function buildAxisReductionTable(positiveFace, negativeFace) {
356
+ const bases = [positiveFace, negativeFace, `${positiveFace}w`, `${negativeFace}w`];
357
+ const paths = new Array(256);
358
+ const queue = new Uint8Array(256);
359
+ let head = 0;
360
+ let tail = 0;
361
+ paths[0] = [];
362
+ queue[tail++] = 0;
363
+ while (head < tail) {
364
+ const key = queue[head++];
365
+ const vector = [key & 3, (key >> 2) & 3, (key >> 4) & 3, (key >> 6) & 3];
366
+ const prefix = paths[key];
367
+ for (const base of bases) {
368
+ for (const [suffix, turns] of AXIS_TURN_OPTIONS) {
369
+ const nextVector = [0, 0, 0, 0];
370
+ applyMoveToAxisVector(vector, base, turns, nextVector);
371
+ const nextKey = axisVectorKey(nextVector);
372
+ if (paths[nextKey] !== undefined)
373
+ continue;
374
+ paths[nextKey] = [...prefix, `${base}${suffix}`];
375
+ queue[tail++] = nextKey;
376
+ }
377
+ }
378
+ }
379
+ if (tail !== 256 || paths.some((path) => path === undefined)) {
380
+ throw new Error(`4x4 move simplifier: incomplete axis table for ${positiveFace}/${negativeFace}.`);
381
+ }
382
+ return paths;
383
+ }
384
+ const AXIS_REDUCTIONS = AXIS_FACES.map(([positive, negative]) => buildAxisReductionTable(positive, negative));
385
+ function encodeAxisRun(moves, axis) {
386
+ const vector = [0, 0, 0, 0];
387
+ const next = [0, 0, 0, 0];
388
+ for (const move of moves) {
389
+ if (move.axis === axis) {
390
+ applyMoveToAxisVector(vector, move.base, move.amount, next);
391
+ vector[0] = next[0];
392
+ vector[1] = next[1];
393
+ vector[2] = next[2];
394
+ vector[3] = next[3];
395
+ }
396
+ }
397
+ return axisVectorKey(vector);
398
+ }
399
+ function addAxisKeys(a, b) {
400
+ let result = 0;
401
+ for (let layer = 0; layer < 4; layer++) {
402
+ const shift = layer * 2;
403
+ const amount = (((a >> shift) & 3) + ((b >> shift) & 3)) & 3;
404
+ result |= amount << shift;
405
+ }
406
+ return result;
407
+ }
408
+ /**
409
+ * Simplifies a 4x4 algorithm by canceling inverse moves and merging redundant
410
+ * turns on the same face or across commuting parallel faces.
411
+ */
412
+ function simplifyCube4Algorithm(moves) {
413
+ // Moves on parallel layers commute, and each maximal same-axis run is a
414
+ // four-coordinate vector of quarter turns (one coordinate per layer).
415
+ // A small exact shortest-path table maps that vector back to the fewest
416
+ // legal face/wide moves, including mixed identities such as Rw R' = the
417
+ // corresponding inner-slice turn. Different axes are never reordered.
418
+ const segments = [];
419
+ let run = [];
420
+ let runAxis = -1;
421
+ const flushRun = () => {
422
+ if (run.length === 0)
423
+ return;
424
+ const key = encodeAxisRun(run, runAxis);
425
+ if (key !== 0) {
426
+ const previous = segments[segments.length - 1];
427
+ if (previous?.axis === runAxis) {
428
+ const mergedKey = addAxisKeys(previous.key, key);
429
+ if (mergedKey === 0)
430
+ segments.pop();
431
+ else {
432
+ previous.key = mergedKey;
433
+ previous.moves = AXIS_REDUCTIONS[runAxis][mergedKey];
434
+ }
435
+ }
436
+ else {
437
+ segments.push({ axis: runAxis, key, moves: AXIS_REDUCTIONS[runAxis][key] });
438
+ }
439
+ }
440
+ run = [];
441
+ runAxis = -1;
442
+ };
443
+ for (const move of moves) {
444
+ const parsed = parse4Move(move);
445
+ if (runAxis !== -1 && parsed.axis !== runAxis)
446
+ flushRun();
447
+ if (runAxis === -1)
448
+ runAxis = parsed.axis;
449
+ run.push(parsed);
450
+ }
451
+ flushRun();
452
+ return segments.flatMap((segment) => segment.moves);
453
+ }