@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
|
@@ -41,6 +41,12 @@ export const CENTER2_GOAL_RL = [0, 18, 28, 46, 54, 56];
|
|
|
41
41
|
export const CENTER2_CT_STATES = 6435;
|
|
42
42
|
export const CENTER2_RL_STATES = 70;
|
|
43
43
|
export const CENTER2_STATES = CENTER2_CT_STATES * CENTER2_RL_STATES;
|
|
44
|
+
const CENTER2_GOAL_FLAGS = (() => {
|
|
45
|
+
const flags = new Uint8Array(CENTER2_RL_STATES);
|
|
46
|
+
for (const rl of CENTER2_GOAL_RL)
|
|
47
|
+
flags[rl] = 1;
|
|
48
|
+
return flags;
|
|
49
|
+
})();
|
|
44
50
|
// ct positions: U(0-3), D(12-15), F(8-11), B(20-23) — the reference's face
|
|
45
51
|
// order {U, D, F, B}, in this repo's face-major numbering.
|
|
46
52
|
const CT_POS = [0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11, 20, 21, 22, 23];
|
|
@@ -104,7 +110,7 @@ export function encodeCenter2(state) {
|
|
|
104
110
|
return { ct: ctRank, rl: rlRank };
|
|
105
111
|
}
|
|
106
112
|
export function isCenter2Goal(ct, rl) {
|
|
107
|
-
return ct === 0 &&
|
|
113
|
+
return ct === 0 && CENTER2_GOAL_FLAGS[rl] === 1;
|
|
108
114
|
}
|
|
109
115
|
/**
|
|
110
116
|
* The phase-2 coordinate is only valid for states where exactly 8 of the
|
|
@@ -292,6 +298,98 @@ export function generateCenter2Tables() {
|
|
|
292
298
|
}
|
|
293
299
|
return { ctMove, rlMove, pruning, pruningWide };
|
|
294
300
|
}
|
|
301
|
+
// ---------------------------------------------------------------------------
|
|
302
|
+
// Chiral (absolute-ct) phase-2 tables
|
|
303
|
+
// ---------------------------------------------------------------------------
|
|
304
|
+
/**
|
|
305
|
+
* The ct rank above is RELATIVE to ct[15], so `ct == 0` accepts both
|
|
306
|
+
* assignments of the two non-R/L axes to the U/D and F/B face-pairs. Only
|
|
307
|
+
* one of them survives the downstream chirality gate (`axisDet`: the U, R, F
|
|
308
|
+
* axes must form a right-handed frame, i.e. U/D must hold axis (a + 2) % 3
|
|
309
|
+
* when R/L holds axis a). The chiral coordinate ranks the ABSOLUTE pattern
|
|
310
|
+
* "which of the 16 U/D/F/B positions hold the U/D-bound axis" (C(16,8) =
|
|
311
|
+
* 12,870) and its pruning measures the distance to the chirality-correct
|
|
312
|
+
* goals only, so the phase-2 search never walks wrong-chirality goal paths.
|
|
313
|
+
* Same goal set as `ct == 0 && axisDet == 1`; generated at runtime (~1.6 MB).
|
|
314
|
+
*/
|
|
315
|
+
export const CENTER2_CHIRAL_CT_STATES = 12870;
|
|
316
|
+
export const CENTER2_CHIRAL_STATES = CENTER2_CHIRAL_CT_STATES * CENTER2_RL_STATES;
|
|
317
|
+
function rankSubset16(bits) {
|
|
318
|
+
let idx = 0;
|
|
319
|
+
let r = 8;
|
|
320
|
+
for (let i = 15; i >= 0 && r > 0; i--) {
|
|
321
|
+
if (bits[i] === 1) {
|
|
322
|
+
idx += CNK[i][r];
|
|
323
|
+
r--;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return idx;
|
|
327
|
+
}
|
|
328
|
+
function unrankSubset16(idx) {
|
|
329
|
+
const bits = new Uint8Array(16);
|
|
330
|
+
let r = 8;
|
|
331
|
+
for (let i = 15; i >= 0 && r > 0; i--) {
|
|
332
|
+
if (idx >= CNK[i][r]) {
|
|
333
|
+
idx -= CNK[i][r];
|
|
334
|
+
bits[i] = 1;
|
|
335
|
+
r--;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return bits;
|
|
339
|
+
}
|
|
340
|
+
/** Chiral ct rank of a coordinate-invariant (bridged) phase-2 state. */
|
|
341
|
+
export function encodeCenter2ChiralCt(state) {
|
|
342
|
+
const rlAxis = state.centers[RL_POS[0]] % 3;
|
|
343
|
+
const udAxis = (rlAxis + 2) % 3;
|
|
344
|
+
const bits = new Uint8Array(16);
|
|
345
|
+
for (let i = 0; i < 16; i++)
|
|
346
|
+
bits[i] = state.centers[CT_POS[i]] % 3 === udAxis ? 1 : 0;
|
|
347
|
+
return rankSubset16(bits);
|
|
348
|
+
}
|
|
349
|
+
export function generateCenter2ChiralTables(rlMove) {
|
|
350
|
+
const moveMaps = buildMovePositionMaps();
|
|
351
|
+
const nMoves = CENTER2_MOVES.length;
|
|
352
|
+
const ctMove = new Uint16Array(CENTER2_CHIRAL_CT_STATES * nMoves);
|
|
353
|
+
const full = new Uint8Array(24);
|
|
354
|
+
const bits = new Uint8Array(16);
|
|
355
|
+
for (let ct = 0; ct < CENTER2_CHIRAL_CT_STATES; ct++) {
|
|
356
|
+
const pattern = unrankSubset16(ct);
|
|
357
|
+
full.fill(0);
|
|
358
|
+
for (let i = 0; i < 16; i++)
|
|
359
|
+
full[CT_POS[i]] = pattern[i];
|
|
360
|
+
for (let m = 0; m < nMoves; m++) {
|
|
361
|
+
for (let i = 0; i < 16; i++)
|
|
362
|
+
bits[i] = full[moveMaps[m * 24 + CT_POS[i]]];
|
|
363
|
+
ctMove[ct * nMoves + m] = rankSubset16(bits);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
const goalBits = new Uint8Array(16);
|
|
367
|
+
for (let i = 0; i < 8; i++)
|
|
368
|
+
goalBits[i] = 1; // CT_POS[0..7] = U and D faces
|
|
369
|
+
const goalCt = rankSubset16(goalBits);
|
|
370
|
+
const pruning = new Uint8Array(CENTER2_CHIRAL_STATES).fill(0xff);
|
|
371
|
+
const queue = new Int32Array(CENTER2_CHIRAL_STATES);
|
|
372
|
+
let tail = 0;
|
|
373
|
+
for (const rl of CENTER2_GOAL_RL) {
|
|
374
|
+
const idx = goalCt * CENTER2_RL_STATES + rl;
|
|
375
|
+
pruning[idx] = 0;
|
|
376
|
+
queue[tail++] = idx;
|
|
377
|
+
}
|
|
378
|
+
for (let head = 0; head < tail; head++) {
|
|
379
|
+
const idx = queue[head];
|
|
380
|
+
const ct = (idx / CENTER2_RL_STATES) | 0;
|
|
381
|
+
const rl = idx - ct * CENTER2_RL_STATES;
|
|
382
|
+
const d = pruning[idx] + 1;
|
|
383
|
+
for (let m = 0; m < CENTER2_SEARCH_MOVES.length; m++) {
|
|
384
|
+
const next = ctMove[ct * nMoves + m] * CENTER2_RL_STATES + rlMove[rl * nMoves + m];
|
|
385
|
+
if (pruning[next] === 0xff) {
|
|
386
|
+
pruning[next] = d;
|
|
387
|
+
queue[tail++] = next;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return { ctMove, pruning, goalCt };
|
|
392
|
+
}
|
|
295
393
|
// The reference's cancellation rule (ckmv over move2std): skip the same base
|
|
296
394
|
// move, or the same axis-class when the previous move's standard index is
|
|
297
395
|
// larger than the current's.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Continuous two-phase 3x3 solver for the 4x4 finish (Kociemba-style).
|
|
3
|
+
*
|
|
4
|
+
* Unlike the batch solver in `cube3/solver` (collect phase-1 solutions,
|
|
5
|
+
* then solve phase 2 for each), this is the classic continuous search: an
|
|
6
|
+
* iterative-deepening phase 1 where EVERY phase-1 solution immediately runs
|
|
7
|
+
* phase 2 under the remaining length budget, and every solution found
|
|
8
|
+
* tightens the bound for the rest of the search. Optional legal views are
|
|
9
|
+
* searched interleaved by phase-1 depth — up to 3 relabelled axes
|
|
10
|
+
* (identity, x, z) × {state, inverse} — which is what makes short solutions
|
|
11
|
+
* cheap to find (the existing cube3 solver's rotation/inverse trick).
|
|
12
|
+
*
|
|
13
|
+
* Tables: the already-loaded cube3 tables (phase-1: twist/flip/slice move
|
|
14
|
+
* tables + twist×flip, slice×twist, slice×flip pruning; phase-2: cp/ep/sp
|
|
15
|
+
* move tables + cp×sp, ep×sp pruning). Nothing is generated here.
|
|
16
|
+
*
|
|
17
|
+
* Deterministic: the only stopping rules are node counts and lengths, so
|
|
18
|
+
* the result is a pure function of (state, options).
|
|
19
|
+
*/
|
|
20
|
+
import type { Cube3Move, Cube3State } from "../../cube3/types.js";
|
|
21
|
+
import { type Cube3Phase1Tables, type Cube3Phase2Tables } from "../../cube3/solver/tables.js";
|
|
22
|
+
export interface Finish3Tables {
|
|
23
|
+
phase1: Cube3Phase1Tables;
|
|
24
|
+
phase2: Cube3Phase2Tables;
|
|
25
|
+
}
|
|
26
|
+
export interface Finish3Options {
|
|
27
|
+
/** Stop as soon as a solution of at most this length is found (default 0 = never). */
|
|
28
|
+
targetLength?: number;
|
|
29
|
+
/**
|
|
30
|
+
* Node budget (phase-1 + phase-2 nodes) after which the best solution so
|
|
31
|
+
* far is returned (default 200_000). The search always runs until a first
|
|
32
|
+
* solution exists, bounded by `hardMaxNodes`.
|
|
33
|
+
*/
|
|
34
|
+
maxNodes?: number;
|
|
35
|
+
/** Absolute node cap; exceeding it without any solution throws (default 20M). */
|
|
36
|
+
hardMaxNodes?: number;
|
|
37
|
+
/** Views searched: 1 (as is), 2 (+inverse), 6 (3 axes × inverse). */
|
|
38
|
+
views?: 1 | 2 | 6;
|
|
39
|
+
/** @deprecated Ignored: views are built by cubie conjugation. */
|
|
40
|
+
viewScramble?: readonly Cube3Move[];
|
|
41
|
+
/** Longest phase-2 segment considered (default 12). */
|
|
42
|
+
maxPhase2Length?: number;
|
|
43
|
+
}
|
|
44
|
+
export interface Finish3Result {
|
|
45
|
+
moves: Cube3Move[];
|
|
46
|
+
nodes: number;
|
|
47
|
+
}
|
|
48
|
+
export declare class Finish3SearchError extends Error {
|
|
49
|
+
readonly code: "cube4-finish3-cap";
|
|
50
|
+
constructor(message: string);
|
|
51
|
+
}
|
|
52
|
+
export declare function solveFinish3(state: Cube3State, tables: Finish3Tables, options?: Finish3Options): Finish3Result;
|
|
53
|
+
//# sourceMappingURL=finish3.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finish3.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/finish3.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAalE,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACvB,MAAM,8BAA8B,CAAC;AAMtC,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,MAAM,EAAE,iBAAiB,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,sFAAsF;IACtF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAClB,iEAAiE;IACjE,YAAY,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;IACpC,uDAAuD;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,EAAG,mBAAmB,CAAU;gBAEjC,OAAO,EAAE,MAAM;CAI5B;AAsKD,wBAAgB,YAAY,CAC1B,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,aAAa,EACrB,OAAO,GAAE,cAAmB,GAC3B,aAAa,CAuMf"}
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Continuous two-phase 3x3 solver for the 4x4 finish (Kociemba-style).
|
|
3
|
+
*
|
|
4
|
+
* Unlike the batch solver in `cube3/solver` (collect phase-1 solutions,
|
|
5
|
+
* then solve phase 2 for each), this is the classic continuous search: an
|
|
6
|
+
* iterative-deepening phase 1 where EVERY phase-1 solution immediately runs
|
|
7
|
+
* phase 2 under the remaining length budget, and every solution found
|
|
8
|
+
* tightens the bound for the rest of the search. Optional legal views are
|
|
9
|
+
* searched interleaved by phase-1 depth — up to 3 relabelled axes
|
|
10
|
+
* (identity, x, z) × {state, inverse} — which is what makes short solutions
|
|
11
|
+
* cheap to find (the existing cube3 solver's rotation/inverse trick).
|
|
12
|
+
*
|
|
13
|
+
* Tables: the already-loaded cube3 tables (phase-1: twist/flip/slice move
|
|
14
|
+
* tables + twist×flip, slice×twist, slice×flip pruning; phase-2: cp/ep/sp
|
|
15
|
+
* move tables + cp×sp, ep×sp pruning). Nothing is generated here.
|
|
16
|
+
*
|
|
17
|
+
* Deterministic: the only stopping rules are node counts and lengths, so
|
|
18
|
+
* the result is a pure function of (state, options).
|
|
19
|
+
*/
|
|
20
|
+
import { CUBE3_MOVES } from "../../cube3/types.js";
|
|
21
|
+
import { applyCube3Move } from "../../cube3/moves.js";
|
|
22
|
+
import { createSolvedCube3State } from "../../cube3/state.js";
|
|
23
|
+
import { applyCube4Algorithm } from "../moves.js";
|
|
24
|
+
import { createSolvedCube4State } from "../state.js";
|
|
25
|
+
import { toCube3State } from "./phase4.js";
|
|
26
|
+
import { encodeCornerOrientation, encodeEdgeOrientation, encodeUdSlice } from "../../cube3/solver/coordinates.js";
|
|
27
|
+
import { CUBE3_PHASE2_MOVES } from "../../cube3/solver/tables.js";
|
|
28
|
+
import { invertCube3RotationSequence, rotateCube3AlgorithmBySequence } from "../../cube3/solver/symmetry.js";
|
|
29
|
+
export class Finish3SearchError extends Error {
|
|
30
|
+
code = "cube4-finish3-cap";
|
|
31
|
+
constructor(message) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.name = "Finish3SearchError";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function toCubie(state) {
|
|
37
|
+
return {
|
|
38
|
+
cp: Int8Array.from(state.corners.permutation),
|
|
39
|
+
co: Int8Array.from(state.corners.orientation),
|
|
40
|
+
ep: Int8Array.from(state.edges.permutation),
|
|
41
|
+
eo: Int8Array.from(state.edges.orientation)
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function toState(cubie) {
|
|
45
|
+
return {
|
|
46
|
+
corners: { permutation: Array.from(cubie.cp), orientation: Array.from(cubie.co) },
|
|
47
|
+
edges: { permutation: Array.from(cubie.ep), orientation: Array.from(cubie.eo) }
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function inverse(a) {
|
|
51
|
+
const out = { cp: new Int8Array(8), co: new Int8Array(8), ep: new Int8Array(12), eo: new Int8Array(12) };
|
|
52
|
+
for (let i = 0; i < 8; i++) {
|
|
53
|
+
out.cp[a.cp[i]] = i;
|
|
54
|
+
out.co[a.cp[i]] = (3 - a.co[i]) % 3;
|
|
55
|
+
}
|
|
56
|
+
for (let i = 0; i < 12; i++) {
|
|
57
|
+
out.ep[a.ep[i]] = i;
|
|
58
|
+
out.eo[a.ep[i]] = a.eo[i];
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
/** Per-move permutation effects, as the solved state with the move applied. */
|
|
63
|
+
const MOVE_CUBIES = CUBE3_MOVES.map((move) => toCubie(applyCube3Move(createSolvedCube3State(), move)));
|
|
64
|
+
const MOVE_CP = Int8Array.from(MOVE_CUBIES.flatMap((c) => Array.from(c.cp)));
|
|
65
|
+
const MOVE_EP = Int8Array.from(MOVE_CUBIES.flatMap((c) => Array.from(c.ep)));
|
|
66
|
+
/** Face (U0 D1 R2 L3 F4 B5) of each phase-1 move index. */
|
|
67
|
+
const FACE = Int8Array.from(CUBE3_MOVES.map((_, m) => Math.floor(m / 3)));
|
|
68
|
+
/** Phase-1 move index of each phase-2 move index. */
|
|
69
|
+
const P2_TO_P1 = Int8Array.from(CUBE3_PHASE2_MOVES.map((move) => CUBE3_MOVES.indexOf(move)));
|
|
70
|
+
/** Phase-1 moves that keep G1 (U, D, R2, L2, F2, B2). */
|
|
71
|
+
const IS_P2_MOVE = new Uint8Array(18);
|
|
72
|
+
for (const m of P2_TO_P1)
|
|
73
|
+
IS_P2_MOVE[m] = 1;
|
|
74
|
+
/** Skip `face` after `last`: same face, or same axis in descending order. */
|
|
75
|
+
const SKIP = new Uint8Array(7 * 6);
|
|
76
|
+
for (let last = -1; last < 6; last++) {
|
|
77
|
+
for (let face = 0; face < 6; face++) {
|
|
78
|
+
SKIP[(last + 1) * 6 + face] =
|
|
79
|
+
last >= 0 && (face === last || ((face >> 1) === (last >> 1) && face < last)) ? 1 : 0;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const FACT = [1, 1, 2, 6, 24, 120, 720, 5040, 40320];
|
|
83
|
+
/** Lehmer rank of values[offset..offset+n) (same order as `encodePermutation`). */
|
|
84
|
+
function lehmer(values, offset, n, base) {
|
|
85
|
+
let rank = 0;
|
|
86
|
+
for (let i = 0; i < n; i++) {
|
|
87
|
+
const v = values[offset + i];
|
|
88
|
+
let smaller = 0;
|
|
89
|
+
for (let j = i + 1; j < n; j++)
|
|
90
|
+
if (values[offset + j] < v)
|
|
91
|
+
smaller++;
|
|
92
|
+
rank += smaller * FACT[n - 1 - i];
|
|
93
|
+
}
|
|
94
|
+
return rank - base * 0;
|
|
95
|
+
}
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
// Views (relabelled scrambles and inverses)
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
const VIEW_ROTATIONS = [[], ["x"], ["z"]];
|
|
100
|
+
function invertMoves(moves) {
|
|
101
|
+
return [...moves].reverse().map((move) => {
|
|
102
|
+
if (move.endsWith("2"))
|
|
103
|
+
return move;
|
|
104
|
+
if (move.endsWith("'"))
|
|
105
|
+
return move.slice(0, -1);
|
|
106
|
+
return `${move}'`;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
function multiply(a, b) {
|
|
110
|
+
const out = { cp: new Int8Array(8), co: new Int8Array(8), ep: new Int8Array(12), eo: new Int8Array(12) };
|
|
111
|
+
for (let i = 0; i < 8; i++) {
|
|
112
|
+
out.cp[i] = a.cp[b.cp[i]];
|
|
113
|
+
out.co[i] = (a.co[b.cp[i]] + b.co[i]) % 3;
|
|
114
|
+
}
|
|
115
|
+
for (let i = 0; i < 12; i++) {
|
|
116
|
+
out.ep[i] = a.ep[b.ep[i]];
|
|
117
|
+
out.eo[i] = (a.eo[b.ep[i]] + b.eo[i]) % 2;
|
|
118
|
+
}
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
/** 3x3 cubies of the whole-cube rotations, read off the rotated 4x4 model. */
|
|
122
|
+
const ROTATION_WORDS = {
|
|
123
|
+
x: ["Rw", "Lw'"],
|
|
124
|
+
z: ["Fw", "Bw'"]
|
|
125
|
+
};
|
|
126
|
+
const ROTATION_CUBIES = new Map(Object.entries(ROTATION_WORDS).map(([name, word]) => [
|
|
127
|
+
name,
|
|
128
|
+
toCubie(toCube3State(applyCube4Algorithm(createSolvedCube4State(), word)))
|
|
129
|
+
]));
|
|
130
|
+
/**
|
|
131
|
+
* Views of the state: conjugations by x / z and inverses. The x-view of S
|
|
132
|
+
* is R⁻¹·S·R (R = the rotation's cubie) — the same state the relabelled
|
|
133
|
+
* scramble `rotate(scramble(S), [x])` reaches (verified in
|
|
134
|
+
* test/cube4-finish3.test.ts), so a view solution maps back by relabelling
|
|
135
|
+
* with the inverse rotation. Conjugation preserves parity and legality.
|
|
136
|
+
*/
|
|
137
|
+
function buildViews(state, count) {
|
|
138
|
+
if (count !== 1 && count !== 2 && count !== 6) {
|
|
139
|
+
throw new RangeError(`finish3: unsupported view count ${count}`);
|
|
140
|
+
}
|
|
141
|
+
const base = toCubie(state);
|
|
142
|
+
const views = [];
|
|
143
|
+
const axes = count === 6 ? VIEW_ROTATIONS : [[]];
|
|
144
|
+
for (const rotation of axes) {
|
|
145
|
+
let viewCubie = base;
|
|
146
|
+
for (const r of rotation) {
|
|
147
|
+
const rc = ROTATION_CUBIES.get(r);
|
|
148
|
+
viewCubie = multiply(multiply(inverse(rc), viewCubie), rc);
|
|
149
|
+
}
|
|
150
|
+
const makeView = (inverted) => {
|
|
151
|
+
views.push({
|
|
152
|
+
cubie: inverted ? inverse(viewCubie) : viewCubie,
|
|
153
|
+
mapSolution: (moves) => {
|
|
154
|
+
const direct = inverted ? invertMoves(moves) : [...moves];
|
|
155
|
+
return rotation.length === 0
|
|
156
|
+
? direct
|
|
157
|
+
: rotateCube3AlgorithmBySequence(direct, invertCube3RotationSequence(rotation));
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
};
|
|
161
|
+
makeView(false);
|
|
162
|
+
if (count >= 2)
|
|
163
|
+
makeView(true);
|
|
164
|
+
}
|
|
165
|
+
return views;
|
|
166
|
+
}
|
|
167
|
+
// ---------------------------------------------------------------------------
|
|
168
|
+
// Search
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
170
|
+
export function solveFinish3(state, tables, options = {}) {
|
|
171
|
+
const targetLength = options.targetLength ?? 0;
|
|
172
|
+
const maxNodes = options.maxNodes ?? 200_000;
|
|
173
|
+
const hardMaxNodes = options.hardMaxNodes ?? 20_000_000;
|
|
174
|
+
const maxP2 = options.maxPhase2Length ?? 12;
|
|
175
|
+
const views = buildViews(state, options.views ?? 1);
|
|
176
|
+
const p1 = tables.phase1;
|
|
177
|
+
const coMove = p1.moveTables.cornerOrientation.values;
|
|
178
|
+
const eoMove = p1.moveTables.edgeOrientation.values;
|
|
179
|
+
const udMove = p1.moveTables.udSlice.values;
|
|
180
|
+
const udSolved = p1.moveTables.udSlice.solvedCoordinate;
|
|
181
|
+
const coeo = p1.pruningTables.cornerOrientationEdgeOrientation.values;
|
|
182
|
+
const udPrun = p1.pruningTables.udSlice.values;
|
|
183
|
+
const jointUdCo = p1.jointTables?.jointUdCo;
|
|
184
|
+
const jointUdEo = p1.jointTables?.jointUdEo;
|
|
185
|
+
const p2 = tables.phase2;
|
|
186
|
+
if (p2.phase2Moves.join(" ") !== CUBE3_PHASE2_MOVES.join(" ")) {
|
|
187
|
+
throw new Error("finish3: unexpected phase-2 move order");
|
|
188
|
+
}
|
|
189
|
+
const cpMove = p2.moveTables.cornerPermutation.values;
|
|
190
|
+
const epMove = p2.moveTables.udEdgePermutation.values;
|
|
191
|
+
const spMove = p2.moveTables.slicePermutation.values;
|
|
192
|
+
const cpsp = p2.pruningTables.cornerPermutationSlicePermutation.values;
|
|
193
|
+
const epsp = p2.pruningTables.udEdgePermutationSlicePermutation.values;
|
|
194
|
+
const nib = (table, index) => (table[index >> 1] >> ((index & 1) << 2)) & 0x0f;
|
|
195
|
+
const h1 = (co, eo, ud) => {
|
|
196
|
+
let h = nib(coeo, (co << 11) + eo);
|
|
197
|
+
if (jointUdCo !== undefined && jointUdEo !== undefined) {
|
|
198
|
+
const a = nib(jointUdCo, ud * 2187 + co);
|
|
199
|
+
if (a > h)
|
|
200
|
+
h = a;
|
|
201
|
+
const b = nib(jointUdEo, ud * 2048 + eo);
|
|
202
|
+
if (b > h)
|
|
203
|
+
h = b;
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
const u = nib(udPrun, ud);
|
|
207
|
+
if (u > h)
|
|
208
|
+
h = u;
|
|
209
|
+
}
|
|
210
|
+
return h;
|
|
211
|
+
};
|
|
212
|
+
const h2 = (cp, ep, sp) => {
|
|
213
|
+
const a = nib(cpsp, cp * 24 + sp);
|
|
214
|
+
const b = nib(epsp, ep * 24 + sp);
|
|
215
|
+
return a > b ? a : b;
|
|
216
|
+
};
|
|
217
|
+
const path1 = new Int8Array(32);
|
|
218
|
+
const path2 = new Int8Array(32);
|
|
219
|
+
const cpScratch = new Int8Array(8);
|
|
220
|
+
const epScratch = new Int8Array(12);
|
|
221
|
+
const cpTmp = new Int8Array(8);
|
|
222
|
+
const epTmp = new Int8Array(12);
|
|
223
|
+
let nodes = 0;
|
|
224
|
+
let bound = 99; // best length found so far (solutions must be shorter)
|
|
225
|
+
let best = null;
|
|
226
|
+
let stop = false;
|
|
227
|
+
let view = views[0];
|
|
228
|
+
// Hot-path guard: `shouldStop` can only change its answer once `nodes`
|
|
229
|
+
// passes this limit (the other conditions are re-checked in `leaf`).
|
|
230
|
+
let nodeLimit = hardMaxNodes;
|
|
231
|
+
const hasJoint = jointUdCo !== undefined && jointUdEo !== undefined;
|
|
232
|
+
const shouldStop = () => {
|
|
233
|
+
if (nodes > hardMaxNodes) {
|
|
234
|
+
if (best === null) {
|
|
235
|
+
throw new Finish3SearchError("finish3: node cap exceeded without a solution");
|
|
236
|
+
}
|
|
237
|
+
stop = true;
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
if (best !== null)
|
|
241
|
+
nodeLimit = Math.min(maxNodes, hardMaxNodes);
|
|
242
|
+
if (best !== null && (nodes > maxNodes || bound <= targetLength)) {
|
|
243
|
+
stop = true;
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
return false;
|
|
247
|
+
};
|
|
248
|
+
const phase2 = (cp, ep, sp, depth, togo, last) => {
|
|
249
|
+
nodes++;
|
|
250
|
+
if (nodes > nodeLimit && shouldStop())
|
|
251
|
+
return false;
|
|
252
|
+
if (togo === 0)
|
|
253
|
+
return cp === 0 && ep === 0 && sp === 0;
|
|
254
|
+
const skipRow = (last + 1) * 6;
|
|
255
|
+
const limit = togo - 1;
|
|
256
|
+
const cpRow = cp * 10;
|
|
257
|
+
const epRow = ep * 10;
|
|
258
|
+
const spRow = sp * 10;
|
|
259
|
+
for (let i = 0; i < 10; i++) {
|
|
260
|
+
if (stop)
|
|
261
|
+
return false;
|
|
262
|
+
const m = P2_TO_P1[i];
|
|
263
|
+
if (SKIP[skipRow + FACE[m]])
|
|
264
|
+
continue;
|
|
265
|
+
const nsp = spMove[spRow + i];
|
|
266
|
+
const ncp = cpMove[cpRow + i];
|
|
267
|
+
const ia = ncp * 24 + nsp;
|
|
268
|
+
if (((cpsp[ia >> 1] >> ((ia & 1) << 2)) & 0x0f) > limit)
|
|
269
|
+
continue;
|
|
270
|
+
const nep = epMove[epRow + i];
|
|
271
|
+
const ib = nep * 24 + nsp;
|
|
272
|
+
if (((epsp[ib >> 1] >> ((ib & 1) << 2)) & 0x0f) > limit)
|
|
273
|
+
continue;
|
|
274
|
+
path2[depth] = m;
|
|
275
|
+
if (phase2(ncp, nep, nsp, depth + 1, togo - 1, FACE[m]))
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
return false;
|
|
279
|
+
};
|
|
280
|
+
/** Runs phase 2 on a phase-1 solution of length d1; true = stop searching. */
|
|
281
|
+
const leaf = (d1) => {
|
|
282
|
+
const base = view.cubie;
|
|
283
|
+
cpScratch.set(base.cp);
|
|
284
|
+
epScratch.set(base.ep);
|
|
285
|
+
for (let k = 0; k < d1; k++) {
|
|
286
|
+
const off = path1[k];
|
|
287
|
+
for (let i = 0; i < 8; i++)
|
|
288
|
+
cpTmp[i] = cpScratch[MOVE_CP[off * 8 + i]];
|
|
289
|
+
for (let i = 0; i < 12; i++)
|
|
290
|
+
epTmp[i] = epScratch[MOVE_EP[off * 12 + i]];
|
|
291
|
+
cpScratch.set(cpTmp);
|
|
292
|
+
epScratch.set(epTmp);
|
|
293
|
+
}
|
|
294
|
+
const cp = lehmer(cpScratch, 0, 8, 0);
|
|
295
|
+
const ep = lehmer(epScratch, 0, 8, 0);
|
|
296
|
+
for (let i = 8; i < 12; i++)
|
|
297
|
+
epTmp[i] = epScratch[i] - 8;
|
|
298
|
+
const sp = lehmer(epTmp, 8, 4, 0);
|
|
299
|
+
const lb = h2(cp, ep, sp);
|
|
300
|
+
const maxD2 = Math.min(bound - 1 - d1, maxP2);
|
|
301
|
+
if (lb > maxD2)
|
|
302
|
+
return false;
|
|
303
|
+
const last = d1 > 0 ? FACE[path1[d1 - 1]] : -1;
|
|
304
|
+
for (let d2 = lb; d2 <= maxD2; d2++) {
|
|
305
|
+
if (phase2(cp, ep, sp, 0, d2, last)) {
|
|
306
|
+
const moves = [];
|
|
307
|
+
for (let k = 0; k < d1; k++)
|
|
308
|
+
moves.push(CUBE3_MOVES[path1[k]]);
|
|
309
|
+
for (let k = 0; k < d2; k++)
|
|
310
|
+
moves.push(CUBE3_MOVES[path2[k]]);
|
|
311
|
+
best = view.mapSolution(moves);
|
|
312
|
+
bound = d1 + d2;
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return shouldStop();
|
|
317
|
+
};
|
|
318
|
+
const phase1 = (co, eo, ud, depth, togo, last) => {
|
|
319
|
+
nodes++;
|
|
320
|
+
if (togo === 0) {
|
|
321
|
+
// Only phase-1 solutions ending in a quarter turn of R/L/F/B: one
|
|
322
|
+
// ending in a G1 move is a shorter phase-1 solution plus phase 2.
|
|
323
|
+
if (depth > 0 && IS_P2_MOVE[path1[depth - 1]])
|
|
324
|
+
return false;
|
|
325
|
+
return leaf(depth);
|
|
326
|
+
}
|
|
327
|
+
if ((nodes & 1023) === 0 && shouldStop())
|
|
328
|
+
return true;
|
|
329
|
+
const skipRow = (last + 1) * 6;
|
|
330
|
+
const limit = togo - 1;
|
|
331
|
+
const lastLevel = togo === 1;
|
|
332
|
+
const coRow = co * 18;
|
|
333
|
+
const eoRow = eo * 18;
|
|
334
|
+
const udRow = ud * 18;
|
|
335
|
+
for (let m = 0; m < 18; m++) {
|
|
336
|
+
if (stop)
|
|
337
|
+
return true;
|
|
338
|
+
if (SKIP[skipRow + FACE[m]])
|
|
339
|
+
continue;
|
|
340
|
+
if (lastLevel && IS_P2_MOVE[m])
|
|
341
|
+
continue;
|
|
342
|
+
const nud = udMove[udRow + m];
|
|
343
|
+
const nco = coMove[coRow + m];
|
|
344
|
+
const neo = eoMove[eoRow + m];
|
|
345
|
+
if (hasJoint) {
|
|
346
|
+
const ia = nud * 2187 + nco;
|
|
347
|
+
if (((jointUdCo[ia >> 1] >> ((ia & 1) << 2)) & 0x0f) > limit)
|
|
348
|
+
continue;
|
|
349
|
+
const ib = nud * 2048 + neo;
|
|
350
|
+
if (((jointUdEo[ib >> 1] >> ((ib & 1) << 2)) & 0x0f) > limit)
|
|
351
|
+
continue;
|
|
352
|
+
const ic = (nco << 11) + neo;
|
|
353
|
+
if (((coeo[ic >> 1] >> ((ic & 1) << 2)) & 0x0f) > limit)
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
else if (h1(nco, neo, nud) > limit)
|
|
357
|
+
continue;
|
|
358
|
+
path1[depth] = m;
|
|
359
|
+
if (phase1(nco, neo, nud, depth + 1, togo - 1, FACE[m]))
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
return false;
|
|
363
|
+
};
|
|
364
|
+
const roots = views.map((v) => {
|
|
365
|
+
const s = toState(v.cubie);
|
|
366
|
+
const co = encodeCornerOrientation(s);
|
|
367
|
+
const eo = encodeEdgeOrientation(s);
|
|
368
|
+
const ud = encodeUdSlice(s);
|
|
369
|
+
return { co, eo, ud, h: h1(co, eo, ud), solvedG1: co === 0 && eo === 0 && ud === udSolved };
|
|
370
|
+
});
|
|
371
|
+
let minDepth = 99;
|
|
372
|
+
for (const r of roots)
|
|
373
|
+
if (r.h < minDepth)
|
|
374
|
+
minDepth = r.h;
|
|
375
|
+
for (let d1 = minDepth; d1 < bound && !stop; d1++) {
|
|
376
|
+
for (let v = 0; v < views.length && !stop; v++) {
|
|
377
|
+
const r = roots[v];
|
|
378
|
+
if (r.h > d1)
|
|
379
|
+
continue;
|
|
380
|
+
view = views[v];
|
|
381
|
+
if (d1 === 0) {
|
|
382
|
+
if (r.solvedG1 && leaf(0))
|
|
383
|
+
stop = true;
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
if (phase1(r.co, r.eo, r.ud, 0, d1, -1))
|
|
387
|
+
stop = true;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if (best === null)
|
|
391
|
+
throw new Finish3SearchError("finish3: no solution found");
|
|
392
|
+
return { moves: best, nodes };
|
|
393
|
+
}
|
|
@@ -34,5 +34,11 @@
|
|
|
34
34
|
"file": "hc-move.bin",
|
|
35
35
|
"range": 999600,
|
|
36
36
|
"checksum": "6bd1d8f36b77e0863425bced1dfb69df7a13dde4fb18ceeaeba00abf14519922"
|
|
37
|
+
},
|
|
38
|
+
"c3PairDist": {
|
|
39
|
+
"file": "c3_pair_dist.bin",
|
|
40
|
+
"range": 4233600,
|
|
41
|
+
"bytes": 2116800,
|
|
42
|
+
"checksum": "2d070dcd9cfa8ede09cf0def93b3b2ba05a7e7e5acf631d6bf25a65bf0057035"
|
|
37
43
|
}
|
|
38
44
|
}
|
|
@@ -35,16 +35,18 @@ export declare function generateHcPdb(onProgress?: (done: number, total: number)
|
|
|
35
35
|
/** Number of reachable states (BFS orbit size). Fixed by the move set. */
|
|
36
36
|
export declare const HC_REACHABLE = 58800;
|
|
37
37
|
export interface HcDenseTables {
|
|
38
|
-
/**
|
|
39
|
-
|
|
38
|
+
/** Sorted reachable full coordinates. Used with binary search (~0.23 MB). */
|
|
39
|
+
coordinates: Uint32Array;
|
|
40
40
|
/** Dense moves: reachableCount x 17, dense indices. */
|
|
41
41
|
moves: Int32Array;
|
|
42
42
|
/** Dense distances (unpacked, 0x0f = unknown). */
|
|
43
43
|
distances: Uint8Array;
|
|
44
44
|
reachableCount: number;
|
|
45
45
|
}
|
|
46
|
-
/** Build the
|
|
46
|
+
/** Build the compact coordinate list + unpacked distances from the packed PDB. */
|
|
47
47
|
export declare function buildHcDense(packed: Uint8Array): HcDenseTables;
|
|
48
|
+
/** Full HC coordinate -> dense index, or -1 when it is unreachable. */
|
|
49
|
+
export declare function hcDenseIndex(dense: Pick<HcDenseTables, "coordinates">, coordinate: number): number;
|
|
48
50
|
/** Dense transition table: for each reachable state and move, dense index (or -1). */
|
|
49
51
|
export declare function generateHcDenseMoves(dense: HcDenseTables): Int32Array;
|
|
50
52
|
//# sourceMappingURL=hc-pdb.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hc-pdb.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/hc-pdb.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,eAAO,MAAM,YAAY,EAAE,SAAS,MAAM,EAA+B,CAAC;AAE1E,4BAA4B;AAC5B,eAAO,MAAM,aAAa,UAAU,CAAC;AAmBrC,oFAAoF;AACpF,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAU3C;AAED,gDAAgD;AAChD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAc7C;AAED,yDAAyD;AACzD,wBAAgB,WAAW,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,CAMvD;AAoBD,wBAAgB,gBAAgB,IAAI,UAAU,CAG7C;AAED,sDAAsD;AACtD,wBAAgB,aAAa,CAAC,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAG5E;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GACjD,UAAU,CA+CZ;AAED,0EAA0E;AAC1E,eAAO,MAAM,YAAY,QAAQ,CAAC;AAElC,MAAM,WAAW,aAAa;IAC5B,
|
|
1
|
+
{"version":3,"file":"hc-pdb.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/hc-pdb.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,eAAO,MAAM,YAAY,EAAE,SAAS,MAAM,EAA+B,CAAC;AAE1E,4BAA4B;AAC5B,eAAO,MAAM,aAAa,UAAU,CAAC;AAmBrC,oFAAoF;AACpF,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAU3C;AAED,gDAAgD;AAChD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAc7C;AAED,yDAAyD;AACzD,wBAAgB,WAAW,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,CAMvD;AAoBD,wBAAgB,gBAAgB,IAAI,UAAU,CAG7C;AAED,sDAAsD;AACtD,wBAAgB,aAAa,CAAC,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAG5E;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GACjD,UAAU,CA+CZ;AAED,0EAA0E;AAC1E,eAAO,MAAM,YAAY,QAAQ,CAAC;AAElC,MAAM,WAAW,aAAa;IAC5B,6EAA6E;IAC7E,WAAW,EAAE,WAAW,CAAC;IACzB,uDAAuD;IACvD,KAAK,EAAE,UAAU,CAAC;IAClB,kDAAkD;IAClD,SAAS,EAAE,UAAU,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,aAAa,CAgB9D;AAED,uEAAuE;AACvE,wBAAgB,YAAY,CAC1B,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,aAAa,CAAC,EACzC,UAAU,EAAE,MAAM,GACjB,MAAM,CAWR;AAED,sFAAsF;AACtF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAgBrE"}
|