@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.
- package/README.md +15 -4
- package/dist/core/api.d.ts.map +1 -1
- package/dist/core/api.js +10 -2
- package/dist/puzzles/cube2/solver/browser-loader.d.ts +4 -7
- package/dist/puzzles/cube2/solver/browser-loader.d.ts.map +1 -1
- package/dist/puzzles/cube2/solver/browser-loader.js +42 -14
- package/dist/puzzles/cube2/solver/generated-assets.d.ts.map +1 -1
- package/dist/puzzles/cube2/solver/generated-assets.js +8 -3
- package/dist/puzzles/cube3/solver/browser-loader.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/browser-loader.js +42 -20
- package/dist/puzzles/cube3/solver/generated-assets.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/generated-assets.js +9 -3
- package/dist/puzzles/cube3/solver/phase1.d.ts +19 -0
- package/dist/puzzles/cube3/solver/phase1.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/phase1.js +29 -6
- package/dist/puzzles/cube3/solver/phase2.d.ts +28 -0
- package/dist/puzzles/cube3/solver/phase2.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/phase2.js +56 -15
- package/dist/puzzles/cube3/solver/tables.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/tables.js +14 -12
- package/dist/puzzles/cube4/browser-worker-pool.d.ts +82 -0
- package/dist/puzzles/cube4/browser-worker-pool.d.ts.map +1 -0
- package/dist/puzzles/cube4/browser-worker-pool.js +205 -0
- package/dist/puzzles/cube4/browser-worker.d.ts +2 -0
- package/dist/puzzles/cube4/browser-worker.d.ts.map +1 -0
- package/dist/puzzles/cube4/browser-worker.js +31 -0
- package/dist/puzzles/cube4/index.d.ts +7 -0
- package/dist/puzzles/cube4/index.d.ts.map +1 -1
- package/dist/puzzles/cube4/index.js +4 -0
- package/dist/puzzles/cube4/moves.d.ts.map +1 -1
- package/dist/puzzles/cube4/moves.js +122 -30
- package/dist/puzzles/cube4/reduction/browser-loader.d.ts +26 -1
- package/dist/puzzles/cube4/reduction/browser-loader.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/browser-loader.js +212 -48
- package/dist/puzzles/cube4/reduction/center2.d.ts +24 -0
- package/dist/puzzles/cube4/reduction/center2.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/center2.js +99 -1
- package/dist/puzzles/cube4/reduction/finish3.d.ts +53 -0
- package/dist/puzzles/cube4/reduction/finish3.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/finish3.js +393 -0
- package/dist/puzzles/cube4/reduction/generated/hc-manifest.json +6 -0
- package/dist/puzzles/cube4/reduction/hc-pdb.d.ts +5 -3
- package/dist/puzzles/cube4/reduction/hc-pdb.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/hc-pdb.js +30 -12
- package/dist/puzzles/cube4/reduction/node-loader.d.ts +10 -1
- package/dist/puzzles/cube4/reduction/node-loader.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/node-loader.js +170 -36
- package/dist/puzzles/cube4/reduction/pair-transversal.d.ts +28 -0
- package/dist/puzzles/cube4/reduction/pair-transversal.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/pair-transversal.js +107 -0
- package/dist/puzzles/cube4/reduction/phase3-search.d.ts +33 -30
- package/dist/puzzles/cube4/reduction/phase3-search.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/phase3-search.js +211 -104
- package/dist/puzzles/cube4/reduction/rotation-fix.d.ts +28 -0
- package/dist/puzzles/cube4/reduction/rotation-fix.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/rotation-fix.js +212 -0
- package/dist/puzzles/cube4/reduction/solve.d.ts +72 -13
- package/dist/puzzles/cube4/reduction/solve.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/solve.js +374 -92
- package/dist/puzzles/cube4/scramble-cache.d.ts +93 -0
- package/dist/puzzles/cube4/scramble-cache.d.ts.map +1 -0
- package/dist/puzzles/cube4/scramble-cache.js +150 -0
- package/dist/puzzles/cube4/scrambler.d.ts +112 -18
- package/dist/puzzles/cube4/scrambler.d.ts.map +1 -1
- package/dist/puzzles/cube4/scrambler.js +271 -83
- package/dist/puzzles/cube4/worker-pool.d.ts +139 -0
- package/dist/puzzles/cube4/worker-pool.d.ts.map +1 -0
- package/dist/puzzles/cube4/worker-pool.js +405 -0
- package/dist/puzzles/cube4/worker-protocol.d.ts +58 -0
- package/dist/puzzles/cube4/worker-protocol.d.ts.map +1 -0
- package/dist/puzzles/cube4/worker-protocol.js +58 -0
- package/dist/puzzles/cube4/worker.d.ts +26 -0
- package/dist/puzzles/cube4/worker.d.ts.map +1 -0
- package/dist/puzzles/cube4/worker.js +49 -0
- package/dist/puzzles/pyraminx/solver/browser-loader.d.ts +4 -8
- package/dist/puzzles/pyraminx/solver/browser-loader.d.ts.map +1 -1
- package/dist/puzzles/pyraminx/solver/browser-loader.js +44 -15
- package/dist/puzzles/pyraminx/solver/generated-assets.d.ts.map +1 -1
- package/dist/puzzles/pyraminx/solver/generated-assets.js +8 -3
- package/dist/puzzles/shared/compressed-fetch.d.ts +37 -1
- package/dist/puzzles/shared/compressed-fetch.d.ts.map +1 -1
- package/dist/puzzles/shared/compressed-fetch.js +226 -31
- package/dist/puzzles/shared/load-once.d.ts +16 -0
- package/dist/puzzles/shared/load-once.d.ts.map +1 -0
- package/dist/puzzles/shared/load-once.js +11 -0
- package/dist/puzzles/skewb/solver/browser-loader.d.ts +4 -11
- package/dist/puzzles/skewb/solver/browser-loader.d.ts.map +1 -1
- package/dist/puzzles/skewb/solver/browser-loader.js +44 -16
- package/dist/puzzles/skewb/solver/generated-assets.d.ts.map +1 -1
- package/dist/puzzles/skewb/solver/generated-assets.js +8 -3
- package/dist/puzzles/sq1/solver/browser-loader.d.ts.map +1 -1
- package/dist/puzzles/sq1/solver/browser-loader.js +58 -26
- package/dist-cjs/core/api.js +10 -2
- package/dist-cjs/puzzles/cube2/solver/browser-loader.js +42 -14
- package/dist-cjs/puzzles/cube2/solver/generated-assets.js +8 -3
- package/dist-cjs/puzzles/cube3/solver/browser-loader.js +42 -20
- package/dist-cjs/puzzles/cube3/solver/generated-assets.js +9 -3
- package/dist-cjs/puzzles/cube3/solver/phase1.js +29 -6
- package/dist-cjs/puzzles/cube3/solver/phase2.js +56 -15
- package/dist-cjs/puzzles/cube3/solver/tables.js +14 -12
- package/dist-cjs/puzzles/cube4/browser-worker-pool.js +211 -0
- package/dist-cjs/puzzles/cube4/index.js +21 -1
- package/dist-cjs/puzzles/cube4/moves.js +122 -30
- package/dist-cjs/puzzles/cube4/reduction/browser-loader.js +212 -48
- package/dist-cjs/puzzles/cube4/reduction/center2.js +102 -2
- package/dist-cjs/puzzles/cube4/reduction/finish3.js +398 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/hc-manifest.json +6 -0
- package/dist-cjs/puzzles/cube4/reduction/hc-pdb.js +31 -12
- package/dist-cjs/puzzles/cube4/reduction/node-loader.js +171 -36
- package/dist-cjs/puzzles/cube4/reduction/pair-transversal.js +112 -0
- package/dist-cjs/puzzles/cube4/reduction/phase3-search.js +211 -104
- package/dist-cjs/puzzles/cube4/reduction/rotation-fix.js +216 -0
- package/dist-cjs/puzzles/cube4/reduction/solve.js +374 -88
- package/dist-cjs/puzzles/cube4/scramble-cache.js +156 -0
- package/dist-cjs/puzzles/cube4/scrambler.js +271 -80
- package/dist-cjs/puzzles/cube4/worker-pool.js +447 -0
- package/dist-cjs/puzzles/cube4/worker-protocol.js +62 -0
- package/dist-cjs/puzzles/pyraminx/solver/browser-loader.js +44 -15
- package/dist-cjs/puzzles/pyraminx/solver/generated-assets.js +8 -3
- package/dist-cjs/puzzles/shared/compressed-fetch.js +229 -31
- package/dist-cjs/puzzles/shared/load-once.js +14 -0
- package/dist-cjs/puzzles/skewb/solver/browser-loader.js +44 -16
- package/dist-cjs/puzzles/skewb/solver/generated-assets.js +8 -3
- package/dist-cjs/puzzles/sq1/solver/browser-loader.js +58 -26
- package/docs/development.md +19 -7
- package/package.json +4 -3
|
@@ -46,7 +46,7 @@ export async function solveCube3Phase2(state, options = {}) {
|
|
|
46
46
|
const maxDepth = options.maxDepth ?? 18;
|
|
47
47
|
const start = encodeCube3Phase2Coordinates(state);
|
|
48
48
|
const deadline = options.timeoutMs === undefined ? undefined : performance.now() + options.timeoutMs;
|
|
49
|
-
const context = createPhase2SearchContext(tables, pairTables, maxDepth, deadline);
|
|
49
|
+
const context = createPhase2SearchContext(tables, pairTables, maxDepth, deadline, options.maxNodes);
|
|
50
50
|
const spRange = tables.moveTables.slicePermutation.range;
|
|
51
51
|
const cpspTable = tables.pruningTables.cornerPermutationSlicePermutation.values;
|
|
52
52
|
const epspTable = tables.pruningTables.udEdgePermutationSlicePermutation.values;
|
|
@@ -77,6 +77,9 @@ export async function solveCube3Phase2(state, options = {}) {
|
|
|
77
77
|
visitedNodes: context.visitedNodes
|
|
78
78
|
};
|
|
79
79
|
}
|
|
80
|
+
if (context.nodesExceeded) {
|
|
81
|
+
throw new Cube3Phase2SearchError(`3x3 Phase 2 node budget exceeded (${options.maxNodes} nodes).`, context.visitedNodes);
|
|
82
|
+
}
|
|
80
83
|
if (context.timedOut) {
|
|
81
84
|
throw new Cube3Phase2SearchError(`3x3 Phase 2 search timed out after ${options.timeoutMs}ms.`, context.visitedNodes);
|
|
82
85
|
}
|
|
@@ -117,7 +120,19 @@ export async function solveCube3State(state, options = {}) {
|
|
|
117
120
|
const p2Tables = options.phase2?.tables ?? await loadCube3Phase2Tables();
|
|
118
121
|
const scoredPairTables = options.phase2?.pairTables ??
|
|
119
122
|
(options.phase2?.skipPairTables ? undefined : await loadCube3PairTables());
|
|
120
|
-
|
|
123
|
+
// Work budget for the improvement loop. Preferred: `improveMaxNodes`
|
|
124
|
+
// (deterministic node counter — the wall deadline stays unarmed so the
|
|
125
|
+
// outcome cannot depend on CPU load/clock). Legacy: `timeoutMs ?? 800`
|
|
126
|
+
// wall deadline, kept for existing callers.
|
|
127
|
+
const nodeMode = options.improveMaxNodes !== undefined;
|
|
128
|
+
let remainingNodes = options.improveMaxNodes ?? 0;
|
|
129
|
+
const deadline = nodeMode ? undefined : performance.now() + (options.timeoutMs ?? 800);
|
|
130
|
+
const budgetExhausted = () => nodeMode ? remainingNodes <= 0 : performance.now() > deadline;
|
|
131
|
+
const spendNodes = (used) => {
|
|
132
|
+
if (nodeMode) {
|
|
133
|
+
remainingNodes = Math.max(0, remainingNodes - used);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
121
136
|
const p2SpRange = p2Tables.moveTables.slicePermutation.range;
|
|
122
137
|
const p2cpsp = p2Tables.pruningTables.cornerPermutationSlicePermutation.values;
|
|
123
138
|
const p2uepsp = p2Tables.pruningTables.udEdgePermutationSlicePermutation.values;
|
|
@@ -129,18 +144,21 @@ export async function solveCube3State(state, options = {}) {
|
|
|
129
144
|
const minDepth = phase1.depth;
|
|
130
145
|
const scored = [];
|
|
131
146
|
for (let d = minDepth; d <= minDepth + improveDepths && d < bestMoves.length; d++) {
|
|
132
|
-
if (
|
|
147
|
+
if (budgetExhausted())
|
|
133
148
|
break;
|
|
134
149
|
const { solutions: p1Solutions, visitedNodes } = await collectCube3Phase1Solutions(state, d, {
|
|
135
150
|
tables: p1Tables,
|
|
136
151
|
limit: solutionLimit,
|
|
137
|
-
|
|
152
|
+
...(nodeMode
|
|
153
|
+
? { maxNodes: Math.max(1, remainingNodes) }
|
|
154
|
+
: { timeoutMs: Math.max(1, deadline - performance.now()) })
|
|
138
155
|
});
|
|
139
156
|
improvementNodes += visitedNodes;
|
|
140
|
-
|
|
157
|
+
spendNodes(visitedNodes);
|
|
158
|
+
if (budgetExhausted())
|
|
141
159
|
break;
|
|
142
160
|
for (const p1Moves of p1Solutions) {
|
|
143
|
-
if (
|
|
161
|
+
if (budgetExhausted())
|
|
144
162
|
break;
|
|
145
163
|
if (p1Moves.length >= bestMoves.length)
|
|
146
164
|
continue;
|
|
@@ -177,7 +195,7 @@ export async function solveCube3State(state, options = {}) {
|
|
|
177
195
|
// Sort ALL candidates globally across depths by admissible total lower bound
|
|
178
196
|
scored.sort((left, right) => left.est - right.est);
|
|
179
197
|
for (const cand of scored) {
|
|
180
|
-
if (
|
|
198
|
+
if (budgetExhausted())
|
|
181
199
|
break;
|
|
182
200
|
// Prune: since candidates are sorted by est, no subsequent candidate can beat bestMoves.length
|
|
183
201
|
if (cand.est >= bestMoves.length)
|
|
@@ -186,19 +204,31 @@ export async function solveCube3State(state, options = {}) {
|
|
|
186
204
|
if (maxP2 <= 0)
|
|
187
205
|
continue;
|
|
188
206
|
try {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
207
|
+
// Strip any legacy wall `timeoutMs` from the spread: in node mode
|
|
208
|
+
// the wall deadline must not be armed at all (pure function of
|
|
209
|
+
// budgets); in wall mode the per-candidate cap below re-adds it.
|
|
210
|
+
const phase2Source = options.phase2 ?? {};
|
|
211
|
+
const { timeoutMs: legacyPhase2Wall, ...phase2CandidateBase } = phase2Source;
|
|
212
|
+
// Per-candidate budget: node cap (deterministic) when the loop runs
|
|
213
|
+
// on `improveMaxNodes`, else the legacy wall cap.
|
|
214
|
+
const candidateCap = nodeMode
|
|
215
|
+
? {
|
|
216
|
+
maxNodes: Math.max(1, Math.min(options.improvePhase2MaxNodes ?? phase2CandidateBase.maxNodes ?? remainingNodes, remainingNodes))
|
|
217
|
+
}
|
|
218
|
+
: {
|
|
219
|
+
timeoutMs: Math.min(options.improvePhase2TimeoutMs ?? legacyPhase2Wall ?? 25, Math.max(1, deadline - performance.now()))
|
|
220
|
+
};
|
|
192
221
|
const candP2 = await solveCube3Phase2(cand.state, {
|
|
193
|
-
...
|
|
222
|
+
...phase2CandidateBase,
|
|
194
223
|
pairTables: scoredPairTables,
|
|
195
224
|
tables: p2Tables,
|
|
196
225
|
maxDepth: maxP2,
|
|
197
226
|
previousFace: cand.pFace,
|
|
198
227
|
previousPreviousFace: cand.ppFace,
|
|
199
|
-
|
|
228
|
+
...candidateCap
|
|
200
229
|
});
|
|
201
230
|
improvementNodes += candP2.visitedNodes;
|
|
231
|
+
spendNodes(candP2.visitedNodes);
|
|
202
232
|
const candTotal = cand.moves.length + candP2.moves.length;
|
|
203
233
|
if (candTotal < bestMoves.length) {
|
|
204
234
|
bestMoves = [...cand.moves, ...candP2.moves];
|
|
@@ -292,7 +322,7 @@ const PHASE2_MOVE_FACES = CUBE3_PHASE2_MOVES.map((move) => {
|
|
|
292
322
|
const char = move[0];
|
|
293
323
|
return char === "U" ? 0 : char === "D" ? 1 : char === "R" ? 2 : char === "L" ? 3 : char === "F" ? 4 : 5;
|
|
294
324
|
});
|
|
295
|
-
function createPhase2SearchContext(tables, pairTables, maxDepth, deadline) {
|
|
325
|
+
function createPhase2SearchContext(tables, pairTables, maxDepth, deadline, nodeCap = undefined) {
|
|
296
326
|
const scratchLevels = maxDepth + 1;
|
|
297
327
|
const scratchSize = scratchLevels * CUBE3_PHASE2_MOVES.length;
|
|
298
328
|
return {
|
|
@@ -300,8 +330,10 @@ function createPhase2SearchContext(tables, pairTables, maxDepth, deadline) {
|
|
|
300
330
|
pairTables,
|
|
301
331
|
maxDepth,
|
|
302
332
|
deadline,
|
|
333
|
+
nodeCap,
|
|
303
334
|
visitedNodes: 0,
|
|
304
335
|
timedOut: false,
|
|
336
|
+
nodesExceeded: false,
|
|
305
337
|
scratchMove: new Int32Array(scratchSize),
|
|
306
338
|
scratchBound: new Int32Array(scratchSize),
|
|
307
339
|
scratchCp: new Int32Array(scratchSize),
|
|
@@ -311,8 +343,17 @@ function createPhase2SearchContext(tables, pairTables, maxDepth, deadline) {
|
|
|
311
343
|
}
|
|
312
344
|
function searchPhase2(cornerPermutation, udEdgePermutation, slicePermutation, previousFace, previousPreviousFace, remainingDepth, path, context) {
|
|
313
345
|
context.visitedNodes += 1;
|
|
314
|
-
if (context.
|
|
346
|
+
if (context.nodeCap !== undefined &&
|
|
347
|
+
context.visitedNodes > context.nodeCap) {
|
|
348
|
+
context.nodesExceeded = true;
|
|
349
|
+
}
|
|
350
|
+
if (!context.nodesExceeded &&
|
|
351
|
+
context.deadline !== undefined &&
|
|
352
|
+
(context.visitedNodes & 1023) === 0 &&
|
|
353
|
+
performance.now() > context.deadline) {
|
|
315
354
|
context.timedOut = true;
|
|
355
|
+
}
|
|
356
|
+
if (context.timedOut || context.nodesExceeded) {
|
|
316
357
|
return false;
|
|
317
358
|
}
|
|
318
359
|
if (remainingDepth === 0) {
|
|
@@ -403,7 +444,7 @@ function searchPhase2(cornerPermutation, udEdgePermutation, slicePermutation, pr
|
|
|
403
444
|
return true;
|
|
404
445
|
}
|
|
405
446
|
path.pop();
|
|
406
|
-
if (context.timedOut) {
|
|
447
|
+
if (context.timedOut || context.nodesExceeded) {
|
|
407
448
|
return false;
|
|
408
449
|
}
|
|
409
450
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube3/solver/tables.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAM7C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube3/solver/tables.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAM7C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAS7D,MAAM,MAAM,mBAAmB,GAAG,MAAM,oBAAoB,CAAC;AAE7D,MAAM,MAAM,qBAAqB,GAC7B,mBAAmB,GACnB,kCAAkC,GAClC,mCAAmC,GACnC,mCAAmC,CAAC;AAExC,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,mBAAmB,CAAC;IAChC,KAAK,EAAE,SAAS,SAAS,EAAE,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,MAAM,EAAE,UAAU,GAAG,WAAW,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,qBAAqB,CAAC;IAClC,KAAK,EAAE,SAAS,SAAS,EAAE,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,SAAS,SAAS,EAAE,CAAC;IAClC,WAAW,EAAE,SAAS,SAAS,EAAE,CAAC;IAClC,UAAU,EAAE;QACV,iBAAiB,EAAE,cAAc,CAAC;QAClC,eAAe,EAAE,cAAc,CAAC;QAChC,OAAO,EAAE,cAAc,CAAC;QACxB,iBAAiB,EAAE,cAAc,CAAC;QAClC,iBAAiB,EAAE,cAAc,CAAC;QAClC,gBAAgB,EAAE,cAAc,CAAC;KAClC,CAAC;IACF,aAAa,EAAE;QACb,iBAAiB,EAAE,iBAAiB,CAAC;QACrC,eAAe,EAAE,iBAAiB,CAAC;QACnC,OAAO,EAAE,iBAAiB,CAAC;QAC3B,gCAAgC,EAAE,iBAAiB,CAAC;QACpD,iBAAiB,EAAE,iBAAiB,CAAC;QACrC,iBAAiB,EAAE,iBAAiB,CAAC;QACrC,gBAAgB,EAAE,iBAAiB,CAAC;QACpC,iCAAiC,EAAE,iBAAiB,CAAC;QACrD,iCAAiC,EAAE,iBAAiB,CAAC;KACtD,CAAC;IACF,WAAW,EAAE,gBAAgB,GAAG,SAAS,CAAC;CAC3C;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,uEAAuE;IACvE,SAAS,EAAE,UAAU,CAAC;IACtB,qEAAqE;IACrE,SAAS,EAAE,UAAU,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,SAAS,SAAS,EAAE,CAAC;IAClC,UAAU,EAAE,IAAI,CACd,iBAAiB,CAAC,YAAY,CAAC,EAC/B,mBAAmB,GAAG,iBAAiB,GAAG,SAAS,CACpD,CAAC;IACF,aAAa,EAAE,IAAI,CACjB,iBAAiB,CAAC,eAAe,CAAC,EAClC,mBAAmB,GAAG,iBAAiB,GAAG,SAAS,CACpD,GAAG;QACF,gCAAgC,EAAE,iBAAiB,CAAC;KACrD,CAAC;IACF,kEAAkE;IAClE,WAAW,EAAE,gBAAgB,GAAG,SAAS,CAAC;CAC3C;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,SAAS,SAAS,EAAE,CAAC;IAClC,UAAU,EAAE,IAAI,CACd,iBAAiB,CAAC,YAAY,CAAC,EAC/B,mBAAmB,GAAG,mBAAmB,GAAG,kBAAkB,CAC/D,CAAC;IACF,aAAa,EAAE,IAAI,CACjB,iBAAiB,CAAC,eAAe,CAAC,EAClC,mBAAmB,GAAG,mBAAmB,GAAG,kBAAkB,CAC/D,GAAG;QACF,iCAAiC,EAAE,iBAAiB,CAAC;QACrD,iCAAiC,EAAE,iBAAiB,CAAC;KACtD,CAAC;CACH;AAED,eAAO,MAAM,kBAAkB,oIAAc,CAAC;AAC9C,eAAO,MAAM,kBAAkB,EAAE,SAAS,SAAS,EAWlD,CAAC;AAUF,wBAAgB,qBAAqB,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAElE;AAED,wBAAgB,qBAAqB,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAElE;AAED,wBAAgB,qBAAqB,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAElE;AAED,wBAAgB,yBAAyB,IAAI,iBAAiB,CAmC7D;AAED,wBAAgB,yBAAyB,IAAI,iBAAiB,CA4B7D;AAED,wBAAgB,yBAAyB,IAAI,iBAAiB,CAsC7D;AAED,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,mBAAmB,EAC/B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,SAAS,SAAS,EAAE,GAC1B,cAAc,CAqBhB;AAED,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,cAAc,GAAG,iBAAiB,CAsCtF;AAED,wBAAgB,iCAAiC,CAC/C,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,cAAc,GACrB,iBAAiB,CAgDnB;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,cAAc,EACrB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,SAAS,GACd,MAAM,CAQR;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,GAAG,MAAM,CAS1D"}
|
|
@@ -2,6 +2,7 @@ import { CUBE3_MOVES } from "../types.js";
|
|
|
2
2
|
import { CUBE3_COORDINATE_RANGES, encodeCube3Coordinates, moveCoordinate } from "./coordinates.js";
|
|
3
3
|
import { createSolvedCube3State } from "../state.js";
|
|
4
4
|
import { loadGeneratedCube3Phase1Tables, loadGeneratedCube3Phase2Tables, loadGeneratedCube3SolverTables } from "./generated-assets.js";
|
|
5
|
+
import { loadOnce } from "../../shared/load-once.js";
|
|
5
6
|
export const CUBE3_PHASE1_MOVES = CUBE3_MOVES;
|
|
6
7
|
export const CUBE3_PHASE2_MOVES = [
|
|
7
8
|
"U",
|
|
@@ -15,20 +16,21 @@ export const CUBE3_PHASE2_MOVES = [
|
|
|
15
16
|
"F2",
|
|
16
17
|
"B2"
|
|
17
18
|
];
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
// Promise slots (shared helper, see shared/load-once.ts): concurrent cold
|
|
20
|
+
// calls share ONE in-flight load instead of racing several loads and
|
|
21
|
+
// clobbering each other's results; a rejection purges the slot so the next
|
|
22
|
+
// call can retry.
|
|
23
|
+
const solverTablesSlot = { promise: undefined };
|
|
24
|
+
const phase1TablesSlot = { promise: undefined };
|
|
25
|
+
const phase2TablesSlot = { promise: undefined };
|
|
26
|
+
export function loadCube3SolverTables() {
|
|
27
|
+
return loadOnce(solverTablesSlot, loadGeneratedCube3SolverTables);
|
|
24
28
|
}
|
|
25
|
-
export
|
|
26
|
-
|
|
27
|
-
return cachedPhase1Tables;
|
|
29
|
+
export function loadCube3Phase1Tables() {
|
|
30
|
+
return loadOnce(phase1TablesSlot, loadGeneratedCube3Phase1Tables);
|
|
28
31
|
}
|
|
29
|
-
export
|
|
30
|
-
|
|
31
|
-
return cachedPhase2Tables;
|
|
32
|
+
export function loadCube3Phase2Tables() {
|
|
33
|
+
return loadOnce(phase2TablesSlot, loadGeneratedCube3Phase2Tables);
|
|
32
34
|
}
|
|
33
35
|
export function generateCube3Phase1Tables() {
|
|
34
36
|
const cornerOrientation = generateCube3MoveTable("cornerOrientation", CUBE3_COORDINATE_RANGES.cornerOrientation, CUBE3_PHASE1_MOVES);
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web Worker pool for 4x4 random-state scrambles — the browser counterpart
|
|
3
|
+
* of `worker-pool.ts` with the same protocol, lazy-spawn lifecycle, FIFO
|
|
4
|
+
* queue, typed error propagation and per-thread table reuse.
|
|
5
|
+
*
|
|
6
|
+
* Unlike the Node pool (which locates its own worker file), the browser
|
|
7
|
+
* pool REQUIRES the caller to pass `workerUrl`, because only the caller's
|
|
8
|
+
* file can host the static `new URL("./browser-worker.js", import.meta.url)`
|
|
9
|
+
* expression that bundlers (Vite, Webpack 5, Parcel) statically analyse to
|
|
10
|
+
* emit a separate worker chunk:
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* const pool = new Cube4BrowserWorkerPool({
|
|
14
|
+
* workerUrl: new URL("./browser-worker.js", import.meta.url)
|
|
15
|
+
* });
|
|
16
|
+
* const batch = await pool.generateBatch(8, { seed: "demo" });
|
|
17
|
+
* await pool.terminate();
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* This file contains no `import.meta` and no static Node imports, so it
|
|
21
|
+
* also compiles in the CommonJS build (unlike a hard-coded
|
|
22
|
+
* `new URL(..., import.meta.url)` default).
|
|
23
|
+
*
|
|
24
|
+
* Errors crossing the worker boundary arrive as `Error` instances with the
|
|
25
|
+
* original `name`, `message` and `code` preserved (`worker-protocol.ts`).
|
|
26
|
+
*/
|
|
27
|
+
import type { ScrambleResult } from "../../core/types.js";
|
|
28
|
+
import { type Cube4WorkerGenerationOptions } from "./worker-protocol.js";
|
|
29
|
+
export interface Cube4BrowserWorkerPoolOptions {
|
|
30
|
+
/**
|
|
31
|
+
* URL of the worker entry, normally
|
|
32
|
+
* `new URL("./browser-worker.js", import.meta.url)` written in the
|
|
33
|
+
* consumer's module (statically analysable by bundlers).
|
|
34
|
+
*/
|
|
35
|
+
workerUrl: string | URL;
|
|
36
|
+
/**
|
|
37
|
+
* Maximum number of Web Workers. Defaults to
|
|
38
|
+
* `navigator.hardwareConcurrency - 1` (min 1).
|
|
39
|
+
*/
|
|
40
|
+
size?: number;
|
|
41
|
+
}
|
|
42
|
+
/** Typed rejection used for post-terminate operations and shutdown itself. */
|
|
43
|
+
export declare class Cube4BrowserWorkerPoolClosedError extends Error {
|
|
44
|
+
readonly code: "cube4-browser-pool-closed";
|
|
45
|
+
constructor(action?: string);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Batch seed derivation — identical to core `generateScrambles` and to
|
|
49
|
+
* `deriveBatchCube4Options` in the Node pool.
|
|
50
|
+
*/
|
|
51
|
+
export declare function deriveBrowserBatchCube4Options(options: Cube4WorkerGenerationOptions, index: number, count: number): Cube4WorkerGenerationOptions;
|
|
52
|
+
export declare class Cube4BrowserWorkerPool {
|
|
53
|
+
private readonly workers;
|
|
54
|
+
private readonly queue;
|
|
55
|
+
private readonly workerUrl;
|
|
56
|
+
private readonly maxSize;
|
|
57
|
+
private pumping;
|
|
58
|
+
private terminatePromise;
|
|
59
|
+
private nextId;
|
|
60
|
+
private terminated;
|
|
61
|
+
constructor(options: Cube4BrowserWorkerPoolOptions);
|
|
62
|
+
/** Threads spawned so far (lazily grown; 0 before the first job). */
|
|
63
|
+
get workerCount(): number;
|
|
64
|
+
get queuedCount(): number;
|
|
65
|
+
get busyCount(): number;
|
|
66
|
+
get closed(): boolean;
|
|
67
|
+
generate(options?: Cube4WorkerGenerationOptions): Promise<ScrambleResult>;
|
|
68
|
+
generateBatch(count: number, options?: Cube4WorkerGenerationOptions): Promise<ScrambleResult[]>;
|
|
69
|
+
/**
|
|
70
|
+
* Prompt shutdown: rejects queued/in-flight tasks and terminates every
|
|
71
|
+
* Web Worker synchronously (no threads to join in the browser).
|
|
72
|
+
* Idempotent.
|
|
73
|
+
*/
|
|
74
|
+
terminate(): void;
|
|
75
|
+
private pump;
|
|
76
|
+
private runPump;
|
|
77
|
+
private spawnWorker;
|
|
78
|
+
private dispatch;
|
|
79
|
+
private handleResponse;
|
|
80
|
+
private handleWorkerGone;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=browser-worker-pool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-worker-pool.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube4/browser-worker-pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAEL,KAAK,4BAA4B,EAGlC,MAAM,sBAAsB,CAAC;AAE9B,MAAM,WAAW,6BAA6B;IAC5C;;;;OAIG;IACH,SAAS,EAAE,MAAM,GAAG,GAAG,CAAC;IACxB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,8EAA8E;AAC9E,qBAAa,iCAAkC,SAAQ,KAAK;IAC1D,QAAQ,CAAC,IAAI,EAAG,2BAA2B,CAAU;gBAEzC,MAAM,SAAc;CAIjC;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,4BAA4B,EACrC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,4BAA4B,CAK9B;AAqBD,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;IAC1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,gBAAgB,CAA4B;IACpD,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,UAAU,CAAS;gBAEf,OAAO,EAAE,6BAA6B;IAiBlD,qEAAqE;IACrE,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,QAAQ,CAAC,OAAO,GAAE,4BAAiC,GAAG,OAAO,CAAC,cAAc,CAAC;IAW7E,aAAa,CACX,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,4BAAiC,GACzC,OAAO,CAAC,cAAc,EAAE,CAAC;IAW5B;;;;OAIG;IACH,SAAS,IAAI,IAAI;IAoBjB,OAAO,CAAC,IAAI;YAUE,OAAO;IAerB,OAAO,CAAC,WAAW;IAoBnB,OAAO,CAAC,QAAQ;IAYhB,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,gBAAgB;CAUzB"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web Worker pool for 4x4 random-state scrambles — the browser counterpart
|
|
3
|
+
* of `worker-pool.ts` with the same protocol, lazy-spawn lifecycle, FIFO
|
|
4
|
+
* queue, typed error propagation and per-thread table reuse.
|
|
5
|
+
*
|
|
6
|
+
* Unlike the Node pool (which locates its own worker file), the browser
|
|
7
|
+
* pool REQUIRES the caller to pass `workerUrl`, because only the caller's
|
|
8
|
+
* file can host the static `new URL("./browser-worker.js", import.meta.url)`
|
|
9
|
+
* expression that bundlers (Vite, Webpack 5, Parcel) statically analyse to
|
|
10
|
+
* emit a separate worker chunk:
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* const pool = new Cube4BrowserWorkerPool({
|
|
14
|
+
* workerUrl: new URL("./browser-worker.js", import.meta.url)
|
|
15
|
+
* });
|
|
16
|
+
* const batch = await pool.generateBatch(8, { seed: "demo" });
|
|
17
|
+
* await pool.terminate();
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* This file contains no `import.meta` and no static Node imports, so it
|
|
21
|
+
* also compiles in the CommonJS build (unlike a hard-coded
|
|
22
|
+
* `new URL(..., import.meta.url)` default).
|
|
23
|
+
*
|
|
24
|
+
* Errors crossing the worker boundary arrive as `Error` instances with the
|
|
25
|
+
* original `name`, `message` and `code` preserved (`worker-protocol.ts`).
|
|
26
|
+
*/
|
|
27
|
+
import { deserializeCube4Error } from "./worker-protocol.js";
|
|
28
|
+
/** Typed rejection used for post-terminate operations and shutdown itself. */
|
|
29
|
+
export class Cube4BrowserWorkerPoolClosedError extends Error {
|
|
30
|
+
code = "cube4-browser-pool-closed";
|
|
31
|
+
constructor(action = "operation") {
|
|
32
|
+
super(`[cube4] browser worker pool is closed (${action} rejected).`);
|
|
33
|
+
this.name = "Cube4BrowserWorkerPoolClosedError";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Batch seed derivation — identical to core `generateScrambles` and to
|
|
38
|
+
* `deriveBatchCube4Options` in the Node pool.
|
|
39
|
+
*/
|
|
40
|
+
export function deriveBrowserBatchCube4Options(options, index, count) {
|
|
41
|
+
if (count === 1 || options.seed === undefined) {
|
|
42
|
+
return options;
|
|
43
|
+
}
|
|
44
|
+
return { ...options, seed: `${options.seed}:${index}` };
|
|
45
|
+
}
|
|
46
|
+
function defaultBrowserSize() {
|
|
47
|
+
if (typeof navigator !== "undefined" && navigator.hardwareConcurrency) {
|
|
48
|
+
return Math.max(1, navigator.hardwareConcurrency - 1);
|
|
49
|
+
}
|
|
50
|
+
return 4;
|
|
51
|
+
}
|
|
52
|
+
export class Cube4BrowserWorkerPool {
|
|
53
|
+
workers = [];
|
|
54
|
+
queue = [];
|
|
55
|
+
workerUrl;
|
|
56
|
+
maxSize;
|
|
57
|
+
pumping = false;
|
|
58
|
+
terminatePromise;
|
|
59
|
+
nextId = 0;
|
|
60
|
+
terminated = false;
|
|
61
|
+
constructor(options) {
|
|
62
|
+
if (typeof Worker === "undefined") {
|
|
63
|
+
throw new Error("[cube4] Web Workers are not available in this environment; " +
|
|
64
|
+
"use generateCube4Scramble() directly for single-threaded generation.");
|
|
65
|
+
}
|
|
66
|
+
if (options.size !== undefined) {
|
|
67
|
+
if (!Number.isInteger(options.size) || options.size < 1) {
|
|
68
|
+
throw new RangeError(`[cube4] pool size must be an integer >= 1 (got ${options.size}).`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
this.workerUrl =
|
|
72
|
+
typeof options.workerUrl === "string" ? options.workerUrl : options.workerUrl.href;
|
|
73
|
+
this.maxSize = options.size ?? defaultBrowserSize();
|
|
74
|
+
}
|
|
75
|
+
/** Threads spawned so far (lazily grown; 0 before the first job). */
|
|
76
|
+
get workerCount() {
|
|
77
|
+
return this.workers.length;
|
|
78
|
+
}
|
|
79
|
+
get queuedCount() {
|
|
80
|
+
return this.queue.length;
|
|
81
|
+
}
|
|
82
|
+
get busyCount() {
|
|
83
|
+
return this.workers.reduce((n, rec) => n + (rec.task !== null ? 1 : 0), 0);
|
|
84
|
+
}
|
|
85
|
+
get closed() {
|
|
86
|
+
return this.terminated;
|
|
87
|
+
}
|
|
88
|
+
generate(options = {}) {
|
|
89
|
+
if (this.terminated) {
|
|
90
|
+
return Promise.reject(new Cube4BrowserWorkerPoolClosedError("generate"));
|
|
91
|
+
}
|
|
92
|
+
const id = this.nextId++;
|
|
93
|
+
return new Promise((resolve, reject) => {
|
|
94
|
+
this.queue.push({ id, options, resolve, reject });
|
|
95
|
+
this.pump();
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
generateBatch(count, options = {}) {
|
|
99
|
+
if (!Number.isInteger(count) || count < 1) {
|
|
100
|
+
return Promise.reject(new RangeError(`count must be a positive integer (got ${count}).`));
|
|
101
|
+
}
|
|
102
|
+
return Promise.all(Array.from({ length: count }, (_, index) => this.generate(deriveBrowserBatchCube4Options(options, index, count))));
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Prompt shutdown: rejects queued/in-flight tasks and terminates every
|
|
106
|
+
* Web Worker synchronously (no threads to join in the browser).
|
|
107
|
+
* Idempotent.
|
|
108
|
+
*/
|
|
109
|
+
terminate() {
|
|
110
|
+
if (this.terminatePromise !== undefined)
|
|
111
|
+
return;
|
|
112
|
+
this.terminated = true;
|
|
113
|
+
const closed = () => new Cube4BrowserWorkerPoolClosedError();
|
|
114
|
+
for (const task of this.queue.splice(0))
|
|
115
|
+
task.reject(closed());
|
|
116
|
+
const records = this.workers.splice(0);
|
|
117
|
+
for (const rec of records) {
|
|
118
|
+
if (rec.task !== null) {
|
|
119
|
+
rec.task.reject(closed());
|
|
120
|
+
rec.task = null;
|
|
121
|
+
}
|
|
122
|
+
rec.worker.terminate();
|
|
123
|
+
}
|
|
124
|
+
this.terminatePromise = Promise.resolve();
|
|
125
|
+
}
|
|
126
|
+
// ── internals (same shape as the Node pool) ──────────────────────────
|
|
127
|
+
pump() {
|
|
128
|
+
if (this.pumping || this.terminated)
|
|
129
|
+
return;
|
|
130
|
+
this.pumping = true;
|
|
131
|
+
void this.runPump()
|
|
132
|
+
.catch(() => undefined)
|
|
133
|
+
.finally(() => {
|
|
134
|
+
this.pumping = false;
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
async runPump() {
|
|
138
|
+
while (!this.terminated && this.queue.length > 0) {
|
|
139
|
+
const idle = this.workers.find((rec) => rec.task === null);
|
|
140
|
+
if (idle !== undefined) {
|
|
141
|
+
this.dispatch(idle, this.queue.shift());
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (this.workers.length < this.maxSize) {
|
|
145
|
+
this.spawnWorker();
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
spawnWorker() {
|
|
152
|
+
const worker = new Worker(this.workerUrl, { type: "module", name: "cubingcenter-444" });
|
|
153
|
+
const rec = { worker, task: null };
|
|
154
|
+
worker.onmessage = (event) => {
|
|
155
|
+
this.handleResponse(rec, event.data);
|
|
156
|
+
};
|
|
157
|
+
worker.onerror = (event) => {
|
|
158
|
+
this.handleWorkerGone(rec, event.error instanceof Error
|
|
159
|
+
? event.error
|
|
160
|
+
: new Error(event.message || "[cube4] browser worker failed."));
|
|
161
|
+
};
|
|
162
|
+
this.workers.push(rec);
|
|
163
|
+
return rec;
|
|
164
|
+
}
|
|
165
|
+
dispatch(rec, task) {
|
|
166
|
+
rec.task = task;
|
|
167
|
+
const message = { id: task.id, options: task.options };
|
|
168
|
+
try {
|
|
169
|
+
rec.worker.postMessage(message);
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
rec.task = null;
|
|
173
|
+
task.reject(error);
|
|
174
|
+
this.pump();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
handleResponse(rec, response) {
|
|
178
|
+
const task = rec.task;
|
|
179
|
+
if (task === null || task.id !== response.id)
|
|
180
|
+
return;
|
|
181
|
+
rec.task = null;
|
|
182
|
+
if (this.terminated)
|
|
183
|
+
return;
|
|
184
|
+
if (response.ok)
|
|
185
|
+
task.resolve(response.result);
|
|
186
|
+
else
|
|
187
|
+
task.reject(deserializeCube4Error(response.error));
|
|
188
|
+
const next = this.queue.shift();
|
|
189
|
+
if (next !== undefined)
|
|
190
|
+
this.dispatch(rec, next);
|
|
191
|
+
this.pump();
|
|
192
|
+
}
|
|
193
|
+
handleWorkerGone(rec, error) {
|
|
194
|
+
const index = this.workers.indexOf(rec);
|
|
195
|
+
if (index === -1)
|
|
196
|
+
return;
|
|
197
|
+
this.workers.splice(index, 1);
|
|
198
|
+
const task = rec.task;
|
|
199
|
+
rec.task = null;
|
|
200
|
+
if (task !== null && !this.terminated)
|
|
201
|
+
task.reject(error);
|
|
202
|
+
if (!this.terminated)
|
|
203
|
+
this.pump();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-worker.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube4/browser-worker.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/// <reference lib="webworker" />
|
|
2
|
+
/**
|
|
3
|
+
* Web Worker entry for 4x4 scramble generation (browser build).
|
|
4
|
+
*
|
|
5
|
+
* Mirror of `worker.ts`: no work at boot, tables loaded on the FIRST task
|
|
6
|
+
* and reused afterwards (the browser loader caches per worker via
|
|
7
|
+
* `scrambler.ts`'s promise-deduplicated `loadCube4Tables`), full options
|
|
8
|
+
* passed through verbatim, errors flattened through `worker-protocol.ts`.
|
|
9
|
+
*
|
|
10
|
+
* Bundlers pick this file up through the static
|
|
11
|
+
* `new Worker(new URL(..., import.meta.url))` pattern used by the pool's
|
|
12
|
+
* consumer — see `Cube4BrowserWorkerPool`'s `workerUrl` option.
|
|
13
|
+
*/
|
|
14
|
+
import { generateCube4Scramble } from "./scrambler.js";
|
|
15
|
+
import { serializeCube4Error } from "./worker-protocol.js";
|
|
16
|
+
const workerScope = self;
|
|
17
|
+
workerScope.onmessage = (event) => {
|
|
18
|
+
const task = event.data;
|
|
19
|
+
void (async () => {
|
|
20
|
+
const response = await (async () => {
|
|
21
|
+
try {
|
|
22
|
+
const result = await generateCube4Scramble(task.options);
|
|
23
|
+
return { id: task.id, ok: true, result };
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
return { id: task.id, ok: false, error: serializeCube4Error(error) };
|
|
27
|
+
}
|
|
28
|
+
})();
|
|
29
|
+
workerScope.postMessage(response);
|
|
30
|
+
})();
|
|
31
|
+
};
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
export { CORNER_COUNT, WING_COUNT, CENTER_COUNT, FACE_COUNT, CENTERS_PER_FACE, U, R, F, D, L, B, CUBE4_FACE_MOVES, CUBE4_ALL_MOVES, CUBE4_OUTER_MOVES } from "./types.js";
|
|
2
2
|
export type { Cube4Face, Cube4FaceMove, Cube4Move, Cube4MoveSuffix, Cube4State, Cube4Sticker, Cube4Facelets, Cube4ValidationResult, Cube4RandomStateOptions } from "./types.js";
|
|
3
3
|
export { createSolvedCube4State, cloneCube4State, cube4StatesEqual, isSolvedCube4State, areCentersReduced, areWingsPaired } from "./state.js";
|
|
4
|
+
export { Cube4FinishCapError, Cube4Phase2CapError, Cube4Phase3CapError } from "./reduction/solve.js";
|
|
4
5
|
export { cube4ScrambleGenerator, generateCube4Scramble, generateCube4Attempt } from "./scrambler.js";
|
|
5
6
|
export type { Cube4ScrambleOptions } from "./scrambler.js";
|
|
7
|
+
export { Cube4WorkerPool, Cube4WorkerPoolClosedError, deriveBatchCube4Options, generateCube4ScrambleBatch, getDefaultCube4WorkerPool, closeDefaultCube4WorkerPool } from "./worker-pool.js";
|
|
8
|
+
export type { Cube4WorkerPoolOptions } from "./worker-pool.js";
|
|
9
|
+
export { Cube4BrowserWorkerPool, Cube4BrowserWorkerPoolClosedError, deriveBrowserBatchCube4Options } from "./browser-worker-pool.js";
|
|
10
|
+
export type { Cube4BrowserWorkerPoolOptions } from "./browser-worker-pool.js";
|
|
11
|
+
export { Cube4ScrambleCache, createCube4ScrambleCache, deriveCube4StreamOptions } from "./scramble-cache.js";
|
|
12
|
+
export type { Cube4ScrambleCacheOptions, Cube4ScrambleGenerator } from "./scramble-cache.js";
|
|
6
13
|
export { applyCube4Move, applyCube4Algorithm, invertCube4Move, invertCube4Algorithm, simplifyCube4Algorithm, formatCube4Algorithm, parseCube4Algorithm, parseCube4Move, parseMove, FACE_MOVE_EFFECTS } from "./moves.js";
|
|
7
14
|
export { generateRandomCube4State } from "./random-state.js";
|
|
8
15
|
export { validateCube4State, isOneMoveFromSolvedCube4State, isTrivialCube4State, isAllowedRandomCube4State } from "./validation.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube4/index.ts"],"names":[],"mappings":"AASA,OAAO,EACL,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAChB,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EAClB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,SAAS,EACT,aAAa,EACb,SAAS,EACT,eAAe,EACf,UAAU,EACV,YAAY,EACZ,aAAa,EACb,qBAAqB,EACrB,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,sBAAsB,EACtB,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,oBAAoB,EACrB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,SAAS,EACT,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,wBAAwB,EACzB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,kBAAkB,EAClB,6BAA6B,EAC7B,mBAAmB,EACnB,yBAAyB,EAC1B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube4/index.ts"],"names":[],"mappings":"AASA,OAAO,EACL,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAChB,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EAClB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,SAAS,EACT,aAAa,EACb,SAAS,EACT,eAAe,EACf,UAAU,EACV,YAAY,EACZ,aAAa,EACb,qBAAqB,EACrB,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,sBAAsB,EACtB,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,oBAAoB,EACrB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,OAAO,EACL,eAAe,EACf,0BAA0B,EAC1B,uBAAuB,EACvB,0BAA0B,EAC1B,yBAAyB,EACzB,2BAA2B,EAC5B,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAE/D,OAAO,EACL,sBAAsB,EACtB,iCAAiC,EACjC,8BAA8B,EAC/B,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,6BAA6B,EAAE,MAAM,0BAA0B,CAAC;AAE9E,OAAO,EACL,kBAAkB,EAClB,wBAAwB,EACxB,wBAAwB,EACzB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,yBAAyB,EACzB,sBAAsB,EACvB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,SAAS,EACT,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,wBAAwB,EACzB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,kBAAkB,EAClB,6BAA6B,EAC7B,mBAAmB,EACnB,yBAAyB,EAC1B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,qBAAqB,CAAC"}
|
|
@@ -6,7 +6,11 @@ import { cube4ScrambleGenerator } from "./scrambler.js";
|
|
|
6
6
|
registerScrambleGenerator(cube4ScrambleGenerator);
|
|
7
7
|
export { CORNER_COUNT, WING_COUNT, CENTER_COUNT, FACE_COUNT, CENTERS_PER_FACE, U, R, F, D, L, B, CUBE4_FACE_MOVES, CUBE4_ALL_MOVES, CUBE4_OUTER_MOVES } from "./types.js";
|
|
8
8
|
export { createSolvedCube4State, cloneCube4State, cube4StatesEqual, isSolvedCube4State, areCentersReduced, areWingsPaired } from "./state.js";
|
|
9
|
+
export { Cube4FinishCapError, Cube4Phase2CapError, Cube4Phase3CapError } from "./reduction/solve.js";
|
|
9
10
|
export { cube4ScrambleGenerator, generateCube4Scramble, generateCube4Attempt } from "./scrambler.js";
|
|
11
|
+
export { Cube4WorkerPool, Cube4WorkerPoolClosedError, deriveBatchCube4Options, generateCube4ScrambleBatch, getDefaultCube4WorkerPool, closeDefaultCube4WorkerPool } from "./worker-pool.js";
|
|
12
|
+
export { Cube4BrowserWorkerPool, Cube4BrowserWorkerPoolClosedError, deriveBrowserBatchCube4Options } from "./browser-worker-pool.js";
|
|
13
|
+
export { Cube4ScrambleCache, createCube4ScrambleCache, deriveCube4StreamOptions } from "./scramble-cache.js";
|
|
10
14
|
export { applyCube4Move, applyCube4Algorithm, invertCube4Move, invertCube4Algorithm, simplifyCube4Algorithm, formatCube4Algorithm, parseCube4Algorithm, parseCube4Move, parseMove, FACE_MOVE_EFFECTS } from "./moves.js";
|
|
11
15
|
export { generateRandomCube4State } from "./random-state.js";
|
|
12
16
|
export { validateCube4State, isOneMoveFromSolvedCube4State, isTrivialCube4State, isAllowedRandomCube4State } from "./validation.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"moves.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube4/moves.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAOxF,UAAU,UAAU;IAClB,+DAA+D;IAC/D,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,yDAAyD;IACzD,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,kFAAkF;IAClF,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,oDAAoD;IACpD,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3B;AA0KD,QAAA,MAAM,iBAAiB,EAAE,MAAM,CAAC,aAAa,EAAE,UAAU,CAGxD,CAAC;AAuDF,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,GAAG;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAc1G;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,GAAG,UAAU,CAK7E;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,UAAU,CA4C9F;AAKD,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAI1D;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,EAAE,CAE7E;AAMD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,MAAM,CAExE;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,CAIlE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAMvD;
|
|
1
|
+
{"version":3,"file":"moves.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube4/moves.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAOxF,UAAU,UAAU;IAClB,+DAA+D;IAC/D,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,yDAAyD;IACzD,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,kFAAkF;IAClF,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,oDAAoD;IACpD,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3B;AA0KD,QAAA,MAAM,iBAAiB,EAAE,MAAM,CAAC,aAAa,EAAE,UAAU,CAGxD,CAAC;AAuDF,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,GAAG;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAc1G;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,GAAG,UAAU,CAK7E;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,UAAU,CA4C9F;AAKD,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAI1D;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,EAAE,CAE7E;AAMD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,MAAM,CAExE;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,CAIlE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAMvD;AA+HD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,EAAE,CAsC/E;AAMD,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
|