@cubingcenter/scrambler 0.5.3 → 0.6.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 +24 -9
- package/dist/cli/index.js +0 -0
- package/dist/core/api.d.ts.map +1 -1
- package/dist/core/api.js +11 -1
- package/dist/core/types.d.ts +1 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/puzzles/cube4/index.d.ts +4 -2
- package/dist/puzzles/cube4/index.d.ts.map +1 -1
- package/dist/puzzles/cube4/index.js +3 -1
- package/dist/puzzles/cube4/random-state.d.ts +16 -0
- package/dist/puzzles/cube4/random-state.d.ts.map +1 -0
- package/dist/puzzles/cube4/random-state.js +76 -0
- package/dist/puzzles/cube4/reduction/browser-loader.d.ts +18 -0
- package/dist/puzzles/cube4/reduction/browser-loader.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/browser-loader.js +95 -0
- package/dist/puzzles/cube4/reduction/center1.d.ts +65 -0
- package/dist/puzzles/cube4/reduction/center1.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/center1.js +245 -0
- package/dist/puzzles/cube4/reduction/center2.d.ts +91 -0
- package/dist/puzzles/cube4/reduction/center2.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/center2.js +449 -0
- package/dist/puzzles/cube4/reduction/center3.d.ts +47 -0
- package/dist/puzzles/cube4/reduction/center3.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/center3.js +278 -0
- package/dist/puzzles/cube4/reduction/edge3.d.ts +100 -0
- package/dist/puzzles/cube4/reduction/edge3.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/edge3.js +511 -0
- package/dist/puzzles/cube4/reduction/generated/center1-distance.bin +1 -0
- package/dist/puzzles/cube4/reduction/generated/center1-manifest.json +46 -0
- package/dist/puzzles/cube4/reduction/generated/center1-move-maps.bin +0 -0
- package/dist/puzzles/cube4/reduction/generated/center2-ct-move.bin +0 -0
- package/dist/puzzles/cube4/reduction/generated/center2-manifest.json +87 -0
- package/dist/puzzles/cube4/reduction/generated/center2-pruning.bin +1 -0
- package/dist/puzzles/cube4/reduction/generated/center2-rl-move.bin +0 -0
- package/dist/puzzles/cube4/reduction/generated/center3-manifest.json +40 -0
- package/dist/puzzles/cube4/reduction/generated/center3-move.bin +0 -0
- package/dist/puzzles/cube4/reduction/generated/center3-pruning.bin +0 -0
- package/dist/puzzles/cube4/reduction/generated/edge3-distances.bin +1 -0
- package/dist/puzzles/cube4/reduction/generated/edge3-manifest.json +12 -0
- package/dist/puzzles/cube4/reduction/geometry.d.ts +33 -0
- package/dist/puzzles/cube4/reduction/geometry.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/geometry.js +302 -0
- package/dist/puzzles/cube4/reduction/node-loader.d.ts +17 -0
- package/dist/puzzles/cube4/reduction/node-loader.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/node-loader.js +98 -0
- package/dist/puzzles/cube4/reduction/pairing-coordinate.d.ts +130 -0
- package/dist/puzzles/cube4/reduction/pairing-coordinate.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/pairing-coordinate.js +281 -0
- package/dist/puzzles/cube4/reduction/phase3-search.d.ts +42 -0
- package/dist/puzzles/cube4/reduction/phase3-search.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/phase3-search.js +133 -0
- package/dist/puzzles/cube4/reduction/phase3.d.ts +34 -0
- package/dist/puzzles/cube4/reduction/phase3.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/phase3.js +83 -0
- package/dist/puzzles/cube4/reduction/phase4.d.ts +35 -0
- package/dist/puzzles/cube4/reduction/phase4.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/phase4.js +82 -0
- package/dist/puzzles/cube4/reduction/solve.d.ts +31 -0
- package/dist/puzzles/cube4/reduction/solve.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/solve.js +311 -0
- package/dist/puzzles/cube4/scrambler.d.ts +9 -1
- package/dist/puzzles/cube4/scrambler.d.ts.map +1 -1
- package/dist/puzzles/cube4/scrambler.js +123 -81
- package/dist/puzzles/cube4/types.d.ts +6 -0
- package/dist/puzzles/cube4/types.d.ts.map +1 -1
- package/dist/puzzles/cube4/validation.d.ts +6 -0
- package/dist/puzzles/cube4/validation.d.ts.map +1 -0
- package/dist/puzzles/cube4/validation.js +91 -0
- package/dist/puzzles/fto/coordinates.d.ts +53 -0
- package/dist/puzzles/fto/coordinates.d.ts.map +1 -0
- package/dist/puzzles/fto/coordinates.js +228 -0
- package/dist/puzzles/fto/facelets.d.ts +6 -0
- package/dist/puzzles/fto/facelets.d.ts.map +1 -0
- package/dist/puzzles/fto/facelets.js +99 -0
- package/dist/puzzles/fto/index.d.ts +12 -0
- package/dist/puzzles/fto/index.d.ts.map +1 -0
- package/dist/puzzles/fto/index.js +13 -0
- package/dist/puzzles/fto/moves.d.ts +12 -0
- package/dist/puzzles/fto/moves.d.ts.map +1 -0
- package/dist/puzzles/fto/moves.js +115 -0
- package/dist/puzzles/fto/random-state.d.ts +17 -0
- package/dist/puzzles/fto/random-state.d.ts.map +1 -0
- package/dist/puzzles/fto/random-state.js +40 -0
- package/dist/puzzles/fto/scrambler.d.ts +6 -0
- package/dist/puzzles/fto/scrambler.d.ts.map +1 -0
- package/dist/puzzles/fto/scrambler.js +49 -0
- package/dist/puzzles/fto/solver/engine.d.ts +18 -0
- package/dist/puzzles/fto/solver/engine.d.ts.map +1 -0
- package/dist/puzzles/fto/solver/engine.js +1168 -0
- package/dist/puzzles/fto/solver/index.d.ts +2 -0
- package/dist/puzzles/fto/solver/index.d.ts.map +1 -0
- package/dist/puzzles/fto/solver/index.js +1 -0
- package/dist/puzzles/fto/state.d.ts +16 -0
- package/dist/puzzles/fto/state.d.ts.map +1 -0
- package/dist/puzzles/fto/state.js +61 -0
- package/dist/puzzles/fto/types.d.ts +32 -0
- package/dist/puzzles/fto/types.d.ts.map +1 -0
- package/dist/puzzles/fto/types.js +46 -0
- package/dist/puzzles/fto/validation.d.ts +19 -0
- package/dist/puzzles/fto/validation.d.ts.map +1 -0
- package/dist/puzzles/fto/validation.js +143 -0
- package/dist/puzzles/index.d.ts +2 -0
- package/dist/puzzles/index.d.ts.map +1 -1
- package/dist/puzzles/index.js +6 -0
- package/dist/puzzles/skewb/index.d.ts +1 -1
- package/dist/puzzles/skewb/index.d.ts.map +1 -1
- package/dist/puzzles/skewb/index.js +1 -1
- package/dist/puzzles/skewb/random-state.d.ts.map +1 -1
- package/dist/puzzles/skewb/random-state.js +4 -1
- package/dist/puzzles/sq1/facelets.d.ts +12 -0
- package/dist/puzzles/sq1/facelets.d.ts.map +1 -0
- package/dist/puzzles/sq1/facelets.js +26 -0
- package/dist/puzzles/sq1/index.d.ts +10 -0
- package/dist/puzzles/sq1/index.d.ts.map +1 -0
- package/dist/puzzles/sq1/index.js +11 -0
- package/dist/puzzles/sq1/moves.d.ts +55 -0
- package/dist/puzzles/sq1/moves.d.ts.map +1 -0
- package/dist/puzzles/sq1/moves.js +220 -0
- package/dist/puzzles/sq1/random-state.d.ts +6 -0
- package/dist/puzzles/sq1/random-state.d.ts.map +1 -0
- package/dist/puzzles/sq1/random-state.js +49 -0
- package/dist/puzzles/sq1/scrambler.d.ts +10 -0
- package/dist/puzzles/sq1/scrambler.d.ts.map +1 -0
- package/dist/puzzles/sq1/scrambler.js +90 -0
- package/dist/puzzles/sq1/solver/browser-loader.d.ts +5 -0
- package/dist/puzzles/sq1/solver/browser-loader.d.ts.map +1 -0
- package/dist/puzzles/sq1/solver/browser-loader.js +92 -0
- package/dist/puzzles/sq1/solver/full-cube.d.ts +53 -0
- package/dist/puzzles/sq1/solver/full-cube.d.ts.map +1 -0
- package/dist/puzzles/sq1/solver/full-cube.js +281 -0
- package/dist/puzzles/sq1/solver/generated/manifest.json +50 -0
- package/dist/puzzles/sq1/solver/generated/shape-bottom-move.bin +0 -0
- package/dist/puzzles/sq1/solver/generated/shape-idx.bin +0 -0
- package/dist/puzzles/sq1/solver/generated/shape-prun.bin +0 -0
- package/dist/puzzles/sq1/solver/generated/shape-top-move.bin +0 -0
- package/dist/puzzles/sq1/solver/generated/shape-twist-move.bin +0 -0
- package/dist/puzzles/sq1/solver/generated/square-bottom-move.bin +0 -0
- package/dist/puzzles/sq1/solver/generated/square-prun.bin +0 -0
- package/dist/puzzles/sq1/solver/generated/square-top-move.bin +0 -0
- package/dist/puzzles/sq1/solver/generated/square-twist-move.bin +0 -0
- package/dist/puzzles/sq1/solver/index.d.ts +5 -0
- package/dist/puzzles/sq1/solver/index.d.ts.map +1 -0
- package/dist/puzzles/sq1/solver/index.js +4 -0
- package/dist/puzzles/sq1/solver/node-loader.d.ts +4 -0
- package/dist/puzzles/sq1/solver/node-loader.d.ts.map +1 -0
- package/dist/puzzles/sq1/solver/node-loader.js +113 -0
- package/dist/puzzles/sq1/solver/search.d.ts +27 -0
- package/dist/puzzles/sq1/solver/search.d.ts.map +1 -0
- package/dist/puzzles/sq1/solver/search.js +225 -0
- package/dist/puzzles/sq1/solver/shape.d.ts +22 -0
- package/dist/puzzles/sq1/solver/shape.d.ts.map +1 -0
- package/dist/puzzles/sq1/solver/shape.js +185 -0
- package/dist/puzzles/sq1/solver/square.d.ts +10 -0
- package/dist/puzzles/sq1/solver/square.d.ts.map +1 -0
- package/dist/puzzles/sq1/solver/square.js +90 -0
- package/dist/puzzles/sq1/solver/table-metadata.d.ts +48 -0
- package/dist/puzzles/sq1/solver/table-metadata.d.ts.map +1 -0
- package/dist/puzzles/sq1/solver/table-metadata.js +47 -0
- package/dist/puzzles/sq1/solver/tables.d.ts +17 -0
- package/dist/puzzles/sq1/solver/tables.d.ts.map +1 -0
- package/dist/puzzles/sq1/solver/tables.js +66 -0
- package/dist/puzzles/sq1/state.d.ts +10 -0
- package/dist/puzzles/sq1/state.d.ts.map +1 -0
- package/dist/puzzles/sq1/state.js +74 -0
- package/dist/puzzles/sq1/types.d.ts +32 -0
- package/dist/puzzles/sq1/types.d.ts.map +1 -0
- package/dist/puzzles/sq1/types.js +1 -0
- package/dist/puzzles/sq1/validation.d.ts +7 -0
- package/dist/puzzles/sq1/validation.d.ts.map +1 -0
- package/dist/puzzles/sq1/validation.js +64 -0
- package/dist/random/prng.d.ts.map +1 -1
- package/dist/random/prng.js +9 -4
- package/dist-cjs/core/api.js +11 -1
- package/dist-cjs/puzzles/cube4/index.js +11 -4
- package/dist-cjs/puzzles/cube4/random-state.js +79 -0
- package/dist-cjs/puzzles/cube4/reduction/browser-loader.js +100 -0
- package/dist-cjs/puzzles/cube4/reduction/center1.js +258 -0
- package/dist-cjs/puzzles/cube4/reduction/center2.js +462 -0
- package/dist-cjs/puzzles/cube4/reduction/center3.js +284 -0
- package/dist-cjs/puzzles/cube4/reduction/edge3.js +522 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/center1-distance.bin +1 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/center1-manifest.json +46 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/center1-move-maps.bin +0 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/center2-ct-move.bin +0 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/center2-manifest.json +87 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/center2-pruning.bin +1 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/center2-rl-move.bin +0 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/center3-manifest.json +40 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/center3-move.bin +0 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/center3-pruning.bin +0 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/edge3-distances.bin +1 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/edge3-manifest.json +12 -0
- package/dist-cjs/puzzles/cube4/reduction/geometry.js +308 -0
- package/dist-cjs/puzzles/cube4/reduction/node-loader.js +104 -0
- package/dist-cjs/puzzles/cube4/reduction/pairing-coordinate.js +294 -0
- package/dist-cjs/puzzles/cube4/reduction/phase3-search.js +138 -0
- package/dist-cjs/puzzles/cube4/reduction/phase3.js +86 -0
- package/dist-cjs/puzzles/cube4/reduction/phase4.js +87 -0
- package/dist-cjs/puzzles/cube4/reduction/solve.js +314 -0
- package/dist-cjs/puzzles/cube4/scrambler.js +122 -79
- package/dist-cjs/puzzles/cube4/validation.js +97 -0
- package/dist-cjs/puzzles/fto/coordinates.js +240 -0
- package/dist-cjs/puzzles/fto/facelets.js +104 -0
- package/dist-cjs/puzzles/fto/index.js +68 -0
- package/dist-cjs/puzzles/fto/moves.js +126 -0
- package/dist-cjs/puzzles/fto/random-state.js +43 -0
- package/dist-cjs/puzzles/fto/scrambler.js +53 -0
- package/dist-cjs/puzzles/fto/solver/engine.js +1175 -0
- package/dist-cjs/puzzles/fto/solver/index.js +9 -0
- package/dist-cjs/puzzles/fto/state.js +69 -0
- package/dist-cjs/puzzles/fto/types.js +49 -0
- package/dist-cjs/puzzles/fto/validation.js +147 -0
- package/dist-cjs/puzzles/index.js +7 -1
- package/dist-cjs/puzzles/skewb/index.js +2 -1
- package/dist-cjs/puzzles/skewb/random-state.js +4 -1
- package/dist-cjs/puzzles/sq1/facelets.js +30 -0
- package/dist-cjs/puzzles/sq1/index.js +52 -0
- package/dist-cjs/puzzles/sq1/moves.js +234 -0
- package/dist-cjs/puzzles/sq1/random-state.js +52 -0
- package/dist-cjs/puzzles/sq1/scrambler.js +95 -0
- package/dist-cjs/puzzles/sq1/solver/browser-loader.js +97 -0
- package/dist-cjs/puzzles/sq1/solver/full-cube.js +289 -0
- package/dist-cjs/puzzles/sq1/solver/generated/manifest.json +50 -0
- package/dist-cjs/puzzles/sq1/solver/generated/shape-bottom-move.bin +0 -0
- package/dist-cjs/puzzles/sq1/solver/generated/shape-idx.bin +0 -0
- package/dist-cjs/puzzles/sq1/solver/generated/shape-prun.bin +0 -0
- package/dist-cjs/puzzles/sq1/solver/generated/shape-top-move.bin +0 -0
- package/dist-cjs/puzzles/sq1/solver/generated/shape-twist-move.bin +0 -0
- package/dist-cjs/puzzles/sq1/solver/generated/square-bottom-move.bin +0 -0
- package/dist-cjs/puzzles/sq1/solver/generated/square-prun.bin +0 -0
- package/dist-cjs/puzzles/sq1/solver/generated/square-top-move.bin +0 -0
- package/dist-cjs/puzzles/sq1/solver/generated/square-twist-move.bin +0 -0
- package/dist-cjs/puzzles/sq1/solver/index.js +18 -0
- package/dist-cjs/puzzles/sq1/solver/node-loader.js +120 -0
- package/dist-cjs/puzzles/sq1/solver/search.js +229 -0
- package/dist-cjs/puzzles/sq1/solver/shape.js +191 -0
- package/dist-cjs/puzzles/sq1/solver/square.js +93 -0
- package/dist-cjs/puzzles/sq1/solver/table-metadata.js +50 -0
- package/dist-cjs/puzzles/sq1/solver/tables.js +106 -0
- package/dist-cjs/puzzles/sq1/state.js +81 -0
- package/dist-cjs/puzzles/sq1/types.js +2 -0
- package/dist-cjs/puzzles/sq1/validation.js +68 -0
- package/dist-cjs/random/prng.js +9 -4
- package/package.json +43 -14
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Center1 (phase 1 of the 4x4 reduction solver): reduce the two colors of a
|
|
4
|
+
* chosen axis (U/D, R/L or F/B) onto a single face-pair.
|
|
5
|
+
*
|
|
6
|
+
* Design (simpler than the reference's symmetry-reduced tables, which use a
|
|
7
|
+
* fragile 48-element non-group of "block-turn symmetries"):
|
|
8
|
+
* - coordinate: the raw C(24,8) = 735,471 mask of the positions holding the
|
|
9
|
+
* two axis colors — NO symmetry reduction;
|
|
10
|
+
* - pruning: the exact BFS distance table from the three face-pair goal
|
|
11
|
+
* masks (God's number 9; the 27-move set reaches the whole space);
|
|
12
|
+
* - search: greedy descent — any move that decreases the exact distance by
|
|
13
|
+
* exactly 1 is on an optimal path, so repeatedly applying the first such
|
|
14
|
+
* move reaches the goal in exactly `distance` moves, with zero
|
|
15
|
+
* backtracking and no iterative deepening.
|
|
16
|
+
*
|
|
17
|
+
* One table serves all three axes (the face-pairs are equivalent goals).
|
|
18
|
+
* The phase-1 move set: 9 axes [U,R,F,D,L,B,Uw,Rw,Fw] × 3 powers = 27.
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.CENTER1_GOAL_MASKS = exports.CENTER1_RAW_STATES = exports.CENTER1_MOVES = exports.CENTER1_AXES = void 0;
|
|
22
|
+
exports.center1Mask = center1Mask;
|
|
23
|
+
exports.rankCenter1Mask = rankCenter1Mask;
|
|
24
|
+
exports.unrankCenter1Mask = unrankCenter1Mask;
|
|
25
|
+
exports.buildCenter1MoveMaps = buildCenter1MoveMaps;
|
|
26
|
+
exports.applyCenter1Move = applyCenter1Move;
|
|
27
|
+
exports.generateCenter1Tables = generateCenter1Tables;
|
|
28
|
+
exports.getCenter1Distance = getCenter1Distance;
|
|
29
|
+
exports.solveCenter1 = solveCenter1;
|
|
30
|
+
exports.solveCenter1All = solveCenter1All;
|
|
31
|
+
exports.verifyCenter1 = verifyCenter1;
|
|
32
|
+
const moves_js_1 = require("../moves.js");
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// Constants
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
exports.CENTER1_AXES = [
|
|
37
|
+
"U", "R", "F", "D", "L", "B", "Uw", "Rw", "Fw"
|
|
38
|
+
];
|
|
39
|
+
exports.CENTER1_MOVES = exports.CENTER1_AXES.flatMap((face) => [face, `${face}2`, `${face}'`]);
|
|
40
|
+
exports.CENTER1_RAW_STATES = 735471;
|
|
41
|
+
// Cnk table up to n=24.
|
|
42
|
+
const CNK = (() => {
|
|
43
|
+
const table = [];
|
|
44
|
+
for (let n = 0; n <= 24; n++) {
|
|
45
|
+
table[n] = [];
|
|
46
|
+
for (let k = 0; k <= 24; k++) {
|
|
47
|
+
if (k === 0 || k === n)
|
|
48
|
+
table[n][k] = 1;
|
|
49
|
+
else if (k > n)
|
|
50
|
+
table[n][k] = 0;
|
|
51
|
+
else
|
|
52
|
+
table[n][k] = table[n - 1][k - 1] + table[n - 1][k];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return table;
|
|
56
|
+
})();
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// Mask utilities (24-bit integers; bit i = center position i)
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
/** The mask of the positions holding the two axis colors (colors % 3 == urf). */
|
|
61
|
+
function center1Mask(centers, urf) {
|
|
62
|
+
let mask = 0;
|
|
63
|
+
for (let i = 0; i < 24; i++) {
|
|
64
|
+
if (centers[i] % 3 === urf)
|
|
65
|
+
mask |= 1 << i;
|
|
66
|
+
}
|
|
67
|
+
return mask;
|
|
68
|
+
}
|
|
69
|
+
/** C(24,8) combination rank of a mask. */
|
|
70
|
+
function rankCenter1Mask(mask) {
|
|
71
|
+
let idx = 0;
|
|
72
|
+
let r = 8;
|
|
73
|
+
for (let i = 23; i >= 0; i--) {
|
|
74
|
+
if ((mask >> i) & 1)
|
|
75
|
+
idx += CNK[i][r--];
|
|
76
|
+
}
|
|
77
|
+
return idx;
|
|
78
|
+
}
|
|
79
|
+
function unrankCenter1Mask(idx) {
|
|
80
|
+
let mask = 0;
|
|
81
|
+
let r = 8;
|
|
82
|
+
for (let i = 23; i >= 0; i--) {
|
|
83
|
+
if (idx >= CNK[i][r]) {
|
|
84
|
+
idx -= CNK[i][r];
|
|
85
|
+
r--;
|
|
86
|
+
mask |= 1 << i;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return mask;
|
|
90
|
+
}
|
|
91
|
+
/** The three face-pair goal masks (bit i = center position i, face-major). */
|
|
92
|
+
exports.CENTER1_GOAL_MASKS = {
|
|
93
|
+
ud: 0xf00f,
|
|
94
|
+
rl: 0xf00f0,
|
|
95
|
+
fb: 0xf00f00
|
|
96
|
+
};
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// Move bit-permutations
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
/** The 27 phase-1 moves as bit-permutations of the 24-bit mask. */
|
|
101
|
+
function buildCenter1MoveMaps() {
|
|
102
|
+
const maps = new Int32Array(exports.CENTER1_MOVES.length * 24);
|
|
103
|
+
for (let m = 0; m < exports.CENTER1_MOVES.length; m++) {
|
|
104
|
+
const { face, turns } = (0, moves_js_1.parseMove)(exports.CENTER1_MOVES[m]);
|
|
105
|
+
const effect = moves_js_1.FACE_MOVE_EFFECTS[face];
|
|
106
|
+
let pos = Array.from({ length: 24 }, (_, i) => i);
|
|
107
|
+
for (let t = 0; t < turns; t++) {
|
|
108
|
+
const next = new Array(24);
|
|
109
|
+
for (let i = 0; i < 24; i++)
|
|
110
|
+
next[i] = pos[effect.ctperm[i]];
|
|
111
|
+
pos = next;
|
|
112
|
+
}
|
|
113
|
+
// apply a move: dest bit i takes source bit pos[i]
|
|
114
|
+
for (let i = 0; i < 24; i++)
|
|
115
|
+
maps[m * 24 + i] = pos[i];
|
|
116
|
+
}
|
|
117
|
+
return maps;
|
|
118
|
+
}
|
|
119
|
+
function applyCenter1Move(mask, moveMap, move) {
|
|
120
|
+
let out = 0;
|
|
121
|
+
for (let i = 0; i < 24; i++) {
|
|
122
|
+
if ((mask >> moveMap[move * 24 + i]) & 1)
|
|
123
|
+
out |= 1 << i;
|
|
124
|
+
}
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
function generateCenter1Tables() {
|
|
128
|
+
const moveMaps = buildCenter1MoveMaps();
|
|
129
|
+
const distance = new Uint8Array(Math.ceil(exports.CENTER1_RAW_STATES / 2)).fill(0xff);
|
|
130
|
+
const setDistance = (rank, d) => {
|
|
131
|
+
const byte = rank >> 1;
|
|
132
|
+
if ((rank & 1) === 0)
|
|
133
|
+
distance[byte] = (distance[byte] & 0xf0) | (d & 0x0f);
|
|
134
|
+
else
|
|
135
|
+
distance[byte] = (distance[byte] & 0x0f) | ((d & 0x0f) << 4);
|
|
136
|
+
};
|
|
137
|
+
const getDistance = (rank) => {
|
|
138
|
+
const byte = distance[rank >> 1];
|
|
139
|
+
return (rank & 1) === 0 ? byte & 0x0f : byte >> 4;
|
|
140
|
+
};
|
|
141
|
+
const isUnset = (rank) => {
|
|
142
|
+
const byte = distance[rank >> 1];
|
|
143
|
+
return (rank & 1) === 0 ? (byte & 0x0f) === 0x0f : (byte >> 4) === 0x0f;
|
|
144
|
+
};
|
|
145
|
+
// BFS from the three goal masks.
|
|
146
|
+
for (const goal of [exports.CENTER1_GOAL_MASKS.ud, exports.CENTER1_GOAL_MASKS.rl, exports.CENTER1_GOAL_MASKS.fb]) {
|
|
147
|
+
setDistance(rankCenter1Mask(goal), 0);
|
|
148
|
+
}
|
|
149
|
+
const queue = [
|
|
150
|
+
rankCenter1Mask(exports.CENTER1_GOAL_MASKS.ud),
|
|
151
|
+
rankCenter1Mask(exports.CENTER1_GOAL_MASKS.rl),
|
|
152
|
+
rankCenter1Mask(exports.CENTER1_GOAL_MASKS.fb)
|
|
153
|
+
];
|
|
154
|
+
let head = 0;
|
|
155
|
+
while (head < queue.length) {
|
|
156
|
+
const rank = queue[head++];
|
|
157
|
+
const mask = unrankCenter1Mask(rank);
|
|
158
|
+
const d = getDistance(rank);
|
|
159
|
+
for (let m = 0; m < exports.CENTER1_MOVES.length; m++) {
|
|
160
|
+
const next = applyCenter1Move(mask, moveMaps, m);
|
|
161
|
+
const nextRank = rankCenter1Mask(next);
|
|
162
|
+
if (isUnset(nextRank)) {
|
|
163
|
+
setDistance(nextRank, d + 1);
|
|
164
|
+
queue.push(nextRank);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// verify completeness: every state reached
|
|
169
|
+
let reached = 0;
|
|
170
|
+
for (let rank = 0; rank < exports.CENTER1_RAW_STATES; rank++) {
|
|
171
|
+
if (!isUnset(rank))
|
|
172
|
+
reached++;
|
|
173
|
+
}
|
|
174
|
+
if (reached !== exports.CENTER1_RAW_STATES) {
|
|
175
|
+
throw new Error(`Center1 BFS incomplete: ${reached}/${exports.CENTER1_RAW_STATES} states reached.`);
|
|
176
|
+
}
|
|
177
|
+
return { distance, moveMaps };
|
|
178
|
+
}
|
|
179
|
+
function getCenter1Distance(mask, tables) {
|
|
180
|
+
const rank = rankCenter1Mask(mask);
|
|
181
|
+
const byte = tables.distance[rank >> 1];
|
|
182
|
+
return (rank & 1) === 0 ? byte & 0x0f : byte >> 4;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Phase-1 greedy descent: bring the axis colors onto some face-pair in
|
|
186
|
+
* exactly `distance` moves (optimal). Every move that decreases the exact
|
|
187
|
+
* distance by 1 is on an optimal path, so no backtracking is needed.
|
|
188
|
+
* Returns null if the state is unreachable (should not happen).
|
|
189
|
+
*/
|
|
190
|
+
function solveCenter1(centers, urf, tables) {
|
|
191
|
+
const solutions = solveCenter1All(centers, urf, tables, 1);
|
|
192
|
+
return solutions.length > 0 ? solutions[0] : null;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Enumerate phase-1 solutions (the optimal descending paths, then longer
|
|
196
|
+
* alternatives up to `cap`). The phase-1 → phase-3 pipeline needs variety:
|
|
197
|
+
* the phase-2 gate rate depends strongly on the phase-1 output's wings.
|
|
198
|
+
*/
|
|
199
|
+
function solveCenter1All(centers, urf, tables, cap = 50) {
|
|
200
|
+
const moveMaps = tables.moveMaps;
|
|
201
|
+
const solutions = [];
|
|
202
|
+
const path = [];
|
|
203
|
+
const maxLength = 30;
|
|
204
|
+
const dfs = (mask, depth) => {
|
|
205
|
+
if (solutions.length >= cap)
|
|
206
|
+
return true;
|
|
207
|
+
const h = getCenter1Distance(mask, tables);
|
|
208
|
+
if (h === 0) {
|
|
209
|
+
solutions.push({ moves: path.slice(0, depth).map((m) => exports.CENTER1_MOVES[m]), length: depth });
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
if (depth >= maxLength)
|
|
213
|
+
return false;
|
|
214
|
+
for (let m = 0; m < exports.CENTER1_MOVES.length; m++) {
|
|
215
|
+
const next = applyCenter1Move(mask, moveMaps, m);
|
|
216
|
+
if (getCenter1Distance(next, tables) !== h - 1)
|
|
217
|
+
continue;
|
|
218
|
+
path[depth] = m;
|
|
219
|
+
if (dfs(next, depth + 1))
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
return false;
|
|
223
|
+
};
|
|
224
|
+
dfs(center1Mask(centers, urf), 0);
|
|
225
|
+
return solutions;
|
|
226
|
+
}
|
|
227
|
+
// ---------------------------------------------------------------------------
|
|
228
|
+
// Verification gates
|
|
229
|
+
// ---------------------------------------------------------------------------
|
|
230
|
+
function verifyCenter1(tables) {
|
|
231
|
+
const errors = [];
|
|
232
|
+
if (getCenter1Distance(exports.CENTER1_GOAL_MASKS.ud, tables) !== 0) {
|
|
233
|
+
errors.push("Goal mask ud must have distance 0.");
|
|
234
|
+
}
|
|
235
|
+
if (getCenter1Distance(exports.CENTER1_GOAL_MASKS.rl, tables) !== 0) {
|
|
236
|
+
errors.push("Goal mask rl must have distance 0.");
|
|
237
|
+
}
|
|
238
|
+
if (getCenter1Distance(exports.CENTER1_GOAL_MASKS.fb, tables) !== 0) {
|
|
239
|
+
errors.push("Goal mask fb must have distance 0.");
|
|
240
|
+
}
|
|
241
|
+
// distances must be consistent with the moves
|
|
242
|
+
let maxDepth = 0;
|
|
243
|
+
for (let rank = 0; rank < exports.CENTER1_RAW_STATES; rank++) {
|
|
244
|
+
const mask = unrankCenter1Mask(rank);
|
|
245
|
+
const d = getCenter1Distance(mask, tables);
|
|
246
|
+
if (d > maxDepth)
|
|
247
|
+
maxDepth = d;
|
|
248
|
+
for (let m = 0; m < exports.CENTER1_MOVES.length; m++) {
|
|
249
|
+
const next = applyCenter1Move(mask, tables.moveMaps, m);
|
|
250
|
+
const nd = getCenter1Distance(next, tables);
|
|
251
|
+
if (nd < d - 1 || nd > d + 1) {
|
|
252
|
+
errors.push(`Distance inconsistency at rank ${rank} move ${exports.CENTER1_MOVES[m]}: ${d} -> ${nd}.`);
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return errors;
|
|
258
|
+
}
|