@cubingcenter/scrambler 0.6.3 → 0.7.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 +2 -0
- package/dist/puzzles/cube2/solver/search.d.ts.map +1 -1
- package/dist/puzzles/cube2/solver/search.js +3 -3
- package/dist/puzzles/cube2/solver/tables.d.ts +6 -0
- package/dist/puzzles/cube2/solver/tables.d.ts.map +1 -1
- package/dist/puzzles/cube2/solver/tables.js +16 -2
- package/dist/puzzles/cube3/index.d.ts +1 -0
- package/dist/puzzles/cube3/index.d.ts.map +1 -1
- package/dist/puzzles/cube3/moves.d.ts.map +1 -1
- package/dist/puzzles/cube3/moves.js +104 -18
- package/dist/puzzles/cube3/scrambler.d.ts +16 -1
- package/dist/puzzles/cube3/scrambler.d.ts.map +1 -1
- package/dist/puzzles/cube3/scrambler.js +1 -1
- package/dist/puzzles/cube3/solver/browser-loader.js +6 -2
- package/dist/puzzles/cube3/solver/index.d.ts +5 -3
- package/dist/puzzles/cube3/solver/index.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/index.js +2 -1
- package/dist/puzzles/cube3/solver/node-loader.js +9 -2
- package/dist/puzzles/cube3/solver/phase1.d.ts +18 -0
- package/dist/puzzles/cube3/solver/phase1.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/phase1.js +132 -14
- package/dist/puzzles/cube3/solver/phase2.d.ts +47 -0
- package/dist/puzzles/cube3/solver/phase2.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/phase2.js +236 -66
- package/dist/puzzles/cube3/solver/search-utils.d.ts +1 -1
- package/dist/puzzles/cube3/solver/search-utils.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/symmetry.d.ts +24 -0
- package/dist/puzzles/cube3/solver/symmetry.d.ts.map +1 -0
- package/dist/puzzles/cube3/solver/symmetry.js +74 -0
- package/dist/puzzles/cube3/solver/tables.d.ts +6 -1
- package/dist/puzzles/cube3/solver/tables.d.ts.map +1 -1
- package/dist/puzzles/cube3/validation.d.ts.map +1 -1
- package/dist/puzzles/cube3/validation.js +15 -7
- package/dist/puzzles/cube4/index.d.ts +3 -2
- package/dist/puzzles/cube4/index.d.ts.map +1 -1
- package/dist/puzzles/cube4/index.js +2 -2
- package/dist/puzzles/cube4/moves.d.ts +5 -0
- package/dist/puzzles/cube4/moves.d.ts.map +1 -1
- package/dist/puzzles/cube4/moves.js +120 -11
- package/dist/puzzles/cube4/reduction/browser-loader.d.ts +3 -0
- package/dist/puzzles/cube4/reduction/browser-loader.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/browser-loader.js +34 -3
- package/dist/puzzles/cube4/reduction/center2.d.ts +20 -3
- package/dist/puzzles/cube4/reduction/center2.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/center2.js +53 -9
- package/dist/puzzles/cube4/reduction/edge3.d.ts +4 -0
- package/dist/puzzles/cube4/reduction/edge3.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/edge3.js +23 -3
- package/dist/puzzles/cube4/reduction/generated/c3_pair_dist.bin +1 -0
- package/dist/puzzles/cube4/reduction/generated/center2-manifest.json +36 -0
- package/dist/puzzles/cube4/reduction/generated/center2-pruning-wide.bin +1 -0
- package/dist/puzzles/cube4/reduction/generated/hc-distances.bin +1 -0
- package/dist/puzzles/cube4/reduction/generated/hc-manifest.json +38 -0
- package/dist/puzzles/cube4/reduction/generated/hc-move.bin +0 -0
- package/dist/puzzles/cube4/reduction/hc-pdb.d.ts +50 -0
- package/dist/puzzles/cube4/reduction/hc-pdb.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/hc-pdb.js +202 -0
- package/dist/puzzles/cube4/reduction/node-loader.d.ts +5 -0
- package/dist/puzzles/cube4/reduction/node-loader.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/node-loader.js +48 -8
- package/dist/puzzles/cube4/reduction/pairing-coordinate.js +5 -5
- package/dist/puzzles/cube4/reduction/phase3-search.d.ts +111 -5
- package/dist/puzzles/cube4/reduction/phase3-search.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/phase3-search.js +341 -22
- package/dist/puzzles/cube4/reduction/solve.d.ts +112 -1
- package/dist/puzzles/cube4/reduction/solve.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/solve.js +920 -181
- package/dist/puzzles/cube4/scrambler.d.ts +105 -4
- package/dist/puzzles/cube4/scrambler.d.ts.map +1 -1
- package/dist/puzzles/cube4/scrambler.js +216 -166
- package/dist/puzzles/cube4/state.d.ts.map +1 -1
- package/dist/puzzles/cube4/state.js +4 -1
- package/dist/puzzles/pyraminx/solver/search.d.ts.map +1 -1
- package/dist/puzzles/pyraminx/solver/search.js +3 -6
- package/dist/puzzles/pyraminx/solver/tables.d.ts +6 -0
- package/dist/puzzles/pyraminx/solver/tables.d.ts.map +1 -1
- package/dist/puzzles/pyraminx/solver/tables.js +17 -0
- package/dist/puzzles/skewb/solver/search.js +3 -3
- package/dist/puzzles/skewb/solver/tables.d.ts +6 -0
- package/dist/puzzles/skewb/solver/tables.d.ts.map +1 -1
- package/dist/puzzles/skewb/solver/tables.js +17 -0
- package/dist-cjs/puzzles/cube2/solver/search.js +2 -2
- package/dist-cjs/puzzles/cube2/solver/tables.js +17 -3
- package/dist-cjs/puzzles/cube3/moves.js +104 -18
- package/dist-cjs/puzzles/cube3/scrambler.js +1 -1
- package/dist-cjs/puzzles/cube3/solver/browser-loader.js +6 -2
- package/dist-cjs/puzzles/cube3/solver/index.js +8 -1
- package/dist-cjs/puzzles/cube3/solver/node-loader.js +9 -2
- package/dist-cjs/puzzles/cube3/solver/phase1.js +133 -14
- package/dist-cjs/puzzles/cube3/solver/phase2.js +233 -63
- package/dist-cjs/puzzles/cube3/solver/symmetry.js +80 -0
- package/dist-cjs/puzzles/cube3/validation.js +15 -7
- package/dist-cjs/puzzles/cube4/index.js +3 -1
- package/dist-cjs/puzzles/cube4/moves.js +121 -11
- package/dist-cjs/puzzles/cube4/reduction/browser-loader.js +34 -3
- package/dist-cjs/puzzles/cube4/reduction/center2.js +54 -9
- package/dist-cjs/puzzles/cube4/reduction/edge3.js +24 -3
- package/dist-cjs/puzzles/cube4/reduction/generated/c3_pair_dist.bin +1 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/center2-manifest.json +36 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/center2-pruning-wide.bin +1 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/hc-distances.bin +1 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/hc-manifest.json +38 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/hc-move.bin +0 -0
- package/dist-cjs/puzzles/cube4/reduction/hc-pdb.js +213 -0
- package/dist-cjs/puzzles/cube4/reduction/node-loader.js +48 -8
- package/dist-cjs/puzzles/cube4/reduction/pairing-coordinate.js +5 -5
- package/dist-cjs/puzzles/cube4/reduction/phase3-search.js +343 -23
- package/dist-cjs/puzzles/cube4/reduction/solve.js +918 -173
- package/dist-cjs/puzzles/cube4/scrambler.js +214 -163
- package/dist-cjs/puzzles/cube4/state.js +4 -1
- package/dist-cjs/puzzles/pyraminx/solver/search.js +2 -5
- package/dist-cjs/puzzles/pyraminx/solver/tables.js +18 -1
- package/dist-cjs/puzzles/skewb/solver/search.js +2 -2
- package/dist-cjs/puzzles/skewb/solver/tables.js +18 -1
- package/package.json +14 -5
package/README.md
CHANGED
|
@@ -196,6 +196,8 @@ Supported moves: `U D R L F B` (outer) and `Uw Dw Rw Lw Fw Bw` (wide), each with
|
|
|
196
196
|
|
|
197
197
|
The public `generateScramble("444")` path is: sample a uniform random state (`generateRandomCube4State` — corners `8! × 3^7`, wings `24!`, centers as a color multiset `24!/(4!)^6`), solve it with the reduction solver (phases: axis-color face-pairs → ct/rl phase-2 goal → joint center3+pairing → 3x3 finish), and invert the solution — so the scramble always reproduces the sampled target. The pairing pruning table is generated by a C++ tool (`tools/edge3-gen/`).
|
|
198
198
|
|
|
199
|
+
Shorter 444 scrambles, two mechanisms: `bestOf: N` solves N independently sampled states and keeps the shortest (trades CPU time, biases toward short-solution states), while `restarts: R` re-runs the full pipeline R times over the SAME state with a deterministically reshuffled search order and keeps the shortest total (uniformity-preserving; measured ~-0.45 moves at R=3, ~-0.85 at R=6 on a 24-state corpus, single-attempt mean ~46.1).
|
|
200
|
+
|
|
199
201
|
`validateCube4State` checks the reachability constraints of a normal (non-super) 4x4: corner permutation + orientation sum divisible by 3, wing permutation (wings have no orientation), and 4-of-each-color centers. There is no visible corner/wing parity constraint on a normal 4x4 (all four parity combinations are reachable).
|
|
200
202
|
|
|
201
203
|
## CLI
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube2/solver/search.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube2/solver/search.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAezD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,UAAU,CAAC;CACnB;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,CAQ/E;AAKD,wBAAsB,sBAAsB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAGjF;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,EAAE,CAKtE;AAoDD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,SAAS,EAAE,GAAG,OAAO,CAUnG;AAGD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,SAAS,EAAE,GAAG,OAAO,CAOnG;AAMD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAclG"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createSolvedCube2State } from "../state.js";
|
|
2
|
-
import { encodeOrientation, encodePermutation, ensureTablesLoaded, PERMUTATION_TABLE, ORIENTATION_TABLE, POLICY_TABLE, ORIENTATION_COUNT, BFS_MOVES, MOVE_COUNT } from "./tables.js";
|
|
2
|
+
import { encodeOrientation, encodePermutation, ensureTablesLoaded, PERMUTATION_TABLE, ORIENTATION_TABLE, POLICY_TABLE, POLICY_TABLE_EXPANDED, ORIENTATION_COUNT, BFS_MOVES, MOVE_COUNT } from "./tables.js";
|
|
3
3
|
export async function solveCube2State(state) {
|
|
4
4
|
await ensureTablesLoaded();
|
|
5
5
|
const indices = solveWithTablesIndices(state);
|
|
@@ -29,7 +29,7 @@ function solveWithTablesIndices(state) {
|
|
|
29
29
|
}
|
|
30
30
|
const permTable = PERMUTATION_TABLE;
|
|
31
31
|
const orientTable = ORIENTATION_TABLE;
|
|
32
|
-
const policy =
|
|
32
|
+
const policy = POLICY_TABLE_EXPANDED;
|
|
33
33
|
let permIndex = encodePermutation(state.corners.permutation);
|
|
34
34
|
let orientIndex = encodeOrientation(state.corners.orientation);
|
|
35
35
|
const moves = [];
|
|
@@ -37,7 +37,7 @@ function solveWithTablesIndices(state) {
|
|
|
37
37
|
// candidate scanning (the old BFS-distance gradient walk) is needed.
|
|
38
38
|
let combined = permIndex * ORIENTATION_COUNT + orientIndex;
|
|
39
39
|
while (combined !== 0) {
|
|
40
|
-
const m =
|
|
40
|
+
const m = policy[combined];
|
|
41
41
|
moves.push(m);
|
|
42
42
|
permIndex = permTable[permIndex * MOVE_COUNT + m];
|
|
43
43
|
orientIndex = orientTable[orientIndex * MOVE_COUNT + m];
|
|
@@ -6,6 +6,12 @@ export declare function encodeOrientation(orientation: readonly number[]): numbe
|
|
|
6
6
|
export declare let PERMUTATION_TABLE: Uint16Array | null;
|
|
7
7
|
export declare let ORIENTATION_TABLE: Uint16Array | null;
|
|
8
8
|
export declare let POLICY_TABLE: Uint8Array | null;
|
|
9
|
+
/**
|
|
10
|
+
* Byte-expanded policy (one optimal move per state, no bit-unpacking in
|
|
11
|
+
* the hot walk). Built at load from the packed table: +3.7 MB RAM,
|
|
12
|
+
* zero wire/format change.
|
|
13
|
+
*/
|
|
14
|
+
export declare let POLICY_TABLE_EXPANDED: Uint8Array | null;
|
|
9
15
|
export declare function ensureTablesLoaded(): Promise<void>;
|
|
10
16
|
export declare function areCube2TablesLoaded(): boolean;
|
|
11
17
|
export declare function getDistance(permIndex: number, orientIndex: number): number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube2/solver/tables.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,UAAU,IAAI,CAAC;AAW5B,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAYxE;AAWD,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAWxE;AAeD,eAAO,IAAI,iBAAiB,EAAE,WAAW,GAAG,IAAW,CAAC;AACxD,eAAO,IAAI,iBAAiB,EAAE,WAAW,GAAG,IAAW,CAAC;AACxD,eAAO,IAAI,YAAY,EAAE,UAAU,GAAG,IAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube2/solver/tables.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,UAAU,IAAI,CAAC;AAW5B,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAYxE;AAWD,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAWxE;AAeD,eAAO,IAAI,iBAAiB,EAAE,WAAW,GAAG,IAAW,CAAC;AACxD,eAAO,IAAI,iBAAiB,EAAE,WAAW,GAAG,IAAW,CAAC;AACxD,eAAO,IAAI,YAAY,EAAE,UAAU,GAAG,IAAW,CAAC;AAClD;;;;GAIG;AACH,eAAO,IAAI,qBAAqB,EAAE,UAAU,GAAG,IAAW,CAAC;AAE3D,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAcxD;AAUD,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAMD,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAkB1E;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAKhF;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAKlF;AAED,eAAO,MAAM,SAAS,EAAE,SAAS,MAAM,EAItC,CAAC"}
|
|
@@ -54,6 +54,12 @@ let tablesLoaded = false;
|
|
|
54
54
|
export let PERMUTATION_TABLE = null;
|
|
55
55
|
export let ORIENTATION_TABLE = null;
|
|
56
56
|
export let POLICY_TABLE = null;
|
|
57
|
+
/**
|
|
58
|
+
* Byte-expanded policy (one optimal move per state, no bit-unpacking in
|
|
59
|
+
* the hot walk). Built at load from the packed table: +3.7 MB RAM,
|
|
60
|
+
* zero wire/format change.
|
|
61
|
+
*/
|
|
62
|
+
export let POLICY_TABLE_EXPANDED = null;
|
|
57
63
|
export async function ensureTablesLoaded() {
|
|
58
64
|
if (tablesLoaded) {
|
|
59
65
|
return;
|
|
@@ -62,8 +68,16 @@ export async function ensureTablesLoaded() {
|
|
|
62
68
|
PERMUTATION_TABLE = tables.permutationTable;
|
|
63
69
|
ORIENTATION_TABLE = tables.orientationTable;
|
|
64
70
|
POLICY_TABLE = tables.policyTable;
|
|
71
|
+
POLICY_TABLE_EXPANDED = expandNibblePolicy(tables.policyTable, PERMUTATION_COUNT * ORIENTATION_COUNT);
|
|
65
72
|
tablesLoaded = true;
|
|
66
73
|
}
|
|
74
|
+
function expandNibblePolicy(packed, count) {
|
|
75
|
+
const expanded = new Uint8Array(count);
|
|
76
|
+
for (let index = 0; index < count; index += 1) {
|
|
77
|
+
expanded[index] = (packed[index >> 1] >> ((index & 1) << 2)) & 0x0f;
|
|
78
|
+
}
|
|
79
|
+
return expanded;
|
|
80
|
+
}
|
|
67
81
|
export function areCube2TablesLoaded() {
|
|
68
82
|
return tablesLoaded;
|
|
69
83
|
}
|
|
@@ -71,7 +85,7 @@ export function areCube2TablesLoaded() {
|
|
|
71
85
|
// Public API
|
|
72
86
|
// ---------------------------------------------------------------------------
|
|
73
87
|
export function getDistance(permIndex, orientIndex) {
|
|
74
|
-
if (!
|
|
88
|
+
if (!POLICY_TABLE_EXPANDED) {
|
|
75
89
|
throw new Error("Cube2 solver tables not loaded. Call ensureTablesLoaded() first.");
|
|
76
90
|
}
|
|
77
91
|
const permTable = PERMUTATION_TABLE;
|
|
@@ -81,7 +95,7 @@ export function getDistance(permIndex, orientIndex) {
|
|
|
81
95
|
let combined = p * ORIENTATION_COUNT + o;
|
|
82
96
|
let distance = 0;
|
|
83
97
|
while (combined !== 0) {
|
|
84
|
-
const m =
|
|
98
|
+
const m = POLICY_TABLE_EXPANDED[combined];
|
|
85
99
|
p = permTable[p * MOVE_COUNT + m];
|
|
86
100
|
o = orientTable[o * MOVE_COUNT + m];
|
|
87
101
|
combined = p * ORIENTATION_COUNT + o;
|
|
@@ -4,6 +4,7 @@ export { cloneCube3State, createSolvedCube3State, cube3StatesEqual, isSolvedCube
|
|
|
4
4
|
export { isAllowedRandomCube3State, isOneMoveFromSolvedCube3State, permutationParity, validateCube3State } from "./validation.js";
|
|
5
5
|
export { generateRandomCube3State } from "./random-state.js";
|
|
6
6
|
export { cube3ScrambleGenerator, generateCube3Scramble } from "./scrambler.js";
|
|
7
|
+
export type { Cube3ScrambleOptions } from "./scrambler.js";
|
|
7
8
|
export { formatCube3Facelets, getCube3FaceOrder, toCube3Facelets } from "./facelets.js";
|
|
8
9
|
export * as solver from "./solver/index.js";
|
|
9
10
|
export type { Cube3Face, Cube3Facelets, Cube3Move, Cube3FaceMove, Cube3MoveSuffix, Cube3RandomStateOptions, Cube3Sticker, Cube3State, Cube3ValidationResult } from "./types.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/index.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,WAAW,EACX,YAAY,EACZ,UAAU,EACX,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,cAAc,EACf,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,yBAAyB,EACzB,6BAA6B,EAC7B,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,wBAAwB,EACzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,EAChB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,YAAY,EACV,SAAS,EACT,aAAa,EACb,SAAS,EACT,aAAa,EACb,eAAe,EACf,uBAAuB,EACvB,YAAY,EACZ,UAAU,EACV,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/index.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,WAAW,EACX,YAAY,EACZ,UAAU,EACX,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,cAAc,EACf,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,yBAAyB,EACzB,6BAA6B,EAC7B,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,wBAAwB,EACzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACtB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,EAChB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,YAAY,EACV,SAAS,EACT,aAAa,EACb,SAAS,EACT,aAAa,EACb,eAAe,EACf,uBAAuB,EACvB,YAAY,EACZ,UAAU,EACV,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,qBAAqB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"moves.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/moves.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAiB,SAAS,EAAmB,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"moves.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/moves.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAiB,SAAS,EAAmB,UAAU,EAAE,MAAM,YAAY,CAAC;AAyGxF,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,GAAG,UAAU,CAsB7E;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,UAAU,CA+B9F;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,EAAE,CAE7E;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,CAoFlE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,MAAM,CAExE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAMvD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAQ1D"}
|
|
@@ -26,16 +26,114 @@ const FACE_MOVE_TABLES = {
|
|
|
26
26
|
edges: { permutation: [0, 1, 2, 11, 4, 5, 6, 10, 8, 9, 3, 7], orientation: [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1] }
|
|
27
27
|
}
|
|
28
28
|
};
|
|
29
|
-
|
|
29
|
+
function quarterEffect(face) {
|
|
30
|
+
const table = FACE_MOVE_TABLES[face];
|
|
31
|
+
return {
|
|
32
|
+
cornerPermutation: table.corners.permutation,
|
|
33
|
+
cornerOrientation: table.corners.orientation,
|
|
34
|
+
edgePermutation: table.edges.permutation,
|
|
35
|
+
edgeOrientation: table.edges.orientation
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
// Compose two effects (apply `first`, then `second`) into one so a move of
|
|
39
|
+
// any suffix costs exactly one pass over the state — no per-quarter-turn
|
|
40
|
+
// cloning, no intermediate states, no closure allocations.
|
|
41
|
+
function composeEffects(first, second) {
|
|
42
|
+
const cornerPermutation = new Array(8);
|
|
43
|
+
const cornerOrientation = new Array(8);
|
|
44
|
+
const edgePermutation = new Array(12);
|
|
45
|
+
const edgeOrientation = new Array(12);
|
|
46
|
+
for (let i = 0; i < 8; i++) {
|
|
47
|
+
const source = second.cornerPermutation[i];
|
|
48
|
+
cornerPermutation[i] = first.cornerPermutation[source];
|
|
49
|
+
cornerOrientation[i] = (first.cornerOrientation[source] + second.cornerOrientation[i]) % 3;
|
|
50
|
+
}
|
|
51
|
+
for (let i = 0; i < 12; i++) {
|
|
52
|
+
const source = second.edgePermutation[i];
|
|
53
|
+
edgePermutation[i] = first.edgePermutation[source];
|
|
54
|
+
edgeOrientation[i] = (first.edgeOrientation[source] + second.edgeOrientation[i]) % 2;
|
|
55
|
+
}
|
|
56
|
+
return { cornerPermutation, cornerOrientation, edgePermutation, edgeOrientation };
|
|
57
|
+
}
|
|
58
|
+
function powerEffect(face, turns) {
|
|
59
|
+
const quarter = quarterEffect(face);
|
|
60
|
+
if (turns === 1)
|
|
61
|
+
return quarter;
|
|
62
|
+
const half = composeEffects(quarter, quarter);
|
|
63
|
+
if (turns === 2)
|
|
64
|
+
return half;
|
|
65
|
+
return composeEffects(half, quarter);
|
|
66
|
+
}
|
|
67
|
+
// Total effect per move string, built once at module load (mirrors the 4x4
|
|
68
|
+
// fast path): one lookup + one pass per move.
|
|
69
|
+
const COMPOSED_EFFECTS = Object.fromEntries(CUBE3_MOVES.map((move) => {
|
|
70
|
+
const face = move[0];
|
|
71
|
+
const suffix = move.slice(1);
|
|
72
|
+
const turns = suffix === "2" ? 2 : suffix === "'" ? 3 : 1;
|
|
73
|
+
return [move, powerEffect(face, turns)];
|
|
74
|
+
}));
|
|
75
|
+
function effectForMove(move) {
|
|
76
|
+
const hit = COMPOSED_EFFECTS[move];
|
|
77
|
+
if (hit !== undefined)
|
|
78
|
+
return hit;
|
|
30
79
|
const { face, turns } = parseMoveParts(move);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
80
|
+
return powerEffect(face, turns);
|
|
81
|
+
}
|
|
82
|
+
export function applyCube3Move(state, move) {
|
|
83
|
+
const effect = effectForMove(move);
|
|
84
|
+
const cornerPermutation = new Array(8);
|
|
85
|
+
const cornerOrientation = new Array(8);
|
|
86
|
+
const edgePermutation = new Array(12);
|
|
87
|
+
const edgeOrientation = new Array(12);
|
|
88
|
+
for (let i = 0; i < 8; i++) {
|
|
89
|
+
const source = effect.cornerPermutation[i];
|
|
90
|
+
cornerPermutation[i] = state.corners.permutation[source];
|
|
91
|
+
cornerOrientation[i] =
|
|
92
|
+
(state.corners.orientation[source] + effect.cornerOrientation[i]) % 3;
|
|
34
93
|
}
|
|
35
|
-
|
|
94
|
+
for (let i = 0; i < 12; i++) {
|
|
95
|
+
const source = effect.edgePermutation[i];
|
|
96
|
+
edgePermutation[i] = state.edges.permutation[source];
|
|
97
|
+
edgeOrientation[i] =
|
|
98
|
+
(state.edges.orientation[source] + effect.edgeOrientation[i]) % 2;
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
corners: { permutation: cornerPermutation, orientation: cornerOrientation },
|
|
102
|
+
edges: { permutation: edgePermutation, orientation: edgeOrientation }
|
|
103
|
+
};
|
|
36
104
|
}
|
|
37
105
|
export function applyCube3Algorithm(state, moves) {
|
|
38
|
-
|
|
106
|
+
if (moves.length === 0)
|
|
107
|
+
return cloneCube3State(state);
|
|
108
|
+
let curCp = [...state.corners.permutation];
|
|
109
|
+
let curCo = [...state.corners.orientation];
|
|
110
|
+
let curEp = [...state.edges.permutation];
|
|
111
|
+
let curEo = [...state.edges.orientation];
|
|
112
|
+
let nxtCp = new Array(8);
|
|
113
|
+
let nxtCo = new Array(8);
|
|
114
|
+
let nxtEp = new Array(12);
|
|
115
|
+
let nxtEo = new Array(12);
|
|
116
|
+
for (const move of moves) {
|
|
117
|
+
const effect = effectForMove(move);
|
|
118
|
+
for (let i = 0; i < 8; i++) {
|
|
119
|
+
const source = effect.cornerPermutation[i];
|
|
120
|
+
nxtCp[i] = curCp[source];
|
|
121
|
+
nxtCo[i] = (curCo[source] + effect.cornerOrientation[i]) % 3;
|
|
122
|
+
}
|
|
123
|
+
for (let i = 0; i < 12; i++) {
|
|
124
|
+
const source = effect.edgePermutation[i];
|
|
125
|
+
nxtEp[i] = curEp[source];
|
|
126
|
+
nxtEo[i] = (curEo[source] + effect.edgeOrientation[i]) % 2;
|
|
127
|
+
}
|
|
128
|
+
[curCp, nxtCp] = [nxtCp, curCp];
|
|
129
|
+
[curCo, nxtCo] = [nxtCo, curCo];
|
|
130
|
+
[curEp, nxtEp] = [nxtEp, curEp];
|
|
131
|
+
[curEo, nxtEo] = [nxtEo, curEo];
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
corners: { permutation: curCp, orientation: curCo },
|
|
135
|
+
edges: { permutation: curEp, orientation: curEo }
|
|
136
|
+
};
|
|
39
137
|
}
|
|
40
138
|
export function invertCube3Algorithm(moves) {
|
|
41
139
|
return [...moves].reverse().map(invertCube3Move);
|
|
@@ -190,18 +288,6 @@ export function invertCube3Move(move) {
|
|
|
190
288
|
}
|
|
191
289
|
return `${face}${suffix === "'" ? "" : "'"}`;
|
|
192
290
|
}
|
|
193
|
-
function applyFaceMove(state, table) {
|
|
194
|
-
return {
|
|
195
|
-
corners: {
|
|
196
|
-
permutation: table.corners.permutation.map((source) => state.corners.permutation[source]),
|
|
197
|
-
orientation: table.corners.permutation.map((source, target) => ((state.corners.orientation[source] + table.corners.orientation[target]) % 3))
|
|
198
|
-
},
|
|
199
|
-
edges: {
|
|
200
|
-
permutation: table.edges.permutation.map((source) => state.edges.permutation[source]),
|
|
201
|
-
orientation: table.edges.permutation.map((source, target) => ((state.edges.orientation[source] + table.edges.orientation[target]) % 2))
|
|
202
|
-
}
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
291
|
function parseMoveParts(move) {
|
|
206
292
|
const face = move[0];
|
|
207
293
|
const suffix = move.slice(1);
|
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
import type { ScrambleGenerator, ScrambleOptions, ScrambleResult } from "../../core/index.js";
|
|
2
|
+
import type { Cube3SolveOptions } from "./solver/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* 333-specific scramble options (pass-through; extra keys are ignored by
|
|
5
|
+
* the generic `generateScramble` API surface).
|
|
6
|
+
*/
|
|
7
|
+
export interface Cube3ScrambleOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Solver tuning. Tiers (browser tables): `fast` (~14 ms, ~23.1 moves),
|
|
10
|
+
* `short` default (~36 ms, ~21.9 moves), `best` (~116 ms, ~20.8 moves,
|
|
11
|
+
* max 22). Granular overrides: `phase1ExtraDepth`,
|
|
12
|
+
* `phase1SolutionLimit`, `improvePhase2TimeoutMs`, `orientations`
|
|
13
|
+
* (1-24), `targetLength`, `timeoutMs`.
|
|
14
|
+
*/
|
|
15
|
+
solver?: Cube3SolveOptions;
|
|
16
|
+
}
|
|
2
17
|
export declare const cube3ScrambleGenerator: ScrambleGenerator;
|
|
3
|
-
export declare function generateCube3Scramble(options?: ScrambleOptions & {
|
|
18
|
+
export declare function generateCube3Scramble(options?: ScrambleOptions & Cube3ScrambleOptions & {
|
|
4
19
|
skipValidation?: boolean;
|
|
5
20
|
}): Promise<ScrambleResult>;
|
|
6
21
|
//# sourceMappingURL=scrambler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scrambler.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/scrambler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"scrambler.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/scrambler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAU9F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B;AAED,eAAO,MAAM,sBAAsB,EAAE,iBAYpC,CAAC;AAEF,wBAAsB,qBAAqB,CACzC,OAAO,GAAE,eAAe,GAAG,oBAAoB,GAAG;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAO,GAClF,OAAO,CAAC,cAAc,CAAC,CAgDzB"}
|
|
@@ -18,7 +18,7 @@ export const cube3ScrambleGenerator = {
|
|
|
18
18
|
};
|
|
19
19
|
export async function generateCube3Scramble(options = {}) {
|
|
20
20
|
const state = generateRandomCube3State(options);
|
|
21
|
-
const solution = await solveCube3State(state);
|
|
21
|
+
const solution = await solveCube3State(state, options.solver ?? {});
|
|
22
22
|
const scrambleMoves = invertCube3Algorithm(solution.moves);
|
|
23
23
|
if (!options.skipValidation) {
|
|
24
24
|
const scrambledState = applyCube3Algorithm(createSolvedCube3State(), scrambleMoves);
|
|
@@ -95,10 +95,14 @@ async function fetchMoveTable(baseUrl, entry) {
|
|
|
95
95
|
if (buffer.byteLength !== expectedBytes) {
|
|
96
96
|
throw new Error(`Invalid byte size for ${entry.coordinate} move table.`);
|
|
97
97
|
}
|
|
98
|
-
const values = new
|
|
98
|
+
const values = new Uint16Array(entry.range * entry.moves.length);
|
|
99
99
|
const view = new DataView(buffer);
|
|
100
100
|
for (let index = 0; index < values.length; index += 1) {
|
|
101
|
-
|
|
101
|
+
const value = view.getInt32(index * 4, true);
|
|
102
|
+
if (value < 0 || value > 0xffff) {
|
|
103
|
+
throw new Error(`Move table value out of range for ${entry.coordinate}: ${value}.`);
|
|
104
|
+
}
|
|
105
|
+
values[index] = value;
|
|
102
106
|
}
|
|
103
107
|
return {
|
|
104
108
|
coordinate: entry.coordinate,
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
export { applyCube3Phase2Moves, encodeCube3Phase2Coordinates, isCube3Phase2Solved, solveCube3Phase2, solveCube3State, Cube3Phase2SearchError } from "./phase2.js";
|
|
2
|
-
export { applyCube3Phase1Moves, encodeCube3Phase1Coordinates, isCube3Phase1Solved, solveCube3Phase1, Cube3Phase1SearchError } from "./phase1.js";
|
|
2
|
+
export { applyCube3Phase1Moves, collectCube3Phase1Solutions, encodeCube3Phase1Coordinates, isCube3Phase1Solved, solveCube3Phase1, Cube3Phase1SearchError } from "./phase1.js";
|
|
3
|
+
export { CUBE3_ORIENTATIONS_24, CUBE3_ROTATION_INVERSE, invertCube3RotationSequence, rotateCube3Algorithm, rotateCube3AlgorithmBySequence } from "./symmetry.js";
|
|
4
|
+
export type { Cube3Rotation, Cube3RotationSequence } from "./symmetry.js";
|
|
3
5
|
export { CUBE3_COORDINATE_RANGES, createRepresentativeStateForCoordinate, decodeCornerOrientation, decodeEdgeOrientation, decodePermutation, decodeSlicePermutation, decodeUdEdgePermutation, decodeUdSlice, encodeCube3Coordinate, encodeCornerOrientation, encodeCube3Coordinates, encodeEdgeOrientation, encodePermutation, encodeSlicePermutation, encodeUdEdgePermutation, encodeUdSlice, moveCoordinate } from "./coordinates.js";
|
|
4
6
|
export { CUBE3_SOLVER_TABLE_METADATA } from "./table-metadata.js";
|
|
5
7
|
export { CUBE3_PHASE1_MOVES, CUBE3_PHASE2_MOVES, checksum, generateCube3Phase1Tables, generateCube3Phase2Tables, generateCube3MoveTable, generateCube3PruningTable, generateCube3SolverTables, loadCube3Phase1Tables, loadCube3Phase2Tables, loadCube3SolverTables, lookupCube3MoveTable } from "./tables.js";
|
|
6
|
-
export type { Cube3Phase2Coordinates, Cube3Phase2Options, Cube3Phase2Result, Cube3SolveOptions, Cube3SolveResult } from "./phase2.js";
|
|
7
|
-
export type { Cube3Phase1Coordinates, Cube3Phase1Options, Cube3Phase1Result } from "./phase1.js";
|
|
8
|
+
export type { Cube3Phase2Coordinates, Cube3Phase2Options, Cube3Phase2Result, Cube3SolveOptions, Cube3SolveQuality, Cube3SolveResult } from "./phase2.js";
|
|
9
|
+
export type { Cube3Phase1CollectOptions, Cube3Phase1Coordinates, Cube3Phase1Options, Cube3Phase1Result } from "./phase1.js";
|
|
8
10
|
export type { Cube3CoordinateState } from "./coordinates.js";
|
|
9
11
|
export type { Cube3CoordinateName, Cube3Phase1Tables, Cube3Phase2Tables, Cube3MoveTable, Cube3PruningTable, Cube3SolverTables } from "./tables.js";
|
|
10
12
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube3/solver/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,4BAA4B,EAC5B,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EACf,sBAAsB,EACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,qBAAqB,EACrB,4BAA4B,EAC5B,mBAAmB,EACnB,gBAAgB,EAChB,sBAAsB,EACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,uBAAuB,EACvB,sCAAsC,EACtC,uBAAuB,EACvB,qBAAqB,EACrB,iBAAiB,EACjB,sBAAsB,EACtB,uBAAuB,EACvB,aAAa,EACb,qBAAqB,EACrB,uBAAuB,EACvB,sBAAsB,EACtB,qBAAqB,EACrB,iBAAiB,EACjB,sBAAsB,EACtB,uBAAuB,EACvB,aAAa,EACb,cAAc,EACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,2BAA2B,EAC5B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,QAAQ,EACR,yBAAyB,EACzB,yBAAyB,EACzB,sBAAsB,EACtB,yBAAyB,EACzB,yBAAyB,EACzB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACrB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,sBAAsB,EACtB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,sBAAsB,EACtB,kBAAkB,EAClB,iBAAiB,EAClB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,oBAAoB,EACrB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube3/solver/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,4BAA4B,EAC5B,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EACf,sBAAsB,EACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,qBAAqB,EACrB,2BAA2B,EAC3B,4BAA4B,EAC5B,mBAAmB,EACnB,gBAAgB,EAChB,sBAAsB,EACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,2BAA2B,EAC3B,oBAAoB,EACpB,8BAA8B,EAC/B,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EACL,uBAAuB,EACvB,sCAAsC,EACtC,uBAAuB,EACvB,qBAAqB,EACrB,iBAAiB,EACjB,sBAAsB,EACtB,uBAAuB,EACvB,aAAa,EACb,qBAAqB,EACrB,uBAAuB,EACvB,sBAAsB,EACtB,qBAAqB,EACrB,iBAAiB,EACjB,sBAAsB,EACtB,uBAAuB,EACvB,aAAa,EACb,cAAc,EACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,2BAA2B,EAC5B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,QAAQ,EACR,yBAAyB,EACzB,yBAAyB,EACzB,sBAAsB,EACtB,yBAAyB,EACzB,yBAAyB,EACzB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACrB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,sBAAsB,EACtB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,yBAAyB,EACzB,sBAAsB,EACtB,kBAAkB,EAClB,iBAAiB,EAClB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,oBAAoB,EACrB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,aAAa,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { applyCube3Phase2Moves, encodeCube3Phase2Coordinates, isCube3Phase2Solved, solveCube3Phase2, solveCube3State, Cube3Phase2SearchError } from "./phase2.js";
|
|
2
|
-
export { applyCube3Phase1Moves, encodeCube3Phase1Coordinates, isCube3Phase1Solved, solveCube3Phase1, Cube3Phase1SearchError } from "./phase1.js";
|
|
2
|
+
export { applyCube3Phase1Moves, collectCube3Phase1Solutions, encodeCube3Phase1Coordinates, isCube3Phase1Solved, solveCube3Phase1, Cube3Phase1SearchError } from "./phase1.js";
|
|
3
|
+
export { CUBE3_ORIENTATIONS_24, CUBE3_ROTATION_INVERSE, invertCube3RotationSequence, rotateCube3Algorithm, rotateCube3AlgorithmBySequence } from "./symmetry.js";
|
|
3
4
|
export { CUBE3_COORDINATE_RANGES, createRepresentativeStateForCoordinate, decodeCornerOrientation, decodeEdgeOrientation, decodePermutation, decodeSlicePermutation, decodeUdEdgePermutation, decodeUdSlice, encodeCube3Coordinate, encodeCornerOrientation, encodeCube3Coordinates, encodeEdgeOrientation, encodePermutation, encodeSlicePermutation, encodeUdEdgePermutation, encodeUdSlice, moveCoordinate } from "./coordinates.js";
|
|
4
5
|
export { CUBE3_SOLVER_TABLE_METADATA } from "./table-metadata.js";
|
|
5
6
|
export { CUBE3_PHASE1_MOVES, CUBE3_PHASE2_MOVES, checksum, generateCube3Phase1Tables, generateCube3Phase2Tables, generateCube3MoveTable, generateCube3PruningTable, generateCube3SolverTables, loadCube3Phase1Tables, loadCube3Phase2Tables, loadCube3SolverTables, lookupCube3MoveTable } from "./tables.js";
|
|
@@ -65,9 +65,16 @@ function readMoveTable(assetDirectory, entry) {
|
|
|
65
65
|
if (buffer.length !== expectedBytes) {
|
|
66
66
|
throw new Error(`Invalid byte size for ${entry.coordinate} move table.`);
|
|
67
67
|
}
|
|
68
|
-
|
|
68
|
+
// Every coordinate fits in 16 bits (max range 40320): store half the
|
|
69
|
+
// bytes the int32 wire format uses. Checksums are order-sensitive FNV
|
|
70
|
+
// over the same numeric sequence, so they still match the manifest.
|
|
71
|
+
const values = new Uint16Array(entry.range * entry.moves.length);
|
|
69
72
|
for (let index = 0; index < values.length; index += 1) {
|
|
70
|
-
|
|
73
|
+
const value = buffer.readInt32LE(index * 4);
|
|
74
|
+
if (value < 0 || value > 0xffff) {
|
|
75
|
+
throw new Error(`Move table value out of range for ${entry.coordinate}: ${value}.`);
|
|
76
|
+
}
|
|
77
|
+
values[index] = value;
|
|
71
78
|
}
|
|
72
79
|
const table = {
|
|
73
80
|
coordinate: entry.coordinate,
|
|
@@ -27,4 +27,22 @@ export declare function solveCube3Phase1(state: Cube3State, options?: Cube3Phase
|
|
|
27
27
|
export declare function encodeCube3Phase1Coordinates(state: Cube3State): Cube3Phase1Coordinates;
|
|
28
28
|
export declare function isCube3Phase1Solved(input: Cube3State | Cube3Phase1Coordinates, tables: Cube3Phase1Tables): boolean;
|
|
29
29
|
export declare function applyCube3Phase1Moves(state: Cube3State, moves: readonly Cube3Move[]): Cube3State;
|
|
30
|
+
export interface Cube3Phase1CollectOptions {
|
|
31
|
+
tables?: Cube3Phase1Tables;
|
|
32
|
+
/** Phase-1 joint tables (ud x co / ud x eo), experimental. */
|
|
33
|
+
jointTables?: Cube3JointTables | undefined;
|
|
34
|
+
timeoutMs?: number;
|
|
35
|
+
/** Stop after collecting this many solutions. Defaults to 32. */
|
|
36
|
+
limit?: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Enumerates up to `limit` phase-1 solutions of exactly `depth` moves for
|
|
40
|
+
* the given state, most promising first (same ordered DFS as the search).
|
|
41
|
+
* Used to try several G1 landing states and keep the best phase-1+phase-2
|
|
42
|
+
* total — a slightly longer phase 1 often collapses phase 2.
|
|
43
|
+
*/
|
|
44
|
+
export declare function collectCube3Phase1Solutions(state: Cube3State, depth: number, options?: Cube3Phase1CollectOptions): Promise<{
|
|
45
|
+
solutions: Cube3Move[][];
|
|
46
|
+
visitedNodes: number;
|
|
47
|
+
}>;
|
|
30
48
|
//# sourceMappingURL=phase1.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"phase1.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube3/solver/phase1.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAMzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAOvE,MAAM,WAAW,sBAAsB;IACrC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,8DAA8D;IAC9D,WAAW,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;CAC5C;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,UAAU,CAAC;IAClB,WAAW,EAAE,sBAAsB,CAAC;IACpC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;gBAElB,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;CAKlD;
|
|
1
|
+
{"version":3,"file":"phase1.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube3/solver/phase1.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAMzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAOvE,MAAM,WAAW,sBAAsB;IACrC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,8DAA8D;IAC9D,WAAW,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;CAC5C;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,UAAU,CAAC;IAClB,WAAW,EAAE,sBAAsB,CAAC;IACpC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;gBAElB,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;CAKlD;AAyCD,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,UAAU,EACjB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,iBAAiB,CAAC,CA2E5B;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,UAAU,GAAG,sBAAsB,CAMtF;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,UAAU,GAAG,sBAAsB,EAC1C,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAQT;AAED,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,SAAS,SAAS,EAAE,GAC1B,UAAU,CAEZ;AA0ED,MAAM,WAAW,yBAAyB;IACxC,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,8DAA8D;IAC9D,WAAW,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAsB,2BAA2B,CAC/C,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC;IAAE,SAAS,EAAE,SAAS,EAAE,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,CAqB7D"}
|
|
@@ -28,14 +28,7 @@ export async function solveCube3Phase1(state, options = {}) {
|
|
|
28
28
|
const maxDepth = options.maxDepth ?? 12;
|
|
29
29
|
const start = encodeCube3Phase1Coordinates(state);
|
|
30
30
|
const deadline = options.timeoutMs === undefined ? undefined : performance.now() + options.timeoutMs;
|
|
31
|
-
const context =
|
|
32
|
-
tables,
|
|
33
|
-
jointTables,
|
|
34
|
-
maxDepth,
|
|
35
|
-
deadline,
|
|
36
|
-
visitedNodes: 0,
|
|
37
|
-
timedOut: false
|
|
38
|
-
};
|
|
31
|
+
const context = createPhase1SearchContext(tables, jointTables, maxDepth, deadline);
|
|
39
32
|
if (await isCube3Phase1Solved(start, tables)) {
|
|
40
33
|
return {
|
|
41
34
|
moves: [],
|
|
@@ -116,6 +109,66 @@ function searchPhase1(cornerOrientation, edgeOrientation, udSlice, previousFace,
|
|
|
116
109
|
edgeOrientation === 0 &&
|
|
117
110
|
udSlice === context.tables.moveTables.udSlice.solvedCoordinate;
|
|
118
111
|
}
|
|
112
|
+
const moveCount = context.tables.phase1Moves.length;
|
|
113
|
+
const childCount = collectPhase1Children(cornerOrientation, edgeOrientation, udSlice, previousFace, previousPreviousFace, remainingDepth, context);
|
|
114
|
+
const slot = remainingDepth * moveCount;
|
|
115
|
+
const scratchMove = context.scratchMove;
|
|
116
|
+
const scratchCo = context.scratchCo;
|
|
117
|
+
const scratchEo = context.scratchEo;
|
|
118
|
+
const scratchUd = context.scratchUd;
|
|
119
|
+
for (let i = 0; i < childCount; i += 1) {
|
|
120
|
+
const moveIndex = scratchMove[slot + i];
|
|
121
|
+
const face = PHASE1_MOVE_FACES[moveIndex];
|
|
122
|
+
path.push(context.tables.phase1Moves[moveIndex]);
|
|
123
|
+
if (searchPhase1(scratchCo[slot + i], scratchEo[slot + i], scratchUd[slot + i], face, previousFace, remainingDepth - 1, path, context)) {
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
path.pop();
|
|
127
|
+
if (context.timedOut) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Enumerates up to `limit` phase-1 solutions of exactly `depth` moves for
|
|
135
|
+
* the given state, most promising first (same ordered DFS as the search).
|
|
136
|
+
* Used to try several G1 landing states and keep the best phase-1+phase-2
|
|
137
|
+
* total — a slightly longer phase 1 often collapses phase 2.
|
|
138
|
+
*/
|
|
139
|
+
export async function collectCube3Phase1Solutions(state, depth, options = {}) {
|
|
140
|
+
const tables = options.tables ?? await loadCube3Phase1Tables();
|
|
141
|
+
const jointTables = options.jointTables ?? tables.jointTables;
|
|
142
|
+
const limit = options.limit ?? 32;
|
|
143
|
+
const deadline = options.timeoutMs === undefined ? undefined : performance.now() + options.timeoutMs;
|
|
144
|
+
const context = createPhase1SearchContext(tables, jointTables, depth, deadline);
|
|
145
|
+
const start = encodeCube3Phase1Coordinates(state);
|
|
146
|
+
const solutions = [];
|
|
147
|
+
searchPhase1Collect(start.cornerOrientation, start.edgeOrientation, start.udSlice, -1, -1, depth, [], solutions, limit, context);
|
|
148
|
+
return { solutions, visitedNodes: context.visitedNodes };
|
|
149
|
+
}
|
|
150
|
+
function createPhase1SearchContext(tables, jointTables, maxDepth, deadline) {
|
|
151
|
+
const scratchLevels = maxDepth + 1;
|
|
152
|
+
const scratchSize = scratchLevels * CUBE3_PHASE1_MOVES.length;
|
|
153
|
+
return {
|
|
154
|
+
tables,
|
|
155
|
+
jointTables,
|
|
156
|
+
maxDepth,
|
|
157
|
+
deadline,
|
|
158
|
+
visitedNodes: 0,
|
|
159
|
+
timedOut: false,
|
|
160
|
+
scratchMove: new Int32Array(scratchSize),
|
|
161
|
+
scratchBound: new Int32Array(scratchSize),
|
|
162
|
+
scratchCo: new Int32Array(scratchSize),
|
|
163
|
+
scratchEo: new Int32Array(scratchSize),
|
|
164
|
+
scratchUd: new Int32Array(scratchSize)
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Fills the scratch slot for `remainingDepth` with the surviving children
|
|
169
|
+
* in ascending lower-bound order (stable). Returns the child count.
|
|
170
|
+
*/
|
|
171
|
+
function collectPhase1Children(cornerOrientation, edgeOrientation, udSlice, previousFace, previousPreviousFace, remainingDepth, context) {
|
|
119
172
|
const moveCount = context.tables.phase1Moves.length;
|
|
120
173
|
const validMoves = PHASE1_VALID_MOVES[previousFace + 1][previousPreviousFace + 1];
|
|
121
174
|
const coTable = context.tables.moveTables.cornerOrientation.values;
|
|
@@ -123,9 +176,17 @@ function searchPhase1(cornerOrientation, edgeOrientation, udSlice, previousFace,
|
|
|
123
176
|
const udTable = context.tables.moveTables.udSlice.values;
|
|
124
177
|
const coeoTable = context.tables.pruningTables.cornerOrientationEdgeOrientation.values;
|
|
125
178
|
const udPruningTable = context.tables.pruningTables.udSlice.values;
|
|
179
|
+
const jointTables = context.jointTables;
|
|
126
180
|
const coOffset = cornerOrientation * moveCount;
|
|
127
181
|
const eoOffset = edgeOrientation * moveCount;
|
|
128
182
|
const udOffset = udSlice * moveCount;
|
|
183
|
+
const slot = remainingDepth * moveCount;
|
|
184
|
+
const scratchMove = context.scratchMove;
|
|
185
|
+
const scratchBound = context.scratchBound;
|
|
186
|
+
const scratchCo = context.scratchCo;
|
|
187
|
+
const scratchEo = context.scratchEo;
|
|
188
|
+
const scratchUd = context.scratchUd;
|
|
189
|
+
let childCount = 0;
|
|
129
190
|
for (let i = 0; i < validMoves.length; i += 1) {
|
|
130
191
|
const moveIndex = validMoves[i];
|
|
131
192
|
const nextCornerOrientation = coTable[coOffset + moveIndex];
|
|
@@ -138,7 +199,6 @@ function searchPhase1(cornerOrientation, edgeOrientation, udSlice, previousFace,
|
|
|
138
199
|
if (nextLowerBound > remainingDepth - 1) {
|
|
139
200
|
continue;
|
|
140
201
|
}
|
|
141
|
-
const jointTables = context.jointTables;
|
|
142
202
|
if (jointTables) {
|
|
143
203
|
const udCoIndex = nextUdSlice * 2187 + nextCornerOrientation;
|
|
144
204
|
const jc = (jointTables.jointUdCo[udCoIndex >> 1] >> ((udCoIndex & 1) << 2)) & 0x0f;
|
|
@@ -154,14 +214,72 @@ function searchPhase1(cornerOrientation, edgeOrientation, udSlice, previousFace,
|
|
|
154
214
|
continue;
|
|
155
215
|
}
|
|
156
216
|
}
|
|
217
|
+
scratchMove[slot + childCount] = moveIndex;
|
|
218
|
+
scratchBound[slot + childCount] = nextLowerBound;
|
|
219
|
+
scratchCo[slot + childCount] = nextCornerOrientation;
|
|
220
|
+
scratchEo[slot + childCount] = nextEdgeOrientation;
|
|
221
|
+
scratchUd[slot + childCount] = nextUdSlice;
|
|
222
|
+
childCount += 1;
|
|
223
|
+
}
|
|
224
|
+
// Stable insertion sort by bound (n <= 18; ties keep reference order).
|
|
225
|
+
for (let i = 1; i < childCount; i += 1) {
|
|
226
|
+
const move = scratchMove[slot + i];
|
|
227
|
+
const bound = scratchBound[slot + i];
|
|
228
|
+
const co = scratchCo[slot + i];
|
|
229
|
+
const eo = scratchEo[slot + i];
|
|
230
|
+
const ud = scratchUd[slot + i];
|
|
231
|
+
let j = i - 1;
|
|
232
|
+
while (j >= 0 && scratchBound[slot + j] > bound) {
|
|
233
|
+
scratchMove[slot + j + 1] = scratchMove[slot + j];
|
|
234
|
+
scratchBound[slot + j + 1] = scratchBound[slot + j];
|
|
235
|
+
scratchCo[slot + j + 1] = scratchCo[slot + j];
|
|
236
|
+
scratchEo[slot + j + 1] = scratchEo[slot + j];
|
|
237
|
+
scratchUd[slot + j + 1] = scratchUd[slot + j];
|
|
238
|
+
j -= 1;
|
|
239
|
+
}
|
|
240
|
+
scratchMove[slot + j + 1] = move;
|
|
241
|
+
scratchBound[slot + j + 1] = bound;
|
|
242
|
+
scratchCo[slot + j + 1] = co;
|
|
243
|
+
scratchEo[slot + j + 1] = eo;
|
|
244
|
+
scratchUd[slot + j + 1] = ud;
|
|
245
|
+
}
|
|
246
|
+
return childCount;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Ordered exact-depth DFS that collects solutions instead of stopping at
|
|
250
|
+
* the first one. Returns true when collection should stop (limit reached
|
|
251
|
+
* or timed out).
|
|
252
|
+
*/
|
|
253
|
+
function searchPhase1Collect(cornerOrientation, edgeOrientation, udSlice, previousFace, previousPreviousFace, remainingDepth, path, solutions, limit, context) {
|
|
254
|
+
context.visitedNodes += 1;
|
|
255
|
+
if (context.deadline !== undefined && (context.visitedNodes & 1023) === 0 && performance.now() > context.deadline) {
|
|
256
|
+
context.timedOut = true;
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
if (remainingDepth === 0) {
|
|
260
|
+
if (cornerOrientation === 0 &&
|
|
261
|
+
edgeOrientation === 0 &&
|
|
262
|
+
udSlice === context.tables.moveTables.udSlice.solvedCoordinate) {
|
|
263
|
+
solutions.push([...path]);
|
|
264
|
+
return solutions.length >= limit;
|
|
265
|
+
}
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
const moveCount = context.tables.phase1Moves.length;
|
|
269
|
+
const childCount = collectPhase1Children(cornerOrientation, edgeOrientation, udSlice, previousFace, previousPreviousFace, remainingDepth, context);
|
|
270
|
+
const slot = remainingDepth * moveCount;
|
|
271
|
+
const scratchMove = context.scratchMove;
|
|
272
|
+
const scratchCo = context.scratchCo;
|
|
273
|
+
const scratchEo = context.scratchEo;
|
|
274
|
+
const scratchUd = context.scratchUd;
|
|
275
|
+
for (let i = 0; i < childCount; i += 1) {
|
|
276
|
+
const moveIndex = scratchMove[slot + i];
|
|
157
277
|
const face = PHASE1_MOVE_FACES[moveIndex];
|
|
158
278
|
path.push(context.tables.phase1Moves[moveIndex]);
|
|
159
|
-
|
|
160
|
-
return true;
|
|
161
|
-
}
|
|
279
|
+
const stop = searchPhase1Collect(scratchCo[slot + i], scratchEo[slot + i], scratchUd[slot + i], face, previousFace, remainingDepth - 1, path, solutions, limit, context);
|
|
162
280
|
path.pop();
|
|
163
|
-
if (
|
|
164
|
-
return
|
|
281
|
+
if (stop) {
|
|
282
|
+
return true;
|
|
165
283
|
}
|
|
166
284
|
}
|
|
167
285
|
return false;
|