@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
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export { CORNER_COUNT, WING_COUNT, CENTER_COUNT, FACE_COUNT, CENTERS_PER_FACE, U, R, F, D, L, B, CUBE4_FACE_MOVES, CUBE4_ALL_MOVES, CUBE4_OUTER_MOVES } from "./types.js";
|
|
2
2
|
export type { Cube4Face, Cube4FaceMove, Cube4Move, Cube4MoveSuffix, Cube4State, Cube4Sticker, Cube4Facelets, Cube4ValidationResult, Cube4RandomStateOptions } from "./types.js";
|
|
3
3
|
export { createSolvedCube4State, cloneCube4State, cube4StatesEqual, isSolvedCube4State, areCentersReduced, areWingsPaired } from "./state.js";
|
|
4
|
-
export { cube4ScrambleGenerator, generateCube4Scramble } from "./scrambler.js";
|
|
5
|
-
export {
|
|
4
|
+
export { cube4ScrambleGenerator, generateCube4Scramble, generateCube4Attempt } from "./scrambler.js";
|
|
5
|
+
export type { Cube4ScrambleOptions } from "./scrambler.js";
|
|
6
|
+
export { applyCube4Move, applyCube4Algorithm, invertCube4Move, invertCube4Algorithm, simplifyCube4Algorithm, formatCube4Algorithm, parseCube4Algorithm, parseCube4Move, parseMove, FACE_MOVE_EFFECTS } from "./moves.js";
|
|
6
7
|
export { generateRandomCube4State } from "./random-state.js";
|
|
7
8
|
export { validateCube4State, isOneMoveFromSolvedCube4State, isTrivialCube4State, isAllowedRandomCube4State } from "./validation.js";
|
|
8
9
|
export { toCube4Facelets, formatCube4Facelets, getCube4FaceOrder } from "./facelets.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube4/index.ts"],"names":[],"mappings":"AASA,OAAO,EACL,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAChB,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EAClB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,SAAS,EACT,aAAa,EACb,SAAS,EACT,eAAe,EACf,UAAU,EACV,YAAY,EACZ,aAAa,EACb,qBAAqB,EACrB,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,sBAAsB,EACtB,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,sBAAsB,EACtB,qBAAqB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube4/index.ts"],"names":[],"mappings":"AASA,OAAO,EACL,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAChB,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EAClB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,SAAS,EACT,aAAa,EACb,SAAS,EACT,eAAe,EACf,UAAU,EACV,YAAY,EACZ,aAAa,EACb,qBAAqB,EACrB,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,sBAAsB,EACtB,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,oBAAoB,EACrB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,SAAS,EACT,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,wBAAwB,EACzB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,kBAAkB,EAClB,6BAA6B,EAC7B,mBAAmB,EACnB,yBAAyB,EAC1B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,qBAAqB,CAAC"}
|
|
@@ -6,8 +6,8 @@ import { cube4ScrambleGenerator } from "./scrambler.js";
|
|
|
6
6
|
registerScrambleGenerator(cube4ScrambleGenerator);
|
|
7
7
|
export { CORNER_COUNT, WING_COUNT, CENTER_COUNT, FACE_COUNT, CENTERS_PER_FACE, U, R, F, D, L, B, CUBE4_FACE_MOVES, CUBE4_ALL_MOVES, CUBE4_OUTER_MOVES } from "./types.js";
|
|
8
8
|
export { createSolvedCube4State, cloneCube4State, cube4StatesEqual, isSolvedCube4State, areCentersReduced, areWingsPaired } from "./state.js";
|
|
9
|
-
export { cube4ScrambleGenerator, generateCube4Scramble } from "./scrambler.js";
|
|
10
|
-
export { applyCube4Move, applyCube4Algorithm, invertCube4Move, invertCube4Algorithm, formatCube4Algorithm, parseCube4Algorithm, parseCube4Move, parseMove, FACE_MOVE_EFFECTS } from "./moves.js";
|
|
9
|
+
export { cube4ScrambleGenerator, generateCube4Scramble, generateCube4Attempt } from "./scrambler.js";
|
|
10
|
+
export { applyCube4Move, applyCube4Algorithm, invertCube4Move, invertCube4Algorithm, simplifyCube4Algorithm, formatCube4Algorithm, parseCube4Algorithm, parseCube4Move, parseMove, FACE_MOVE_EFFECTS } from "./moves.js";
|
|
11
11
|
export { generateRandomCube4State } from "./random-state.js";
|
|
12
12
|
export { validateCube4State, isOneMoveFromSolvedCube4State, isTrivialCube4State, isAllowedRandomCube4State } from "./validation.js";
|
|
13
13
|
export { toCube4Facelets, formatCube4Facelets, getCube4FaceOrder } from "./facelets.js";
|
|
@@ -33,5 +33,10 @@ export declare function invertCube4Algorithm(moves: readonly Cube4Move[]): Cube4
|
|
|
33
33
|
export declare function formatCube4Algorithm(moves: readonly Cube4Move[]): string;
|
|
34
34
|
export declare function parseCube4Algorithm(algorithm: string): Cube4Move[];
|
|
35
35
|
export declare function parseCube4Move(value: string): Cube4Move;
|
|
36
|
+
/**
|
|
37
|
+
* Simplifies a 4x4 algorithm by canceling inverse moves and merging redundant
|
|
38
|
+
* turns on the same face or across commuting parallel faces.
|
|
39
|
+
*/
|
|
40
|
+
export declare function simplifyCube4Algorithm(moves: readonly Cube4Move[]): Cube4Move[];
|
|
36
41
|
export { FACE_MOVE_EFFECTS };
|
|
37
42
|
//# sourceMappingURL=moves.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"moves.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube4/moves.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"moves.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube4/moves.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAOxF,UAAU,UAAU;IAClB,+DAA+D;IAC/D,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,yDAAyD;IACzD,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,kFAAkF;IAClF,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,oDAAoD;IACpD,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3B;AA0KD,QAAA,MAAM,iBAAiB,EAAE,MAAM,CAAC,aAAa,EAAE,UAAU,CAGxD,CAAC;AAuDF,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,GAAG;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAc1G;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,GAAG,UAAU,CAK7E;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,UAAU,CA4C9F;AAKD,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAI1D;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,EAAE,CAE7E;AAMD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,MAAM,CAExE;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,CAIlE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAMvD;AAkCD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,EAAE,CAsB/E;AAMD,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
|
|
@@ -158,6 +158,32 @@ const FACE_MOVE_EFFECTS = {
|
|
|
158
158
|
Uw: WIDE_Uw, Dw: WIDE_Dw, Rw: WIDE_Rw, Lw: WIDE_Lw, Fw: WIDE_Fw, Bw: WIDE_Bw
|
|
159
159
|
};
|
|
160
160
|
// ---------------------------------------------------------------------------
|
|
161
|
+
// Precomposed total effect per move string ("R'" = R x3, "U2" = U x2),
|
|
162
|
+
// built once at module load. applyCube4Algorithm then costs one lookup +
|
|
163
|
+
// one pass per move: no per-move string parsing, no repeated
|
|
164
|
+
// quarter-turn passes (average ~2 passes collapse into exactly 1).
|
|
165
|
+
// ---------------------------------------------------------------------------
|
|
166
|
+
function composePower(face, turns) {
|
|
167
|
+
const quarter = FACE_MOVE_EFFECTS[face];
|
|
168
|
+
if (turns === 2)
|
|
169
|
+
return composeEffects(quarter, quarter);
|
|
170
|
+
if (turns === 3)
|
|
171
|
+
return composeEffects(composeEffects(quarter, quarter), quarter);
|
|
172
|
+
return quarter;
|
|
173
|
+
}
|
|
174
|
+
const COMPOSED_EFFECTS = Object.fromEntries(CUBE4_ALL_MOVES.map((move) => {
|
|
175
|
+
const { face, turns } = parseMove(move);
|
|
176
|
+
return [move, composePower(face, turns)];
|
|
177
|
+
}));
|
|
178
|
+
/** Total effect for one move string (precomposed fast path + parse fallback). */
|
|
179
|
+
function effectForMove(move) {
|
|
180
|
+
const hit = COMPOSED_EFFECTS[move];
|
|
181
|
+
if (hit !== undefined)
|
|
182
|
+
return hit;
|
|
183
|
+
const { face, turns } = parseMove(move);
|
|
184
|
+
return composePower(face, turns);
|
|
185
|
+
}
|
|
186
|
+
// ---------------------------------------------------------------------------
|
|
161
187
|
// Apply a single move to a state
|
|
162
188
|
// ---------------------------------------------------------------------------
|
|
163
189
|
function applyEffect(state, effect) {
|
|
@@ -192,20 +218,52 @@ export function parseMove(move) {
|
|
|
192
218
|
return { face: face, suffix, turns };
|
|
193
219
|
}
|
|
194
220
|
export function applyCube4Move(state, move) {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
current = applyEffect(current, effect);
|
|
200
|
-
}
|
|
201
|
-
return current;
|
|
221
|
+
// Single-pass fast path through the precomposed total effect: one lookup
|
|
222
|
+
// + one pass, no string re-parsing, no repeated quarter-turn passes, no
|
|
223
|
+
// intermediate states.
|
|
224
|
+
return applyEffect(state, effectForMove(move));
|
|
202
225
|
}
|
|
203
226
|
export function applyCube4Algorithm(state, moves) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
227
|
+
if (moves.length === 0)
|
|
228
|
+
return state;
|
|
229
|
+
const bufA = {
|
|
230
|
+
cp: new Uint8Array(state.cp),
|
|
231
|
+
co: new Uint8Array(state.co),
|
|
232
|
+
wp: new Uint8Array(state.wp),
|
|
233
|
+
centers: new Uint8Array(state.centers)
|
|
234
|
+
};
|
|
235
|
+
const bufB = {
|
|
236
|
+
cp: new Uint8Array(8),
|
|
237
|
+
co: new Uint8Array(8),
|
|
238
|
+
wp: new Uint8Array(24),
|
|
239
|
+
centers: new Uint8Array(24)
|
|
240
|
+
};
|
|
241
|
+
let cur = bufA;
|
|
242
|
+
let nxt = bufB;
|
|
243
|
+
for (let mIdx = 0; mIdx < moves.length; mIdx++) {
|
|
244
|
+
const move = moves[mIdx];
|
|
245
|
+
const eff = effectForMove(move);
|
|
246
|
+
for (let i = 0; i < 8; i++) {
|
|
247
|
+
const from = eff.cperm[i];
|
|
248
|
+
nxt.cp[i] = cur.cp[from];
|
|
249
|
+
nxt.co[i] = (cur.co[from] + eff.corient[i]) % 3;
|
|
250
|
+
}
|
|
251
|
+
for (let i = 0; i < 24; i++) {
|
|
252
|
+
nxt.wp[i] = cur.wp[eff.wperm[i]];
|
|
253
|
+
}
|
|
254
|
+
for (let i = 0; i < 24; i++) {
|
|
255
|
+
nxt.centers[i] = cur.centers[eff.ctperm[i]];
|
|
256
|
+
}
|
|
257
|
+
const tmp = cur;
|
|
258
|
+
cur = nxt;
|
|
259
|
+
nxt = tmp;
|
|
207
260
|
}
|
|
208
|
-
return
|
|
261
|
+
return {
|
|
262
|
+
cp: cur.cp,
|
|
263
|
+
co: cur.co,
|
|
264
|
+
wp: cur.wp,
|
|
265
|
+
centers: cur.centers
|
|
266
|
+
};
|
|
209
267
|
}
|
|
210
268
|
// ---------------------------------------------------------------------------
|
|
211
269
|
// Move inversion
|
|
@@ -237,6 +295,57 @@ export function parseCube4Move(value) {
|
|
|
237
295
|
}
|
|
238
296
|
throw new Error(`Invalid 4x4 move: ${value}`);
|
|
239
297
|
}
|
|
298
|
+
function parse4Move(m) {
|
|
299
|
+
const match = m.match(/^([UDFBLR]w?)(2|')?$/);
|
|
300
|
+
if (!match)
|
|
301
|
+
throw new Error(`Invalid move format: ${m}`);
|
|
302
|
+
const base = match[1];
|
|
303
|
+
const suffix = match[2] ?? "";
|
|
304
|
+
const amount = suffix === "2" ? 2 : suffix === "'" ? 3 : 1;
|
|
305
|
+
const axis = (base[0] === "U" || base[0] === "D") ? 0 :
|
|
306
|
+
(base[0] === "R" || base[0] === "L") ? 1 : 2;
|
|
307
|
+
return { base, axis, amount };
|
|
308
|
+
}
|
|
309
|
+
function format4Move(base, amount) {
|
|
310
|
+
const amt = ((amount % 4) + 4) % 4;
|
|
311
|
+
if (amt === 0)
|
|
312
|
+
return null;
|
|
313
|
+
const suffix = amt === 1 ? "" : amt === 2 ? "2" : "'";
|
|
314
|
+
return (base + suffix);
|
|
315
|
+
}
|
|
316
|
+
function movesCommute4(a, b) {
|
|
317
|
+
return a.axis === b.axis && a.base !== b.base;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Simplifies a 4x4 algorithm by canceling inverse moves and merging redundant
|
|
321
|
+
* turns on the same face or across commuting parallel faces.
|
|
322
|
+
*/
|
|
323
|
+
export function simplifyCube4Algorithm(moves) {
|
|
324
|
+
// Single left-to-right pass with an output stack: each move merges with
|
|
325
|
+
// the nearest same-base entry across trailing commuting parallel-face
|
|
326
|
+
// moves (verified equivalent to the old restart-from-zero loop on 20000
|
|
327
|
+
// random algorithms). O(n·window) instead of O(n²) restarts with splices.
|
|
328
|
+
const out = [];
|
|
329
|
+
for (const move of moves) {
|
|
330
|
+
const current = parse4Move(move);
|
|
331
|
+
let index = out.length - 1;
|
|
332
|
+
while (index >= 0 && movesCommute4(out[index], current))
|
|
333
|
+
index--;
|
|
334
|
+
if (index >= 0 && out[index].base === current.base) {
|
|
335
|
+
const merged = (out[index].amount + current.amount) % 4;
|
|
336
|
+
if (merged === 0)
|
|
337
|
+
out.splice(index, 1);
|
|
338
|
+
else
|
|
339
|
+
out[index].amount = merged;
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
out.push(current);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return out
|
|
346
|
+
.map((p) => format4Move(p.base, p.amount))
|
|
347
|
+
.filter((m) => m !== null);
|
|
348
|
+
}
|
|
240
349
|
// ---------------------------------------------------------------------------
|
|
241
350
|
// Export the raw effects for use by the reduction solver
|
|
242
351
|
// ---------------------------------------------------------------------------
|
|
@@ -6,11 +6,14 @@ import type { Center1Tables } from "./center1.js";
|
|
|
6
6
|
import type { Center2Tables } from "./center2.js";
|
|
7
7
|
import type { Center3Tables } from "./center3.js";
|
|
8
8
|
import type { Edge3Tables } from "./edge3.js";
|
|
9
|
+
import { type HcDenseTables } from "./hc-pdb.js";
|
|
9
10
|
export interface Cube4ReductionTables {
|
|
10
11
|
center1: Center1Tables;
|
|
11
12
|
center2: Center2Tables;
|
|
12
13
|
center3: Center3Tables;
|
|
13
14
|
edge3: Edge3Tables;
|
|
15
|
+
/** Half-class exact PDB distances (optional; absent = bound disabled). */
|
|
16
|
+
hc?: HcDenseTables;
|
|
14
17
|
}
|
|
15
18
|
export declare function getCachedCube4Tables(): Cube4ReductionTables | undefined;
|
|
16
19
|
export declare function clearCachedCube4Tables(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-loader.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/browser-loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,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;
|
|
1
|
+
{"version":3,"file":"browser-loader.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/browser-loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,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,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAE/D,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,KAAK,EAAE,WAAW,CAAC;IACnB,0EAA0E;IAC1E,EAAE,CAAC,EAAE,aAAa,CAAC;CACpB;AAOD,wBAAgB,oBAAoB,IAAI,oBAAoB,GAAG,SAAS,CAEvE;AAED,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAgBD,wBAAsB,+BAA+B,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAerG"}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { edge3DistanceLookup } from "./edge3.js";
|
|
6
6
|
import { fetchTableBuffer } from "../../shared/compressed-fetch.js";
|
|
7
|
+
import { buildHcDense } from "./hc-pdb.js";
|
|
7
8
|
const GENERATED_DIRECTORY = "puzzles/cube4/reduction/generated";
|
|
8
9
|
const CACHE_NAME = "cubingcenter-scrambler-tables-v1";
|
|
9
10
|
let cachedTables;
|
|
@@ -31,13 +32,16 @@ export async function loadCube4ReductionTablesBrowser(baseUrl) {
|
|
|
31
32
|
return cachedTables;
|
|
32
33
|
}
|
|
33
34
|
const resolved = resolveBaseUrl(baseUrl);
|
|
34
|
-
const [center1, center2, center3, edge3] = await Promise.all([
|
|
35
|
+
const [center1, center2, center3, edge3, hc] = await Promise.all([
|
|
35
36
|
loadCenter1(resolved),
|
|
36
37
|
loadCenter2(resolved),
|
|
37
38
|
loadCenter3(resolved),
|
|
38
|
-
loadEdge3(resolved)
|
|
39
|
+
loadEdge3(resolved),
|
|
40
|
+
loadHc(resolved)
|
|
39
41
|
]);
|
|
40
42
|
cachedTables = { center1, center2, center3, edge3 };
|
|
43
|
+
if (hc)
|
|
44
|
+
cachedTables.hc = hc;
|
|
41
45
|
return cachedTables;
|
|
42
46
|
}
|
|
43
47
|
async function fetchJson(baseUrl, file) {
|
|
@@ -74,7 +78,18 @@ async function loadCenter2(baseUrl) {
|
|
|
74
78
|
const byte = pruningPacked[i >> 1];
|
|
75
79
|
pruning[i] = (i & 1) === 0 ? byte & 0x0f : byte >> 4;
|
|
76
80
|
}
|
|
77
|
-
|
|
81
|
+
const tables = { ctMove, rlMove, pruning };
|
|
82
|
+
if (manifest.pruningWideTable) {
|
|
83
|
+
const wideBuffer = await fetchTableBuffer(baseUrl, GENERATED_DIRECTORY, manifest.pruningWideTable.file, { cacheName: CACHE_NAME, compressed: manifest.pruningWideTable.compressed !== false });
|
|
84
|
+
const widePacked = new Uint8Array(wideBuffer);
|
|
85
|
+
const pruningWide = new Uint8Array(450450);
|
|
86
|
+
for (let i = 0; i < 450450; i++) {
|
|
87
|
+
const byte = widePacked[i >> 1];
|
|
88
|
+
pruningWide[i] = (i & 1) === 0 ? byte & 0x0f : byte >> 4;
|
|
89
|
+
}
|
|
90
|
+
tables.pruningWide = pruningWide;
|
|
91
|
+
}
|
|
92
|
+
return tables;
|
|
78
93
|
}
|
|
79
94
|
async function loadCenter3(baseUrl) {
|
|
80
95
|
const manifest = await fetchJson(baseUrl, "center3-manifest.json");
|
|
@@ -90,6 +105,22 @@ async function loadCenter3(baseUrl) {
|
|
|
90
105
|
async function loadEdge3(baseUrl) {
|
|
91
106
|
const manifest = await fetchJson(baseUrl, "edge3-manifest.json");
|
|
92
107
|
const distancesBuffer = await fetchTableBuffer(baseUrl, GENERATED_DIRECTORY, manifest.distances.file, { cacheName: CACHE_NAME, compressed: manifest.distances.compressed !== false });
|
|
108
|
+
// Keep nibble-packed (see node-loader.ts): all readers handle both formats.
|
|
93
109
|
const distances = new Uint8Array(distancesBuffer);
|
|
94
110
|
return { distances, distanceOfEdge: edge3DistanceLookup(distances) };
|
|
95
111
|
}
|
|
112
|
+
async function loadHc(baseUrl) {
|
|
113
|
+
try {
|
|
114
|
+
const manifest = await fetchJson(baseUrl, "hc-manifest.json");
|
|
115
|
+
const distancesBuffer = await fetchTableBuffer(baseUrl, GENERATED_DIRECTORY, manifest.distances.file, { cacheName: CACHE_NAME, compressed: manifest.distances.compressed !== false });
|
|
116
|
+
const dense = buildHcDense(new Uint8Array(distancesBuffer));
|
|
117
|
+
if (manifest.moveTable) {
|
|
118
|
+
const moveBuffer = await fetchTableBuffer(baseUrl, GENERATED_DIRECTORY, manifest.moveTable.file, { cacheName: CACHE_NAME, compressed: manifest.moveTable.compressed !== false });
|
|
119
|
+
dense.moves = new Int32Array(moveBuffer);
|
|
120
|
+
}
|
|
121
|
+
return dense;
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
* - goal: ct == 0 and rl in the 6 reference goal patterns (even parity);
|
|
13
13
|
* - the 28-move set keeps the ct and rl position sets separate, so the move
|
|
14
14
|
* tables factor: ctmv (6,435×28) and rlmv (70×28);
|
|
15
|
-
* - the search explores the first 23 moves (face moves + Uw2/Rw/Rw2/Rw')
|
|
15
|
+
* - the search explores the first 23 moves (face moves + Uw2/Rw/Rw2/Rw')
|
|
16
|
+
* by default; the wide set (`CENTER2_SEARCH_MOVES_WIDE`, all 28, with
|
|
17
|
+
* the `pruningWide` table) additionally tries Dw2/Lw/Lw2/Lw'/Bw2.
|
|
16
18
|
*
|
|
17
19
|
* The phase-1 → phase-2 bridge (alignment + encode) is in `reduction.ts`.
|
|
18
20
|
*/
|
|
@@ -21,6 +23,12 @@ import type { Cube4State, Cube4Move } from "../types.js";
|
|
|
21
23
|
export declare const CENTER2_MOVES: readonly Cube4Move[];
|
|
22
24
|
/** The search's exploration set (the first 23 of CENTER2_MOVES). */
|
|
23
25
|
export declare const CENTER2_SEARCH_MOVES: readonly Cube4Move[];
|
|
26
|
+
/**
|
|
27
|
+
* The wide exploration set: all 28 moves (adds Dw2/Lw/Lw2/Lw'/Bw2).
|
|
28
|
+
* Searches over this set must use the `pruningWide` table — the
|
|
29
|
+
* 23-move pruning distances are inadmissible here (they overestimate).
|
|
30
|
+
*/
|
|
31
|
+
export declare const CENTER2_SEARCH_MOVES_WIDE: readonly Cube4Move[];
|
|
24
32
|
/** The 6 reference goal rl patterns (full 70-space values, even parity). */
|
|
25
33
|
export declare const CENTER2_GOAL_RL: readonly number[];
|
|
26
34
|
export declare const CENTER2_CT_STATES = 6435;
|
|
@@ -34,9 +42,12 @@ export declare function wingPermutationParity(wp: Uint8Array): 0 | 1;
|
|
|
34
42
|
/** Encode a full 4x4 state to the (ct, rl) coordinate (0..450,449). */
|
|
35
43
|
export declare function encodeCenter2(state: Cube4State): Center2Encoded;
|
|
36
44
|
export declare function isCenter2Goal(ct: number, rl: number): boolean;
|
|
45
|
+
export declare const ALIGNMENT_ROTATIONS: readonly (readonly string[])[];
|
|
37
46
|
export interface Center2BridgeResult {
|
|
38
47
|
/** Alignment moves (0-2). */
|
|
39
48
|
alignment: Cube4Move[];
|
|
49
|
+
/** Alignment rotation sequence (0-1 rotations: [], ["z"], or ["y"]). */
|
|
50
|
+
rotation: string[];
|
|
40
51
|
/** The aligned state (invariant). */
|
|
41
52
|
state: Cube4State;
|
|
42
53
|
/** The (ct, rl) coordinate of the aligned state. */
|
|
@@ -57,8 +68,13 @@ export interface Center2Tables {
|
|
|
57
68
|
ctMove: Int32Array;
|
|
58
69
|
/** 70 × 28 — rl rank (parity included) after each move. */
|
|
59
70
|
rlMove: Int32Array;
|
|
60
|
-
/** 450,450 exact BFS distances from the goal. */
|
|
71
|
+
/** 450,450 exact BFS distances from the goal (23-move set). */
|
|
61
72
|
pruning: Uint8Array;
|
|
73
|
+
/**
|
|
74
|
+
* 450,450 exact BFS distances from the goal (wide 28-move set).
|
|
75
|
+
* Optional in memory (older assets); required for wide searches.
|
|
76
|
+
*/
|
|
77
|
+
pruningWide?: Uint8Array | undefined;
|
|
62
78
|
}
|
|
63
79
|
export declare function generateCenter2Tables(): Center2Tables;
|
|
64
80
|
export interface Center2Phase2Result {
|
|
@@ -77,6 +93,7 @@ export declare function solveCenter2(startCt: number, startRl: number, tables: C
|
|
|
77
93
|
* (transversal + even wing permutation), which most states fail.
|
|
78
94
|
*/
|
|
79
95
|
export declare function solveCenter2All(startCt: number, startRl: number, tables: Center2Tables, maxLength?: number, cap?: number): Center2Phase2Result[];
|
|
96
|
+
export declare function getWingMoveMaps(): Int32Array;
|
|
80
97
|
/**
|
|
81
98
|
* Solve Center 2 gated for an exact target length.
|
|
82
99
|
*/
|
|
@@ -87,5 +104,5 @@ export declare function solveCenter2GatedLength(startCt: number, startRl: number
|
|
|
87
104
|
* transversal (the phase-3 entry gate).
|
|
88
105
|
*/
|
|
89
106
|
export declare function solveCenter2Gated(startCt: number, startRl: number, startWp: Uint8Array, tables: Center2Tables, isGateEligible: (wp: Uint8Array) => boolean, maxLength?: number): Center2Phase2Result | null;
|
|
90
|
-
export declare function verifyCenter2(tables: Center2Tables): string[];
|
|
107
|
+
export declare function verifyCenter2(tables: Center2Tables, pruning?: Uint8Array, moves?: readonly Cube4Move[]): string[];
|
|
91
108
|
//# sourceMappingURL=center2.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"center2.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/center2.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"center2.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/center2.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
* - goal: ct == 0 and rl in the 6 reference goal patterns (even parity);
|
|
13
13
|
* - the 28-move set keeps the ct and rl position sets separate, so the move
|
|
14
14
|
* tables factor: ctmv (6,435×28) and rlmv (70×28);
|
|
15
|
-
* - the search explores the first 23 moves (face moves + Uw2/Rw/Rw2/Rw')
|
|
15
|
+
* - the search explores the first 23 moves (face moves + Uw2/Rw/Rw2/Rw')
|
|
16
|
+
* by default; the wide set (`CENTER2_SEARCH_MOVES_WIDE`, all 28, with
|
|
17
|
+
* the `pruningWide` table) additionally tries Dw2/Lw/Lw2/Lw'/Bw2.
|
|
16
18
|
*
|
|
17
19
|
* The phase-1 → phase-2 bridge (alignment + encode) is in `reduction.ts`.
|
|
18
20
|
*/
|
|
@@ -28,6 +30,12 @@ export const CENTER2_MOVES = [
|
|
|
28
30
|
];
|
|
29
31
|
/** The search's exploration set (the first 23 of CENTER2_MOVES). */
|
|
30
32
|
export const CENTER2_SEARCH_MOVES = CENTER2_MOVES.slice(0, 23);
|
|
33
|
+
/**
|
|
34
|
+
* The wide exploration set: all 28 moves (adds Dw2/Lw/Lw2/Lw'/Bw2).
|
|
35
|
+
* Searches over this set must use the `pruningWide` table — the
|
|
36
|
+
* 23-move pruning distances are inadmissible here (they overestimate).
|
|
37
|
+
*/
|
|
38
|
+
export const CENTER2_SEARCH_MOVES_WIDE = CENTER2_MOVES;
|
|
31
39
|
/** The 6 reference goal rl patterns (full 70-space values, even parity). */
|
|
32
40
|
export const CENTER2_GOAL_RL = [0, 18, 28, 46, 54, 56];
|
|
33
41
|
export const CENTER2_CT_STATES = 6435;
|
|
@@ -112,6 +120,11 @@ const ALIGNMENT_CANDIDATES = [
|
|
|
112
120
|
["Uw", "Dw'"]
|
|
113
121
|
];
|
|
114
122
|
function center2InvariantDiffs(centers) {
|
|
123
|
+
const rlAxis = centers[RL_POS[7]] % 3;
|
|
124
|
+
for (let i = 0; i < 7; i++) {
|
|
125
|
+
if (centers[RL_POS[i]] % 3 !== rlAxis)
|
|
126
|
+
return { ctDiff: -1, rlDiff: -1 };
|
|
127
|
+
}
|
|
115
128
|
let ctDiff = 0;
|
|
116
129
|
let rlDiff = 0;
|
|
117
130
|
for (let i = 0; i < 15; i++) {
|
|
@@ -124,12 +137,18 @@ function center2InvariantDiffs(centers) {
|
|
|
124
137
|
}
|
|
125
138
|
return { ctDiff, rlDiff };
|
|
126
139
|
}
|
|
140
|
+
export const ALIGNMENT_ROTATIONS = [
|
|
141
|
+
[],
|
|
142
|
+
["z"],
|
|
143
|
+
["y"]
|
|
144
|
+
];
|
|
127
145
|
/**
|
|
128
146
|
* Phase-1 → phase-2 bridge: apply the shortest alignment candidate that
|
|
129
147
|
* makes the state coordinate-invariant, then encode.
|
|
130
148
|
*/
|
|
131
149
|
export function center2Bridge(state) {
|
|
132
|
-
for (
|
|
150
|
+
for (let idx = 0; idx < ALIGNMENT_CANDIDATES.length; idx++) {
|
|
151
|
+
const alignment = ALIGNMENT_CANDIDATES[idx];
|
|
133
152
|
const aligned = alignment.length === 0
|
|
134
153
|
? state
|
|
135
154
|
: applyCube4Algorithm(state, alignment);
|
|
@@ -137,6 +156,7 @@ export function center2Bridge(state) {
|
|
|
137
156
|
if (ctDiff === 8 && rlDiff === 4) {
|
|
138
157
|
return {
|
|
139
158
|
alignment: [...alignment],
|
|
159
|
+
rotation: [...ALIGNMENT_ROTATIONS[idx]],
|
|
140
160
|
state: aligned,
|
|
141
161
|
encoded: encodeCenter2(aligned)
|
|
142
162
|
};
|
|
@@ -246,7 +266,31 @@ export function generateCenter2Tables() {
|
|
|
246
266
|
}
|
|
247
267
|
}
|
|
248
268
|
}
|
|
249
|
-
|
|
269
|
+
// Wide pruning BFS: same goals, all 28 moves (exact distances for the
|
|
270
|
+
// wide search; the 23-move table overestimates there).
|
|
271
|
+
const pruningWide = new Uint8Array(CENTER2_STATES).fill(0xff);
|
|
272
|
+
const wideQueue = [];
|
|
273
|
+
for (const rl of CENTER2_GOAL_RL) {
|
|
274
|
+
pruningWide[rl] = 0;
|
|
275
|
+
wideQueue.push(rl);
|
|
276
|
+
}
|
|
277
|
+
let wideHead = 0;
|
|
278
|
+
while (wideHead < wideQueue.length) {
|
|
279
|
+
const idx = wideQueue[wideHead++];
|
|
280
|
+
const ct = Math.floor(idx / CENTER2_RL_STATES);
|
|
281
|
+
const rl = idx % CENTER2_RL_STATES;
|
|
282
|
+
const d = pruningWide[idx];
|
|
283
|
+
for (let m = 0; m < CENTER2_SEARCH_MOVES_WIDE.length; m++) {
|
|
284
|
+
const nextCt = ctMove[ct * CENTER2_MOVES.length + m];
|
|
285
|
+
const nextRl = rlMove[rl * CENTER2_MOVES.length + m];
|
|
286
|
+
const next = nextCt * CENTER2_RL_STATES + nextRl;
|
|
287
|
+
if (pruningWide[next] === 0xff) {
|
|
288
|
+
pruningWide[next] = d + 1;
|
|
289
|
+
wideQueue.push(next);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return { ctMove, rlMove, pruning, pruningWide };
|
|
250
294
|
}
|
|
251
295
|
// The reference's cancellation rule (ckmv over move2std): skip the same base
|
|
252
296
|
// move, or the same axis-class when the previous move's standard index is
|
|
@@ -320,7 +364,7 @@ export function solveCenter2All(startCt, startRl, tables, maxLength = 40, cap =
|
|
|
320
364
|
}
|
|
321
365
|
}
|
|
322
366
|
let cachedWingMaps = null;
|
|
323
|
-
function getWingMoveMaps() {
|
|
367
|
+
export function getWingMoveMaps() {
|
|
324
368
|
if (!cachedWingMaps) {
|
|
325
369
|
cachedWingMaps = buildWingMoveMaps();
|
|
326
370
|
}
|
|
@@ -421,24 +465,24 @@ function buildWingMoveMaps() {
|
|
|
421
465
|
// ---------------------------------------------------------------------------
|
|
422
466
|
// Verification gates
|
|
423
467
|
// ---------------------------------------------------------------------------
|
|
424
|
-
export function verifyCenter2(tables) {
|
|
468
|
+
export function verifyCenter2(tables, pruning = tables.pruning, moves = CENTER2_SEARCH_MOVES) {
|
|
425
469
|
const errors = [];
|
|
426
470
|
for (const rl of CENTER2_GOAL_RL) {
|
|
427
|
-
if (
|
|
471
|
+
if (pruning[rl] !== 0) {
|
|
428
472
|
errors.push(`Goal rl ${rl} must have pruning 0.`);
|
|
429
473
|
}
|
|
430
474
|
}
|
|
431
475
|
let maxDepth = 0;
|
|
432
476
|
for (let idx = 0; idx < CENTER2_STATES; idx++) {
|
|
433
|
-
const d =
|
|
477
|
+
const d = pruning[idx];
|
|
434
478
|
if (d > maxDepth)
|
|
435
479
|
maxDepth = d;
|
|
436
480
|
const ct = Math.floor(idx / CENTER2_RL_STATES);
|
|
437
481
|
const rl = idx % CENTER2_RL_STATES;
|
|
438
|
-
for (let m = 0; m <
|
|
482
|
+
for (let m = 0; m < moves.length; m++) {
|
|
439
483
|
const nc = tables.ctMove[ct * CENTER2_MOVES.length + m];
|
|
440
484
|
const nr = tables.rlMove[rl * CENTER2_MOVES.length + m];
|
|
441
|
-
const nd =
|
|
485
|
+
const nd = pruning[nc * CENTER2_RL_STATES + nr];
|
|
442
486
|
if (nd < d - 1 || nd > d + 1) {
|
|
443
487
|
errors.push(`Pruning inconsistency at ${idx} move ${CENTER2_MOVES[m]}: ${d} -> ${nd}.`);
|
|
444
488
|
break;
|
|
@@ -80,6 +80,10 @@ export interface Edge3Tables {
|
|
|
80
80
|
* `maxDepthCap` stops the BFS early (validation only).
|
|
81
81
|
*/
|
|
82
82
|
export declare function generateEdge3Tables(maxDepthCap?: number): Edge3Tables;
|
|
83
|
+
/**
|
|
84
|
+
* Unpack 4-bit nibble-packed distances into a byte-expanded Uint8Array (1 byte per state).
|
|
85
|
+
*/
|
|
86
|
+
export declare function unpackEdge3Distances(packed: Uint8Array): Uint8Array;
|
|
83
87
|
/**
|
|
84
88
|
* The distance lookup over a loaded distances buffer: builds the sym
|
|
85
89
|
* machinery once (cached) and returns a closure from the std-form edge
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edge3.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/edge3.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAWH,mEAAmE;AACnE,eAAO,MAAM,WAAW,WAAa,CAAC;AACtC,eAAO,MAAM,aAAa,OAAO,CAAC;AAClC,eAAO,MAAM,WAAW,QAAS,CAAC;AAGlC,qEAAqE;AACrE,eAAO,MAAM,eAAe,KAAK,CAAC;AAUlC,+EAA+E;AAC/E,eAAO,MAAM,SAAS,EAAE,SAAS,MAAM,EAEtC,CAAC;AAaF,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE1C;AAkCD;;;;;GAKG;AACH,iBAAS,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CA2G3E;AAkCD,iFAAiF;AACjF,iBAAS,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAO3E;AAMD;;;;GAIG;AACH,iBAAS,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,UAAQ,GAAG,MAAM,CAS3E;AAED;;;GAGG;AACH,iBAAS,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAmBhD;AAOD,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,UAAU,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,WAAW,CAAC;IACrB,oEAAoE;IACpE,QAAQ,EAAE,WAAW,CAAC;IACtB,gDAAgD;IAChD,OAAO,EAAE,WAAW,CAAC;IACrB,iFAAiF;IACjF,QAAQ,EAAE,WAAW,CAAC;IACtB,qFAAqF;IACrF,OAAO,EAAE,WAAW,CAAC;CACtB;AAID,wBAAgB,YAAY,IAAI,SAAS,CAKxC;AAED,wBAAgB,cAAc,IAAI,SAAS,CAoE1C;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,UAAU,EAClB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,WAAW,EACrB,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,UAAU,EAClB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CA6BR;AAMD,MAAM,WAAW,WAAW;IAC1B,uDAAuD;IACvD,SAAS,EAAE,UAAU,CAAC;IACtB,0DAA0D;IAC1D,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;CAC1C;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,SAAK,GAAG,WAAW,
|
|
1
|
+
{"version":3,"file":"edge3.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube4/reduction/edge3.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAWH,mEAAmE;AACnE,eAAO,MAAM,WAAW,WAAa,CAAC;AACtC,eAAO,MAAM,aAAa,OAAO,CAAC;AAClC,eAAO,MAAM,WAAW,QAAS,CAAC;AAGlC,qEAAqE;AACrE,eAAO,MAAM,eAAe,KAAK,CAAC;AAUlC,+EAA+E;AAC/E,eAAO,MAAM,SAAS,EAAE,SAAS,MAAM,EAEtC,CAAC;AAaF,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE1C;AAkCD;;;;;GAKG;AACH,iBAAS,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CA2G3E;AAkCD,iFAAiF;AACjF,iBAAS,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAO3E;AAMD;;;;GAIG;AACH,iBAAS,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,UAAQ,GAAG,MAAM,CAS3E;AAED;;;GAGG;AACH,iBAAS,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAmBhD;AAOD,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,UAAU,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,WAAW,CAAC;IACrB,oEAAoE;IACpE,QAAQ,EAAE,WAAW,CAAC;IACtB,gDAAgD;IAChD,OAAO,EAAE,WAAW,CAAC;IACrB,iFAAiF;IACjF,QAAQ,EAAE,WAAW,CAAC;IACtB,qFAAqF;IACrF,OAAO,EAAE,WAAW,CAAC;CACtB;AAID,wBAAgB,YAAY,IAAI,SAAS,CAKxC;AAED,wBAAgB,cAAc,IAAI,SAAS,CAoE1C;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,UAAU,EAClB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,WAAW,EACrB,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,UAAU,EAClB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CA6BR;AAMD,MAAM,WAAW,WAAW;IAC1B,uDAAuD;IACvD,SAAS,EAAE,UAAU,CAAC;IACtB,0DAA0D;IAC1D,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;CAC1C;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,SAAK,GAAG,WAAW,CAmEjE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CASnE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,UAAU,GAAG,CAAC,IAAI,EAAE,UAAU,KAAK,MAAM,CAWvF;AAMD,uBAAuB;AACvB,wBAAgB,gBAAgB;;;;;;;EAE/B;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,EAAE,CAkBzD"}
|
|
@@ -468,8 +468,23 @@ export function generateEdge3Tables(maxDepthCap = -1) {
|
|
|
468
468
|
frontier = next;
|
|
469
469
|
depth++;
|
|
470
470
|
}
|
|
471
|
-
const
|
|
472
|
-
|
|
471
|
+
const unpacked = unpackEdge3Distances(distances);
|
|
472
|
+
const distanceOfEdge = edge3DistanceLookup(unpacked);
|
|
473
|
+
return { distances: unpacked, distanceOfEdge };
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Unpack 4-bit nibble-packed distances into a byte-expanded Uint8Array (1 byte per state).
|
|
477
|
+
*/
|
|
478
|
+
export function unpackEdge3Distances(packed) {
|
|
479
|
+
if (packed.length >= EDGE3_TOTAL)
|
|
480
|
+
return packed;
|
|
481
|
+
const unpacked = new Uint8Array(EDGE3_TOTAL);
|
|
482
|
+
for (let i = 0; i < packed.length; i++) {
|
|
483
|
+
const b = packed[i];
|
|
484
|
+
unpacked[i << 1] = b & 0x0f;
|
|
485
|
+
unpacked[(i << 1) | 1] = b >> 4;
|
|
486
|
+
}
|
|
487
|
+
return unpacked;
|
|
473
488
|
}
|
|
474
489
|
/**
|
|
475
490
|
* The distance lookup over a loaded distances buffer: builds the sym
|
|
@@ -480,9 +495,12 @@ export function edge3DistanceLookup(distances) {
|
|
|
480
495
|
const t = symTables ?? buildSymTables();
|
|
481
496
|
const { sym2Raw, sym2Mask, raw2Sym, mvrot, mvroto } = t;
|
|
482
497
|
const identity = Uint8Array.from({ length: 12 }, (_, i) => i);
|
|
498
|
+
const isUnpacked = distances.length >= EDGE3_TOTAL;
|
|
483
499
|
return (e) => {
|
|
484
500
|
const std = normalizePairingState(e, identity).edge;
|
|
485
501
|
const idx = getMvSymFrom(std, identity, 20, sym2Raw, sym2Mask, raw2Sym, mvrot, mvroto) >> 3;
|
|
502
|
+
if (isUnpacked)
|
|
503
|
+
return distances[idx];
|
|
486
504
|
return (distances[idx >> 1] >> ((idx & 1) * 4)) & 0x0f;
|
|
487
505
|
};
|
|
488
506
|
}
|
|
@@ -500,8 +518,10 @@ export function verifyEdge3(tables) {
|
|
|
500
518
|
errors.push("Solved state must have distance 0.");
|
|
501
519
|
}
|
|
502
520
|
let seen = 0;
|
|
521
|
+
const isUnpacked = tables.distances.length >= EDGE3_TOTAL;
|
|
503
522
|
for (let i = 0; i < EDGE3_TOTAL; i++) {
|
|
504
|
-
|
|
523
|
+
const d = isUnpacked ? tables.distances[i] : ((tables.distances[i >> 1] >> ((i & 1) * 4)) & 0x0f);
|
|
524
|
+
if (d !== 0x0f)
|
|
505
525
|
seen++;
|
|
506
526
|
}
|
|
507
527
|
if (seen !== EDGE3_TOTAL) {
|