@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,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pairing coordinate (phase 3 of the 4x4 reduction solver).
|
|
3
|
+
*
|
|
4
|
+
* Semantics ported from the reference `scramble_444.js` (Edge3 class) and
|
|
5
|
+
* adapted to this repo's position numbering.
|
|
6
|
+
*
|
|
7
|
+
* The pairing state is the *relative* permutation between the two halves of
|
|
8
|
+
* the edge slots: `edge[i]` = the slot whose second half holds the pair
|
|
9
|
+
* occupying slot i's first half. It exists (and `edge == identity` iff all
|
|
10
|
+
* 12 slots are paired) only for states with the *transversal* property:
|
|
11
|
+
* the 12 second-half positions must hold exactly one wing of each pair.
|
|
12
|
+
*
|
|
13
|
+
* Slot convention: the reference's `FullEdgeMap` slot layout, converted to
|
|
14
|
+
* this repo's position numbering (via the sticker isomorphism between the
|
|
15
|
+
* two layouts). It is co-adapted with the phase-3 move set — all 20 moves
|
|
16
|
+
* preserve the transversal property (verified: 0 breaks over random walks).
|
|
17
|
+
* A naive even/odd split does NOT work (Rw2/Lw2 break the transversal).
|
|
18
|
+
*
|
|
19
|
+
* The phase-3 move set contains ONLY even wing-permutation moves (face
|
|
20
|
+
* turns + double wide turns), so wing parity is invariant in this phase and
|
|
21
|
+
* is tracked by the caller (Center3 coordinate), never here.
|
|
22
|
+
*/
|
|
23
|
+
import { CUBE4_ALL_MOVES } from "../types.js";
|
|
24
|
+
import { FACE_MOVE_EFFECTS, parseMove } from "../moves.js";
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Move sets
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
/**
|
|
29
|
+
* Phase-3 move set (20 moves, the reference's `move3std` minus its trailing
|
|
30
|
+
* no-op sentinel — index 36, used only as the "previous move" dummy for
|
|
31
|
+
* cancellation pruning): face turns + double wide turns. All of them are
|
|
32
|
+
* even wing permutations.
|
|
33
|
+
*/
|
|
34
|
+
export const PAIRING_MOVES = [
|
|
35
|
+
"U", "U2", "U'", "R2", "F", "F2", "F'", "D", "D2", "D'",
|
|
36
|
+
"L2", "B", "B2", "B'", "Uw2", "Rw2", "Fw2", "Dw2", "Lw2", "Bw2"
|
|
37
|
+
];
|
|
38
|
+
/**
|
|
39
|
+
* The 17-move BFS subset used to build the pruning table (reference
|
|
40
|
+
* `bfsMoves`): omits Dw2/Lw2/Bw2 (rotation-conjugate to Uw2/Rw2/Fw2).
|
|
41
|
+
*/
|
|
42
|
+
export const PAIRING_BFS_MOVES = [
|
|
43
|
+
"U2", "U", "U'", "R2", "F2", "F", "F'", "D2", "D", "D'",
|
|
44
|
+
"L2", "B2", "B", "B'", "Uw2", "Rw2", "Fw2"
|
|
45
|
+
];
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Slot layout
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
/**
|
|
50
|
+
* The 12 edge slots as (first-half, second-half) position pairs, in this
|
|
51
|
+
* repo's position numbering. Derived from the reference's `FullEdgeMap`
|
|
52
|
+
* through the sticker isomorphism between the two layouts:
|
|
53
|
+
*
|
|
54
|
+
* slot i = (π(FullEdgeMap[i]), π(FullEdgeMap[i] + 12))
|
|
55
|
+
*
|
|
56
|
+
* where π maps the reference's position ids to ours by matching the two
|
|
57
|
+
* stickers of each wing position. The phase-3 move set preserves the
|
|
58
|
+
* transversal property for exactly this split.
|
|
59
|
+
*/
|
|
60
|
+
export const PAIRING_SLOTS = [
|
|
61
|
+
[3, 2], [7, 6], [14, 15], [10, 11], [5, 4], [0, 1],
|
|
62
|
+
[9, 8], [12, 13], [19, 18], [20, 21], [23, 22], [16, 17]
|
|
63
|
+
];
|
|
64
|
+
/** First-half position of each slot. */
|
|
65
|
+
export const PAIRING_FIRST_HALVES = PAIRING_SLOTS.map(([f]) => f);
|
|
66
|
+
/** Second-half position of each slot. */
|
|
67
|
+
export const PAIRING_SECOND_HALVES = PAIRING_SLOTS.map(([, s]) => s);
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// Pair-id array (ground truth of the pairing state)
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
/**
|
|
72
|
+
* Map the 24 labeled wings to their pair ids: `arr24[i] = wp[i] >> 1`.
|
|
73
|
+
* Each pair id 0-11 appears exactly twice.
|
|
74
|
+
*/
|
|
75
|
+
export function pairIdArray(wp) {
|
|
76
|
+
const arr = new Uint8Array(24);
|
|
77
|
+
for (let i = 0; i < 24; i++)
|
|
78
|
+
arr[i] = wp[i] >> 1;
|
|
79
|
+
return arr;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Apply a move to a 24-entry pair-id array (position permutation from the
|
|
83
|
+
* verified move tables).
|
|
84
|
+
*/
|
|
85
|
+
export function applyPairingMoveToArray(arr24, move) {
|
|
86
|
+
const { face, turns } = parseMove(move);
|
|
87
|
+
const effect = FACE_MOVE_EFFECTS[face];
|
|
88
|
+
let current = arr24;
|
|
89
|
+
for (let t = 0; t < turns; t++) {
|
|
90
|
+
const next = new Uint8Array(24);
|
|
91
|
+
for (let i = 0; i < 24; i++)
|
|
92
|
+
next[i] = current[effect.wperm[i]];
|
|
93
|
+
current = next;
|
|
94
|
+
}
|
|
95
|
+
return current;
|
|
96
|
+
}
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// Transversal gate
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
/**
|
|
101
|
+
* True iff the 12 second-half positions hold exactly one wing of each pair —
|
|
102
|
+
* i.e. the pair ids there are all distinct. Required by `encodePairingState`
|
|
103
|
+
* and preserved by the phase-3 move set (verified).
|
|
104
|
+
*/
|
|
105
|
+
export function hasPairingTransversal(wp) {
|
|
106
|
+
const seen = new Uint8Array(12);
|
|
107
|
+
for (const s of PAIRING_SECOND_HALVES) {
|
|
108
|
+
const pair = wp[s] >> 1;
|
|
109
|
+
if (seen[pair] !== 0)
|
|
110
|
+
return false;
|
|
111
|
+
seen[pair] = 1;
|
|
112
|
+
}
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Wing-permutation parity of the state. For transversal-eligible states this
|
|
117
|
+
* is the phase-2 → phase-3 filter parity (the reference's `checkPhase2Edge`
|
|
118
|
+
* `parity == 0` condition, verified equivalent): the bridge must only accept
|
|
119
|
+
* even-parity states so the derived 3x3 edge permutation comes out even.
|
|
120
|
+
*/
|
|
121
|
+
export function pairingGateParity(wp) {
|
|
122
|
+
let parity = 0;
|
|
123
|
+
const seen = new Array(24).fill(false);
|
|
124
|
+
for (let start = 0; start < 24; start++) {
|
|
125
|
+
if (seen[start] || wp[start] === start)
|
|
126
|
+
continue;
|
|
127
|
+
let length = 0;
|
|
128
|
+
let current = start;
|
|
129
|
+
while (!seen[current]) {
|
|
130
|
+
seen[current] = true;
|
|
131
|
+
current = wp[current];
|
|
132
|
+
length++;
|
|
133
|
+
}
|
|
134
|
+
parity ^= (length - 1) & 1;
|
|
135
|
+
}
|
|
136
|
+
return parity;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Encode a 24-labeled-wing state to the pairing permutation.
|
|
140
|
+
*
|
|
141
|
+
* Requires the transversal property (`hasPairingTransversal`), else null.
|
|
142
|
+
*
|
|
143
|
+
* Construction (reference $set_6, adapted to PAIRING_SLOTS):
|
|
144
|
+
* - `edge[i]` starts as the pair id at the second-half position of slot i
|
|
145
|
+
* - cycle-sort it to identity while tracking the inverse `temp` and parity
|
|
146
|
+
* - `edge[i] = temp[pair id at the first-half position of slot i]`
|
|
147
|
+
*
|
|
148
|
+
* The result is in $std-normalized form (second halves implicitly hold
|
|
149
|
+
* pairs 0..11 in order).
|
|
150
|
+
*/
|
|
151
|
+
export function encodePairingState(wp) {
|
|
152
|
+
if (!hasPairingTransversal(wp))
|
|
153
|
+
return null;
|
|
154
|
+
const temp = new Uint8Array(12);
|
|
155
|
+
const edge = new Uint8Array(12);
|
|
156
|
+
for (let i = 0; i < 12; i++) {
|
|
157
|
+
temp[i] = i;
|
|
158
|
+
edge[i] = wp[PAIRING_SECOND_HALVES[i]] >> 1;
|
|
159
|
+
}
|
|
160
|
+
let parity = 1;
|
|
161
|
+
for (let i = 0; i < 12; i++) {
|
|
162
|
+
while (edge[i] !== i) {
|
|
163
|
+
const t = edge[i];
|
|
164
|
+
edge[i] = edge[t];
|
|
165
|
+
edge[t] = t;
|
|
166
|
+
const s = temp[i];
|
|
167
|
+
temp[i] = temp[t];
|
|
168
|
+
temp[t] = s;
|
|
169
|
+
parity ^= 1;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
for (let i = 0; i < 12; i++) {
|
|
173
|
+
edge[i] = temp[wp[PAIRING_FIRST_HALVES[i]] >> 1];
|
|
174
|
+
}
|
|
175
|
+
return { edge, parity: parity };
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Decode the canonical representative: slot i's first-half position holds the
|
|
179
|
+
* even wing of pair `edge[i]`, its second-half position the odd wing of pair
|
|
180
|
+
* `edgeo[i]`. With `edgeo = identity` this is the $set_4 canonical form.
|
|
181
|
+
*/
|
|
182
|
+
export function decodePairingState(edge, edgeo) {
|
|
183
|
+
const wp = new Uint8Array(24);
|
|
184
|
+
for (let i = 0; i < 12; i++) {
|
|
185
|
+
wp[PAIRING_FIRST_HALVES[i]] = 2 * edge[i];
|
|
186
|
+
wp[PAIRING_SECOND_HALVES[i]] = 2 * edgeo[i] + 1;
|
|
187
|
+
}
|
|
188
|
+
return wp;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* True iff the pairing permutation is fully reduced (all 12 slots paired).
|
|
192
|
+
*/
|
|
193
|
+
export function isPairingReduced(edge) {
|
|
194
|
+
for (let i = 0; i < 12; i++) {
|
|
195
|
+
if (edge[i] !== i)
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Apply a phase-3 move to a pairing state, keeping the (edge, edgeo)
|
|
202
|
+
* representation: decode → physical move (verified move tables) → re-derive
|
|
203
|
+
* the slot halves → $std-normalize. Valid because the phase-3 move set
|
|
204
|
+
* preserves the transversal property (verified).
|
|
205
|
+
*/
|
|
206
|
+
export function applyPairingMoveToState(edge, edgeo, move) {
|
|
207
|
+
const wp = decodePairingState(edge, edgeo);
|
|
208
|
+
const arr24 = applyPairingMoveToArray(pairIdArray(wp), move);
|
|
209
|
+
const e = new Uint8Array(12);
|
|
210
|
+
const eo = new Uint8Array(12);
|
|
211
|
+
for (let i = 0; i < 12; i++) {
|
|
212
|
+
e[i] = arr24[PAIRING_FIRST_HALVES[i]];
|
|
213
|
+
eo[i] = arr24[PAIRING_SECOND_HALVES[i]];
|
|
214
|
+
}
|
|
215
|
+
return normalizePairingState(e, eo);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* $std-normalize a pairing state: reindex so the second halves hold pairs
|
|
219
|
+
* 0..11 in order (the `edgeo` array becomes the identity).
|
|
220
|
+
*/
|
|
221
|
+
export function normalizePairingState(edge, edgeo) {
|
|
222
|
+
const temp = new Uint8Array(12);
|
|
223
|
+
for (let i = 0; i < 12; i++)
|
|
224
|
+
temp[edgeo[i]] = i;
|
|
225
|
+
const out = new Uint8Array(12);
|
|
226
|
+
const identity = new Uint8Array(12);
|
|
227
|
+
for (let i = 0; i < 12; i++) {
|
|
228
|
+
out[i] = temp[edge[i]];
|
|
229
|
+
identity[i] = i;
|
|
230
|
+
}
|
|
231
|
+
return { edge: out, edgeo: identity };
|
|
232
|
+
}
|
|
233
|
+
// ---------------------------------------------------------------------------
|
|
234
|
+
// Verification gates
|
|
235
|
+
// ---------------------------------------------------------------------------
|
|
236
|
+
export function verifyPairingCoordinate() {
|
|
237
|
+
const errors = [];
|
|
238
|
+
for (const move of PAIRING_MOVES) {
|
|
239
|
+
if (!CUBE4_ALL_MOVES.includes(move)) {
|
|
240
|
+
errors.push(`Pairing move ${move} is not a valid 4x4 move.`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// The slot halves must be a bijection over the 24 positions.
|
|
244
|
+
const seen = new Set();
|
|
245
|
+
for (const [f, s] of PAIRING_SLOTS) {
|
|
246
|
+
if (f < 0 || f > 23 || s < 0 || s > 23)
|
|
247
|
+
errors.push(`Slot out of range: (${f}, ${s}).`);
|
|
248
|
+
seen.add(f);
|
|
249
|
+
seen.add(s);
|
|
250
|
+
}
|
|
251
|
+
if (seen.size !== 24) {
|
|
252
|
+
errors.push(`Pairing slots do not cover the 24 positions (${seen.size} distinct).`);
|
|
253
|
+
}
|
|
254
|
+
// All phase-3 moves must be even wing permutations (parity is invariant in
|
|
255
|
+
// this phase and tracked by the caller).
|
|
256
|
+
for (const move of PAIRING_MOVES) {
|
|
257
|
+
const { face, turns } = parseMove(move);
|
|
258
|
+
const parity = wingPermutationParity(FACE_MOVE_EFFECTS[face].wperm) * (turns & 1);
|
|
259
|
+
if ((parity & 1) !== 0) {
|
|
260
|
+
errors.push(`Pairing move ${move} has odd wing permutation parity.`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return errors;
|
|
264
|
+
}
|
|
265
|
+
function wingPermutationParity(perm) {
|
|
266
|
+
let parity = 0;
|
|
267
|
+
const seen = new Array(perm.length).fill(false);
|
|
268
|
+
for (let start = 0; start < perm.length; start++) {
|
|
269
|
+
if (seen[start] || perm[start] === start)
|
|
270
|
+
continue;
|
|
271
|
+
let length = 0;
|
|
272
|
+
let current = start;
|
|
273
|
+
while (!seen[current]) {
|
|
274
|
+
seen[current] = true;
|
|
275
|
+
current = perm[current];
|
|
276
|
+
length++;
|
|
277
|
+
}
|
|
278
|
+
parity ^= (length - 1) & 1;
|
|
279
|
+
}
|
|
280
|
+
return parity;
|
|
281
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fast table-driven Phase-3 search (Center3 + Edge3).
|
|
3
|
+
*
|
|
4
|
+
* Solves the joint state from Phase 2 to fully-solved monochrome centers (ct3 == 0)
|
|
5
|
+
* and all 12 wing pairs paired (sym-index == 0), using the 17-move exploration set.
|
|
6
|
+
*/
|
|
7
|
+
import type { Cube4Move } from "../types.js";
|
|
8
|
+
import { type Center3Tables } from "./center3.js";
|
|
9
|
+
import { type Edge3Tables } from "./edge3.js";
|
|
10
|
+
import type { Phase3Start } from "./phase3.js";
|
|
11
|
+
/** The 17 exploration moves (the reference's m < 17 bound). */
|
|
12
|
+
export declare const SEARCH_MOVES: readonly Cube4Move[];
|
|
13
|
+
export declare const ckmv3: boolean[][];
|
|
14
|
+
export declare const skipAxis3: number[];
|
|
15
|
+
export interface Phase3Result {
|
|
16
|
+
moves: Cube4Move[];
|
|
17
|
+
ct3: number;
|
|
18
|
+
edge: Uint8Array;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Fast table-driven Phase 3 IDA* solver class.
|
|
22
|
+
*/
|
|
23
|
+
export declare class FastPhase3Solver {
|
|
24
|
+
private readonly tempep;
|
|
25
|
+
private readonly moveLog;
|
|
26
|
+
private readonly sym;
|
|
27
|
+
constructor();
|
|
28
|
+
private getPrun;
|
|
29
|
+
/**
|
|
30
|
+
* Recursive IDA* worker on table transitions.
|
|
31
|
+
*/
|
|
32
|
+
search(eplast: Uint8Array, ct: number, prun: number, maxl: number, lm: number, depth: number, center3Tables: Center3Tables, edge3Distances: Uint8Array): Promise<boolean>;
|
|
33
|
+
/**
|
|
34
|
+
* Search for a phase-3 solution with exact target length.
|
|
35
|
+
*/
|
|
36
|
+
solveLength(edge: Uint8Array, ct3: number, edist: number, length3: number, center3Tables: Center3Tables, edge3Distances: Uint8Array): Promise<Cube4Move[] | null>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Solve Phase 3 with iterative deepening on a single Phase3Start.
|
|
40
|
+
*/
|
|
41
|
+
export declare function solvePhase3(start: Phase3Start, center3Tables: Center3Tables, edge3Tables: Edge3Tables, maxLength?: number): Promise<Phase3Result | null>;
|
|
42
|
+
//# sourceMappingURL=phase3-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phase3-search.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/phase3-search.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAIL,KAAK,WAAW,EAEjB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,+DAA+D;AAC/D,eAAO,MAAM,YAAY,EAAE,SAAS,SAAS,EAA+B,CAAC;AAa7E,eAAO,MAAM,KAAK,EAAE,OAAO,EAAE,EAAgE,CAAC;AAO9F,eAAO,MAAM,SAAS,EAAE,MAAM,EAA2B,CAAC;AAU1D,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,UAAU,CAAC;CAClB;AAED;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAW;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAY;;IAQhC,OAAO,CAAC,OAAO;IAKf;;OAEG;IACU,MAAM,CACjB,MAAM,EAAE,UAAU,EAClB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,aAAa,EAC5B,cAAc,EAAE,UAAU,GACzB,OAAO,CAAC,OAAO,CAAC;IA8DnB;;OAEG;IACU,WAAW,CACtB,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,aAAa,EAC5B,cAAc,EAAE,UAAU,GACzB,OAAO,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC;CAkB/B;AAID;;GAEG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,WAAW,EAClB,aAAa,EAAE,aAAa,EAC5B,WAAW,EAAE,WAAW,EACxB,SAAS,SAAK,GACb,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAmC9B"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fast table-driven Phase-3 search (Center3 + Edge3).
|
|
3
|
+
*
|
|
4
|
+
* Solves the joint state from Phase 2 to fully-solved monochrome centers (ct3 == 0)
|
|
5
|
+
* and all 12 wing pairs paired (sym-index == 0), using the 17-move exploration set.
|
|
6
|
+
*/
|
|
7
|
+
import { CENTER3_MOVES } from "./center3.js";
|
|
8
|
+
import { getSymTables, getMvSymFrom, MOVE3_STD } from "./edge3.js";
|
|
9
|
+
/** The 17 exploration moves (the reference's m < 17 bound). */
|
|
10
|
+
export const SEARCH_MOVES = CENTER3_MOVES.slice(0, 17);
|
|
11
|
+
// Commutation / move-cancellation pruning tables
|
|
12
|
+
const ckmv = Array.from({ length: 37 }, () => new Array(36).fill(false));
|
|
13
|
+
for (let i = 0; i < 36; i++) {
|
|
14
|
+
for (let j = 0; j < 36; j++) {
|
|
15
|
+
ckmv[i][j] =
|
|
16
|
+
Math.floor(i / 3) === Math.floor(j / 3) ||
|
|
17
|
+
(Math.floor(i / 3) % 3 === Math.floor(j / 3) % 3 && i > j);
|
|
18
|
+
}
|
|
19
|
+
ckmv[36][i] = false;
|
|
20
|
+
}
|
|
21
|
+
export const ckmv3 = Array.from({ length: 21 }, () => new Array(20).fill(false));
|
|
22
|
+
for (let i = 0; i < 21; i++) {
|
|
23
|
+
for (let j = 0; j < 20; j++) {
|
|
24
|
+
ckmv3[i][j] = ckmv[MOVE3_STD[i]][MOVE3_STD[j]];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export const skipAxis3 = new Array(20).fill(20);
|
|
28
|
+
for (let i = 0; i < 20; i++) {
|
|
29
|
+
for (let j = i; j < 20; j++) {
|
|
30
|
+
if (!ckmv3[i][j]) {
|
|
31
|
+
skipAxis3[i] = j - 1;
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Fast table-driven Phase 3 IDA* solver class.
|
|
38
|
+
*/
|
|
39
|
+
export class FastPhase3Solver {
|
|
40
|
+
tempep;
|
|
41
|
+
moveLog;
|
|
42
|
+
sym;
|
|
43
|
+
constructor() {
|
|
44
|
+
this.tempep = Array.from({ length: 30 }, () => new Uint8Array(12));
|
|
45
|
+
this.moveLog = new Array(30);
|
|
46
|
+
this.sym = getSymTables();
|
|
47
|
+
}
|
|
48
|
+
getPrun(distances, idx) {
|
|
49
|
+
const byte = distances[idx >> 1];
|
|
50
|
+
return (idx & 1) === 0 ? byte & 0x0f : byte >> 4;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Recursive IDA* worker on table transitions.
|
|
54
|
+
*/
|
|
55
|
+
async search(eplast, ct, prun, maxl, lm, depth, center3Tables, edge3Distances) {
|
|
56
|
+
if (maxl === 0)
|
|
57
|
+
return true;
|
|
58
|
+
if (depth % 500 === 0)
|
|
59
|
+
await new Promise(r => setTimeout(r, 0));
|
|
60
|
+
const ep = this.tempep[depth];
|
|
61
|
+
if (lm !== 20) {
|
|
62
|
+
const movoBase = (lm << 3) * 12;
|
|
63
|
+
const movBase = (lm << 3) * 12;
|
|
64
|
+
for (let i = 0; i < 12; i++) {
|
|
65
|
+
ep[i] = this.sym.mvroto[movoBase + eplast[this.sym.mvrot[movBase + i]]];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
ep.set(eplast);
|
|
70
|
+
}
|
|
71
|
+
for (let m = 0; m < 17; m++) {
|
|
72
|
+
if (ckmv3[lm][m]) {
|
|
73
|
+
m = skipAxis3[m];
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const nc = center3Tables.moveTable[ct * CENTER3_MOVES.length + m];
|
|
77
|
+
if (nc < 0)
|
|
78
|
+
continue;
|
|
79
|
+
const prun1 = center3Tables.pruning[nc];
|
|
80
|
+
if (prun1 >= maxl) {
|
|
81
|
+
if (prun1 > maxl && m < 14)
|
|
82
|
+
m = skipAxis3[m];
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const symIdx = getMvSymFrom(ep, ep, m, this.sym.sym2Raw, this.sym.sym2Mask, this.sym.raw2Sym, this.sym.mvrot, this.sym.mvroto);
|
|
86
|
+
const prunx = this.getPrun(edge3Distances, symIdx >> 3);
|
|
87
|
+
if (prunx >= maxl) {
|
|
88
|
+
if (prunx > maxl && m < 14)
|
|
89
|
+
m = skipAxis3[m];
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (await this.search(ep, nc, prunx, maxl - 1, m, depth + 1, center3Tables, edge3Distances)) {
|
|
93
|
+
this.moveLog[depth] = m;
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Search for a phase-3 solution with exact target length.
|
|
101
|
+
*/
|
|
102
|
+
async solveLength(edge, ct3, edist, length3, center3Tables, edge3Distances) {
|
|
103
|
+
this.tempep[0].set(edge);
|
|
104
|
+
if (await this.search(this.tempep[0], ct3, edist, length3, 20, 0, center3Tables, edge3Distances)) {
|
|
105
|
+
return this.moveLog.slice(0, length3).map((m) => SEARCH_MOVES[m]);
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const defaultSolver = new FastPhase3Solver();
|
|
111
|
+
/**
|
|
112
|
+
* Solve Phase 3 with iterative deepening on a single Phase3Start.
|
|
113
|
+
*/
|
|
114
|
+
export async function solvePhase3(start, center3Tables, edge3Tables, maxLength = 20) {
|
|
115
|
+
const sym = getSymTables();
|
|
116
|
+
const symIdx = getMvSymFrom(start.edge, start.edge, 20, sym.sym2Raw, sym.sym2Mask, sym.raw2Sym, sym.mvrot, sym.mvroto);
|
|
117
|
+
const edist = (edge3Tables.distances[(symIdx >> 3) >> 1] >> (((symIdx >> 3) & 1) * 4)) & 0x0f;
|
|
118
|
+
const cdist = center3Tables.pruning[start.ct3];
|
|
119
|
+
if (cdist === 0xff || edist === 0x0f)
|
|
120
|
+
return null;
|
|
121
|
+
const minLength = Math.max(cdist, edist);
|
|
122
|
+
for (let l = minLength; l <= maxLength; l++) {
|
|
123
|
+
const moves = await defaultSolver.solveLength(start.edge, start.ct3, edist, l, center3Tables, edge3Tables.distances);
|
|
124
|
+
if (moves) {
|
|
125
|
+
return {
|
|
126
|
+
moves,
|
|
127
|
+
ct3: 0,
|
|
128
|
+
edge: Uint8Array.from({ length: 12 }, (_, i) => i)
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase-2 → phase-3 bridge + the joint phase-3 search (Center3 + Edge3).
|
|
3
|
+
*
|
|
4
|
+
* The phase-3 pairing coordinate requires the wing transversal: the phase-2
|
|
5
|
+
* solutions' end states are otherwise almost never eligible (~0.15% of
|
|
6
|
+
* states, and the T-space is closed under the phase-3 moves), so the bridge
|
|
7
|
+
* runs the phase-2 search with the wings tracked and the transversal checked
|
|
8
|
+
* at the goal (see `solveCenter2Gated`). For the rare states where one
|
|
9
|
+
* phase-1 solution offers no eligible end, the bridge retries with the
|
|
10
|
+
* alternative phase-1 solutions (their outputs differ in the wings).
|
|
11
|
+
*/
|
|
12
|
+
import type { Cube4State, Cube4Move } from "../types.js";
|
|
13
|
+
import { type Center1Tables } from "./center1.js";
|
|
14
|
+
import { type Center2Tables } from "./center2.js";
|
|
15
|
+
import { type Center3Tables } from "./center3.js";
|
|
16
|
+
export interface Phase3Start {
|
|
17
|
+
/** The gated phase-2 end state (its wings are transversal-eligible). */
|
|
18
|
+
state: Cube4State;
|
|
19
|
+
/** Center3 coordinate (parity bit included). */
|
|
20
|
+
ct3: number;
|
|
21
|
+
/** Pairing permutation (normalized, edgeo = identity). */
|
|
22
|
+
edge: Uint8Array;
|
|
23
|
+
/** The $set_6 pairing parity XOR corner parity. */
|
|
24
|
+
edgeXorCornerParity: 0 | 1;
|
|
25
|
+
/** The accumulated phase-1 + alignment + phase-2 moves. */
|
|
26
|
+
moves: Cube4Move[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Build the phase-3 start from the ORIGINAL scramble state. Solves phase 1
|
|
30
|
+
* (with alternatives), aligns, and runs the gated phase-2 search.
|
|
31
|
+
* Returns null if the enumeration budget is exhausted.
|
|
32
|
+
*/
|
|
33
|
+
export declare function bridgePhase2To3(randomState: Cube4State, center1Tables: Center1Tables, center2Tables: Center2Tables, center3Tables: Center3Tables, urf?: 0 | 1 | 2, phase1Cap?: number, maxLength?: number): Phase3Start | null;
|
|
34
|
+
//# sourceMappingURL=phase3.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phase3.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/phase3.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEzD,OAAO,EAAmB,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AACnE,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAGjE,MAAM,WAAW,WAAW;IAC1B,wEAAwE;IACxE,KAAK,EAAE,UAAU,CAAC;IAClB,gDAAgD;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,0DAA0D;IAC1D,IAAI,EAAE,UAAU,CAAC;IACjB,mDAAmD;IACnD,mBAAmB,EAAE,CAAC,GAAG,CAAC,CAAC;IAC3B,2DAA2D;IAC3D,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB;AA0DD;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,WAAW,EAAE,UAAU,EACvB,aAAa,EAAE,aAAa,EAC5B,aAAa,EAAE,aAAa,EAC5B,aAAa,EAAE,aAAa,EAC5B,GAAG,GAAE,CAAC,GAAG,CAAC,GAAG,CAAK,EAClB,SAAS,SAAK,EACd,SAAS,SAAK,GACb,WAAW,GAAG,IAAI,CAoBpB"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase-2 → phase-3 bridge + the joint phase-3 search (Center3 + Edge3).
|
|
3
|
+
*
|
|
4
|
+
* The phase-3 pairing coordinate requires the wing transversal: the phase-2
|
|
5
|
+
* solutions' end states are otherwise almost never eligible (~0.15% of
|
|
6
|
+
* states, and the T-space is closed under the phase-3 moves), so the bridge
|
|
7
|
+
* runs the phase-2 search with the wings tracked and the transversal checked
|
|
8
|
+
* at the goal (see `solveCenter2Gated`). For the rare states where one
|
|
9
|
+
* phase-1 solution offers no eligible end, the bridge retries with the
|
|
10
|
+
* alternative phase-1 solutions (their outputs differ in the wings).
|
|
11
|
+
*/
|
|
12
|
+
import { applyCube4Algorithm } from "../moves.js";
|
|
13
|
+
import { solveCenter1All } from "./center1.js";
|
|
14
|
+
import { center2Bridge, solveCenter2Gated } from "./center2.js";
|
|
15
|
+
import { encodeCenter3 } from "./center3.js";
|
|
16
|
+
import { encodePairingState, hasPairingTransversal, pairingGateParity } from "./pairing-coordinate.js";
|
|
17
|
+
function cornerPermutationParity(cp) {
|
|
18
|
+
let parity = 0;
|
|
19
|
+
const seen = new Array(8).fill(false);
|
|
20
|
+
for (let start = 0; start < 8; start++) {
|
|
21
|
+
if (seen[start] || cp[start] === start)
|
|
22
|
+
continue;
|
|
23
|
+
let length = 0;
|
|
24
|
+
let current = start;
|
|
25
|
+
while (!seen[current]) {
|
|
26
|
+
seen[current] = true;
|
|
27
|
+
current = cp[current];
|
|
28
|
+
length++;
|
|
29
|
+
}
|
|
30
|
+
parity ^= (length - 1) & 1;
|
|
31
|
+
}
|
|
32
|
+
return parity;
|
|
33
|
+
}
|
|
34
|
+
function gateEligibleWp(wp) {
|
|
35
|
+
return hasPairingTransversal(wp) && pairingGateParity(wp) === 0;
|
|
36
|
+
}
|
|
37
|
+
function buildPhase3Start(state, moves) {
|
|
38
|
+
const pairing = encodePairingState(state.wp);
|
|
39
|
+
const cornerParity = cornerPermutationParity(state.cp);
|
|
40
|
+
const eXc = (pairing.parity ^ cornerParity);
|
|
41
|
+
const ct3 = encodeCenter3(state, eXc);
|
|
42
|
+
return {
|
|
43
|
+
state,
|
|
44
|
+
ct3: ct3.coordinate,
|
|
45
|
+
edge: pairing.edge,
|
|
46
|
+
edgeXorCornerParity: eXc,
|
|
47
|
+
moves
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/** Try one aligned phase-2 start with the gated search. */
|
|
51
|
+
function tryPhase2Start(aligned, accumulated, center2Tables, center3Tables, maxLength) {
|
|
52
|
+
const result = solveCenter2Gated(aligned.encoded.ct, aligned.encoded.rl, aligned.state.wp, center2Tables, gateEligibleWp, maxLength);
|
|
53
|
+
if (result === null || result.wp === undefined)
|
|
54
|
+
return null;
|
|
55
|
+
const endState = applyCube4Algorithm(aligned.state, result.moves);
|
|
56
|
+
return buildPhase3Start(endState, [...accumulated, ...result.moves]);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Build the phase-3 start from the ORIGINAL scramble state. Solves phase 1
|
|
60
|
+
* (with alternatives), aligns, and runs the gated phase-2 search.
|
|
61
|
+
* Returns null if the enumeration budget is exhausted.
|
|
62
|
+
*/
|
|
63
|
+
export function bridgePhase2To3(randomState, center1Tables, center2Tables, center3Tables, urf = 0, phase1Cap = 50, maxLength = 30) {
|
|
64
|
+
const phase1Solutions = solveCenter1All(randomState.centers, urf, center1Tables, phase1Cap);
|
|
65
|
+
const alignments = [
|
|
66
|
+
[],
|
|
67
|
+
["Fw", "Bw'"],
|
|
68
|
+
["Uw", "Dw'"]
|
|
69
|
+
];
|
|
70
|
+
for (const solution of phase1Solutions) {
|
|
71
|
+
const output = applyCube4Algorithm(randomState, solution.moves);
|
|
72
|
+
for (const alignment of alignments) {
|
|
73
|
+
const alignedState = applyCube4Algorithm(output, alignment);
|
|
74
|
+
const aligned = center2Bridge(alignedState);
|
|
75
|
+
if (aligned === null)
|
|
76
|
+
continue;
|
|
77
|
+
const start = tryPhase2Start(aligned, [...solution.moves, ...alignment], center2Tables, center3Tables, maxLength);
|
|
78
|
+
if (start !== null)
|
|
79
|
+
return start;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 4: reduce the solved-centers + paired-wings 4x4 state to a 3x3
|
|
3
|
+
* cubie state and solve it with the existing cube3 two-phase solver.
|
|
4
|
+
*
|
|
5
|
+
* The corner permutation/orientation carry over directly (same numbering as
|
|
6
|
+
* the cube3). The 12 paired edges: each slot holds one 3x3 edge; the edge
|
|
7
|
+
* permutation = the pair id and the orientation = the chirality flip (which
|
|
8
|
+
* wing of the pair sits at the slot's first-half position).
|
|
9
|
+
*/
|
|
10
|
+
import type { Cube4State, Cube4Move } from "../types.js";
|
|
11
|
+
import type { Cube3State, Cube3Move } from "../../cube3/types.js";
|
|
12
|
+
export interface Cube4ReductionError {
|
|
13
|
+
/** All 12 slots paired? */
|
|
14
|
+
wingsPaired: boolean;
|
|
15
|
+
/** All 6 faces uniform? */
|
|
16
|
+
centersSolved: boolean;
|
|
17
|
+
}
|
|
18
|
+
/** Verify the reduction preconditions (the reference's `to333Facelet` check). */
|
|
19
|
+
export declare function checkCube4Reduction(state: Cube4State): Cube4ReductionError | null;
|
|
20
|
+
/**
|
|
21
|
+
* Build the 3x3 cubie state from a reduced 4x4 state. Returns null if the
|
|
22
|
+
* reduction preconditions are not met.
|
|
23
|
+
*/
|
|
24
|
+
export declare function toCube3State(state: Cube4State): Cube3State | null;
|
|
25
|
+
/**
|
|
26
|
+
* Full solve: run the reduction phases (1-3) + the 3x3 finish. The caller
|
|
27
|
+
* provides the phase-3 solution; this applies it and finishes with the cube3
|
|
28
|
+
* solver, returning the complete move sequence.
|
|
29
|
+
*/
|
|
30
|
+
export declare function solveReduced4x4(phase3End: Cube4State, phase3Moves: readonly Cube4Move[], solveCube3: (state: Cube3State) => Promise<{
|
|
31
|
+
moves: Cube3Move[];
|
|
32
|
+
}>): Promise<{
|
|
33
|
+
moves: Cube4Move[];
|
|
34
|
+
}>;
|
|
35
|
+
//# sourceMappingURL=phase4.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phase4.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/phase4.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAkBlE,MAAM,WAAW,mBAAmB;IAClC,2BAA2B;IAC3B,WAAW,EAAE,OAAO,CAAC;IACrB,2BAA2B;IAC3B,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,iFAAiF;AACjF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,mBAAmB,GAAG,IAAI,CAejF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI,CAuBjE;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,UAAU,EACrB,WAAW,EAAE,SAAS,SAAS,EAAE,EACjC,UAAU,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC;IAAE,KAAK,EAAE,SAAS,EAAE,CAAA;CAAE,CAAC,GACjE,OAAO,CAAC;IAAE,KAAK,EAAE,SAAS,EAAE,CAAA;CAAE,CAAC,CAWjC"}
|