@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,294 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Pairing coordinate (phase 3 of the 4x4 reduction solver).
|
|
4
|
+
*
|
|
5
|
+
* Semantics ported from the reference `scramble_444.js` (Edge3 class) and
|
|
6
|
+
* adapted to this repo's position numbering.
|
|
7
|
+
*
|
|
8
|
+
* The pairing state is the *relative* permutation between the two halves of
|
|
9
|
+
* the edge slots: `edge[i]` = the slot whose second half holds the pair
|
|
10
|
+
* occupying slot i's first half. It exists (and `edge == identity` iff all
|
|
11
|
+
* 12 slots are paired) only for states with the *transversal* property:
|
|
12
|
+
* the 12 second-half positions must hold exactly one wing of each pair.
|
|
13
|
+
*
|
|
14
|
+
* Slot convention: the reference's `FullEdgeMap` slot layout, converted to
|
|
15
|
+
* this repo's position numbering (via the sticker isomorphism between the
|
|
16
|
+
* two layouts). It is co-adapted with the phase-3 move set — all 20 moves
|
|
17
|
+
* preserve the transversal property (verified: 0 breaks over random walks).
|
|
18
|
+
* A naive even/odd split does NOT work (Rw2/Lw2 break the transversal).
|
|
19
|
+
*
|
|
20
|
+
* The phase-3 move set contains ONLY even wing-permutation moves (face
|
|
21
|
+
* turns + double wide turns), so wing parity is invariant in this phase and
|
|
22
|
+
* is tracked by the caller (Center3 coordinate), never here.
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.PAIRING_SECOND_HALVES = exports.PAIRING_FIRST_HALVES = exports.PAIRING_SLOTS = exports.PAIRING_BFS_MOVES = exports.PAIRING_MOVES = void 0;
|
|
26
|
+
exports.pairIdArray = pairIdArray;
|
|
27
|
+
exports.applyPairingMoveToArray = applyPairingMoveToArray;
|
|
28
|
+
exports.hasPairingTransversal = hasPairingTransversal;
|
|
29
|
+
exports.pairingGateParity = pairingGateParity;
|
|
30
|
+
exports.encodePairingState = encodePairingState;
|
|
31
|
+
exports.decodePairingState = decodePairingState;
|
|
32
|
+
exports.isPairingReduced = isPairingReduced;
|
|
33
|
+
exports.applyPairingMoveToState = applyPairingMoveToState;
|
|
34
|
+
exports.normalizePairingState = normalizePairingState;
|
|
35
|
+
exports.verifyPairingCoordinate = verifyPairingCoordinate;
|
|
36
|
+
const types_js_1 = require("../types.js");
|
|
37
|
+
const moves_js_1 = require("../moves.js");
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// Move sets
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
/**
|
|
42
|
+
* Phase-3 move set (20 moves, the reference's `move3std` minus its trailing
|
|
43
|
+
* no-op sentinel — index 36, used only as the "previous move" dummy for
|
|
44
|
+
* cancellation pruning): face turns + double wide turns. All of them are
|
|
45
|
+
* even wing permutations.
|
|
46
|
+
*/
|
|
47
|
+
exports.PAIRING_MOVES = [
|
|
48
|
+
"U", "U2", "U'", "R2", "F", "F2", "F'", "D", "D2", "D'",
|
|
49
|
+
"L2", "B", "B2", "B'", "Uw2", "Rw2", "Fw2", "Dw2", "Lw2", "Bw2"
|
|
50
|
+
];
|
|
51
|
+
/**
|
|
52
|
+
* The 17-move BFS subset used to build the pruning table (reference
|
|
53
|
+
* `bfsMoves`): omits Dw2/Lw2/Bw2 (rotation-conjugate to Uw2/Rw2/Fw2).
|
|
54
|
+
*/
|
|
55
|
+
exports.PAIRING_BFS_MOVES = [
|
|
56
|
+
"U2", "U", "U'", "R2", "F2", "F", "F'", "D2", "D", "D'",
|
|
57
|
+
"L2", "B2", "B", "B'", "Uw2", "Rw2", "Fw2"
|
|
58
|
+
];
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
// Slot layout
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
/**
|
|
63
|
+
* The 12 edge slots as (first-half, second-half) position pairs, in this
|
|
64
|
+
* repo's position numbering. Derived from the reference's `FullEdgeMap`
|
|
65
|
+
* through the sticker isomorphism between the two layouts:
|
|
66
|
+
*
|
|
67
|
+
* slot i = (π(FullEdgeMap[i]), π(FullEdgeMap[i] + 12))
|
|
68
|
+
*
|
|
69
|
+
* where π maps the reference's position ids to ours by matching the two
|
|
70
|
+
* stickers of each wing position. The phase-3 move set preserves the
|
|
71
|
+
* transversal property for exactly this split.
|
|
72
|
+
*/
|
|
73
|
+
exports.PAIRING_SLOTS = [
|
|
74
|
+
[3, 2], [7, 6], [14, 15], [10, 11], [5, 4], [0, 1],
|
|
75
|
+
[9, 8], [12, 13], [19, 18], [20, 21], [23, 22], [16, 17]
|
|
76
|
+
];
|
|
77
|
+
/** First-half position of each slot. */
|
|
78
|
+
exports.PAIRING_FIRST_HALVES = exports.PAIRING_SLOTS.map(([f]) => f);
|
|
79
|
+
/** Second-half position of each slot. */
|
|
80
|
+
exports.PAIRING_SECOND_HALVES = exports.PAIRING_SLOTS.map(([, s]) => s);
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
// Pair-id array (ground truth of the pairing state)
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
/**
|
|
85
|
+
* Map the 24 labeled wings to their pair ids: `arr24[i] = wp[i] >> 1`.
|
|
86
|
+
* Each pair id 0-11 appears exactly twice.
|
|
87
|
+
*/
|
|
88
|
+
function pairIdArray(wp) {
|
|
89
|
+
const arr = new Uint8Array(24);
|
|
90
|
+
for (let i = 0; i < 24; i++)
|
|
91
|
+
arr[i] = wp[i] >> 1;
|
|
92
|
+
return arr;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Apply a move to a 24-entry pair-id array (position permutation from the
|
|
96
|
+
* verified move tables).
|
|
97
|
+
*/
|
|
98
|
+
function applyPairingMoveToArray(arr24, move) {
|
|
99
|
+
const { face, turns } = (0, moves_js_1.parseMove)(move);
|
|
100
|
+
const effect = moves_js_1.FACE_MOVE_EFFECTS[face];
|
|
101
|
+
let current = arr24;
|
|
102
|
+
for (let t = 0; t < turns; t++) {
|
|
103
|
+
const next = new Uint8Array(24);
|
|
104
|
+
for (let i = 0; i < 24; i++)
|
|
105
|
+
next[i] = current[effect.wperm[i]];
|
|
106
|
+
current = next;
|
|
107
|
+
}
|
|
108
|
+
return current;
|
|
109
|
+
}
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
// Transversal gate
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
/**
|
|
114
|
+
* True iff the 12 second-half positions hold exactly one wing of each pair —
|
|
115
|
+
* i.e. the pair ids there are all distinct. Required by `encodePairingState`
|
|
116
|
+
* and preserved by the phase-3 move set (verified).
|
|
117
|
+
*/
|
|
118
|
+
function hasPairingTransversal(wp) {
|
|
119
|
+
const seen = new Uint8Array(12);
|
|
120
|
+
for (const s of exports.PAIRING_SECOND_HALVES) {
|
|
121
|
+
const pair = wp[s] >> 1;
|
|
122
|
+
if (seen[pair] !== 0)
|
|
123
|
+
return false;
|
|
124
|
+
seen[pair] = 1;
|
|
125
|
+
}
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Wing-permutation parity of the state. For transversal-eligible states this
|
|
130
|
+
* is the phase-2 → phase-3 filter parity (the reference's `checkPhase2Edge`
|
|
131
|
+
* `parity == 0` condition, verified equivalent): the bridge must only accept
|
|
132
|
+
* even-parity states so the derived 3x3 edge permutation comes out even.
|
|
133
|
+
*/
|
|
134
|
+
function pairingGateParity(wp) {
|
|
135
|
+
let parity = 0;
|
|
136
|
+
const seen = new Array(24).fill(false);
|
|
137
|
+
for (let start = 0; start < 24; start++) {
|
|
138
|
+
if (seen[start] || wp[start] === start)
|
|
139
|
+
continue;
|
|
140
|
+
let length = 0;
|
|
141
|
+
let current = start;
|
|
142
|
+
while (!seen[current]) {
|
|
143
|
+
seen[current] = true;
|
|
144
|
+
current = wp[current];
|
|
145
|
+
length++;
|
|
146
|
+
}
|
|
147
|
+
parity ^= (length - 1) & 1;
|
|
148
|
+
}
|
|
149
|
+
return parity;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Encode a 24-labeled-wing state to the pairing permutation.
|
|
153
|
+
*
|
|
154
|
+
* Requires the transversal property (`hasPairingTransversal`), else null.
|
|
155
|
+
*
|
|
156
|
+
* Construction (reference $set_6, adapted to PAIRING_SLOTS):
|
|
157
|
+
* - `edge[i]` starts as the pair id at the second-half position of slot i
|
|
158
|
+
* - cycle-sort it to identity while tracking the inverse `temp` and parity
|
|
159
|
+
* - `edge[i] = temp[pair id at the first-half position of slot i]`
|
|
160
|
+
*
|
|
161
|
+
* The result is in $std-normalized form (second halves implicitly hold
|
|
162
|
+
* pairs 0..11 in order).
|
|
163
|
+
*/
|
|
164
|
+
function encodePairingState(wp) {
|
|
165
|
+
if (!hasPairingTransversal(wp))
|
|
166
|
+
return null;
|
|
167
|
+
const temp = new Uint8Array(12);
|
|
168
|
+
const edge = new Uint8Array(12);
|
|
169
|
+
for (let i = 0; i < 12; i++) {
|
|
170
|
+
temp[i] = i;
|
|
171
|
+
edge[i] = wp[exports.PAIRING_SECOND_HALVES[i]] >> 1;
|
|
172
|
+
}
|
|
173
|
+
let parity = 1;
|
|
174
|
+
for (let i = 0; i < 12; i++) {
|
|
175
|
+
while (edge[i] !== i) {
|
|
176
|
+
const t = edge[i];
|
|
177
|
+
edge[i] = edge[t];
|
|
178
|
+
edge[t] = t;
|
|
179
|
+
const s = temp[i];
|
|
180
|
+
temp[i] = temp[t];
|
|
181
|
+
temp[t] = s;
|
|
182
|
+
parity ^= 1;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
for (let i = 0; i < 12; i++) {
|
|
186
|
+
edge[i] = temp[wp[exports.PAIRING_FIRST_HALVES[i]] >> 1];
|
|
187
|
+
}
|
|
188
|
+
return { edge, parity: parity };
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Decode the canonical representative: slot i's first-half position holds the
|
|
192
|
+
* even wing of pair `edge[i]`, its second-half position the odd wing of pair
|
|
193
|
+
* `edgeo[i]`. With `edgeo = identity` this is the $set_4 canonical form.
|
|
194
|
+
*/
|
|
195
|
+
function decodePairingState(edge, edgeo) {
|
|
196
|
+
const wp = new Uint8Array(24);
|
|
197
|
+
for (let i = 0; i < 12; i++) {
|
|
198
|
+
wp[exports.PAIRING_FIRST_HALVES[i]] = 2 * edge[i];
|
|
199
|
+
wp[exports.PAIRING_SECOND_HALVES[i]] = 2 * edgeo[i] + 1;
|
|
200
|
+
}
|
|
201
|
+
return wp;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* True iff the pairing permutation is fully reduced (all 12 slots paired).
|
|
205
|
+
*/
|
|
206
|
+
function isPairingReduced(edge) {
|
|
207
|
+
for (let i = 0; i < 12; i++) {
|
|
208
|
+
if (edge[i] !== i)
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Apply a phase-3 move to a pairing state, keeping the (edge, edgeo)
|
|
215
|
+
* representation: decode → physical move (verified move tables) → re-derive
|
|
216
|
+
* the slot halves → $std-normalize. Valid because the phase-3 move set
|
|
217
|
+
* preserves the transversal property (verified).
|
|
218
|
+
*/
|
|
219
|
+
function applyPairingMoveToState(edge, edgeo, move) {
|
|
220
|
+
const wp = decodePairingState(edge, edgeo);
|
|
221
|
+
const arr24 = applyPairingMoveToArray(pairIdArray(wp), move);
|
|
222
|
+
const e = new Uint8Array(12);
|
|
223
|
+
const eo = new Uint8Array(12);
|
|
224
|
+
for (let i = 0; i < 12; i++) {
|
|
225
|
+
e[i] = arr24[exports.PAIRING_FIRST_HALVES[i]];
|
|
226
|
+
eo[i] = arr24[exports.PAIRING_SECOND_HALVES[i]];
|
|
227
|
+
}
|
|
228
|
+
return normalizePairingState(e, eo);
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* $std-normalize a pairing state: reindex so the second halves hold pairs
|
|
232
|
+
* 0..11 in order (the `edgeo` array becomes the identity).
|
|
233
|
+
*/
|
|
234
|
+
function normalizePairingState(edge, edgeo) {
|
|
235
|
+
const temp = new Uint8Array(12);
|
|
236
|
+
for (let i = 0; i < 12; i++)
|
|
237
|
+
temp[edgeo[i]] = i;
|
|
238
|
+
const out = new Uint8Array(12);
|
|
239
|
+
const identity = new Uint8Array(12);
|
|
240
|
+
for (let i = 0; i < 12; i++) {
|
|
241
|
+
out[i] = temp[edge[i]];
|
|
242
|
+
identity[i] = i;
|
|
243
|
+
}
|
|
244
|
+
return { edge: out, edgeo: identity };
|
|
245
|
+
}
|
|
246
|
+
// ---------------------------------------------------------------------------
|
|
247
|
+
// Verification gates
|
|
248
|
+
// ---------------------------------------------------------------------------
|
|
249
|
+
function verifyPairingCoordinate() {
|
|
250
|
+
const errors = [];
|
|
251
|
+
for (const move of exports.PAIRING_MOVES) {
|
|
252
|
+
if (!types_js_1.CUBE4_ALL_MOVES.includes(move)) {
|
|
253
|
+
errors.push(`Pairing move ${move} is not a valid 4x4 move.`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// The slot halves must be a bijection over the 24 positions.
|
|
257
|
+
const seen = new Set();
|
|
258
|
+
for (const [f, s] of exports.PAIRING_SLOTS) {
|
|
259
|
+
if (f < 0 || f > 23 || s < 0 || s > 23)
|
|
260
|
+
errors.push(`Slot out of range: (${f}, ${s}).`);
|
|
261
|
+
seen.add(f);
|
|
262
|
+
seen.add(s);
|
|
263
|
+
}
|
|
264
|
+
if (seen.size !== 24) {
|
|
265
|
+
errors.push(`Pairing slots do not cover the 24 positions (${seen.size} distinct).`);
|
|
266
|
+
}
|
|
267
|
+
// All phase-3 moves must be even wing permutations (parity is invariant in
|
|
268
|
+
// this phase and tracked by the caller).
|
|
269
|
+
for (const move of exports.PAIRING_MOVES) {
|
|
270
|
+
const { face, turns } = (0, moves_js_1.parseMove)(move);
|
|
271
|
+
const parity = wingPermutationParity(moves_js_1.FACE_MOVE_EFFECTS[face].wperm) * (turns & 1);
|
|
272
|
+
if ((parity & 1) !== 0) {
|
|
273
|
+
errors.push(`Pairing move ${move} has odd wing permutation parity.`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return errors;
|
|
277
|
+
}
|
|
278
|
+
function wingPermutationParity(perm) {
|
|
279
|
+
let parity = 0;
|
|
280
|
+
const seen = new Array(perm.length).fill(false);
|
|
281
|
+
for (let start = 0; start < perm.length; start++) {
|
|
282
|
+
if (seen[start] || perm[start] === start)
|
|
283
|
+
continue;
|
|
284
|
+
let length = 0;
|
|
285
|
+
let current = start;
|
|
286
|
+
while (!seen[current]) {
|
|
287
|
+
seen[current] = true;
|
|
288
|
+
current = perm[current];
|
|
289
|
+
length++;
|
|
290
|
+
}
|
|
291
|
+
parity ^= (length - 1) & 1;
|
|
292
|
+
}
|
|
293
|
+
return parity;
|
|
294
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Fast table-driven Phase-3 search (Center3 + Edge3).
|
|
4
|
+
*
|
|
5
|
+
* Solves the joint state from Phase 2 to fully-solved monochrome centers (ct3 == 0)
|
|
6
|
+
* and all 12 wing pairs paired (sym-index == 0), using the 17-move exploration set.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.FastPhase3Solver = exports.skipAxis3 = exports.ckmv3 = exports.SEARCH_MOVES = void 0;
|
|
10
|
+
exports.solvePhase3 = solvePhase3;
|
|
11
|
+
const center3_js_1 = require("./center3.js");
|
|
12
|
+
const edge3_js_1 = require("./edge3.js");
|
|
13
|
+
/** The 17 exploration moves (the reference's m < 17 bound). */
|
|
14
|
+
exports.SEARCH_MOVES = center3_js_1.CENTER3_MOVES.slice(0, 17);
|
|
15
|
+
// Commutation / move-cancellation pruning tables
|
|
16
|
+
const ckmv = Array.from({ length: 37 }, () => new Array(36).fill(false));
|
|
17
|
+
for (let i = 0; i < 36; i++) {
|
|
18
|
+
for (let j = 0; j < 36; j++) {
|
|
19
|
+
ckmv[i][j] =
|
|
20
|
+
Math.floor(i / 3) === Math.floor(j / 3) ||
|
|
21
|
+
(Math.floor(i / 3) % 3 === Math.floor(j / 3) % 3 && i > j);
|
|
22
|
+
}
|
|
23
|
+
ckmv[36][i] = false;
|
|
24
|
+
}
|
|
25
|
+
exports.ckmv3 = Array.from({ length: 21 }, () => new Array(20).fill(false));
|
|
26
|
+
for (let i = 0; i < 21; i++) {
|
|
27
|
+
for (let j = 0; j < 20; j++) {
|
|
28
|
+
exports.ckmv3[i][j] = ckmv[edge3_js_1.MOVE3_STD[i]][edge3_js_1.MOVE3_STD[j]];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.skipAxis3 = new Array(20).fill(20);
|
|
32
|
+
for (let i = 0; i < 20; i++) {
|
|
33
|
+
for (let j = i; j < 20; j++) {
|
|
34
|
+
if (!exports.ckmv3[i][j]) {
|
|
35
|
+
exports.skipAxis3[i] = j - 1;
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Fast table-driven Phase 3 IDA* solver class.
|
|
42
|
+
*/
|
|
43
|
+
class FastPhase3Solver {
|
|
44
|
+
tempep;
|
|
45
|
+
moveLog;
|
|
46
|
+
sym;
|
|
47
|
+
constructor() {
|
|
48
|
+
this.tempep = Array.from({ length: 30 }, () => new Uint8Array(12));
|
|
49
|
+
this.moveLog = new Array(30);
|
|
50
|
+
this.sym = (0, edge3_js_1.getSymTables)();
|
|
51
|
+
}
|
|
52
|
+
getPrun(distances, idx) {
|
|
53
|
+
const byte = distances[idx >> 1];
|
|
54
|
+
return (idx & 1) === 0 ? byte & 0x0f : byte >> 4;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Recursive IDA* worker on table transitions.
|
|
58
|
+
*/
|
|
59
|
+
async search(eplast, ct, prun, maxl, lm, depth, center3Tables, edge3Distances) {
|
|
60
|
+
if (maxl === 0)
|
|
61
|
+
return true;
|
|
62
|
+
if (depth % 500 === 0)
|
|
63
|
+
await new Promise(r => setTimeout(r, 0));
|
|
64
|
+
const ep = this.tempep[depth];
|
|
65
|
+
if (lm !== 20) {
|
|
66
|
+
const movoBase = (lm << 3) * 12;
|
|
67
|
+
const movBase = (lm << 3) * 12;
|
|
68
|
+
for (let i = 0; i < 12; i++) {
|
|
69
|
+
ep[i] = this.sym.mvroto[movoBase + eplast[this.sym.mvrot[movBase + i]]];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
ep.set(eplast);
|
|
74
|
+
}
|
|
75
|
+
for (let m = 0; m < 17; m++) {
|
|
76
|
+
if (exports.ckmv3[lm][m]) {
|
|
77
|
+
m = exports.skipAxis3[m];
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const nc = center3Tables.moveTable[ct * center3_js_1.CENTER3_MOVES.length + m];
|
|
81
|
+
if (nc < 0)
|
|
82
|
+
continue;
|
|
83
|
+
const prun1 = center3Tables.pruning[nc];
|
|
84
|
+
if (prun1 >= maxl) {
|
|
85
|
+
if (prun1 > maxl && m < 14)
|
|
86
|
+
m = exports.skipAxis3[m];
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const symIdx = (0, edge3_js_1.getMvSymFrom)(ep, ep, m, this.sym.sym2Raw, this.sym.sym2Mask, this.sym.raw2Sym, this.sym.mvrot, this.sym.mvroto);
|
|
90
|
+
const prunx = this.getPrun(edge3Distances, symIdx >> 3);
|
|
91
|
+
if (prunx >= maxl) {
|
|
92
|
+
if (prunx > maxl && m < 14)
|
|
93
|
+
m = exports.skipAxis3[m];
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (await this.search(ep, nc, prunx, maxl - 1, m, depth + 1, center3Tables, edge3Distances)) {
|
|
97
|
+
this.moveLog[depth] = m;
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Search for a phase-3 solution with exact target length.
|
|
105
|
+
*/
|
|
106
|
+
async solveLength(edge, ct3, edist, length3, center3Tables, edge3Distances) {
|
|
107
|
+
this.tempep[0].set(edge);
|
|
108
|
+
if (await this.search(this.tempep[0], ct3, edist, length3, 20, 0, center3Tables, edge3Distances)) {
|
|
109
|
+
return this.moveLog.slice(0, length3).map((m) => exports.SEARCH_MOVES[m]);
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
exports.FastPhase3Solver = FastPhase3Solver;
|
|
115
|
+
const defaultSolver = new FastPhase3Solver();
|
|
116
|
+
/**
|
|
117
|
+
* Solve Phase 3 with iterative deepening on a single Phase3Start.
|
|
118
|
+
*/
|
|
119
|
+
async function solvePhase3(start, center3Tables, edge3Tables, maxLength = 20) {
|
|
120
|
+
const sym = (0, edge3_js_1.getSymTables)();
|
|
121
|
+
const symIdx = (0, edge3_js_1.getMvSymFrom)(start.edge, start.edge, 20, sym.sym2Raw, sym.sym2Mask, sym.raw2Sym, sym.mvrot, sym.mvroto);
|
|
122
|
+
const edist = (edge3Tables.distances[(symIdx >> 3) >> 1] >> (((symIdx >> 3) & 1) * 4)) & 0x0f;
|
|
123
|
+
const cdist = center3Tables.pruning[start.ct3];
|
|
124
|
+
if (cdist === 0xff || edist === 0x0f)
|
|
125
|
+
return null;
|
|
126
|
+
const minLength = Math.max(cdist, edist);
|
|
127
|
+
for (let l = minLength; l <= maxLength; l++) {
|
|
128
|
+
const moves = await defaultSolver.solveLength(start.edge, start.ct3, edist, l, center3Tables, edge3Tables.distances);
|
|
129
|
+
if (moves) {
|
|
130
|
+
return {
|
|
131
|
+
moves,
|
|
132
|
+
ct3: 0,
|
|
133
|
+
edge: Uint8Array.from({ length: 12 }, (_, i) => i)
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Phase-2 → phase-3 bridge + the joint phase-3 search (Center3 + Edge3).
|
|
4
|
+
*
|
|
5
|
+
* The phase-3 pairing coordinate requires the wing transversal: the phase-2
|
|
6
|
+
* solutions' end states are otherwise almost never eligible (~0.15% of
|
|
7
|
+
* states, and the T-space is closed under the phase-3 moves), so the bridge
|
|
8
|
+
* runs the phase-2 search with the wings tracked and the transversal checked
|
|
9
|
+
* at the goal (see `solveCenter2Gated`). For the rare states where one
|
|
10
|
+
* phase-1 solution offers no eligible end, the bridge retries with the
|
|
11
|
+
* alternative phase-1 solutions (their outputs differ in the wings).
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.bridgePhase2To3 = bridgePhase2To3;
|
|
15
|
+
const moves_js_1 = require("../moves.js");
|
|
16
|
+
const center1_js_1 = require("./center1.js");
|
|
17
|
+
const center2_js_1 = require("./center2.js");
|
|
18
|
+
const center3_js_1 = require("./center3.js");
|
|
19
|
+
const pairing_coordinate_js_1 = require("./pairing-coordinate.js");
|
|
20
|
+
function cornerPermutationParity(cp) {
|
|
21
|
+
let parity = 0;
|
|
22
|
+
const seen = new Array(8).fill(false);
|
|
23
|
+
for (let start = 0; start < 8; start++) {
|
|
24
|
+
if (seen[start] || cp[start] === start)
|
|
25
|
+
continue;
|
|
26
|
+
let length = 0;
|
|
27
|
+
let current = start;
|
|
28
|
+
while (!seen[current]) {
|
|
29
|
+
seen[current] = true;
|
|
30
|
+
current = cp[current];
|
|
31
|
+
length++;
|
|
32
|
+
}
|
|
33
|
+
parity ^= (length - 1) & 1;
|
|
34
|
+
}
|
|
35
|
+
return parity;
|
|
36
|
+
}
|
|
37
|
+
function gateEligibleWp(wp) {
|
|
38
|
+
return (0, pairing_coordinate_js_1.hasPairingTransversal)(wp) && (0, pairing_coordinate_js_1.pairingGateParity)(wp) === 0;
|
|
39
|
+
}
|
|
40
|
+
function buildPhase3Start(state, moves) {
|
|
41
|
+
const pairing = (0, pairing_coordinate_js_1.encodePairingState)(state.wp);
|
|
42
|
+
const cornerParity = cornerPermutationParity(state.cp);
|
|
43
|
+
const eXc = (pairing.parity ^ cornerParity);
|
|
44
|
+
const ct3 = (0, center3_js_1.encodeCenter3)(state, eXc);
|
|
45
|
+
return {
|
|
46
|
+
state,
|
|
47
|
+
ct3: ct3.coordinate,
|
|
48
|
+
edge: pairing.edge,
|
|
49
|
+
edgeXorCornerParity: eXc,
|
|
50
|
+
moves
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** Try one aligned phase-2 start with the gated search. */
|
|
54
|
+
function tryPhase2Start(aligned, accumulated, center2Tables, center3Tables, maxLength) {
|
|
55
|
+
const result = (0, center2_js_1.solveCenter2Gated)(aligned.encoded.ct, aligned.encoded.rl, aligned.state.wp, center2Tables, gateEligibleWp, maxLength);
|
|
56
|
+
if (result === null || result.wp === undefined)
|
|
57
|
+
return null;
|
|
58
|
+
const endState = (0, moves_js_1.applyCube4Algorithm)(aligned.state, result.moves);
|
|
59
|
+
return buildPhase3Start(endState, [...accumulated, ...result.moves]);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Build the phase-3 start from the ORIGINAL scramble state. Solves phase 1
|
|
63
|
+
* (with alternatives), aligns, and runs the gated phase-2 search.
|
|
64
|
+
* Returns null if the enumeration budget is exhausted.
|
|
65
|
+
*/
|
|
66
|
+
function bridgePhase2To3(randomState, center1Tables, center2Tables, center3Tables, urf = 0, phase1Cap = 50, maxLength = 30) {
|
|
67
|
+
const phase1Solutions = (0, center1_js_1.solveCenter1All)(randomState.centers, urf, center1Tables, phase1Cap);
|
|
68
|
+
const alignments = [
|
|
69
|
+
[],
|
|
70
|
+
["Fw", "Bw'"],
|
|
71
|
+
["Uw", "Dw'"]
|
|
72
|
+
];
|
|
73
|
+
for (const solution of phase1Solutions) {
|
|
74
|
+
const output = (0, moves_js_1.applyCube4Algorithm)(randomState, solution.moves);
|
|
75
|
+
for (const alignment of alignments) {
|
|
76
|
+
const alignedState = (0, moves_js_1.applyCube4Algorithm)(output, alignment);
|
|
77
|
+
const aligned = (0, center2_js_1.center2Bridge)(alignedState);
|
|
78
|
+
if (aligned === null)
|
|
79
|
+
continue;
|
|
80
|
+
const start = tryPhase2Start(aligned, [...solution.moves, ...alignment], center2Tables, center3Tables, maxLength);
|
|
81
|
+
if (start !== null)
|
|
82
|
+
return start;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Phase 4: reduce the solved-centers + paired-wings 4x4 state to a 3x3
|
|
4
|
+
* cubie state and solve it with the existing cube3 two-phase solver.
|
|
5
|
+
*
|
|
6
|
+
* The corner permutation/orientation carry over directly (same numbering as
|
|
7
|
+
* the cube3). The 12 paired edges: each slot holds one 3x3 edge; the edge
|
|
8
|
+
* permutation = the pair id and the orientation = the chirality flip (which
|
|
9
|
+
* wing of the pair sits at the slot's first-half position).
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.checkCube4Reduction = checkCube4Reduction;
|
|
13
|
+
exports.toCube3State = toCube3State;
|
|
14
|
+
exports.solveReduced4x4 = solveReduced4x4;
|
|
15
|
+
const moves_js_1 = require("../moves.js");
|
|
16
|
+
const pairing_coordinate_js_1 = require("./pairing-coordinate.js");
|
|
17
|
+
// The "right-chiral" wing/position classes (same set as facelets.ts).
|
|
18
|
+
const RIGHT_GROUP = new Set([1, 2, 4, 6, 8, 11, 13, 15, 16, 19, 20, 23]);
|
|
19
|
+
// For each 3x3 edge position p (positions 2p, 2p+1), the pairing slot whose
|
|
20
|
+
// first-half position belongs to that pair.
|
|
21
|
+
const SLOT_OF_EDGE = (() => {
|
|
22
|
+
const table = new Array(12);
|
|
23
|
+
for (let slot = 0; slot < 12; slot++) {
|
|
24
|
+
const pair = pairing_coordinate_js_1.PAIRING_SLOTS[slot][0] >> 1;
|
|
25
|
+
table[pair] = slot;
|
|
26
|
+
}
|
|
27
|
+
return table;
|
|
28
|
+
})();
|
|
29
|
+
/** Verify the reduction preconditions (the reference's `to333Facelet` check). */
|
|
30
|
+
function checkCube4Reduction(state) {
|
|
31
|
+
for (let face = 0; face < 6; face++) {
|
|
32
|
+
const color = state.centers[face * 4];
|
|
33
|
+
for (let i = 1; i < 4; i++) {
|
|
34
|
+
if (state.centers[face * 4 + i] !== color) {
|
|
35
|
+
return { wingsPaired: false, centersSolved: false };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
for (const [f, s] of pairing_coordinate_js_1.PAIRING_SLOTS) {
|
|
40
|
+
if ((state.wp[f] >> 1) !== (state.wp[s] >> 1)) {
|
|
41
|
+
return { wingsPaired: false, centersSolved: true };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Build the 3x3 cubie state from a reduced 4x4 state. Returns null if the
|
|
48
|
+
* reduction preconditions are not met.
|
|
49
|
+
*/
|
|
50
|
+
function toCube3State(state) {
|
|
51
|
+
if (checkCube4Reduction(state) !== null)
|
|
52
|
+
return null;
|
|
53
|
+
const edges = { permutation: new Array(12), orientation: new Array(12) };
|
|
54
|
+
for (let edge = 0; edge < 12; edge++) {
|
|
55
|
+
// the 3x3 edge positions are the position pairs (2p, 2p+1); the slot of
|
|
56
|
+
// the edge p is the slot whose first-half position belongs to that pair.
|
|
57
|
+
const slot = SLOT_OF_EDGE[edge];
|
|
58
|
+
const first = pairing_coordinate_js_1.PAIRING_SLOTS[slot][0];
|
|
59
|
+
const wing = state.wp[first];
|
|
60
|
+
edges.permutation[edge] = wing >> 1;
|
|
61
|
+
// the chirality flip: the derived edge is flipped when the wing and the
|
|
62
|
+
// slot-half have opposite chirality (the facelets.ts RIGHT_GROUP rule)
|
|
63
|
+
edges.orientation[edge] = RIGHT_GROUP.has(wing) !== RIGHT_GROUP.has(first) ? 1 : 0;
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
corners: {
|
|
67
|
+
permutation: Array.from(state.cp),
|
|
68
|
+
orientation: Array.from(state.co)
|
|
69
|
+
},
|
|
70
|
+
edges
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Full solve: run the reduction phases (1-3) + the 3x3 finish. The caller
|
|
75
|
+
* provides the phase-3 solution; this applies it and finishes with the cube3
|
|
76
|
+
* solver, returning the complete move sequence.
|
|
77
|
+
*/
|
|
78
|
+
async function solveReduced4x4(phase3End, phase3Moves, solveCube3) {
|
|
79
|
+
const state = (0, moves_js_1.applyCube4Algorithm)(phase3End, phase3Moves);
|
|
80
|
+
const reductionError = checkCube4Reduction(state);
|
|
81
|
+
if (reductionError !== null) {
|
|
82
|
+
throw new Error(`4x4 reduction failed: wingsPaired=${reductionError.wingsPaired} centersSolved=${reductionError.centersSolved}`);
|
|
83
|
+
}
|
|
84
|
+
const cube3State = toCube3State(state);
|
|
85
|
+
const solution = await solveCube3(cube3State);
|
|
86
|
+
return { moves: [...phase3Moves, ...solution.moves] };
|
|
87
|
+
}
|