@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,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"puzzle": "cube4",
|
|
4
|
+
"phase": "edge3",
|
|
5
|
+
"classCount": 31006080,
|
|
6
|
+
"maxDepth": 13,
|
|
7
|
+
"distances": {
|
|
8
|
+
"file": "edge3-distances.bin",
|
|
9
|
+
"range": 15503040,
|
|
10
|
+
"checksum": "b441c57a052dc5650958426c7fddbdf5cab195f615449bc2235364f2ceb6c939"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reduction geometry for the 4x4 solver.
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - the 24-rotation symmetry group as permutations on the 24 center
|
|
6
|
+
* positions and the 24 wing positions (built from a 3D sticker model),
|
|
7
|
+
* - the phase move sets (F1: all moves, F2: UD-preserving, F3: pairing),
|
|
8
|
+
* - per-move corner/wing permutation parities,
|
|
9
|
+
* - the wing orbit structure preserved by the phase-3 move set,
|
|
10
|
+
* - verification gates (group order, move-set invariants, orbit structure).
|
|
11
|
+
*
|
|
12
|
+
* Everything here is verified programmatically by the gates so a hand error
|
|
13
|
+
* in any table fails loudly instead of silently corrupting solutions.
|
|
14
|
+
*/
|
|
15
|
+
import type { Cube4Move } from "../types.js";
|
|
16
|
+
export declare const PHASE_1_MOVES: readonly Cube4Move[];
|
|
17
|
+
export declare const PHASE_2_MOVES: readonly Cube4Move[];
|
|
18
|
+
export declare const PHASE_3_MOVES: readonly Cube4Move[];
|
|
19
|
+
export interface ReductionGeometry {
|
|
20
|
+
/** 24 rotation matrices (3×3) of the cube symmetry group. */
|
|
21
|
+
rotations: readonly (readonly (readonly number[])[])[];
|
|
22
|
+
centerRotations: readonly (readonly number[])[];
|
|
23
|
+
wingRotations: readonly (readonly number[])[];
|
|
24
|
+
moveCornerParity: readonly number[];
|
|
25
|
+
moveWingParity: readonly number[];
|
|
26
|
+
phase1Moves: readonly Cube4Move[];
|
|
27
|
+
phase2Moves: readonly Cube4Move[];
|
|
28
|
+
phase3Moves: readonly Cube4Move[];
|
|
29
|
+
}
|
|
30
|
+
export declare function buildReductionGeometry(): ReductionGeometry;
|
|
31
|
+
export declare function permutationParity(values: readonly number[]): 0 | 1;
|
|
32
|
+
export declare function verifyReductionGeometry(): string[];
|
|
33
|
+
//# sourceMappingURL=geometry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geometry.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/geometry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAQ7C,eAAO,MAAM,aAAa,EAAE,SAAS,SAAS,EAAoB,CAAC;AAInE,eAAO,MAAM,aAAa,EAAE,SAAS,SAAS,EAI7C,CAAC;AAKF,eAAO,MAAM,aAAa,EAAE,SAAS,SAAS,EAI7C,CAAC;AAiIF,MAAM,WAAW,iBAAiB;IAChC,6DAA6D;IAC7D,SAAS,EAAE,SAAS,CAAC,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;IACvD,eAAe,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC;IAChD,aAAa,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC;IAC9C,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,WAAW,EAAE,SAAS,SAAS,EAAE,CAAC;IAClC,WAAW,EAAE,SAAS,SAAS,EAAE,CAAC;IAClC,WAAW,EAAE,SAAS,SAAS,EAAE,CAAC;CACnC;AAED,wBAAgB,sBAAsB,IAAI,iBAAiB,CAW1D;AAmGD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAUlE;AAMD,wBAAgB,uBAAuB,IAAI,MAAM,EAAE,CA6DlD"}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reduction geometry for the 4x4 solver.
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - the 24-rotation symmetry group as permutations on the 24 center
|
|
6
|
+
* positions and the 24 wing positions (built from a 3D sticker model),
|
|
7
|
+
* - the phase move sets (F1: all moves, F2: UD-preserving, F3: pairing),
|
|
8
|
+
* - per-move corner/wing permutation parities,
|
|
9
|
+
* - the wing orbit structure preserved by the phase-3 move set,
|
|
10
|
+
* - verification gates (group order, move-set invariants, orbit structure).
|
|
11
|
+
*
|
|
12
|
+
* Everything here is verified programmatically by the gates so a hand error
|
|
13
|
+
* in any table fails loudly instead of silently corrupting solutions.
|
|
14
|
+
*/
|
|
15
|
+
import { CUBE4_ALL_MOVES } from "../types.js";
|
|
16
|
+
import { FACE_MOVE_EFFECTS, parseMove } from "../moves.js";
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Phase move sets
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
export const PHASE_1_MOVES = CUBE4_ALL_MOVES;
|
|
21
|
+
// Moves that keep U/D-colored centers on U/D faces: all face turns plus
|
|
22
|
+
// Uw/Dw (the u/d slices cycle centers among the R/F/L/B faces only).
|
|
23
|
+
export const PHASE_2_MOVES = [
|
|
24
|
+
"U", "U2", "U'", "R", "R2", "R'", "F", "F2", "F'",
|
|
25
|
+
"D", "D2", "D'", "L", "L2", "L'", "B", "B2", "B'",
|
|
26
|
+
"Uw", "Uw2", "Uw'", "Dw", "Dw2", "Dw'"
|
|
27
|
+
];
|
|
28
|
+
// Wing-pairing moves (cstimer-style): all face turns plus quarter wide
|
|
29
|
+
// turns. The wide turns move centers mid-search (guided back by the center
|
|
30
|
+
// coordinate) and flip wing permutation parity (tracked as the phase-2 bit).
|
|
31
|
+
export const PHASE_3_MOVES = [
|
|
32
|
+
"U", "U2", "U'", "R", "F", "F2", "F'",
|
|
33
|
+
"D", "D2", "D'", "L", "B", "B2", "B'",
|
|
34
|
+
"Uw", "Rw", "Fw", "Dw", "Lw", "Bw"
|
|
35
|
+
];
|
|
36
|
+
const FACE_BASES = [
|
|
37
|
+
{ n: [0, 0, 1], u: [1, 0, 0], v: [0, 1, 0] }, // U
|
|
38
|
+
{ n: [1, 0, 0], u: [0, -1, 0], v: [0, 0, 1] }, // R
|
|
39
|
+
{ n: [0, 1, 0], u: [1, 0, 0], v: [0, 0, 1] }, // F
|
|
40
|
+
{ n: [0, 0, -1], u: [1, 0, 0], v: [0, 1, 0] }, // D
|
|
41
|
+
{ n: [-1, 0, 0], u: [0, 1, 0], v: [0, 0, 1] }, // L
|
|
42
|
+
{ n: [0, -1, 0], u: [-1, 0, 0], v: [0, 0, 1] } // B
|
|
43
|
+
];
|
|
44
|
+
function stickerCoord(face, row, col) {
|
|
45
|
+
const { n, u, v } = FACE_BASES[face];
|
|
46
|
+
return [
|
|
47
|
+
n[0] * 4 + (col - 1.5) * 2 * u[0] + (1.5 - row) * 2 * v[0],
|
|
48
|
+
n[1] * 4 + (col - 1.5) * 2 * u[1] + (1.5 - row) * 2 * v[1],
|
|
49
|
+
n[2] * 4 + (col - 1.5) * 2 * u[2] + (1.5 - row) * 2 * v[2]
|
|
50
|
+
];
|
|
51
|
+
}
|
|
52
|
+
function faceAtCoord(c) {
|
|
53
|
+
let best = 0;
|
|
54
|
+
let bestDot = -Infinity;
|
|
55
|
+
for (let face = 0; face < 6; face += 1) {
|
|
56
|
+
const { n } = FACE_BASES[face];
|
|
57
|
+
const dot = c[0] * n[0] + c[1] * n[1] + c[2] * n[2];
|
|
58
|
+
if (dot > bestDot) {
|
|
59
|
+
bestDot = dot;
|
|
60
|
+
best = face;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return best;
|
|
64
|
+
}
|
|
65
|
+
function rowColAtCoord(face, c) {
|
|
66
|
+
const { u, v } = FACE_BASES[face];
|
|
67
|
+
const uu = c[0] * u[0] + c[1] * u[1] + c[2] * u[2];
|
|
68
|
+
const vv = c[0] * v[0] + c[1] * v[1] + c[2] * v[2];
|
|
69
|
+
const col = Math.round(uu / 2 + 1.5);
|
|
70
|
+
const row = Math.round(1.5 - vv / 2);
|
|
71
|
+
return [row, col];
|
|
72
|
+
}
|
|
73
|
+
// Rotation generators (as 3x3 integer matrices):
|
|
74
|
+
// Rz90: 90° about the UD axis (x,y,z) → (-y,x,z)
|
|
75
|
+
// Rdiag: 120° about (1,1,1) (x,y,z) → (z,x,y)
|
|
76
|
+
const RZ90 = [
|
|
77
|
+
[0, -1, 0],
|
|
78
|
+
[1, 0, 0],
|
|
79
|
+
[0, 0, 1]
|
|
80
|
+
];
|
|
81
|
+
const RDIAG = [
|
|
82
|
+
[0, 0, 1],
|
|
83
|
+
[1, 0, 0],
|
|
84
|
+
[0, 1, 0]
|
|
85
|
+
];
|
|
86
|
+
function applyMatrix(m, v) {
|
|
87
|
+
return [
|
|
88
|
+
m[0][0] * v[0] + m[0][1] * v[1] + m[0][2] * v[2],
|
|
89
|
+
m[1][0] * v[0] + m[1][1] * v[1] + m[1][2] * v[2],
|
|
90
|
+
m[2][0] * v[0] + m[2][1] * v[1] + m[2][2] * v[2]
|
|
91
|
+
];
|
|
92
|
+
}
|
|
93
|
+
function composeMatrix(a, b) {
|
|
94
|
+
return b.map((col) => applyMatrix(a, col));
|
|
95
|
+
}
|
|
96
|
+
function matrixKey(m) {
|
|
97
|
+
return m.map((col) => col.join(",")).join(";");
|
|
98
|
+
}
|
|
99
|
+
function buildRotationGroup() {
|
|
100
|
+
const seen = new Map();
|
|
101
|
+
const group = [];
|
|
102
|
+
const queue = [RZ90, RDIAG];
|
|
103
|
+
while (queue.length > 0) {
|
|
104
|
+
const current = queue.pop();
|
|
105
|
+
const key = matrixKey(current);
|
|
106
|
+
if (seen.has(key))
|
|
107
|
+
continue;
|
|
108
|
+
seen.set(key, current);
|
|
109
|
+
group.push(current);
|
|
110
|
+
for (const generator of [RZ90, RDIAG]) {
|
|
111
|
+
const composed = composeMatrix(current, generator);
|
|
112
|
+
if (!seen.has(matrixKey(composed))) {
|
|
113
|
+
queue.push(composed);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return group;
|
|
118
|
+
}
|
|
119
|
+
const ROTATIONS = buildRotationGroup();
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
// Position mappings under a rotation
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
function centerCoord(index) {
|
|
124
|
+
const face = Math.floor(index / 4);
|
|
125
|
+
const slot = index % 4;
|
|
126
|
+
// slot → (row, col) for the inner 2×2 at face positions 5,6,9,10
|
|
127
|
+
const rowCol = [
|
|
128
|
+
[1, 1], [1, 2], [2, 1], [2, 2]
|
|
129
|
+
];
|
|
130
|
+
const [row, col] = rowCol[slot];
|
|
131
|
+
return stickerCoord(face, row, col);
|
|
132
|
+
}
|
|
133
|
+
function centerIndexAtCoord(c) {
|
|
134
|
+
const face = faceAtCoord(c);
|
|
135
|
+
const [row, col] = rowColAtCoord(face, c);
|
|
136
|
+
const slot = row === 1 && col === 1 ? 0 : row === 1 && col === 2 ? 1 : row === 2 && col === 1 ? 2 : 3;
|
|
137
|
+
return face * 4 + slot;
|
|
138
|
+
}
|
|
139
|
+
export function buildReductionGeometry() {
|
|
140
|
+
return {
|
|
141
|
+
rotations: ROTATIONS,
|
|
142
|
+
centerRotations: ROTATIONS.map((m) => buildPermutation(m, centerCoord, centerIndexAtCoord)),
|
|
143
|
+
wingRotations: ROTATIONS.map((m) => buildWingRotation(m)),
|
|
144
|
+
moveCornerParity: CUBE4_ALL_MOVES.map((move) => permutationParityOf(FACE_MOVE_EFFECTS[parseMove(move).face].cperm)),
|
|
145
|
+
moveWingParity: CUBE4_ALL_MOVES.map((move) => permutationParityOf(FACE_MOVE_EFFECTS[parseMove(move).face].wperm)),
|
|
146
|
+
phase1Moves: PHASE_1_MOVES,
|
|
147
|
+
phase2Moves: PHASE_2_MOVES,
|
|
148
|
+
phase3Moves: PHASE_3_MOVES
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function buildPermutation(matrix, positionCoord, positionAtCoord) {
|
|
152
|
+
const perm = new Array(24);
|
|
153
|
+
for (let index = 0; index < 24; index += 1) {
|
|
154
|
+
perm[positionAtCoord(applyMatrix(matrix, positionCoord(index)))] = index;
|
|
155
|
+
}
|
|
156
|
+
return perm;
|
|
157
|
+
}
|
|
158
|
+
function buildWingRotation(matrix) {
|
|
159
|
+
const stickers = wingStickers();
|
|
160
|
+
const perm = new Array(24);
|
|
161
|
+
for (let index = 0; index < 24; index += 1) {
|
|
162
|
+
const [s1, s2] = stickers[index];
|
|
163
|
+
const r1 = applyMatrix(matrix, s1);
|
|
164
|
+
const r2 = applyMatrix(matrix, s2);
|
|
165
|
+
perm[wingIndexAtSticker(r1)] = index;
|
|
166
|
+
}
|
|
167
|
+
return perm;
|
|
168
|
+
}
|
|
169
|
+
// Wing position → its two stickers as (face, sticker 0-15), from facelets.ts.
|
|
170
|
+
const WING_STICKER_POSITIONS = [
|
|
171
|
+
[0, 11], [1, 1], [0, 7], [1, 2], [0, 14], [2, 2], [0, 13], [2, 1],
|
|
172
|
+
[0, 8], [4, 2], [0, 4], [4, 1], [0, 1], [5, 2], [0, 2], [5, 1],
|
|
173
|
+
[3, 7], [1, 13], [3, 11], [1, 14], [3, 2], [2, 14], [3, 1], [2, 13],
|
|
174
|
+
[3, 4], [4, 14], [3, 8], [4, 13], [3, 13], [5, 14], [3, 14], [5, 13],
|
|
175
|
+
[2, 7], [1, 4], [2, 11], [1, 8], [2, 4], [4, 7], [2, 8], [4, 11],
|
|
176
|
+
[5, 7], [4, 4], [5, 11], [4, 8], [5, 4], [1, 7], [5, 8], [1, 11]
|
|
177
|
+
];
|
|
178
|
+
const WING_INDEX_BY_STICKER = (() => {
|
|
179
|
+
const map = new Map();
|
|
180
|
+
for (let position = 0; position < 24; position += 1) {
|
|
181
|
+
for (let k = 0; k < 2; k += 1) {
|
|
182
|
+
const [face, sticker] = WING_STICKER_POSITIONS[position * 2 + k];
|
|
183
|
+
map.set(`${face},${Math.floor(sticker / 4)},${sticker % 4}`, position);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return map;
|
|
187
|
+
})();
|
|
188
|
+
function wingStickers() {
|
|
189
|
+
const stickers = WING_STICKER_POSITIONS.map(([face, sticker]) => stickerCoord(face, Math.floor(sticker / 4), sticker % 4));
|
|
190
|
+
const result = [];
|
|
191
|
+
for (let position = 0; position < 24; position += 1) {
|
|
192
|
+
result.push([stickers[position * 2], stickers[position * 2 + 1]]);
|
|
193
|
+
}
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
function wingIndexAtSticker(c) {
|
|
197
|
+
const face = faceAtCoord(c);
|
|
198
|
+
const [row, col] = rowColAtCoord(face, c);
|
|
199
|
+
const index = WING_INDEX_BY_STICKER.get(`${face},${row},${col}`);
|
|
200
|
+
if (index === undefined) {
|
|
201
|
+
throw new Error(`Not a wing sticker position: face=${face} row=${row} col=${col}`);
|
|
202
|
+
}
|
|
203
|
+
return index;
|
|
204
|
+
}
|
|
205
|
+
// ---------------------------------------------------------------------------
|
|
206
|
+
// Move helpers
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
// Position map of a move effect: piece at position p moves to position
|
|
209
|
+
// inversePerm[p] where newState[i] = oldState[perm[i]].
|
|
210
|
+
function positionMapOf(perm) {
|
|
211
|
+
const map = new Array(perm.length);
|
|
212
|
+
for (let destination = 0; destination < perm.length; destination += 1) {
|
|
213
|
+
map[perm[destination]] = destination;
|
|
214
|
+
}
|
|
215
|
+
return map;
|
|
216
|
+
}
|
|
217
|
+
function permutationParityOf(perm) {
|
|
218
|
+
let parity = 0;
|
|
219
|
+
const seen = new Array(perm.length).fill(false);
|
|
220
|
+
for (let start = 0; start < perm.length; start += 1) {
|
|
221
|
+
if (seen[start] || perm[start] === start)
|
|
222
|
+
continue;
|
|
223
|
+
let length = 0;
|
|
224
|
+
let current = start;
|
|
225
|
+
while (!seen[current]) {
|
|
226
|
+
seen[current] = true;
|
|
227
|
+
current = perm[current];
|
|
228
|
+
length += 1;
|
|
229
|
+
}
|
|
230
|
+
parity ^= (length - 1) & 1;
|
|
231
|
+
}
|
|
232
|
+
return parity;
|
|
233
|
+
}
|
|
234
|
+
export function permutationParity(values) {
|
|
235
|
+
let inversions = 0;
|
|
236
|
+
for (let left = 0; left < values.length; left += 1) {
|
|
237
|
+
for (let right = left + 1; right < values.length; right += 1) {
|
|
238
|
+
if (values[left] > values[right]) {
|
|
239
|
+
inversions += 1;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return (inversions % 2);
|
|
244
|
+
}
|
|
245
|
+
// ---------------------------------------------------------------------------
|
|
246
|
+
// Verification gates
|
|
247
|
+
// ---------------------------------------------------------------------------
|
|
248
|
+
export function verifyReductionGeometry() {
|
|
249
|
+
const errors = [];
|
|
250
|
+
if (ROTATIONS.length !== 24) {
|
|
251
|
+
errors.push(`Rotation group has ${ROTATIONS.length} elements, expected 24.`);
|
|
252
|
+
}
|
|
253
|
+
const geometry = buildReductionGeometry();
|
|
254
|
+
// Rotations must be bijections.
|
|
255
|
+
for (let r = 0; r < geometry.centerRotations.length; r += 1) {
|
|
256
|
+
const perm = geometry.centerRotations[r];
|
|
257
|
+
if (new Set(perm).size !== 24) {
|
|
258
|
+
errors.push(`Center rotation ${r} is not a bijection.`);
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
const wperm = geometry.wingRotations[r];
|
|
262
|
+
if (new Set(wperm).size !== 24) {
|
|
263
|
+
errors.push(`Wing rotation ${r} is not a bijection.`);
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
// Phase 2 moves must keep U/D colors on U/D center positions.
|
|
268
|
+
const udPositions = new Set([0, 1, 2, 3, 12, 13, 14, 15]);
|
|
269
|
+
for (const move of PHASE_2_MOVES) {
|
|
270
|
+
const map = positionMapOf(FACE_MOVE_EFFECTS[parseMove(move).face].ctperm);
|
|
271
|
+
for (const position of udPositions) {
|
|
272
|
+
if (!udPositions.has(map[position])) {
|
|
273
|
+
errors.push(`Phase 2 move ${move} moves a U/D center position out of U/D faces.`);
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// Wing permutation parity flips exactly on the wide quarter turns (the
|
|
279
|
+
// bit the phase-2 search absorbs). Parity of the n-th power is
|
|
280
|
+
// base-parity × (turns mod 2).
|
|
281
|
+
for (const move of PHASE_3_MOVES) {
|
|
282
|
+
const { face, turns } = parseMove(move);
|
|
283
|
+
const effect = FACE_MOVE_EFFECTS[face];
|
|
284
|
+
const wingParity = (permutationParityOf(effect.wperm) * (turns & 1)) & 1;
|
|
285
|
+
const expectedWing = move.includes("w") && !move.endsWith("2") ? 1 : 0;
|
|
286
|
+
if (wingParity !== expectedWing) {
|
|
287
|
+
errors.push(`Phase 3 move ${move} has unexpected wing permutation parity.`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// Phase 2 moves keep U/D colors on U/D faces; wing-parity flips only on
|
|
291
|
+
// the Uw/Dw quarter turns (the tracked bit).
|
|
292
|
+
for (const move of PHASE_2_MOVES) {
|
|
293
|
+
const { face, turns } = parseMove(move);
|
|
294
|
+
const effect = FACE_MOVE_EFFECTS[face];
|
|
295
|
+
const wingParity = (permutationParityOf(effect.wperm) * (turns & 1)) & 1;
|
|
296
|
+
const expectedWing = move.includes("w") && !move.endsWith("2") ? 1 : 0;
|
|
297
|
+
if (wingParity !== expectedWing) {
|
|
298
|
+
errors.push(`Phase 2 move ${move} has unexpected wing permutation parity.`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return errors;
|
|
302
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node loader for the cube4 reduction tables (the committed .bin files).
|
|
3
|
+
* The manifests are per-phase (`center1-manifest.json`, ...).
|
|
4
|
+
*/
|
|
5
|
+
import type { Center1Tables } from "./center1.js";
|
|
6
|
+
import type { Center2Tables } from "./center2.js";
|
|
7
|
+
import type { Center3Tables } from "./center3.js";
|
|
8
|
+
import type { Edge3Tables } from "./edge3.js";
|
|
9
|
+
export interface Cube4ReductionTables {
|
|
10
|
+
center1: Center1Tables;
|
|
11
|
+
center2: Center2Tables;
|
|
12
|
+
center3: Center3Tables;
|
|
13
|
+
edge3: Edge3Tables;
|
|
14
|
+
}
|
|
15
|
+
/** Load all cube4 reduction tables (throws if the generated assets are missing). */
|
|
16
|
+
export declare function loadCube4ReductionTables(): Cube4ReductionTables;
|
|
17
|
+
//# sourceMappingURL=node-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-loader.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/node-loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAI9C,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,KAAK,EAAE,WAAW,CAAC;CACpB;AAuGD,oFAAoF;AACpF,wBAAgB,wBAAwB,IAAI,oBAAoB,CAW/D"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node loader for the cube4 reduction tables (the committed .bin files).
|
|
3
|
+
* The manifests are per-phase (`center1-manifest.json`, ...).
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
+
import { createRequire } from "node:module";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { createHash } from "node:crypto";
|
|
9
|
+
import { edge3DistanceLookup } from "./edge3.js";
|
|
10
|
+
const GENERATED_DIRECTORY = "puzzles/cube4/reduction/generated";
|
|
11
|
+
function resolveGeneratedAssetDirectory() {
|
|
12
|
+
const envDirectory = process.env.CUBINGCENTER_SCRAMBLER_TABLES_DIR;
|
|
13
|
+
if (envDirectory && existsSync(path.join(envDirectory, "center1-manifest.json"))) {
|
|
14
|
+
return envDirectory;
|
|
15
|
+
}
|
|
16
|
+
for (const candidate of getAssetDirectoryCandidates()) {
|
|
17
|
+
if (existsSync(path.join(candidate, "center1-manifest.json"))) {
|
|
18
|
+
return candidate;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return path.join(process.cwd(), "src", GENERATED_DIRECTORY);
|
|
22
|
+
}
|
|
23
|
+
function getAssetDirectoryCandidates() {
|
|
24
|
+
const cwd = process.cwd();
|
|
25
|
+
const candidates = [
|
|
26
|
+
path.join(cwd, "src", GENERATED_DIRECTORY),
|
|
27
|
+
path.join(cwd, "dist", GENERATED_DIRECTORY),
|
|
28
|
+
path.join(cwd, "dist-cjs", GENERATED_DIRECTORY)
|
|
29
|
+
];
|
|
30
|
+
try {
|
|
31
|
+
const requireFromCwd = createRequire(path.join(cwd, "package.json"));
|
|
32
|
+
const packageEntry = requireFromCwd.resolve("@cubingcenter/scrambler");
|
|
33
|
+
candidates.push(path.join(path.dirname(packageEntry), GENERATED_DIRECTORY));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Source-tree usage is covered by the cwd candidates.
|
|
37
|
+
}
|
|
38
|
+
return candidates;
|
|
39
|
+
}
|
|
40
|
+
function sha256(buffer) {
|
|
41
|
+
return createHash("sha256").update(buffer).digest("hex");
|
|
42
|
+
}
|
|
43
|
+
function readChecked(file, expectedChecksum) {
|
|
44
|
+
const buffer = readFileSync(file);
|
|
45
|
+
if (sha256(buffer) !== expectedChecksum) {
|
|
46
|
+
throw new Error(`Table checksum mismatch: ${file}`);
|
|
47
|
+
}
|
|
48
|
+
return buffer;
|
|
49
|
+
}
|
|
50
|
+
function readInt32(buffer) {
|
|
51
|
+
return new Int32Array(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength));
|
|
52
|
+
}
|
|
53
|
+
function loadCenter1(assetDirectory) {
|
|
54
|
+
const manifest = JSON.parse(readFileSync(path.join(assetDirectory, "center1-manifest.json"), "utf8"));
|
|
55
|
+
return {
|
|
56
|
+
distance: new Uint8Array(readChecked(path.join(assetDirectory, manifest.distanceTable.file), manifest.distanceTable.checksum)),
|
|
57
|
+
moveMaps: readInt32(readChecked(path.join(assetDirectory, manifest.moveMaps.file), manifest.moveMaps.checksum))
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function loadCenter2(assetDirectory) {
|
|
61
|
+
const manifest = JSON.parse(readFileSync(path.join(assetDirectory, "center2-manifest.json"), "utf8"));
|
|
62
|
+
const pruningBuffer = readChecked(path.join(assetDirectory, manifest.pruningTable.file), manifest.pruningTable.checksum);
|
|
63
|
+
const pruning = new Uint8Array(450450);
|
|
64
|
+
for (let i = 0; i < 450450; i++) {
|
|
65
|
+
const byte = pruningBuffer[i >> 1];
|
|
66
|
+
pruning[i] = (i & 1) === 0 ? byte & 0x0f : byte >> 4;
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
ctMove: readInt32(readChecked(path.join(assetDirectory, manifest.ctMove.file), manifest.ctMove.checksum)),
|
|
70
|
+
rlMove: readInt32(readChecked(path.join(assetDirectory, manifest.rlMove.file), manifest.rlMove.checksum)),
|
|
71
|
+
pruning
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function loadCenter3(assetDirectory) {
|
|
75
|
+
const manifest = JSON.parse(readFileSync(path.join(assetDirectory, "center3-manifest.json"), "utf8"));
|
|
76
|
+
return {
|
|
77
|
+
moveTable: readInt32(readChecked(path.join(assetDirectory, manifest.moveTable.file), manifest.moveTable.checksum)),
|
|
78
|
+
pruning: new Uint8Array(readChecked(path.join(assetDirectory, manifest.pruningTable.file), manifest.pruningTable.checksum))
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function loadEdge3(assetDirectory) {
|
|
82
|
+
const manifest = JSON.parse(readFileSync(path.join(assetDirectory, "edge3-manifest.json"), "utf8"));
|
|
83
|
+
const distances = new Uint8Array(readChecked(path.join(assetDirectory, manifest.distances.file), manifest.distances.checksum));
|
|
84
|
+
return { distances, distanceOfEdge: edge3DistanceLookup(distances) };
|
|
85
|
+
}
|
|
86
|
+
/** Load all cube4 reduction tables (throws if the generated assets are missing). */
|
|
87
|
+
export function loadCube4ReductionTables() {
|
|
88
|
+
const assetDirectory = resolveGeneratedAssetDirectory();
|
|
89
|
+
if (!existsSync(path.join(assetDirectory, "center1-manifest.json"))) {
|
|
90
|
+
throw new Error("Cube4 reduction tables not generated — run pnpm cube4:center1:tables etc.");
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
center1: loadCenter1(assetDirectory),
|
|
94
|
+
center2: loadCenter2(assetDirectory),
|
|
95
|
+
center3: loadCenter3(assetDirectory),
|
|
96
|
+
edge3: loadEdge3(assetDirectory)
|
|
97
|
+
};
|
|
98
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
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 type { Cube4Move } from "../types.js";
|
|
24
|
+
/**
|
|
25
|
+
* Phase-3 move set (20 moves, the reference's `move3std` minus its trailing
|
|
26
|
+
* no-op sentinel — index 36, used only as the "previous move" dummy for
|
|
27
|
+
* cancellation pruning): face turns + double wide turns. All of them are
|
|
28
|
+
* even wing permutations.
|
|
29
|
+
*/
|
|
30
|
+
export declare const PAIRING_MOVES: readonly Cube4Move[];
|
|
31
|
+
/**
|
|
32
|
+
* The 17-move BFS subset used to build the pruning table (reference
|
|
33
|
+
* `bfsMoves`): omits Dw2/Lw2/Bw2 (rotation-conjugate to Uw2/Rw2/Fw2).
|
|
34
|
+
*/
|
|
35
|
+
export declare const PAIRING_BFS_MOVES: readonly Cube4Move[];
|
|
36
|
+
/**
|
|
37
|
+
* The 12 edge slots as (first-half, second-half) position pairs, in this
|
|
38
|
+
* repo's position numbering. Derived from the reference's `FullEdgeMap`
|
|
39
|
+
* through the sticker isomorphism between the two layouts:
|
|
40
|
+
*
|
|
41
|
+
* slot i = (π(FullEdgeMap[i]), π(FullEdgeMap[i] + 12))
|
|
42
|
+
*
|
|
43
|
+
* where π maps the reference's position ids to ours by matching the two
|
|
44
|
+
* stickers of each wing position. The phase-3 move set preserves the
|
|
45
|
+
* transversal property for exactly this split.
|
|
46
|
+
*/
|
|
47
|
+
export declare const PAIRING_SLOTS: readonly (readonly [number, number])[];
|
|
48
|
+
/** First-half position of each slot. */
|
|
49
|
+
export declare const PAIRING_FIRST_HALVES: readonly number[];
|
|
50
|
+
/** Second-half position of each slot. */
|
|
51
|
+
export declare const PAIRING_SECOND_HALVES: readonly number[];
|
|
52
|
+
/**
|
|
53
|
+
* Map the 24 labeled wings to their pair ids: `arr24[i] = wp[i] >> 1`.
|
|
54
|
+
* Each pair id 0-11 appears exactly twice.
|
|
55
|
+
*/
|
|
56
|
+
export declare function pairIdArray(wp: Uint8Array): Uint8Array;
|
|
57
|
+
/**
|
|
58
|
+
* Apply a move to a 24-entry pair-id array (position permutation from the
|
|
59
|
+
* verified move tables).
|
|
60
|
+
*/
|
|
61
|
+
export declare function applyPairingMoveToArray(arr24: Uint8Array, move: Cube4Move): Uint8Array;
|
|
62
|
+
/**
|
|
63
|
+
* True iff the 12 second-half positions hold exactly one wing of each pair —
|
|
64
|
+
* i.e. the pair ids there are all distinct. Required by `encodePairingState`
|
|
65
|
+
* and preserved by the phase-3 move set (verified).
|
|
66
|
+
*/
|
|
67
|
+
export declare function hasPairingTransversal(wp: Uint8Array): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Wing-permutation parity of the state. For transversal-eligible states this
|
|
70
|
+
* is the phase-2 → phase-3 filter parity (the reference's `checkPhase2Edge`
|
|
71
|
+
* `parity == 0` condition, verified equivalent): the bridge must only accept
|
|
72
|
+
* even-parity states so the derived 3x3 edge permutation comes out even.
|
|
73
|
+
*/
|
|
74
|
+
export declare function pairingGateParity(wp: Uint8Array): 0 | 1;
|
|
75
|
+
export interface PairingEncodeResult {
|
|
76
|
+
/** 12-permutation, `identity` iff all 12 slots are paired. */
|
|
77
|
+
edge: Uint8Array;
|
|
78
|
+
/**
|
|
79
|
+
* Parity bit returned by the encode (the reference XORs this into the
|
|
80
|
+
* phase-3 center coordinate: `parity ^ cornerParity`, and its evolution
|
|
81
|
+
* under the phase-3 moves — the wide doubles flip it — is tracked by that
|
|
82
|
+
* coordinate's `pmove` bits, NOT here). Validated determinism only; the
|
|
83
|
+
* evolution semantics are verified with the Center3 integration.
|
|
84
|
+
*/
|
|
85
|
+
parity: 0 | 1;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Encode a 24-labeled-wing state to the pairing permutation.
|
|
89
|
+
*
|
|
90
|
+
* Requires the transversal property (`hasPairingTransversal`), else null.
|
|
91
|
+
*
|
|
92
|
+
* Construction (reference $set_6, adapted to PAIRING_SLOTS):
|
|
93
|
+
* - `edge[i]` starts as the pair id at the second-half position of slot i
|
|
94
|
+
* - cycle-sort it to identity while tracking the inverse `temp` and parity
|
|
95
|
+
* - `edge[i] = temp[pair id at the first-half position of slot i]`
|
|
96
|
+
*
|
|
97
|
+
* The result is in $std-normalized form (second halves implicitly hold
|
|
98
|
+
* pairs 0..11 in order).
|
|
99
|
+
*/
|
|
100
|
+
export declare function encodePairingState(wp: Uint8Array): PairingEncodeResult | null;
|
|
101
|
+
/**
|
|
102
|
+
* Decode the canonical representative: slot i's first-half position holds the
|
|
103
|
+
* even wing of pair `edge[i]`, its second-half position the odd wing of pair
|
|
104
|
+
* `edgeo[i]`. With `edgeo = identity` this is the $set_4 canonical form.
|
|
105
|
+
*/
|
|
106
|
+
export declare function decodePairingState(edge: Uint8Array, edgeo: Uint8Array): Uint8Array;
|
|
107
|
+
/**
|
|
108
|
+
* True iff the pairing permutation is fully reduced (all 12 slots paired).
|
|
109
|
+
*/
|
|
110
|
+
export declare function isPairingReduced(edge: Uint8Array): boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Apply a phase-3 move to a pairing state, keeping the (edge, edgeo)
|
|
113
|
+
* representation: decode → physical move (verified move tables) → re-derive
|
|
114
|
+
* the slot halves → $std-normalize. Valid because the phase-3 move set
|
|
115
|
+
* preserves the transversal property (verified).
|
|
116
|
+
*/
|
|
117
|
+
export declare function applyPairingMoveToState(edge: Uint8Array, edgeo: Uint8Array, move: Cube4Move): {
|
|
118
|
+
edge: Uint8Array;
|
|
119
|
+
edgeo: Uint8Array;
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* $std-normalize a pairing state: reindex so the second halves hold pairs
|
|
123
|
+
* 0..11 in order (the `edgeo` array becomes the identity).
|
|
124
|
+
*/
|
|
125
|
+
export declare function normalizePairingState(edge: Uint8Array, edgeo: Uint8Array): {
|
|
126
|
+
edge: Uint8Array;
|
|
127
|
+
edgeo: Uint8Array;
|
|
128
|
+
};
|
|
129
|
+
export declare function verifyPairingCoordinate(): string[];
|
|
130
|
+
//# sourceMappingURL=pairing-coordinate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pairing-coordinate.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/pairing-coordinate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAQ7C;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,SAAS,EAGpC,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,SAAS,EAGxC,CAAC;AAMX;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAG/D,CAAC;AAEF,wCAAwC;AACxC,eAAO,MAAM,oBAAoB,EAAE,SAAS,MAAM,EAAkC,CAAC;AAErF,yCAAyC;AACzC,eAAO,MAAM,qBAAqB,EAAE,SAAS,MAAM,EAAoC,CAAC;AAMxF;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,UAAU,GAAG,UAAU,CAItD;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,GAAG,UAAU,CAUtF;AAMD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,UAAU,GAAG,OAAO,CAQ7D;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,UAAU,GAAG,CAAC,GAAG,CAAC,CAevD;AAMD,MAAM,WAAW,mBAAmB;IAClC,8DAA8D;IAC9D,IAAI,EAAE,UAAU,CAAC;IACjB;;;;;;OAMG;IACH,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC;CACf;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,UAAU,GAAG,mBAAmB,GAAG,IAAI,CA2B7E;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,UAAU,CAOlF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAK1D;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,SAAS,GACd;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,CAUzC;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,UAAU,GAChB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,CAUzC;AAMD,wBAAgB,uBAAuB,IAAI,MAAM,EAAE,CA+BlD"}
|