@cubingcenter/scrambler 0.6.2 → 0.7.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 +2 -0
- package/dist/puzzles/cube2/solver/search.d.ts.map +1 -1
- package/dist/puzzles/cube2/solver/search.js +3 -3
- package/dist/puzzles/cube2/solver/tables.d.ts +6 -0
- package/dist/puzzles/cube2/solver/tables.d.ts.map +1 -1
- package/dist/puzzles/cube2/solver/tables.js +16 -2
- package/dist/puzzles/cube3/index.d.ts +1 -0
- package/dist/puzzles/cube3/index.d.ts.map +1 -1
- package/dist/puzzles/cube3/moves.d.ts.map +1 -1
- package/dist/puzzles/cube3/moves.js +104 -18
- package/dist/puzzles/cube3/scrambler.d.ts +16 -1
- package/dist/puzzles/cube3/scrambler.d.ts.map +1 -1
- package/dist/puzzles/cube3/scrambler.js +1 -1
- package/dist/puzzles/cube3/solver/browser-loader.js +6 -2
- package/dist/puzzles/cube3/solver/index.d.ts +5 -3
- package/dist/puzzles/cube3/solver/index.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/index.js +2 -1
- package/dist/puzzles/cube3/solver/node-loader.js +9 -2
- package/dist/puzzles/cube3/solver/phase1.d.ts +18 -0
- package/dist/puzzles/cube3/solver/phase1.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/phase1.js +132 -14
- package/dist/puzzles/cube3/solver/phase2.d.ts +47 -0
- package/dist/puzzles/cube3/solver/phase2.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/phase2.js +236 -66
- package/dist/puzzles/cube3/solver/search-utils.d.ts +1 -1
- package/dist/puzzles/cube3/solver/search-utils.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/symmetry.d.ts +24 -0
- package/dist/puzzles/cube3/solver/symmetry.d.ts.map +1 -0
- package/dist/puzzles/cube3/solver/symmetry.js +74 -0
- package/dist/puzzles/cube3/solver/tables.d.ts +6 -1
- package/dist/puzzles/cube3/solver/tables.d.ts.map +1 -1
- package/dist/puzzles/cube3/validation.d.ts.map +1 -1
- package/dist/puzzles/cube3/validation.js +15 -7
- package/dist/puzzles/cube4/index.d.ts +3 -2
- package/dist/puzzles/cube4/index.d.ts.map +1 -1
- package/dist/puzzles/cube4/index.js +2 -2
- package/dist/puzzles/cube4/moves.d.ts +5 -0
- package/dist/puzzles/cube4/moves.d.ts.map +1 -1
- package/dist/puzzles/cube4/moves.js +120 -11
- package/dist/puzzles/cube4/reduction/browser-loader.d.ts +3 -0
- package/dist/puzzles/cube4/reduction/browser-loader.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/browser-loader.js +34 -3
- package/dist/puzzles/cube4/reduction/center2.d.ts +20 -3
- package/dist/puzzles/cube4/reduction/center2.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/center2.js +53 -9
- package/dist/puzzles/cube4/reduction/edge3.d.ts +4 -0
- package/dist/puzzles/cube4/reduction/edge3.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/edge3.js +23 -3
- package/dist/puzzles/cube4/reduction/generated/c3_pair_dist.bin +1 -0
- package/dist/puzzles/cube4/reduction/generated/center2-manifest.json +36 -0
- package/dist/puzzles/cube4/reduction/generated/center2-pruning-wide.bin +1 -0
- package/dist/puzzles/cube4/reduction/generated/hc-distances.bin +1 -0
- package/dist/puzzles/cube4/reduction/generated/hc-manifest.json +38 -0
- package/dist/puzzles/cube4/reduction/generated/hc-move.bin +0 -0
- package/dist/puzzles/cube4/reduction/hc-pdb.d.ts +50 -0
- package/dist/puzzles/cube4/reduction/hc-pdb.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/hc-pdb.js +202 -0
- package/dist/puzzles/cube4/reduction/node-loader.d.ts +5 -0
- package/dist/puzzles/cube4/reduction/node-loader.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/node-loader.js +48 -8
- package/dist/puzzles/cube4/reduction/pairing-coordinate.js +5 -5
- package/dist/puzzles/cube4/reduction/phase3-search.d.ts +111 -5
- package/dist/puzzles/cube4/reduction/phase3-search.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/phase3-search.js +341 -22
- package/dist/puzzles/cube4/reduction/solve.d.ts +112 -1
- package/dist/puzzles/cube4/reduction/solve.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/solve.js +920 -181
- package/dist/puzzles/cube4/scrambler.d.ts +105 -4
- package/dist/puzzles/cube4/scrambler.d.ts.map +1 -1
- package/dist/puzzles/cube4/scrambler.js +221 -161
- package/dist/puzzles/cube4/state.d.ts.map +1 -1
- package/dist/puzzles/cube4/state.js +4 -1
- package/dist/puzzles/pyraminx/solver/search.d.ts.map +1 -1
- package/dist/puzzles/pyraminx/solver/search.js +3 -6
- package/dist/puzzles/pyraminx/solver/tables.d.ts +6 -0
- package/dist/puzzles/pyraminx/solver/tables.d.ts.map +1 -1
- package/dist/puzzles/pyraminx/solver/tables.js +17 -0
- package/dist/puzzles/skewb/solver/search.js +3 -3
- package/dist/puzzles/skewb/solver/tables.d.ts +6 -0
- package/dist/puzzles/skewb/solver/tables.d.ts.map +1 -1
- package/dist/puzzles/skewb/solver/tables.js +17 -0
- package/dist-cjs/puzzles/cube2/solver/search.js +2 -2
- package/dist-cjs/puzzles/cube2/solver/tables.js +17 -3
- package/dist-cjs/puzzles/cube3/moves.js +104 -18
- package/dist-cjs/puzzles/cube3/scrambler.js +1 -1
- package/dist-cjs/puzzles/cube3/solver/browser-loader.js +6 -2
- package/dist-cjs/puzzles/cube3/solver/index.js +8 -1
- package/dist-cjs/puzzles/cube3/solver/node-loader.js +9 -2
- package/dist-cjs/puzzles/cube3/solver/phase1.js +133 -14
- package/dist-cjs/puzzles/cube3/solver/phase2.js +233 -63
- package/dist-cjs/puzzles/cube3/solver/symmetry.js +80 -0
- package/dist-cjs/puzzles/cube3/validation.js +15 -7
- package/dist-cjs/puzzles/cube4/index.js +3 -1
- package/dist-cjs/puzzles/cube4/moves.js +121 -11
- package/dist-cjs/puzzles/cube4/reduction/browser-loader.js +34 -3
- package/dist-cjs/puzzles/cube4/reduction/center2.js +54 -9
- package/dist-cjs/puzzles/cube4/reduction/edge3.js +24 -3
- package/dist-cjs/puzzles/cube4/reduction/generated/c3_pair_dist.bin +1 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/center2-manifest.json +36 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/center2-pruning-wide.bin +1 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/hc-distances.bin +1 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/hc-manifest.json +38 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/hc-move.bin +0 -0
- package/dist-cjs/puzzles/cube4/reduction/hc-pdb.js +213 -0
- package/dist-cjs/puzzles/cube4/reduction/node-loader.js +48 -8
- package/dist-cjs/puzzles/cube4/reduction/pairing-coordinate.js +5 -5
- package/dist-cjs/puzzles/cube4/reduction/phase3-search.js +343 -23
- package/dist-cjs/puzzles/cube4/reduction/solve.js +918 -173
- package/dist-cjs/puzzles/cube4/scrambler.js +219 -158
- package/dist-cjs/puzzles/cube4/state.js +4 -1
- package/dist-cjs/puzzles/pyraminx/solver/search.js +2 -5
- package/dist-cjs/puzzles/pyraminx/solver/tables.js +18 -1
- package/dist-cjs/puzzles/skewb/solver/search.js +2 -2
- package/dist-cjs/puzzles/skewb/solver/tables.js +18 -1
- package/package.json +14 -5
|
@@ -3,13 +3,114 @@
|
|
|
3
3
|
* the reduction solver (phases 1-4), and invert the solution. The scramble
|
|
4
4
|
* always reproduces the sampled target state and facelets exactly.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* Reduction-only, no resampling: each call runs one round of up to `bestOf`
|
|
7
|
+
* independent attempts and returns the shortest success (ties keep the
|
|
8
|
+
* lowest job index). If every attempt fails, the last error is thrown.
|
|
9
9
|
*/
|
|
10
10
|
import type { ScrambleGenerator, ScrambleOptions, ScrambleResult } from "../../core/index.js";
|
|
11
11
|
export declare const cube4ScrambleGenerator: ScrambleGenerator;
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* 444-specific scramble options (pass-through; extra keys are ignored by
|
|
14
|
+
* the generic `generateScramble` API surface).
|
|
15
|
+
*/
|
|
16
|
+
export interface Cube4ScrambleOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Number of reduction passes to run on the SAME sampled state (alias for `restarts`).
|
|
19
|
+
* `1` (default): single deterministic pass.
|
|
20
|
+
* Higher values reshuffle the search traversal order on the same physical cube,
|
|
21
|
+
* returning the shortest valid scramble while strictly preserving uniform random-state
|
|
22
|
+
* distribution (WCA compliant).
|
|
23
|
+
*/
|
|
24
|
+
bestOf?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Last-resort effort for hard states: widens every solver work cap
|
|
27
|
+
* (phase-2 depth/nodes, phase-3 budgets/lengths, pool caps).
|
|
28
|
+
* Default false — the fast caps solve essentially everything at the
|
|
29
|
+
* same length (measured: identical totals on 54 states, ~-19% time),
|
|
30
|
+
* and a failed fast attempt automatically retries with `deep: true`
|
|
31
|
+
* (bestOf 1), so hard states still resolve.
|
|
32
|
+
*/
|
|
33
|
+
deep?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Search all 3 axial views (UD, RL, FB) of the same physical cube.
|
|
36
|
+
* Shaves ~1-2 moves on average without resampling.
|
|
37
|
+
*/
|
|
38
|
+
threeAxes?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Phase-4 attempts among valid reductions (default centersBeam * edgesPerCenter, i.e. 9).
|
|
41
|
+
* Passed through to the reduction solver: the first N reductions each get a 3x3
|
|
42
|
+
* finish and the shortest simplified total wins.
|
|
43
|
+
*/
|
|
44
|
+
finishTries?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Number of distinct center candidates from phase 2 to explore in phase 3
|
|
47
|
+
* (default 3). Part of hierarchical beam search.
|
|
48
|
+
*/
|
|
49
|
+
centersBeam?: number;
|
|
50
|
+
/**
|
|
51
|
+
* Maximum number of distinct edge pairings to collect per center candidate
|
|
52
|
+
* in phase 3 (default 3). Part of hierarchical beam search.
|
|
53
|
+
*/
|
|
54
|
+
edgesPerCenter?: number;
|
|
55
|
+
/**
|
|
56
|
+
* F1 enumeration slack beyond the per-axis optimum (default 3) and
|
|
57
|
+
* collection cap (default 60). Passed through to the reduction
|
|
58
|
+
* solver. Reversible A/B knob for the diversity-vs-length trade-off
|
|
59
|
+
* of phase 1.
|
|
60
|
+
*/
|
|
61
|
+
phase1ExtraDepths?: number;
|
|
62
|
+
phase1Cap?: number;
|
|
63
|
+
/**
|
|
64
|
+
* Phase-2 wide move set: search all 28 center2 moves (adds
|
|
65
|
+
* Dw2/Lw/Lw2/Lw'/Bw2) instead of the default 23. Default false
|
|
66
|
+
* (measured ~-0.2 moves for ~+15% time on typical states; opt-in).
|
|
67
|
+
* Caution: on some hard states the wider branching can blow a single
|
|
68
|
+
* attempt past 100 s (no in-search cancellation), delaying every later
|
|
69
|
+
* bestOf job — prefer `restarts` for length.
|
|
70
|
+
*/
|
|
71
|
+
center2Wide?: boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Deep multi-candidate polish pass in phase 3 (top-3 reductions).
|
|
74
|
+
* Defaults to the value of `threeAxes`.
|
|
75
|
+
*/
|
|
76
|
+
deepPolish?: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Full-pipeline restarts over the SAME sampled state (default 1).
|
|
79
|
+
* Passed through to the reduction solver: each restart reshuffles the
|
|
80
|
+
* search order deterministically and the shortest total wins.
|
|
81
|
+
* Same-state diversity (uniformity-preserving), unlike `bestOf`.
|
|
82
|
+
*/
|
|
83
|
+
restarts?: number;
|
|
84
|
+
/**
|
|
85
|
+
* Tuning for the 3x3 finish (cube3 two-phase solver). All fields fall
|
|
86
|
+
* back to the production defaults, which prioritize short finishes:
|
|
87
|
+
* `orientations: 6`, `phase1ExtraDepth: 1`, `phase1SolutionLimit: 48`,
|
|
88
|
+
* `targetLength: 17`, budgets 350/100 ms with one 600/150 ms retry.
|
|
89
|
+
* A cheaper finish (e.g. `orientations: 3`, `phase1SolutionLimit: 24`)
|
|
90
|
+
* is much faster per call at a small length cost — the finish runs
|
|
91
|
+
* dozens of times per attempt, so this dominates attempt latency.
|
|
92
|
+
*/
|
|
93
|
+
finish?: {
|
|
94
|
+
orientations?: number;
|
|
95
|
+
phase1ExtraDepth?: number;
|
|
96
|
+
phase1SolutionLimit?: number;
|
|
97
|
+
targetLength?: number;
|
|
98
|
+
timeoutMs?: number;
|
|
99
|
+
phase2TimeoutMs?: number;
|
|
100
|
+
retryTimeoutMs?: number;
|
|
101
|
+
retryPhase2TimeoutMs?: number;
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
export declare function generateCube4Scramble(options?: ScrambleOptions & Cube4ScrambleOptions & {
|
|
105
|
+
skipValidation?: boolean;
|
|
106
|
+
}): Promise<ScrambleResult>;
|
|
107
|
+
/**
|
|
108
|
+
* Single-attempt 444 solve: samples ONE state (from `options.seed`) and
|
|
109
|
+
* solves it with the reduction solver. Throws on failure — no resampling.
|
|
110
|
+
* Fan-out primitive for hosts: run with seeds `seed`, `seed:bestN`, ...
|
|
111
|
+
* across workers and keep the shortest valid result.
|
|
112
|
+
*/
|
|
113
|
+
export declare function generateCube4Attempt(tables: any, pairTables: any, options?: ScrambleOptions & Cube4ScrambleOptions & {
|
|
13
114
|
skipValidation?: boolean;
|
|
14
115
|
}): Promise<ScrambleResult>;
|
|
15
116
|
//# sourceMappingURL=scrambler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scrambler.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube4/scrambler.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"scrambler.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube4/scrambler.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAiD9F,eAAO,MAAM,sBAAsB,EAAE,iBAYpC,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE;QACP,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,oBAAoB,CAAC,EAAE,MAAM,CAAC;KAC/B,CAAC;CACH;AAuCD,wBAAsB,qBAAqB,CACzC,OAAO,GAAE,eAAe,GAAG,oBAAoB,GAAG;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAO,GAClF,OAAO,CAAC,cAAc,CAAC,CA+CzB;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,GAAG,EACX,UAAU,EAAE,GAAG,EACf,OAAO,GAAE,eAAe,GAAG,oBAAoB,GAAG;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAO,GAClF,OAAO,CAAC,cAAc,CAAC,CAgKzB"}
|
|
@@ -3,104 +3,18 @@
|
|
|
3
3
|
* the reduction solver (phases 1-4), and invert the solution. The scramble
|
|
4
4
|
* always reproduces the sampled target state and facelets exactly.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* Reduction-only, no resampling: each call runs one round of up to `bestOf`
|
|
7
|
+
* independent attempts and returns the shortest success (ties keep the
|
|
8
|
+
* lowest job index). If every attempt fails, the last error is thrown.
|
|
9
9
|
*/
|
|
10
|
-
import { formatCube4Algorithm, applyCube4Algorithm } from "./moves.js";
|
|
10
|
+
import { formatCube4Algorithm, applyCube4Algorithm, invertCube4Algorithm } from "./moves.js";
|
|
11
11
|
import { generateRandomCube4State } from "./random-state.js";
|
|
12
|
-
import { createSolvedCube4State,
|
|
12
|
+
import { createSolvedCube4State, isSolvedCube4State } from "./state.js";
|
|
13
13
|
import { formatCube4Facelets, toCube4Facelets } from "./facelets.js";
|
|
14
|
-
import { solveCube4State } from "./reduction/solve.js";
|
|
14
|
+
import { solveCube4State, applyCubeRotation, rotateAlgorithm } from "./reduction/solve.js";
|
|
15
15
|
import { solveCube3State } from "../cube3/solver/index.js";
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
// cubing.js fallback (random-state, WCA-compliant)
|
|
19
|
-
// ────────────────────────────────────────────────────────────
|
|
20
|
-
/**
|
|
21
|
-
* Try to generate a 4x4 scramble using cubing.js `randomScrambleForEvent`.
|
|
22
|
-
* Uses a dynamic import so browser bundles can tree-shake it when unused.
|
|
23
|
-
* Returns null if cubing.js is unavailable.
|
|
24
|
-
*/
|
|
25
|
-
async function generateCube4WithCubingJs() {
|
|
26
|
-
try {
|
|
27
|
-
const { randomScrambleForEvent } = await import(
|
|
28
|
-
/* webpackIgnore: true */
|
|
29
|
-
/* @vite-ignore */
|
|
30
|
-
"cubing/scramble");
|
|
31
|
-
const alg = await randomScrambleForEvent("444");
|
|
32
|
-
const scramble = alg.toString();
|
|
33
|
-
const moves = scramble.trim().split(/\s+/);
|
|
34
|
-
return {
|
|
35
|
-
eventId: "444",
|
|
36
|
-
scramble,
|
|
37
|
-
moves,
|
|
38
|
-
quality: "random-state",
|
|
39
|
-
generator: "cubing-js",
|
|
40
|
-
metadata: {
|
|
41
|
-
generator: "cubing-js",
|
|
42
|
-
distribution: "random-state",
|
|
43
|
-
note: "Generated by cubing.js randomScrambleForEvent as fallback."
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
// ────────────────────────────────────────────────────────────
|
|
52
|
-
// Last-resort: random-moves (no external dep, always works)
|
|
53
|
-
// ────────────────────────────────────────────────────────────
|
|
54
|
-
const CUBE4_FACES = ["U", "R", "F", "D", "L", "B"];
|
|
55
|
-
// Opposite face indices (U↔D, R↔L, F↔B)
|
|
56
|
-
const CUBE4_OPPOSITE = { U: "D", D: "U", R: "L", L: "R", F: "B", B: "F" };
|
|
57
|
-
const CUBE4_SUFFIXES = ["", "2", "'"];
|
|
58
|
-
const CUBE4_WIDE_FACES = ["Uw", "Rw", "Fw", "Dw", "Lw", "Bw"];
|
|
59
|
-
// Opposite wide-move faces
|
|
60
|
-
const CUBE4_WIDE_OPPOSITE = {
|
|
61
|
-
Uw: "Dw", Dw: "Uw", Rw: "Lw", Lw: "Rw", Fw: "Bw", Bw: "Fw"
|
|
62
|
-
};
|
|
63
|
-
// All available move bases (outer + wide). Outer and wide turns are treated
|
|
64
|
-
// independently for the same-face/opposite-face check using their base letter.
|
|
65
|
-
const ALL_MOVE_BASES = [...CUBE4_FACES, ...CUBE4_WIDE_FACES];
|
|
66
|
-
function generateCube4RandomMoves(options = {}) {
|
|
67
|
-
const rng = createRandomSource(options.seed);
|
|
68
|
-
const moveCount = 40;
|
|
69
|
-
const moves = [];
|
|
70
|
-
let lastFace = null;
|
|
71
|
-
let secondLastFace = null;
|
|
72
|
-
const getBaseLetter = (face) => face.length > 1 ? face[0] : face;
|
|
73
|
-
for (let i = 0; i < moveCount; i++) {
|
|
74
|
-
let face;
|
|
75
|
-
// Reject same face as last or opposite face when the last two moves
|
|
76
|
-
// share the same axis (to avoid redundant move pairs like R L R).
|
|
77
|
-
do {
|
|
78
|
-
face = ALL_MOVE_BASES[rng.nextInt(ALL_MOVE_BASES.length)];
|
|
79
|
-
} while (face === lastFace ||
|
|
80
|
-
(lastFace !== null &&
|
|
81
|
-
(CUBE4_OPPOSITE[face] === lastFace || CUBE4_WIDE_OPPOSITE[face] === lastFace) &&
|
|
82
|
-
secondLastFace !== null &&
|
|
83
|
-
getBaseLetter(secondLastFace) === getBaseLetter(face)));
|
|
84
|
-
const suffix = CUBE4_SUFFIXES[rng.nextInt(3)];
|
|
85
|
-
moves.push(`${face}${suffix}`);
|
|
86
|
-
secondLastFace = lastFace;
|
|
87
|
-
lastFace = face;
|
|
88
|
-
}
|
|
89
|
-
const scramble = moves.join(" ");
|
|
90
|
-
return {
|
|
91
|
-
eventId: "444",
|
|
92
|
-
scramble,
|
|
93
|
-
moves,
|
|
94
|
-
quality: "random-moves",
|
|
95
|
-
generator: "cube4-random-moves-fallback",
|
|
96
|
-
metadata: {
|
|
97
|
-
generator: "cube4-random-moves-fallback",
|
|
98
|
-
distribution: "random-moves",
|
|
99
|
-
note: "Reduction solver and cubing.js both failed; using random-moves.",
|
|
100
|
-
...options.metadata
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
}
|
|
16
|
+
import { loadCube3Phase1Tables } from "../cube3/solver/tables.js";
|
|
17
|
+
import { loadCube3PairTables } from "../cube3/solver/pair-tables.js";
|
|
104
18
|
// ────────────────────────────────────────────────────────────
|
|
105
19
|
let cachedTables;
|
|
106
20
|
async function loadCube4Tables(baseUrl) {
|
|
@@ -140,88 +54,234 @@ export const cube4ScrambleGenerator = {
|
|
|
140
54
|
puzzle: "cube-4x4",
|
|
141
55
|
quality: "random-state",
|
|
142
56
|
generator: "cube4-reduction",
|
|
143
|
-
description: "Random-state 4x4 scramble via the reduction solver (centers + pairing + 3x3 finish).
|
|
57
|
+
description: "Random-state 4x4 scramble via the reduction solver (centers + pairing + 3x3 finish)."
|
|
144
58
|
},
|
|
145
59
|
generate(options = {}) {
|
|
146
60
|
return generateCube4Scramble(options);
|
|
147
61
|
}
|
|
148
62
|
};
|
|
63
|
+
function invertRotationSequence(rotSeq) {
|
|
64
|
+
return rotSeq.slice().reverse().map((r) => (r.endsWith("'") ? r.slice(0, -1) : r.endsWith("2") ? r : `${r}'`));
|
|
65
|
+
}
|
|
66
|
+
/** Shortest-first pick (ties keep `a`, i.e. the lowest job index). */
|
|
67
|
+
function shorterScramble(a, b) {
|
|
68
|
+
if (!a || b.moves.length < a.moves.length)
|
|
69
|
+
return b;
|
|
70
|
+
return a;
|
|
71
|
+
}
|
|
72
|
+
function seedForBestJob(options, job) {
|
|
73
|
+
if (job === 0)
|
|
74
|
+
return options;
|
|
75
|
+
const next = { ...options };
|
|
76
|
+
if (options.seed !== undefined) {
|
|
77
|
+
next.seed = `${options.seed}:best${job}`;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
delete next.seed;
|
|
81
|
+
}
|
|
82
|
+
return next;
|
|
83
|
+
}
|
|
84
|
+
function isRetryableCube4Error(error) {
|
|
85
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
86
|
+
const isPhase2Error = typeof error?.name === "string" &&
|
|
87
|
+
error.name.includes("Cube3Phase2");
|
|
88
|
+
return (error instanceof Error &&
|
|
89
|
+
(msg.includes("reduction failed") ||
|
|
90
|
+
msg.includes("failed to convert") ||
|
|
91
|
+
msg.includes("no valid phase 2") ||
|
|
92
|
+
msg.includes("timeout") ||
|
|
93
|
+
isPhase2Error ||
|
|
94
|
+
msg.includes("Phase 2 solution not found")));
|
|
95
|
+
}
|
|
149
96
|
export async function generateCube4Scramble(options = {}) {
|
|
150
|
-
const
|
|
97
|
+
const bestOf = Math.max(1, Math.floor(options.bestOf ?? 1) || 1);
|
|
98
|
+
let tables;
|
|
99
|
+
let pairTables;
|
|
100
|
+
try {
|
|
101
|
+
const [loadedTables, cube3P1Tables, loadedPairTables] = await Promise.all([
|
|
102
|
+
loadCube4Tables(),
|
|
103
|
+
loadCube3Phase1Tables(),
|
|
104
|
+
loadCube3PairTables()
|
|
105
|
+
]);
|
|
106
|
+
tables = loadedTables;
|
|
107
|
+
tables.cube3Phase1 = cube3P1Tables;
|
|
108
|
+
tables.pairTables = loadedPairTables;
|
|
109
|
+
pairTables = loadedPairTables;
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
throw new Error("[cube4-scrambler] Failed to load reduction tables; run pnpm cube4:center1:tables etc. " +
|
|
113
|
+
`(${error instanceof Error ? error.message : String(error)})`);
|
|
114
|
+
}
|
|
115
|
+
const totalRestarts = Math.max(1, Math.floor(options.restarts ?? options.bestOf ?? 1) || 1);
|
|
151
116
|
let lastError;
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
117
|
+
try {
|
|
118
|
+
return await generateCube4Attempt(tables, pairTables, {
|
|
119
|
+
...options,
|
|
120
|
+
restarts: totalRestarts
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
if (!isRetryableCube4Error(error))
|
|
125
|
+
throw error;
|
|
126
|
+
lastError = error;
|
|
127
|
+
}
|
|
128
|
+
if (!options.deep) {
|
|
129
|
+
try {
|
|
130
|
+
return await generateCube4Attempt(tables, pairTables, {
|
|
131
|
+
...options,
|
|
132
|
+
restarts: totalRestarts,
|
|
133
|
+
deep: true
|
|
134
|
+
});
|
|
159
135
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
else {
|
|
166
|
-
delete attemptOptions.seed;
|
|
167
|
-
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
if (isRetryableCube4Error(error))
|
|
138
|
+
lastError = error;
|
|
139
|
+
else
|
|
140
|
+
throw error;
|
|
168
141
|
}
|
|
169
|
-
|
|
142
|
+
}
|
|
143
|
+
throw lastError instanceof Error
|
|
144
|
+
? lastError
|
|
145
|
+
: new Error(`[cube4-scrambler] Reduction solver failed for restarts=${totalRestarts}: ${String(lastError)}`);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Single-attempt 444 solve: samples ONE state (from `options.seed`) and
|
|
149
|
+
* solves it with the reduction solver. Throws on failure — no resampling.
|
|
150
|
+
* Fan-out primitive for hosts: run with seeds `seed`, `seed:bestN`, ...
|
|
151
|
+
* across workers and keep the shortest valid result.
|
|
152
|
+
*/
|
|
153
|
+
export async function generateCube4Attempt(tables, pairTables, options = {}) {
|
|
154
|
+
const attemptState = generateRandomCube4State(options);
|
|
155
|
+
const axes = options.threeAxes ? [[], ["z"], ["y'"]] : [[]];
|
|
156
|
+
let bestScrambleMoves = null;
|
|
157
|
+
let bestSolutionMoves = null;
|
|
158
|
+
let bestPhases;
|
|
159
|
+
let bestNodes;
|
|
160
|
+
let bestTimings;
|
|
161
|
+
let bestReductionLen;
|
|
162
|
+
let lastError;
|
|
163
|
+
const tAttemptStart = performance.now();
|
|
164
|
+
const finishOpts = options.finish ?? {};
|
|
165
|
+
for (const rot of axes) {
|
|
170
166
|
try {
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
167
|
+
const sRot = rot.length === 0 ? attemptState : applyCubeRotation(attemptState, rot);
|
|
168
|
+
// The reduction search is bounded by deterministic work caps (see
|
|
169
|
+
// solve.ts), so these timeouts are only wall-clock backstops for
|
|
170
|
+
// pathological cases.
|
|
171
|
+
const solvePromise = solveCube4State(sRot, tables, async (cube3State) => {
|
|
172
|
+
// Single solver-side best-of-orientations finish call with
|
|
173
|
+
// extra phase-1 improvement: same lengths as the former
|
|
174
|
+
// hand-rolled UD/X/Z triple at a fraction of the cost.
|
|
175
|
+
const solveFinish = async (timeBudget, phase2Budget) => {
|
|
176
|
+
const solution = await solveCube3State(cube3State, {
|
|
177
|
+
orientations: finishOpts.orientations ?? 3,
|
|
178
|
+
phase1ExtraDepth: finishOpts.phase1ExtraDepth ?? 1,
|
|
179
|
+
phase1SolutionLimit: finishOpts.phase1SolutionLimit ?? 24,
|
|
180
|
+
targetLength: finishOpts.targetLength ?? 20,
|
|
181
|
+
timeoutMs: timeBudget,
|
|
182
|
+
// The 54MB pair tables are skipped for the 444 finish: pair
|
|
183
|
+
// pruning changes nothing measurable here (identical lengths
|
|
184
|
+
// on a 44-state corpus, zero extra finish retries), and
|
|
185
|
+
// loading them dominates cold-start I/O. Hosts that already
|
|
186
|
+
// hold pairTables can still pass them via generateCube4Attempt.
|
|
187
|
+
phase2: { pairTables, skipPairTables: pairTables === undefined, timeoutMs: phase2Budget }
|
|
188
|
+
});
|
|
189
|
+
return { moves: solution.moves };
|
|
190
|
+
};
|
|
191
|
+
// The first call seeds everything, so a flaky finish gets one
|
|
192
|
+
// budgeted retry before the whole finish is declared failed.
|
|
193
|
+
try {
|
|
194
|
+
return await solveFinish(finishOpts.timeoutMs ?? 150, finishOpts.phase2TimeoutMs ?? 50);
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return await solveFinish(finishOpts.retryTimeoutMs ?? 300, finishOpts.retryPhase2TimeoutMs ?? 100);
|
|
178
198
|
}
|
|
199
|
+
}, {
|
|
200
|
+
timeoutMs: 75000,
|
|
201
|
+
deep: options.deep === true,
|
|
202
|
+
deepPolish: options.deepPolish ?? options.threeAxes,
|
|
203
|
+
...(options.finishTries !== undefined ? { finishTries: options.finishTries } : {}),
|
|
204
|
+
...(options.centersBeam !== undefined ? { centersBeam: options.centersBeam } : {}),
|
|
205
|
+
...(options.edgesPerCenter !== undefined ? { edgesPerCenter: options.edgesPerCenter } : {}),
|
|
206
|
+
...(options.phase1ExtraDepths !== undefined ? { phase1ExtraDepths: options.phase1ExtraDepths } : {}),
|
|
207
|
+
...(options.phase1Cap !== undefined ? { phase1Cap: options.phase1Cap } : {}),
|
|
208
|
+
...(options.center2Wide !== undefined ? { center2Wide: options.center2Wide } : {}),
|
|
209
|
+
...(options.restarts !== undefined ? { restarts: options.restarts } : {}),
|
|
210
|
+
...(options.prepCap !== undefined ? { prepCap: options.prepCap } : {}),
|
|
211
|
+
...(options.p1CandCap !== undefined ? { p1CandCap: options.p1CandCap } : {}),
|
|
212
|
+
...(options.stagnationCut !== undefined ? { stagnationCut: options.stagnationCut } : {}),
|
|
213
|
+
maxReductionLen: bestReductionLen
|
|
214
|
+
});
|
|
215
|
+
let timeoutHandle;
|
|
216
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
217
|
+
timeoutHandle = setTimeout(() => reject(new Error("4x4 solve: timeout")), 90000);
|
|
218
|
+
});
|
|
219
|
+
let solveOutcome;
|
|
220
|
+
try {
|
|
221
|
+
solveOutcome = await Promise.race([solvePromise, timeoutPromise]);
|
|
222
|
+
}
|
|
223
|
+
finally {
|
|
224
|
+
// The search is work-capped and always settles first: clearing the
|
|
225
|
+
// backstop avoids leaking a live 90s timer per axis (which kept the
|
|
226
|
+
// event loop — and CLI/test processes — alive needlessly).
|
|
227
|
+
clearTimeout(timeoutHandle);
|
|
179
228
|
}
|
|
180
|
-
const
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
distribution: "random-state",
|
|
193
|
-
...options.metadata
|
|
229
|
+
const { scramble: scrambleMoves, moves: solutionMoves, phases, timings, nodes } = solveOutcome;
|
|
230
|
+
const invRot = rot.length === 0 ? [] : invertRotationSequence(rot);
|
|
231
|
+
const fullScramble = rot.length === 0 ? scrambleMoves : rotateAlgorithm(scrambleMoves, invRot);
|
|
232
|
+
const fullSolution = rot.length === 0 ? solutionMoves : rotateAlgorithm(solutionMoves, invRot);
|
|
233
|
+
if (!bestScrambleMoves || fullScramble.length < bestScrambleMoves.length) {
|
|
234
|
+
bestScrambleMoves = fullScramble;
|
|
235
|
+
bestSolutionMoves = fullSolution;
|
|
236
|
+
bestPhases = phases;
|
|
237
|
+
bestTimings = timings;
|
|
238
|
+
bestNodes = nodes;
|
|
239
|
+
if (phases) {
|
|
240
|
+
bestReductionLen = phases.length12 + phases.length3;
|
|
194
241
|
}
|
|
195
|
-
};
|
|
196
|
-
if (options.seed !== undefined) {
|
|
197
|
-
result.seed = options.seed;
|
|
198
242
|
}
|
|
199
|
-
return result;
|
|
200
243
|
}
|
|
201
|
-
catch (
|
|
202
|
-
lastError =
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
244
|
+
catch (err) {
|
|
245
|
+
lastError = err;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (!bestScrambleMoves || !bestSolutionMoves) {
|
|
249
|
+
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
250
|
+
}
|
|
251
|
+
const scrambleMoves = bestScrambleMoves;
|
|
252
|
+
const solutionMoves = bestSolutionMoves;
|
|
253
|
+
const scrambledState = applyCube4Algorithm(createSolvedCube4State(), scrambleMoves);
|
|
254
|
+
if (!options.skipValidation) {
|
|
255
|
+
const solvedCheck = applyCube4Algorithm(scrambledState, solutionMoves);
|
|
256
|
+
if (!isSolvedCube4State(solvedCheck)) {
|
|
257
|
+
throw new Error("Generated 4x4 scramble does not solve back to solved state.");
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
const facelets = toCube4Facelets(scrambledState);
|
|
261
|
+
const result = {
|
|
262
|
+
eventId: "444",
|
|
263
|
+
scramble: formatCube4Algorithm(scrambleMoves),
|
|
264
|
+
moves: scrambleMoves,
|
|
265
|
+
quality: "random-state",
|
|
266
|
+
generator: "cube4-reduction",
|
|
267
|
+
metadata: {
|
|
268
|
+
generator: "cube4-reduction",
|
|
269
|
+
state: scrambledState,
|
|
270
|
+
facelets,
|
|
271
|
+
faceletString: formatCube4Facelets(facelets),
|
|
272
|
+
distribution: "random-state",
|
|
273
|
+
solutionMoves: invertCube4Algorithm(scrambleMoves),
|
|
274
|
+
// Winning-reduction breakdown (unsimplified lengths) + wall-clock
|
|
275
|
+
// split, for tuning and A/B comparisons.
|
|
276
|
+
phases: bestPhases,
|
|
277
|
+
phaseTimings: bestTimings,
|
|
278
|
+
nodes: bestNodes,
|
|
279
|
+
attemptMs: performance.now() - tAttemptStart,
|
|
280
|
+
...options.metadata
|
|
218
281
|
}
|
|
282
|
+
};
|
|
283
|
+
if (options.seed !== undefined) {
|
|
284
|
+
result.seed = options.seed;
|
|
219
285
|
}
|
|
220
|
-
|
|
221
|
-
console.warn("[cube4-scrambler] Reduction solver failed after 5 attempts; trying cubing.js fallback.", lastError);
|
|
222
|
-
const cubingJsResult = await generateCube4WithCubingJs();
|
|
223
|
-
if (cubingJsResult)
|
|
224
|
-
return cubingJsResult;
|
|
225
|
-
console.warn("[cube4-scrambler] cubing.js fallback also failed; using random-moves.");
|
|
226
|
-
return generateCube4RandomMoves(options);
|
|
286
|
+
return result;
|
|
227
287
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube4/state.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAM7C,wBAAgB,sBAAsB,IAAI,UAAU,CAanD;AAMD,wBAAgB,eAAe,CAAC,CAAC,EAAE,UAAU,GAAG,UAAU,CAOzD;AAMD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,OAAO,CAOtE;
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube4/state.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAM7C,wBAAgB,sBAAsB,IAAI,UAAU,CAanD;AAMD,wBAAgB,eAAe,CAAC,CAAC,EAAE,UAAU,GAAG,UAAU,CAOzD;AAMD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,OAAO,CAOtE;AAMD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAE7D;AAMD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAS5D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAUzD"}
|
|
@@ -36,8 +36,11 @@ export function cube4StatesEqual(a, b) {
|
|
|
36
36
|
typedArrayEqual(a.wp, b.wp) &&
|
|
37
37
|
typedArrayEqual(a.centers, b.centers));
|
|
38
38
|
}
|
|
39
|
+
// Read-only canonical solved state for comparisons (never mutated, so a
|
|
40
|
+
// single shared instance is safe and avoids allocating 4 arrays per call).
|
|
41
|
+
const SOLVED_STATE = createSolvedCube4State();
|
|
39
42
|
export function isSolvedCube4State(state) {
|
|
40
|
-
return cube4StatesEqual(state,
|
|
43
|
+
return cube4StatesEqual(state, SOLVED_STATE);
|
|
41
44
|
}
|
|
42
45
|
// ---------------------------------------------------------------------------
|
|
43
46
|
// Helpers
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/pyraminx/solver/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/pyraminx/solver/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAgB/D,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAGjF;AAED,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAG3F;AAMD,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAGlF;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,EAAE,CAGvE;AAoCD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,YAAY,EAAE,CAGtE;AAaD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,YAAY,EAAE,GAAG,OAAO,CAsCzG;AAKD,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B,OAAO,CAmCT"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { POLICY_TABLE, CENTERS_TABLE, PERMUTATION_TABLE, ORIENTATION_TABLE, PYRAMINX_MOVES, MOVE_COUNT, PERMUTATION_COUNT, getCentersIndex, getOrientationIndex, getPermutationCompactIndex, ensureTablesLoaded } from "./tables.js";
|
|
1
|
+
import { POLICY_TABLE, POLICY_TABLE_EXPANDED, CENTERS_TABLE, PERMUTATION_TABLE, ORIENTATION_TABLE, PYRAMINX_MOVES, MOVE_COUNT, PERMUTATION_COUNT, getCentersIndex, getOrientationIndex, getPermutationCompactIndex, ensureTablesLoaded } from "./tables.js";
|
|
2
2
|
export async function solvePyraminx(state) {
|
|
3
3
|
await ensureTablesLoaded();
|
|
4
4
|
return solveWithTables(state).moves;
|
|
@@ -29,7 +29,7 @@ function solveWithTablesIndices(state) {
|
|
|
29
29
|
const centersTable = CENTERS_TABLE;
|
|
30
30
|
const permTable = PERMUTATION_TABLE;
|
|
31
31
|
const orientTable = ORIENTATION_TABLE;
|
|
32
|
-
const policy =
|
|
32
|
+
const policy = POLICY_TABLE_EXPANDED;
|
|
33
33
|
let c = getCentersIndex(state.centers);
|
|
34
34
|
let p = getPermutationCompactIndex(state.edgesPerm);
|
|
35
35
|
let o = getOrientationIndex(state.edgesOrient);
|
|
@@ -38,10 +38,7 @@ function solveWithTablesIndices(state) {
|
|
|
38
38
|
return [];
|
|
39
39
|
const moves = [];
|
|
40
40
|
while (stateIndex !== 0) {
|
|
41
|
-
const
|
|
42
|
-
const byteIndex = bits >> 3;
|
|
43
|
-
const offset = bits & 7;
|
|
44
|
-
const m = ((policy[byteIndex] | ((policy[byteIndex + 1] ?? 0) << 8)) >> offset) & 0x07;
|
|
41
|
+
const m = policy[stateIndex];
|
|
45
42
|
moves.push(m);
|
|
46
43
|
c = centersTable[c * MOVE_COUNT + m];
|
|
47
44
|
p = permTable[p * MOVE_COUNT + m];
|
|
@@ -34,6 +34,12 @@ export declare let CENTERS_TABLE: Uint8Array | null;
|
|
|
34
34
|
export declare let PERMUTATION_TABLE: Uint16Array | null;
|
|
35
35
|
export declare let ORIENTATION_TABLE: Uint8Array | null;
|
|
36
36
|
export declare let POLICY_TABLE: Uint8Array | null;
|
|
37
|
+
/**
|
|
38
|
+
* Byte-expanded policy (one optimal move per state, no bit-unpacking in
|
|
39
|
+
* the hot walk). Built at load from the 3-bit packed table: +0.9 MB RAM,
|
|
40
|
+
* zero wire/format change.
|
|
41
|
+
*/
|
|
42
|
+
export declare let POLICY_TABLE_EXPANDED: Uint8Array | null;
|
|
37
43
|
export declare function ensureTablesLoaded(): Promise<void>;
|
|
38
44
|
export declare function arePyraminxTablesLoaded(): boolean;
|
|
39
45
|
export declare function getPolicyMove(stateIndex: number): number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/pyraminx/solver/tables.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAQ/D,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAWnE;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAY1E;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAMrE;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAY/D;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAElE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAQ3D;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAIjE;AAGD,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,eAAe,yBAAkD,CAAC;AAC/E,eAAO,MAAM,eAAe,0BAAqC,CAAC;AAalE,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAK1E;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIlE;AAED,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAErE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAK1D;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAatE;AAMD,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,eAAO,MAAM,iBAAiB,KAAK,CAAC;AACpC,eAAO,MAAM,UAAU,IAAI,CAAC;AAC5B,eAAO,MAAM,WAAW,QAA6C,CAAC;AACtE,eAAO,MAAM,qBAAqB,QAAmC,CAAC;AAGtE,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAMtF;AAED,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAEjD,CAAC;AAMF,wBAAgB,oBAAoB,IAAI,UAAU,CAgBjD;AAED,wBAAgB,wBAAwB,IAAI,WAAW,CAkBtD;AAED,wBAAgB,wBAAwB,IAAI,UAAU,CAgBrD;AAKD,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,UAAU,EACxB,SAAS,EAAE,WAAW,EACtB,WAAW,EAAE,UAAU,GACtB;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CA8C1C;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,GAAG,MAAM,CAO1D;AAOD,eAAO,IAAI,aAAa,EAAE,UAAU,GAAG,IAAW,CAAC;AACnD,eAAO,IAAI,iBAAiB,EAAE,WAAW,GAAG,IAAW,CAAC;AACxD,eAAO,IAAI,iBAAiB,EAAE,UAAU,GAAG,IAAW,CAAC;AACvD,eAAO,IAAI,YAAY,EAAE,UAAU,GAAG,IAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/pyraminx/solver/tables.ts"],"names":[],"mappings":"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"}
|