@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,511 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edge3 (the pairing pruning table for the phase-3 search).
|
|
3
|
+
*
|
|
4
|
+
* The BFS from the solved pairing state over the 17 BFS moves, canonicalized
|
|
5
|
+
* by the 8 rotation symmetries of the reference's Edge3 phase (`$rot_1` /
|
|
6
|
+
* `$rotate_0` decomposition with the `$circlex` / `$swapx` coordinate
|
|
7
|
+
* effects). The pairing state is the relative 12-permutation between the
|
|
8
|
+
* two halves of the edge slots (see `pairing-coordinate.ts`), stored in the
|
|
9
|
+
* reference's `(edge, edgeo)` form with shared labels and `$std`-normalized
|
|
10
|
+
* (second halves hold pairs 0..11 in order).
|
|
11
|
+
*
|
|
12
|
+
* Canonicalization and indexing follow the reference exactly:
|
|
13
|
+
* - the state space is the 12!/2 even edge permutations (parity is
|
|
14
|
+
* invariant under the phase-3 moves), grouped into 1538 first-4
|
|
15
|
+
* "sym" classes under the 8 rotations (the reference's
|
|
16
|
+
* `initEdge3Sym2Raw`);
|
|
17
|
+
* - the canonical sym-index of a state is `symcord1 * 20160 + cord2`,
|
|
18
|
+
* computed by `getMvSym` (the reference's move-conjugated
|
|
19
|
+
* canonicalization with the `mvrot`/`mvroto` tables);
|
|
20
|
+
* - the table stores the exact BFS distance per sym-index (4-bit packed
|
|
21
|
+
* nibbles): 31,006,080 * 4 bits = 15.5 MB, no class-key file needed.
|
|
22
|
+
*/
|
|
23
|
+
import { normalizePairingState } from "./pairing-coordinate.js";
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// The reference's constants
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
/** The sym-index space: 1538 first-4 classes × 20160 remaining. */
|
|
28
|
+
export const EDGE3_TOTAL = 31_006_080;
|
|
29
|
+
export const EDGE3_CLASSES = 1538;
|
|
30
|
+
export const EDGE3_CORD2 = 20_160;
|
|
31
|
+
/** The first-4 Lehmer range (12 * 11 * 10 * 9). */
|
|
32
|
+
const EDGE3_FIRST4 = 11_880;
|
|
33
|
+
/** The maximum BFS depth (the reference's prunValues length - 1). */
|
|
34
|
+
export const EDGE3_MAX_DEPTH = 13;
|
|
35
|
+
/** The reference's `factX` (factorials / 2, with the small ones = 1). */
|
|
36
|
+
const FACT_X = [
|
|
37
|
+
1, 1, 1, 3, 12, 60, 360, 2520, 20160, 181440, 1814400, 19958400, 239500800
|
|
38
|
+
];
|
|
39
|
+
/** The reference's `syminv_0`: inverse rotation index mapping. */
|
|
40
|
+
const SYMINV_0 = [0, 1, 6, 3, 4, 5, 2, 7];
|
|
41
|
+
/** The reference's `move3std`: $move_4 case → std3 move id (the % 3 class). */
|
|
42
|
+
export const MOVE3_STD = [
|
|
43
|
+
0, 1, 2, 4, 6, 7, 8, 9, 10, 11, 13, 15, 16, 17, 19, 22, 25, 28, 31, 34, 36
|
|
44
|
+
];
|
|
45
|
+
/** The reference's `bfsMoves` (the $move_4 case indices). */
|
|
46
|
+
const EDGE3_MOVE_CASES = [1, 0, 2, 3, 5, 4, 6, 8, 7, 9, 10, 12, 11, 13, 14, 15, 16];
|
|
47
|
+
const BIT_COUNT = (() => {
|
|
48
|
+
const table = new Uint8Array(4096);
|
|
49
|
+
for (let i = 0; i < 4096; i++) {
|
|
50
|
+
table[i] = table[i >> 1] + (i & 1);
|
|
51
|
+
}
|
|
52
|
+
return table;
|
|
53
|
+
})();
|
|
54
|
+
export function bitCount(v) {
|
|
55
|
+
return BIT_COUNT[v & 4095] + BIT_COUNT[(v >> 12) & 4095] + BIT_COUNT[v >> 24];
|
|
56
|
+
}
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// The (edge, edgeo) coordinate with the shared labels
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
/**
|
|
61
|
+
* The reference's `$swap_0`: the two transpositions (a↔c, b↔d) on one array.
|
|
62
|
+
*/
|
|
63
|
+
function swap0(arr, a, b, c, d) {
|
|
64
|
+
let temp = arr[a];
|
|
65
|
+
arr[a] = arr[c];
|
|
66
|
+
arr[c] = temp;
|
|
67
|
+
temp = arr[b];
|
|
68
|
+
arr[b] = arr[d];
|
|
69
|
+
arr[d] = temp;
|
|
70
|
+
}
|
|
71
|
+
/** The reference's `$circlex`: a 4-cycle across the two halves. */
|
|
72
|
+
function circlex(edge, edgeo, a, b, c, d) {
|
|
73
|
+
const temp = edgeo[d];
|
|
74
|
+
edgeo[d] = edge[c];
|
|
75
|
+
edge[c] = edgeo[b];
|
|
76
|
+
edgeo[b] = edge[a];
|
|
77
|
+
edge[a] = temp;
|
|
78
|
+
}
|
|
79
|
+
/** The reference's `$swapx`: swap the halves across slots x and y. */
|
|
80
|
+
function swapx(edge, edgeo, x, y) {
|
|
81
|
+
const temp = edge[x];
|
|
82
|
+
edge[x] = edgeo[y];
|
|
83
|
+
edgeo[y] = temp;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The reference's `$move_4` (cases 0-19) as coordinate effects: the same
|
|
87
|
+
* slot permutations applied to `edge` and `edgeo`. The `circle` direction
|
|
88
|
+
* (the cubing.js `mathlib.circle`: the value at a moves to b) is verified
|
|
89
|
+
* against the physical move tables in `verifyEdge3`.
|
|
90
|
+
*/
|
|
91
|
+
function applyMoveCase(edge, edgeo, m) {
|
|
92
|
+
const circle = (arr, a, b, c, d) => {
|
|
93
|
+
const temp = arr[a];
|
|
94
|
+
arr[a] = arr[d];
|
|
95
|
+
arr[d] = arr[c];
|
|
96
|
+
arr[c] = arr[b];
|
|
97
|
+
arr[b] = temp;
|
|
98
|
+
};
|
|
99
|
+
const circle2 = (arr, a, b) => {
|
|
100
|
+
const temp = arr[a];
|
|
101
|
+
arr[a] = arr[b];
|
|
102
|
+
arr[b] = temp;
|
|
103
|
+
};
|
|
104
|
+
switch (m) {
|
|
105
|
+
case 0:
|
|
106
|
+
circle(edge, 0, 4, 1, 5);
|
|
107
|
+
circle(edgeo, 0, 4, 1, 5);
|
|
108
|
+
break;
|
|
109
|
+
case 1:
|
|
110
|
+
swap0(edge, 0, 4, 1, 5);
|
|
111
|
+
swap0(edgeo, 0, 4, 1, 5);
|
|
112
|
+
break;
|
|
113
|
+
case 2:
|
|
114
|
+
circle(edge, 0, 5, 1, 4);
|
|
115
|
+
circle(edgeo, 0, 5, 1, 4);
|
|
116
|
+
break;
|
|
117
|
+
case 3:
|
|
118
|
+
swap0(edge, 5, 10, 6, 11);
|
|
119
|
+
swap0(edgeo, 5, 10, 6, 11);
|
|
120
|
+
break;
|
|
121
|
+
case 4:
|
|
122
|
+
circle(edge, 0, 11, 3, 8);
|
|
123
|
+
circle(edgeo, 0, 11, 3, 8);
|
|
124
|
+
break;
|
|
125
|
+
case 5:
|
|
126
|
+
swap0(edge, 0, 11, 3, 8);
|
|
127
|
+
swap0(edgeo, 0, 11, 3, 8);
|
|
128
|
+
break;
|
|
129
|
+
case 6:
|
|
130
|
+
circle(edge, 0, 8, 3, 11);
|
|
131
|
+
circle(edgeo, 0, 8, 3, 11);
|
|
132
|
+
break;
|
|
133
|
+
case 7:
|
|
134
|
+
circle(edge, 2, 7, 3, 6);
|
|
135
|
+
circle(edgeo, 2, 7, 3, 6);
|
|
136
|
+
break;
|
|
137
|
+
case 8:
|
|
138
|
+
swap0(edge, 2, 7, 3, 6);
|
|
139
|
+
swap0(edgeo, 2, 7, 3, 6);
|
|
140
|
+
break;
|
|
141
|
+
case 9:
|
|
142
|
+
circle(edge, 2, 6, 3, 7);
|
|
143
|
+
circle(edgeo, 2, 6, 3, 7);
|
|
144
|
+
break;
|
|
145
|
+
case 10:
|
|
146
|
+
swap0(edge, 4, 8, 7, 9);
|
|
147
|
+
swap0(edgeo, 4, 8, 7, 9);
|
|
148
|
+
break;
|
|
149
|
+
case 11:
|
|
150
|
+
circle(edge, 1, 9, 2, 10);
|
|
151
|
+
circle(edgeo, 1, 9, 2, 10);
|
|
152
|
+
break;
|
|
153
|
+
case 12:
|
|
154
|
+
swap0(edge, 1, 9, 2, 10);
|
|
155
|
+
swap0(edgeo, 1, 9, 2, 10);
|
|
156
|
+
break;
|
|
157
|
+
case 13:
|
|
158
|
+
circle(edge, 1, 10, 2, 9);
|
|
159
|
+
circle(edgeo, 1, 10, 2, 9);
|
|
160
|
+
break;
|
|
161
|
+
case 14:
|
|
162
|
+
swap0(edge, 0, 4, 1, 5);
|
|
163
|
+
swap0(edgeo, 0, 4, 1, 5);
|
|
164
|
+
circle2(edge, 9, 11);
|
|
165
|
+
circle2(edgeo, 8, 10);
|
|
166
|
+
break;
|
|
167
|
+
case 15:
|
|
168
|
+
swap0(edge, 5, 10, 6, 11);
|
|
169
|
+
swap0(edgeo, 5, 10, 6, 11);
|
|
170
|
+
circle2(edge, 1, 3);
|
|
171
|
+
circle2(edgeo, 0, 2);
|
|
172
|
+
break;
|
|
173
|
+
case 16:
|
|
174
|
+
swap0(edge, 0, 11, 3, 8);
|
|
175
|
+
swap0(edgeo, 0, 11, 3, 8);
|
|
176
|
+
circle2(edge, 5, 7);
|
|
177
|
+
circle2(edgeo, 4, 6);
|
|
178
|
+
break;
|
|
179
|
+
case 17:
|
|
180
|
+
swap0(edge, 2, 7, 3, 6);
|
|
181
|
+
swap0(edgeo, 2, 7, 3, 6);
|
|
182
|
+
circle2(edge, 8, 10);
|
|
183
|
+
circle2(edgeo, 9, 11);
|
|
184
|
+
break;
|
|
185
|
+
case 18:
|
|
186
|
+
swap0(edge, 4, 8, 7, 9);
|
|
187
|
+
swap0(edgeo, 4, 8, 7, 9);
|
|
188
|
+
circle2(edge, 0, 2);
|
|
189
|
+
circle2(edgeo, 1, 3);
|
|
190
|
+
break;
|
|
191
|
+
case 19:
|
|
192
|
+
swap0(edge, 1, 9, 2, 10);
|
|
193
|
+
swap0(edgeo, 1, 9, 2, 10);
|
|
194
|
+
circle2(edge, 4, 6);
|
|
195
|
+
circle2(edgeo, 5, 7);
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/** The reference's `$rot_1` (r = 0, 1, 2) on the (edge, edgeo) pair. */
|
|
200
|
+
function rot1(edge, edgeo, r) {
|
|
201
|
+
switch (r) {
|
|
202
|
+
case 0:
|
|
203
|
+
applyMoveCase(edge, edgeo, 14);
|
|
204
|
+
applyMoveCase(edge, edgeo, 17);
|
|
205
|
+
break;
|
|
206
|
+
case 1:
|
|
207
|
+
circlex(edge, edgeo, 11, 5, 10, 6);
|
|
208
|
+
circlex(edge, edgeo, 5, 10, 6, 11);
|
|
209
|
+
circlex(edge, edgeo, 1, 2, 3, 0);
|
|
210
|
+
circlex(edge, edgeo, 4, 9, 7, 8);
|
|
211
|
+
circlex(edge, edgeo, 8, 4, 9, 7);
|
|
212
|
+
circlex(edge, edgeo, 0, 1, 2, 3);
|
|
213
|
+
break;
|
|
214
|
+
case 2:
|
|
215
|
+
swapx(edge, edgeo, 4, 5);
|
|
216
|
+
swapx(edge, edgeo, 5, 4);
|
|
217
|
+
swapx(edge, edgeo, 11, 8);
|
|
218
|
+
swapx(edge, edgeo, 8, 11);
|
|
219
|
+
swapx(edge, edgeo, 7, 6);
|
|
220
|
+
swapx(edge, edgeo, 6, 7);
|
|
221
|
+
swapx(edge, edgeo, 9, 10);
|
|
222
|
+
swapx(edge, edgeo, 10, 9);
|
|
223
|
+
swapx(edge, edgeo, 1, 1);
|
|
224
|
+
swapx(edge, edgeo, 0, 0);
|
|
225
|
+
swapx(edge, edgeo, 3, 3);
|
|
226
|
+
swapx(edge, edgeo, 2, 2);
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/** The reference's `$rotate_0`: rotation r (0..7) via the rot1 decomposition. */
|
|
231
|
+
function rotateByIndex(edge, edgeo, r) {
|
|
232
|
+
while (r >= 2) {
|
|
233
|
+
r -= 2;
|
|
234
|
+
rot1(edge, edgeo, 1);
|
|
235
|
+
rot1(edge, edgeo, 2);
|
|
236
|
+
}
|
|
237
|
+
if (r !== 0)
|
|
238
|
+
rot1(edge, edgeo, 0);
|
|
239
|
+
}
|
|
240
|
+
// ---------------------------------------------------------------------------
|
|
241
|
+
// Lehmer helpers (the reference's get12Perm / $set_4)
|
|
242
|
+
// ---------------------------------------------------------------------------
|
|
243
|
+
/**
|
|
244
|
+
* The mixed-radix Lehmer encode of the first `end` values (the reference's
|
|
245
|
+
* `get12Perm`): idx = Σ digit_i * (12-i)!-radix, with the digits reduced by
|
|
246
|
+
* the values already seen.
|
|
247
|
+
*/
|
|
248
|
+
function get12Perm(arr, end, returnMask = false) {
|
|
249
|
+
let idx = 0;
|
|
250
|
+
let mask = 0;
|
|
251
|
+
for (let i = 0; i < end; i++) {
|
|
252
|
+
const val = arr[i];
|
|
253
|
+
idx = idx * (12 - i) + val - bitCount(mask & ((1 << val) - 1));
|
|
254
|
+
mask |= 1 << val;
|
|
255
|
+
}
|
|
256
|
+
return returnMask ? mask : idx;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Decode a 12-permutation from the mixed-radix index (the reference's
|
|
260
|
+
* `$set_4`): the 11 digits with the parity-corrected last two positions.
|
|
261
|
+
*/
|
|
262
|
+
function set4(out, idx) {
|
|
263
|
+
let parity = 0;
|
|
264
|
+
const unused = [];
|
|
265
|
+
for (let i = 0; i < 12; i++)
|
|
266
|
+
unused.push(i);
|
|
267
|
+
for (let i = 0; i < 11; i++) {
|
|
268
|
+
const p = FACT_X[11 - i];
|
|
269
|
+
const v = Math.floor(idx / p);
|
|
270
|
+
idx = idx % p;
|
|
271
|
+
parity ^= v;
|
|
272
|
+
out[i] = unused[v];
|
|
273
|
+
unused.splice(v, 1);
|
|
274
|
+
}
|
|
275
|
+
const last = unused[0];
|
|
276
|
+
if ((parity & 1) === 0) {
|
|
277
|
+
out[11] = last;
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
out[11] = out[10];
|
|
281
|
+
out[10] = last;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
let symTables = null;
|
|
285
|
+
export function getSymTables() {
|
|
286
|
+
if (!symTables) {
|
|
287
|
+
symTables = buildSymTables();
|
|
288
|
+
}
|
|
289
|
+
return symTables;
|
|
290
|
+
}
|
|
291
|
+
export function buildSymTables() {
|
|
292
|
+
const mvrot = new Uint8Array(21 * 8 * 12);
|
|
293
|
+
const mvroto = new Uint8Array(21 * 8 * 12);
|
|
294
|
+
const edge = new Uint8Array(12);
|
|
295
|
+
const edgeo = new Uint8Array(12);
|
|
296
|
+
// initEdge3MvRot: the move-conjugated rotation effects on the solved state.
|
|
297
|
+
for (let m = 0; m < 21; m++) {
|
|
298
|
+
for (let r = 0; r < 8; r++) {
|
|
299
|
+
set4(edge, 0);
|
|
300
|
+
for (let i = 0; i < 12; i++)
|
|
301
|
+
edgeo[i] = i;
|
|
302
|
+
if (m < 20)
|
|
303
|
+
applyMoveCase(edge, edgeo, m);
|
|
304
|
+
rotateByIndex(edge, edgeo, r);
|
|
305
|
+
const base = (m << 3 | r) * 12;
|
|
306
|
+
for (let i = 0; i < 12; i++)
|
|
307
|
+
mvrot[base + i] = edge[i];
|
|
308
|
+
// the reference's $std: temp[edgeo[i]] = i; mvroto = the temp (the
|
|
309
|
+
// inverse of the pre-std edgeo), NOT the std'ed edge.
|
|
310
|
+
const temp = new Uint8Array(12);
|
|
311
|
+
for (let i = 0; i < 12; i++)
|
|
312
|
+
temp[edgeo[i]] = i;
|
|
313
|
+
for (let i = 0; i < 12; i++)
|
|
314
|
+
mvroto[base + i] = temp[i];
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// initEdge3Sym2Raw: the 1538 first-4 classes under the 8 rotations.
|
|
318
|
+
const occ = new Uint8Array(1485);
|
|
319
|
+
const sym2Raw = new Uint32Array(EDGE3_CLASSES);
|
|
320
|
+
const sym2Mask = new Uint32Array(EDGE3_CLASSES);
|
|
321
|
+
const raw2Sym = new Uint32Array(EDGE3_FIRST4);
|
|
322
|
+
const symState = new Uint32Array(EDGE3_CLASSES);
|
|
323
|
+
let count = 0;
|
|
324
|
+
for (let i = 0; i < EDGE3_FIRST4; i++) {
|
|
325
|
+
if ((occ[i >> 3] & (1 << (i & 7))) !== 0)
|
|
326
|
+
continue;
|
|
327
|
+
set4(edge, i * FACT_X[8]);
|
|
328
|
+
for (let k = 0; k < 12; k++)
|
|
329
|
+
edgeo[k] = k;
|
|
330
|
+
sym2Raw[count] = i;
|
|
331
|
+
sym2Mask[count] = get12Perm(normalizePairingState(edge, edgeo).edge, 4, true);
|
|
332
|
+
for (let j = 0; j < 8; j++) {
|
|
333
|
+
const idx = get12Perm(normalizePairingState(edge, edgeo).edge, 4);
|
|
334
|
+
if (idx === i)
|
|
335
|
+
symState[count] = (symState[count] | (1 << j)) >>> 0;
|
|
336
|
+
occ[idx >> 3] = (occ[idx >> 3] | (1 << (idx & 7)));
|
|
337
|
+
raw2Sym[idx] = (count << 3 | SYMINV_0[j]) >>> 0;
|
|
338
|
+
rot1(edge, edgeo, 0);
|
|
339
|
+
if ((j & 1) === 1) {
|
|
340
|
+
rot1(edge, edgeo, 1);
|
|
341
|
+
rot1(edge, edgeo, 2);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
count++;
|
|
345
|
+
}
|
|
346
|
+
if (count !== EDGE3_CLASSES) {
|
|
347
|
+
throw new Error(`Edge3: expected ${EDGE3_CLASSES} classes, found ${count}`);
|
|
348
|
+
}
|
|
349
|
+
// the reference's initEdge3Sym2Raw second half: the sym-advance table.
|
|
350
|
+
const symMove = new Uint16Array(20 * EDGE3_CLASSES);
|
|
351
|
+
for (let i = 0; i < EDGE3_CLASSES; i++) {
|
|
352
|
+
set4(edge, sym2Raw[i] * FACT_X[8]);
|
|
353
|
+
for (let k = 0; k < 12; k++)
|
|
354
|
+
edgeo[k] = k;
|
|
355
|
+
for (let m = 0; m < 20; m++) {
|
|
356
|
+
if (MOVE3_STD[m] % 3 !== 1)
|
|
357
|
+
continue;
|
|
358
|
+
const idx = getMvSymFrom(edge, edgeo, m, sym2Raw, sym2Mask, raw2Sym, mvrot, mvroto);
|
|
359
|
+
symMove[m * EDGE3_CLASSES + i] = (Math.floor((idx >> 3) / EDGE3_CORD2) << 3 | (idx & 7));
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
const tables = { mvrot, mvroto, sym2Raw, sym2Mask, raw2Sym, symState, symMove };
|
|
363
|
+
symTables = tables;
|
|
364
|
+
return tables;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* The reference's `getMvSym`: the canonical sym-index of the state (edge,
|
|
368
|
+
* edgeo) under the move-conjugated rotation, as `(sym-index << 3 | r)`.
|
|
369
|
+
*/
|
|
370
|
+
export function getMvSymFrom(edge, _edgeo, mv, sym2Raw, sym2Mask, raw2Sym, mvrot, mvroto, assumeIdx) {
|
|
371
|
+
let mrIdx = mv << 3;
|
|
372
|
+
let idx;
|
|
373
|
+
if (assumeIdx !== undefined && MOVE3_STD[mv] % 3 === 1) {
|
|
374
|
+
mrIdx |= assumeIdx & 7;
|
|
375
|
+
idx = assumeIdx >> 3;
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
const movoBase = mrIdx * 12;
|
|
379
|
+
let baseIdx = 0;
|
|
380
|
+
let mask = 0;
|
|
381
|
+
for (let i = 0; i < 4; i++) {
|
|
382
|
+
const val = mvroto[movoBase + edge[mvrot[movoBase + i]]];
|
|
383
|
+
baseIdx = baseIdx * (12 - i) + val - bitCount(mask & ((1 << val) - 1));
|
|
384
|
+
mask |= 1 << val;
|
|
385
|
+
}
|
|
386
|
+
baseIdx = raw2Sym[baseIdx];
|
|
387
|
+
mrIdx |= baseIdx & 7;
|
|
388
|
+
baseIdx >>= 3;
|
|
389
|
+
idx = baseIdx;
|
|
390
|
+
}
|
|
391
|
+
const mrIdxBase = mrIdx * 12;
|
|
392
|
+
let mask = sym2Mask[idx];
|
|
393
|
+
let out = idx;
|
|
394
|
+
for (let i = 4; i < 10; i++) {
|
|
395
|
+
const val = mvroto[mrIdxBase + edge[mvrot[mrIdxBase + i]]];
|
|
396
|
+
out = out * (12 - i) + val - bitCount(mask & ((1 << val) - 1));
|
|
397
|
+
mask |= 1 << val;
|
|
398
|
+
}
|
|
399
|
+
return (out << 3 | (mrIdx & 7)) >>> 0;
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Generate the exact BFS distances over the 31,006,080 sym-index space.
|
|
403
|
+
* `maxDepthCap` stops the BFS early (validation only).
|
|
404
|
+
*/
|
|
405
|
+
export function generateEdge3Tables(maxDepthCap = -1) {
|
|
406
|
+
const t = buildSymTables();
|
|
407
|
+
const { sym2Raw, sym2Mask, raw2Sym, mvrot, mvroto, symMove, symState } = t;
|
|
408
|
+
const distances = new Uint8Array(Math.ceil(EDGE3_TOTAL / 2)).fill(0xff);
|
|
409
|
+
const setDistance = (idx, depth) => {
|
|
410
|
+
const byte = idx >> 1;
|
|
411
|
+
const shift = (idx & 1) * 4;
|
|
412
|
+
distances[byte] = (distances[byte] & ~(0x0f << shift)) | ((depth & 0x0f) << shift);
|
|
413
|
+
};
|
|
414
|
+
const getDistance = (idx) => (distances[idx >> 1] >> ((idx & 1) * 4)) & 0x0f;
|
|
415
|
+
const edge = new Uint8Array(12);
|
|
416
|
+
const edgeo = new Uint8Array(12);
|
|
417
|
+
const fEdge = new Uint8Array(12);
|
|
418
|
+
const fEdgeo = new Uint8Array(12);
|
|
419
|
+
const gEdge = new Uint8Array(12);
|
|
420
|
+
const gEdgeo = new Uint8Array(12);
|
|
421
|
+
setDistance(0, 0);
|
|
422
|
+
let frontier = [0];
|
|
423
|
+
let depth = 0;
|
|
424
|
+
while (frontier.length > 0) {
|
|
425
|
+
if (maxDepthCap >= 0 && depth >= maxDepthCap)
|
|
426
|
+
break;
|
|
427
|
+
const next = [];
|
|
428
|
+
for (const si of frontier) {
|
|
429
|
+
const symcord1 = Math.floor(si / EDGE3_CORD2);
|
|
430
|
+
const cord2 = si % EDGE3_CORD2;
|
|
431
|
+
set4(edge, sym2Raw[symcord1] * FACT_X[8] + cord2);
|
|
432
|
+
for (let k = 0; k < 12; k++)
|
|
433
|
+
edgeo[k] = k;
|
|
434
|
+
for (let mi = 0; mi < 17; mi++) {
|
|
435
|
+
const m = EDGE3_MOVE_CASES[mi];
|
|
436
|
+
// the reference's getMvSym takes the UNMOVED state: the
|
|
437
|
+
// move-conjugated tables provide the moved canonical.
|
|
438
|
+
const idx = getMvSymFrom(edge, edgeo, m, sym2Raw, sym2Mask, raw2Sym, mvrot, mvroto, symMove[m * EDGE3_CLASSES + symcord1]);
|
|
439
|
+
const canonical = idx >> 3;
|
|
440
|
+
if (getDistance(canonical) !== 0x0f)
|
|
441
|
+
continue;
|
|
442
|
+
setDistance(canonical, depth + 1);
|
|
443
|
+
next.push(canonical);
|
|
444
|
+
// the rotation-conjugated representatives of the moved class
|
|
445
|
+
const symcord1x = Math.floor(canonical / EDGE3_CORD2);
|
|
446
|
+
let symStateBits = symState[symcord1x];
|
|
447
|
+
if (symStateBits === 1)
|
|
448
|
+
continue;
|
|
449
|
+
fEdge.set(edge);
|
|
450
|
+
fEdgeo.set(edgeo);
|
|
451
|
+
applyMoveCase(fEdge, fEdgeo, m);
|
|
452
|
+
rotateByIndex(fEdge, fEdgeo, idx & 7);
|
|
453
|
+
for (let j = 1; (symStateBits = symStateBits >> 1) !== 0; j++) {
|
|
454
|
+
if ((symStateBits & 1) !== 1)
|
|
455
|
+
continue;
|
|
456
|
+
gEdge.set(fEdge);
|
|
457
|
+
gEdgeo.set(fEdgeo);
|
|
458
|
+
rotateByIndex(gEdge, gEdgeo, j);
|
|
459
|
+
const normalized = normalizePairingState(gEdge, gEdgeo);
|
|
460
|
+
const idxx = symcord1x * EDGE3_CORD2 + (get12Perm(normalized.edge, 10) % EDGE3_CORD2);
|
|
461
|
+
if (getDistance(idxx) !== 0x0f)
|
|
462
|
+
continue;
|
|
463
|
+
setDistance(idxx, depth + 1);
|
|
464
|
+
next.push(idxx);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
frontier = next;
|
|
469
|
+
depth++;
|
|
470
|
+
}
|
|
471
|
+
const distanceOfEdge = edge3DistanceLookup(distances);
|
|
472
|
+
return { distances, distanceOfEdge };
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* The distance lookup over a loaded distances buffer: builds the sym
|
|
476
|
+
* machinery once (cached) and returns a closure from the std-form edge
|
|
477
|
+
* (a 12-permutation) to its BFS distance.
|
|
478
|
+
*/
|
|
479
|
+
export function edge3DistanceLookup(distances) {
|
|
480
|
+
const t = symTables ?? buildSymTables();
|
|
481
|
+
const { sym2Raw, sym2Mask, raw2Sym, mvrot, mvroto } = t;
|
|
482
|
+
const identity = Uint8Array.from({ length: 12 }, (_, i) => i);
|
|
483
|
+
return (e) => {
|
|
484
|
+
const std = normalizePairingState(e, identity).edge;
|
|
485
|
+
const idx = getMvSymFrom(std, identity, 20, sym2Raw, sym2Mask, raw2Sym, mvrot, mvroto) >> 3;
|
|
486
|
+
return (distances[idx >> 1] >> ((idx & 1) * 4)) & 0x0f;
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
// ---------------------------------------------------------------------------
|
|
490
|
+
// Verification gates
|
|
491
|
+
// ---------------------------------------------------------------------------
|
|
492
|
+
/** Test-only hooks. */
|
|
493
|
+
export function __edge3TestHooks() {
|
|
494
|
+
return { buildSymTables, applyMoveCase, get12Perm, set4, rotateByIndex, getMvSymFrom };
|
|
495
|
+
}
|
|
496
|
+
export function verifyEdge3(tables) {
|
|
497
|
+
const errors = [];
|
|
498
|
+
const identity = Uint8Array.from({ length: 12 }, (_, i) => i);
|
|
499
|
+
if (tables.distanceOfEdge(identity) !== 0) {
|
|
500
|
+
errors.push("Solved state must have distance 0.");
|
|
501
|
+
}
|
|
502
|
+
let seen = 0;
|
|
503
|
+
for (let i = 0; i < EDGE3_TOTAL; i++) {
|
|
504
|
+
if (((tables.distances[i >> 1] >> ((i & 1) * 4)) & 0x0f) !== 0x0f)
|
|
505
|
+
seen++;
|
|
506
|
+
}
|
|
507
|
+
if (seen !== EDGE3_TOTAL) {
|
|
508
|
+
errors.push(`Expected ${EDGE3_TOTAL} reachable classes, saw ${seen}.`);
|
|
509
|
+
}
|
|
510
|
+
return errors;
|
|
511
|
+
}
|