@cubingcenter/scrambler 0.7.0 → 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 (126) hide show
  1. package/README.md +15 -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/cube3/solver/browser-loader.d.ts.map +1 -1
  10. package/dist/puzzles/cube3/solver/browser-loader.js +42 -20
  11. package/dist/puzzles/cube3/solver/generated-assets.d.ts.map +1 -1
  12. package/dist/puzzles/cube3/solver/generated-assets.js +9 -3
  13. package/dist/puzzles/cube3/solver/phase1.d.ts +19 -0
  14. package/dist/puzzles/cube3/solver/phase1.d.ts.map +1 -1
  15. package/dist/puzzles/cube3/solver/phase1.js +29 -6
  16. package/dist/puzzles/cube3/solver/phase2.d.ts +28 -0
  17. package/dist/puzzles/cube3/solver/phase2.d.ts.map +1 -1
  18. package/dist/puzzles/cube3/solver/phase2.js +56 -15
  19. package/dist/puzzles/cube3/solver/tables.d.ts.map +1 -1
  20. package/dist/puzzles/cube3/solver/tables.js +14 -12
  21. package/dist/puzzles/cube4/browser-worker-pool.d.ts +82 -0
  22. package/dist/puzzles/cube4/browser-worker-pool.d.ts.map +1 -0
  23. package/dist/puzzles/cube4/browser-worker-pool.js +205 -0
  24. package/dist/puzzles/cube4/browser-worker.d.ts +2 -0
  25. package/dist/puzzles/cube4/browser-worker.d.ts.map +1 -0
  26. package/dist/puzzles/cube4/browser-worker.js +31 -0
  27. package/dist/puzzles/cube4/index.d.ts +7 -0
  28. package/dist/puzzles/cube4/index.d.ts.map +1 -1
  29. package/dist/puzzles/cube4/index.js +4 -0
  30. package/dist/puzzles/cube4/moves.d.ts.map +1 -1
  31. package/dist/puzzles/cube4/moves.js +122 -30
  32. package/dist/puzzles/cube4/reduction/browser-loader.d.ts +26 -1
  33. package/dist/puzzles/cube4/reduction/browser-loader.d.ts.map +1 -1
  34. package/dist/puzzles/cube4/reduction/browser-loader.js +212 -48
  35. package/dist/puzzles/cube4/reduction/center2.d.ts +24 -0
  36. package/dist/puzzles/cube4/reduction/center2.d.ts.map +1 -1
  37. package/dist/puzzles/cube4/reduction/center2.js +99 -1
  38. package/dist/puzzles/cube4/reduction/finish3.d.ts +53 -0
  39. package/dist/puzzles/cube4/reduction/finish3.d.ts.map +1 -0
  40. package/dist/puzzles/cube4/reduction/finish3.js +393 -0
  41. package/dist/puzzles/cube4/reduction/generated/hc-manifest.json +6 -0
  42. package/dist/puzzles/cube4/reduction/hc-pdb.d.ts +5 -3
  43. package/dist/puzzles/cube4/reduction/hc-pdb.d.ts.map +1 -1
  44. package/dist/puzzles/cube4/reduction/hc-pdb.js +30 -12
  45. package/dist/puzzles/cube4/reduction/node-loader.d.ts +10 -1
  46. package/dist/puzzles/cube4/reduction/node-loader.d.ts.map +1 -1
  47. package/dist/puzzles/cube4/reduction/node-loader.js +170 -36
  48. package/dist/puzzles/cube4/reduction/pair-transversal.d.ts +28 -0
  49. package/dist/puzzles/cube4/reduction/pair-transversal.d.ts.map +1 -0
  50. package/dist/puzzles/cube4/reduction/pair-transversal.js +107 -0
  51. package/dist/puzzles/cube4/reduction/phase3-search.d.ts +33 -30
  52. package/dist/puzzles/cube4/reduction/phase3-search.d.ts.map +1 -1
  53. package/dist/puzzles/cube4/reduction/phase3-search.js +211 -104
  54. package/dist/puzzles/cube4/reduction/rotation-fix.d.ts +28 -0
  55. package/dist/puzzles/cube4/reduction/rotation-fix.d.ts.map +1 -0
  56. package/dist/puzzles/cube4/reduction/rotation-fix.js +212 -0
  57. package/dist/puzzles/cube4/reduction/solve.d.ts +72 -13
  58. package/dist/puzzles/cube4/reduction/solve.d.ts.map +1 -1
  59. package/dist/puzzles/cube4/reduction/solve.js +374 -92
  60. package/dist/puzzles/cube4/scramble-cache.d.ts +93 -0
  61. package/dist/puzzles/cube4/scramble-cache.d.ts.map +1 -0
  62. package/dist/puzzles/cube4/scramble-cache.js +150 -0
  63. package/dist/puzzles/cube4/scrambler.d.ts +112 -18
  64. package/dist/puzzles/cube4/scrambler.d.ts.map +1 -1
  65. package/dist/puzzles/cube4/scrambler.js +271 -83
  66. package/dist/puzzles/cube4/worker-pool.d.ts +139 -0
  67. package/dist/puzzles/cube4/worker-pool.d.ts.map +1 -0
  68. package/dist/puzzles/cube4/worker-pool.js +405 -0
  69. package/dist/puzzles/cube4/worker-protocol.d.ts +58 -0
  70. package/dist/puzzles/cube4/worker-protocol.d.ts.map +1 -0
  71. package/dist/puzzles/cube4/worker-protocol.js +58 -0
  72. package/dist/puzzles/cube4/worker.d.ts +26 -0
  73. package/dist/puzzles/cube4/worker.d.ts.map +1 -0
  74. package/dist/puzzles/cube4/worker.js +49 -0
  75. package/dist/puzzles/pyraminx/solver/browser-loader.d.ts +4 -8
  76. package/dist/puzzles/pyraminx/solver/browser-loader.d.ts.map +1 -1
  77. package/dist/puzzles/pyraminx/solver/browser-loader.js +44 -15
  78. package/dist/puzzles/pyraminx/solver/generated-assets.d.ts.map +1 -1
  79. package/dist/puzzles/pyraminx/solver/generated-assets.js +8 -3
  80. package/dist/puzzles/shared/compressed-fetch.d.ts +37 -1
  81. package/dist/puzzles/shared/compressed-fetch.d.ts.map +1 -1
  82. package/dist/puzzles/shared/compressed-fetch.js +226 -31
  83. package/dist/puzzles/shared/load-once.d.ts +16 -0
  84. package/dist/puzzles/shared/load-once.d.ts.map +1 -0
  85. package/dist/puzzles/shared/load-once.js +11 -0
  86. package/dist/puzzles/skewb/solver/browser-loader.d.ts +4 -11
  87. package/dist/puzzles/skewb/solver/browser-loader.d.ts.map +1 -1
  88. package/dist/puzzles/skewb/solver/browser-loader.js +44 -16
  89. package/dist/puzzles/skewb/solver/generated-assets.d.ts.map +1 -1
  90. package/dist/puzzles/skewb/solver/generated-assets.js +8 -3
  91. package/dist/puzzles/sq1/solver/browser-loader.d.ts.map +1 -1
  92. package/dist/puzzles/sq1/solver/browser-loader.js +58 -26
  93. package/dist-cjs/core/api.js +10 -2
  94. package/dist-cjs/puzzles/cube2/solver/browser-loader.js +42 -14
  95. package/dist-cjs/puzzles/cube2/solver/generated-assets.js +8 -3
  96. package/dist-cjs/puzzles/cube3/solver/browser-loader.js +42 -20
  97. package/dist-cjs/puzzles/cube3/solver/generated-assets.js +9 -3
  98. package/dist-cjs/puzzles/cube3/solver/phase1.js +29 -6
  99. package/dist-cjs/puzzles/cube3/solver/phase2.js +56 -15
  100. package/dist-cjs/puzzles/cube3/solver/tables.js +14 -12
  101. package/dist-cjs/puzzles/cube4/browser-worker-pool.js +211 -0
  102. package/dist-cjs/puzzles/cube4/index.js +21 -1
  103. package/dist-cjs/puzzles/cube4/moves.js +122 -30
  104. package/dist-cjs/puzzles/cube4/reduction/browser-loader.js +212 -48
  105. package/dist-cjs/puzzles/cube4/reduction/center2.js +102 -2
  106. package/dist-cjs/puzzles/cube4/reduction/finish3.js +398 -0
  107. package/dist-cjs/puzzles/cube4/reduction/generated/hc-manifest.json +6 -0
  108. package/dist-cjs/puzzles/cube4/reduction/hc-pdb.js +31 -12
  109. package/dist-cjs/puzzles/cube4/reduction/node-loader.js +171 -36
  110. package/dist-cjs/puzzles/cube4/reduction/pair-transversal.js +112 -0
  111. package/dist-cjs/puzzles/cube4/reduction/phase3-search.js +211 -104
  112. package/dist-cjs/puzzles/cube4/reduction/rotation-fix.js +216 -0
  113. package/dist-cjs/puzzles/cube4/reduction/solve.js +374 -88
  114. package/dist-cjs/puzzles/cube4/scramble-cache.js +156 -0
  115. package/dist-cjs/puzzles/cube4/scrambler.js +271 -80
  116. package/dist-cjs/puzzles/cube4/worker-pool.js +447 -0
  117. package/dist-cjs/puzzles/cube4/worker-protocol.js +62 -0
  118. package/dist-cjs/puzzles/pyraminx/solver/browser-loader.js +44 -15
  119. package/dist-cjs/puzzles/pyraminx/solver/generated-assets.js +8 -3
  120. package/dist-cjs/puzzles/shared/compressed-fetch.js +229 -31
  121. package/dist-cjs/puzzles/shared/load-once.js +14 -0
  122. package/dist-cjs/puzzles/skewb/solver/browser-loader.js +44 -16
  123. package/dist-cjs/puzzles/skewb/solver/generated-assets.js +8 -3
  124. package/dist-cjs/puzzles/sq1/solver/browser-loader.js +58 -26
  125. package/docs/development.md +19 -7
  126. package/package.json +4 -3
@@ -16,6 +16,7 @@ const types_js_1 = require("../types.js");
16
16
  const coordinates_js_1 = require("./coordinates.js");
17
17
  const state_js_1 = require("../state.js");
18
18
  const generated_assets_js_1 = require("./generated-assets.js");
19
+ const load_once_js_1 = require("../../shared/load-once.js");
19
20
  exports.CUBE3_PHASE1_MOVES = types_js_1.CUBE3_MOVES;
20
21
  exports.CUBE3_PHASE2_MOVES = [
21
22
  "U",
@@ -29,20 +30,21 @@ exports.CUBE3_PHASE2_MOVES = [
29
30
  "F2",
30
31
  "B2"
31
32
  ];
32
- let cachedTables;
33
- let cachedPhase1Tables;
34
- let cachedPhase2Tables;
35
- async function loadCube3SolverTables() {
36
- cachedTables ??= await (0, generated_assets_js_1.loadGeneratedCube3SolverTables)();
37
- return cachedTables;
33
+ // Promise slots (shared helper, see shared/load-once.ts): concurrent cold
34
+ // calls share ONE in-flight load instead of racing several loads and
35
+ // clobbering each other's results; a rejection purges the slot so the next
36
+ // call can retry.
37
+ const solverTablesSlot = { promise: undefined };
38
+ const phase1TablesSlot = { promise: undefined };
39
+ const phase2TablesSlot = { promise: undefined };
40
+ function loadCube3SolverTables() {
41
+ return (0, load_once_js_1.loadOnce)(solverTablesSlot, generated_assets_js_1.loadGeneratedCube3SolverTables);
38
42
  }
39
- async function loadCube3Phase1Tables() {
40
- cachedPhase1Tables ??= await (0, generated_assets_js_1.loadGeneratedCube3Phase1Tables)();
41
- return cachedPhase1Tables;
43
+ function loadCube3Phase1Tables() {
44
+ return (0, load_once_js_1.loadOnce)(phase1TablesSlot, generated_assets_js_1.loadGeneratedCube3Phase1Tables);
42
45
  }
43
- async function loadCube3Phase2Tables() {
44
- cachedPhase2Tables ??= await (0, generated_assets_js_1.loadGeneratedCube3Phase2Tables)();
45
- return cachedPhase2Tables;
46
+ function loadCube3Phase2Tables() {
47
+ return (0, load_once_js_1.loadOnce)(phase2TablesSlot, generated_assets_js_1.loadGeneratedCube3Phase2Tables);
46
48
  }
47
49
  function generateCube3Phase1Tables() {
48
50
  const cornerOrientation = generateCube3MoveTable("cornerOrientation", coordinates_js_1.CUBE3_COORDINATE_RANGES.cornerOrientation, exports.CUBE3_PHASE1_MOVES);
@@ -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.simplifyCube4Algorithm = exports.invertCube4Algorithm = exports.invertCube4Move = exports.applyCube4Algorithm = exports.applyCube4Move = exports.generateCube4Attempt = 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,10 +30,29 @@ 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; } });
35
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; } });
36
56
  var moves_js_1 = require("./moves.js");
37
57
  Object.defineProperty(exports, "applyCube4Move", { enumerable: true, get: function () { return moves_js_1.applyCube4Move; } });
38
58
  Object.defineProperty(exports, "applyCube4Algorithm", { enumerable: true, get: function () { return moves_js_1.applyCube4Algorithm; } });
@@ -319,43 +319,135 @@ function parse4Move(m) {
319
319
  (base[0] === "R" || base[0] === "L") ? 1 : 2;
320
320
  return { base, axis, amount };
321
321
  }
322
- function format4Move(base, amount) {
323
- const amt = ((amount % 4) + 4) % 4;
324
- if (amt === 0)
325
- return null;
326
- const suffix = amt === 1 ? "" : amt === 2 ? "2" : "'";
327
- return (base + suffix);
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);
328
334
  }
329
- function movesCommute4(a, b) {
330
- return a.axis === b.axis && a.base !== b.base;
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;
331
407
  }
332
408
  /**
333
409
  * Simplifies a 4x4 algorithm by canceling inverse moves and merging redundant
334
410
  * turns on the same face or across commuting parallel faces.
335
411
  */
336
412
  function simplifyCube4Algorithm(moves) {
337
- // Single left-to-right pass with an output stack: each move merges with
338
- // the nearest same-base entry across trailing commuting parallel-face
339
- // moves (verified equivalent to the old restart-from-zero loop on 20000
340
- // random algorithms). O(n·window) instead of O(n²) restarts with splices.
341
- const out = [];
342
- for (const move of moves) {
343
- const current = parse4Move(move);
344
- let index = out.length - 1;
345
- while (index >= 0 && movesCommute4(out[index], current))
346
- index--;
347
- if (index >= 0 && out[index].base === current.base) {
348
- const merged = (out[index].amount + current.amount) % 4;
349
- if (merged === 0)
350
- out.splice(index, 1);
351
- else
352
- out[index].amount = merged;
353
- }
354
- else {
355
- out.push(current);
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
+ }
356
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);
357
450
  }
358
- return out
359
- .map((p) => format4Move(p.base, p.amount))
360
- .filter((m) => m !== null);
451
+ flushRun();
452
+ return segments.flatMap((segment) => segment.moves);
361
453
  }