@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
|
@@ -261,6 +261,12 @@ export let CENTERS_TABLE = null;
|
|
|
261
261
|
export let PERMUTATION_TABLE = null;
|
|
262
262
|
export let ORIENTATION_TABLE = null;
|
|
263
263
|
export let POLICY_TABLE = null;
|
|
264
|
+
/**
|
|
265
|
+
* Byte-expanded policy (one optimal move per state, no bit-unpacking in
|
|
266
|
+
* the hot walk). Built at load from the 3-bit packed table: +0.9 MB RAM,
|
|
267
|
+
* zero wire/format change.
|
|
268
|
+
*/
|
|
269
|
+
export let POLICY_TABLE_EXPANDED = null;
|
|
264
270
|
export async function ensureTablesLoaded() {
|
|
265
271
|
if (tablesLoaded)
|
|
266
272
|
return;
|
|
@@ -269,8 +275,18 @@ export async function ensureTablesLoaded() {
|
|
|
269
275
|
PERMUTATION_TABLE = tables.permutationTable;
|
|
270
276
|
ORIENTATION_TABLE = tables.orientationTable;
|
|
271
277
|
POLICY_TABLE = tables.policyTable;
|
|
278
|
+
POLICY_TABLE_EXPANDED = expandPolicy3Bit(tables.policyTable, STATE_COUNT);
|
|
272
279
|
tablesLoaded = true;
|
|
273
280
|
}
|
|
281
|
+
function expandPolicy3Bit(packed, count) {
|
|
282
|
+
const expanded = new Uint8Array(count);
|
|
283
|
+
for (let index = 0; index < count; index += 1) {
|
|
284
|
+
const bits = index * 3;
|
|
285
|
+
const byteIndex = bits >> 3;
|
|
286
|
+
expanded[index] = (((packed[byteIndex] | ((packed[byteIndex + 1] ?? 0) << 8)) >> (bits & 7)) & 0x07);
|
|
287
|
+
}
|
|
288
|
+
return expanded;
|
|
289
|
+
}
|
|
274
290
|
export function arePyraminxTablesLoaded() {
|
|
275
291
|
return tablesLoaded;
|
|
276
292
|
}
|
|
@@ -300,4 +316,5 @@ export function resetTablesForTests() {
|
|
|
300
316
|
PERMUTATION_TABLE = null;
|
|
301
317
|
ORIENTATION_TABLE = null;
|
|
302
318
|
POLICY_TABLE = null;
|
|
319
|
+
POLICY_TABLE_EXPANDED = null;
|
|
303
320
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ensureTablesLoaded, getCenterIndex, getOrientationIndex, getPerm7Index,
|
|
1
|
+
import { ensureTablesLoaded, getCenterIndex, getOrientationIndex, getPerm7Index, MOVE_COUNT, PERMUTATION_TABLE, ORIENTATION_TABLE, CENTERS_TABLE, POLICY_TABLE, POLICY_TABLE_EXPANDED, rankFromComponents, SKEWB_MOVES, SOLVED_RANK } from "./tables.js";
|
|
2
2
|
export async function solveSkewb(state) {
|
|
3
3
|
await ensureTablesLoaded();
|
|
4
4
|
return solveWithTables(state).moves;
|
|
@@ -33,7 +33,7 @@ function solveWithTables(state) {
|
|
|
33
33
|
return { moves, depth: moves.length };
|
|
34
34
|
}
|
|
35
35
|
function solveWithTablesIndices(state) {
|
|
36
|
-
const policy =
|
|
36
|
+
const policy = POLICY_TABLE_EXPANDED;
|
|
37
37
|
const permTable = PERMUTATION_TABLE;
|
|
38
38
|
const orientTable = ORIENTATION_TABLE;
|
|
39
39
|
const centersTable = CENTERS_TABLE;
|
|
@@ -50,7 +50,7 @@ function solveWithTablesIndices(state) {
|
|
|
50
50
|
}
|
|
51
51
|
const moves = [];
|
|
52
52
|
while (rank !== SOLVED_RANK) {
|
|
53
|
-
const m =
|
|
53
|
+
const m = policy[rank];
|
|
54
54
|
moves.push(m);
|
|
55
55
|
r7 = permTable[r7 * MOVE_COUNT + m];
|
|
56
56
|
rO = orientTable[rO * MOVE_COUNT + m];
|
|
@@ -23,6 +23,12 @@ export declare let PERMUTATION_TABLE: Uint16Array | null;
|
|
|
23
23
|
export declare let ORIENTATION_TABLE: Uint16Array | null;
|
|
24
24
|
export declare let CENTERS_TABLE: Uint16Array | null;
|
|
25
25
|
export declare let POLICY_TABLE: Uint8Array | null;
|
|
26
|
+
/**
|
|
27
|
+
* Byte-expanded policy (one optimal move per rank, no bit-unpacking in
|
|
28
|
+
* the hot walk). Built at load from the 3-bit packed table: +3.1 MB RAM,
|
|
29
|
+
* zero wire/format change.
|
|
30
|
+
*/
|
|
31
|
+
export declare let POLICY_TABLE_EXPANDED: Uint8Array | null;
|
|
26
32
|
export declare let EVEN_CENTER_RANK: Uint16Array | null;
|
|
27
33
|
export declare let EVEN_CENTER_FULL: Uint16Array | null;
|
|
28
34
|
export declare let ORIENT_CLASS: Uint8Array | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/skewb/solver/tables.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAazD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAanE;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAY1E;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAOrE;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAS/D;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAWjE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAE1D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAOvD;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAY3D;AAYD,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,YAAY,MAAM,CAAC;AAChC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,kBAAkB,IAAI,CAAC;AACpC,eAAO,MAAM,YAAY,IAAI,CAAC;AAC9B,eAAO,MAAM,UAAU,IAAI,CAAC;AAC5B,eAAO,MAAM,WAAW,QAAuD,CAAC;AAChF,eAAO,MAAM,qBAAqB,QAAmC,CAAC;AAEtE,eAAO,MAAM,WAAW,EAAE,SAAS,SAAS,EAAiD,CAAC;AAE9F,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAa7D;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAYzD;AAOD,eAAO,IAAI,iBAAiB,EAAE,WAAW,GAAG,IAAW,CAAC;AACxD,eAAO,IAAI,iBAAiB,EAAE,WAAW,GAAG,IAAW,CAAC;AACxD,eAAO,IAAI,aAAa,EAAE,WAAW,GAAG,IAAW,CAAC;AACpD,eAAO,IAAI,YAAY,EAAE,UAAU,GAAG,IAAW,CAAC;AAClD,eAAO,IAAI,gBAAgB,EAAE,WAAW,GAAG,IAAW,CAAC;AACvD,eAAO,IAAI,gBAAgB,EAAE,WAAW,GAAG,IAAW,CAAC;AACvD,eAAO,IAAI,YAAY,EAAE,UAAU,GAAG,IAAW,CAAC;AAClD,eAAO,IAAI,OAAO,EAAE,WAAW,GAAG,IAAW,CAAC;AAC9C,eAAO,IAAI,YAAY,EAAE,UAAU,GAAG,IAAW,CAAC;AAClD,eAAO,IAAI,aAAa,EAAE,UAAU,GAAG,IAAW,CAAC;AACnD,eAAO,IAAI,aAAa,EAAE,UAAU,GAAG,IAAW,CAAC;AACnD,eAAO,IAAI,WAAW,QAAI,CAAC;AAE3B,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAIxD;AAED,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE;IACnC,gBAAgB,EAAE,WAAW,CAAC;IAC9B,gBAAgB,EAAE,WAAW,CAAC;IAC9B,YAAY,EAAE,WAAW,CAAC;IAC1B,WAAW,EAAE,UAAU,CAAC;IACxB,cAAc,EAAE,WAAW,CAAC;IAC5B,WAAW,EAAE,UAAU,CAAC;IACxB,MAAM,EAAE,WAAW,CAAC;CACrB,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/skewb/solver/tables.ts"],"names":[],"mappings":"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"}
|
|
@@ -143,6 +143,12 @@ export let PERMUTATION_TABLE = null; // r7 * MOVE_COUNT + m
|
|
|
143
143
|
export let ORIENTATION_TABLE = null; // rO * MOVE_COUNT + m
|
|
144
144
|
export let CENTERS_TABLE = null; // rC * MOVE_COUNT + m
|
|
145
145
|
export let POLICY_TABLE = null; // optimal move per rank, 3-bit packed
|
|
146
|
+
/**
|
|
147
|
+
* Byte-expanded policy (one optimal move per rank, no bit-unpacking in
|
|
148
|
+
* the hot walk). Built at load from the 3-bit packed table: +3.1 MB RAM,
|
|
149
|
+
* zero wire/format change.
|
|
150
|
+
*/
|
|
151
|
+
export let POLICY_TABLE_EXPANDED = null;
|
|
146
152
|
export let EVEN_CENTER_RANK = null; // rC -> compact 0..359 (65535 = odd)
|
|
147
153
|
export let EVEN_CENTER_FULL = null; // compact 0..359 -> rC (derived at load)
|
|
148
154
|
export let ORIENT_CLASS = null; // rO -> class 0..8
|
|
@@ -192,9 +198,19 @@ export function assignTables(tables) {
|
|
|
192
198
|
for (let i = 0; i < 7; i++)
|
|
193
199
|
ORIENT_UNRANK[rO * 7 + i] = orient[i + 1];
|
|
194
200
|
}
|
|
201
|
+
POLICY_TABLE_EXPANDED = expandPolicy3Bit(tables.policyTable, STATE_COUNT);
|
|
195
202
|
SOLVED_RANK = rankFromComponents(0, 0, 0);
|
|
196
203
|
tablesLoaded = true;
|
|
197
204
|
}
|
|
205
|
+
function expandPolicy3Bit(packed, count) {
|
|
206
|
+
const expanded = new Uint8Array(count);
|
|
207
|
+
for (let index = 0; index < count; index += 1) {
|
|
208
|
+
const bits = index * 3;
|
|
209
|
+
const byteIndex = bits >> 3;
|
|
210
|
+
expanded[index] = (((packed[byteIndex] | ((packed[byteIndex + 1] ?? 0) << 8)) >> (bits & 7)) & 0x07);
|
|
211
|
+
}
|
|
212
|
+
return expanded;
|
|
213
|
+
}
|
|
198
214
|
export function resetTablesForTests() {
|
|
199
215
|
tablesLoaded = false;
|
|
200
216
|
PERMUTATION_TABLE = null;
|
|
@@ -208,6 +224,7 @@ export function resetTablesForTests() {
|
|
|
208
224
|
PERM7_UNRANK = null;
|
|
209
225
|
CENTER_UNRANK = null;
|
|
210
226
|
ORIENT_UNRANK = null;
|
|
227
|
+
POLICY_TABLE_EXPANDED = null;
|
|
211
228
|
}
|
|
212
229
|
// ---------------------------------------------------------------------------
|
|
213
230
|
// Compact rank helpers
|
|
@@ -37,7 +37,7 @@ function solveWithTablesIndices(state) {
|
|
|
37
37
|
}
|
|
38
38
|
const permTable = tables_js_1.PERMUTATION_TABLE;
|
|
39
39
|
const orientTable = tables_js_1.ORIENTATION_TABLE;
|
|
40
|
-
const policy = tables_js_1.
|
|
40
|
+
const policy = tables_js_1.POLICY_TABLE_EXPANDED;
|
|
41
41
|
let permIndex = (0, tables_js_1.encodePermutation)(state.corners.permutation);
|
|
42
42
|
let orientIndex = (0, tables_js_1.encodeOrientation)(state.corners.orientation);
|
|
43
43
|
const moves = [];
|
|
@@ -45,7 +45,7 @@ function solveWithTablesIndices(state) {
|
|
|
45
45
|
// candidate scanning (the old BFS-distance gradient walk) is needed.
|
|
46
46
|
let combined = permIndex * tables_js_1.ORIENTATION_COUNT + orientIndex;
|
|
47
47
|
while (combined !== 0) {
|
|
48
|
-
const m =
|
|
48
|
+
const m = policy[combined];
|
|
49
49
|
moves.push(m);
|
|
50
50
|
permIndex = permTable[permIndex * tables_js_1.MOVE_COUNT + m];
|
|
51
51
|
orientIndex = orientTable[orientIndex * tables_js_1.MOVE_COUNT + m];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.BFS_MOVES = exports.POLICY_TABLE = exports.ORIENTATION_TABLE = exports.PERMUTATION_TABLE = exports.MOVE_COUNT = exports.ORIENTATION_COUNT = exports.PERMUTATION_COUNT = void 0;
|
|
3
|
+
exports.BFS_MOVES = exports.POLICY_TABLE_EXPANDED = exports.POLICY_TABLE = exports.ORIENTATION_TABLE = exports.PERMUTATION_TABLE = exports.MOVE_COUNT = exports.ORIENTATION_COUNT = exports.PERMUTATION_COUNT = void 0;
|
|
4
4
|
exports.encodePermutation = encodePermutation;
|
|
5
5
|
exports.encodeOrientation = encodeOrientation;
|
|
6
6
|
exports.ensureTablesLoaded = ensureTablesLoaded;
|
|
@@ -64,6 +64,12 @@ let tablesLoaded = false;
|
|
|
64
64
|
exports.PERMUTATION_TABLE = null;
|
|
65
65
|
exports.ORIENTATION_TABLE = null;
|
|
66
66
|
exports.POLICY_TABLE = null;
|
|
67
|
+
/**
|
|
68
|
+
* Byte-expanded policy (one optimal move per state, no bit-unpacking in
|
|
69
|
+
* the hot walk). Built at load from the packed table: +3.7 MB RAM,
|
|
70
|
+
* zero wire/format change.
|
|
71
|
+
*/
|
|
72
|
+
exports.POLICY_TABLE_EXPANDED = null;
|
|
67
73
|
async function ensureTablesLoaded() {
|
|
68
74
|
if (tablesLoaded) {
|
|
69
75
|
return;
|
|
@@ -72,8 +78,16 @@ async function ensureTablesLoaded() {
|
|
|
72
78
|
exports.PERMUTATION_TABLE = tables.permutationTable;
|
|
73
79
|
exports.ORIENTATION_TABLE = tables.orientationTable;
|
|
74
80
|
exports.POLICY_TABLE = tables.policyTable;
|
|
81
|
+
exports.POLICY_TABLE_EXPANDED = expandNibblePolicy(tables.policyTable, exports.PERMUTATION_COUNT * exports.ORIENTATION_COUNT);
|
|
75
82
|
tablesLoaded = true;
|
|
76
83
|
}
|
|
84
|
+
function expandNibblePolicy(packed, count) {
|
|
85
|
+
const expanded = new Uint8Array(count);
|
|
86
|
+
for (let index = 0; index < count; index += 1) {
|
|
87
|
+
expanded[index] = (packed[index >> 1] >> ((index & 1) << 2)) & 0x0f;
|
|
88
|
+
}
|
|
89
|
+
return expanded;
|
|
90
|
+
}
|
|
77
91
|
function areCube2TablesLoaded() {
|
|
78
92
|
return tablesLoaded;
|
|
79
93
|
}
|
|
@@ -81,7 +95,7 @@ function areCube2TablesLoaded() {
|
|
|
81
95
|
// Public API
|
|
82
96
|
// ---------------------------------------------------------------------------
|
|
83
97
|
function getDistance(permIndex, orientIndex) {
|
|
84
|
-
if (!exports.
|
|
98
|
+
if (!exports.POLICY_TABLE_EXPANDED) {
|
|
85
99
|
throw new Error("Cube2 solver tables not loaded. Call ensureTablesLoaded() first.");
|
|
86
100
|
}
|
|
87
101
|
const permTable = exports.PERMUTATION_TABLE;
|
|
@@ -91,7 +105,7 @@ function getDistance(permIndex, orientIndex) {
|
|
|
91
105
|
let combined = p * exports.ORIENTATION_COUNT + o;
|
|
92
106
|
let distance = 0;
|
|
93
107
|
while (combined !== 0) {
|
|
94
|
-
const m =
|
|
108
|
+
const m = exports.POLICY_TABLE_EXPANDED[combined];
|
|
95
109
|
p = permTable[p * exports.MOVE_COUNT + m];
|
|
96
110
|
o = orientTable[o * exports.MOVE_COUNT + m];
|
|
97
111
|
combined = p * exports.ORIENTATION_COUNT + o;
|
|
@@ -35,16 +35,114 @@ const FACE_MOVE_TABLES = {
|
|
|
35
35
|
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] }
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
|
-
function
|
|
38
|
+
function quarterEffect(face) {
|
|
39
|
+
const table = FACE_MOVE_TABLES[face];
|
|
40
|
+
return {
|
|
41
|
+
cornerPermutation: table.corners.permutation,
|
|
42
|
+
cornerOrientation: table.corners.orientation,
|
|
43
|
+
edgePermutation: table.edges.permutation,
|
|
44
|
+
edgeOrientation: table.edges.orientation
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// Compose two effects (apply `first`, then `second`) into one so a move of
|
|
48
|
+
// any suffix costs exactly one pass over the state — no per-quarter-turn
|
|
49
|
+
// cloning, no intermediate states, no closure allocations.
|
|
50
|
+
function composeEffects(first, second) {
|
|
51
|
+
const cornerPermutation = new Array(8);
|
|
52
|
+
const cornerOrientation = new Array(8);
|
|
53
|
+
const edgePermutation = new Array(12);
|
|
54
|
+
const edgeOrientation = new Array(12);
|
|
55
|
+
for (let i = 0; i < 8; i++) {
|
|
56
|
+
const source = second.cornerPermutation[i];
|
|
57
|
+
cornerPermutation[i] = first.cornerPermutation[source];
|
|
58
|
+
cornerOrientation[i] = (first.cornerOrientation[source] + second.cornerOrientation[i]) % 3;
|
|
59
|
+
}
|
|
60
|
+
for (let i = 0; i < 12; i++) {
|
|
61
|
+
const source = second.edgePermutation[i];
|
|
62
|
+
edgePermutation[i] = first.edgePermutation[source];
|
|
63
|
+
edgeOrientation[i] = (first.edgeOrientation[source] + second.edgeOrientation[i]) % 2;
|
|
64
|
+
}
|
|
65
|
+
return { cornerPermutation, cornerOrientation, edgePermutation, edgeOrientation };
|
|
66
|
+
}
|
|
67
|
+
function powerEffect(face, turns) {
|
|
68
|
+
const quarter = quarterEffect(face);
|
|
69
|
+
if (turns === 1)
|
|
70
|
+
return quarter;
|
|
71
|
+
const half = composeEffects(quarter, quarter);
|
|
72
|
+
if (turns === 2)
|
|
73
|
+
return half;
|
|
74
|
+
return composeEffects(half, quarter);
|
|
75
|
+
}
|
|
76
|
+
// Total effect per move string, built once at module load (mirrors the 4x4
|
|
77
|
+
// fast path): one lookup + one pass per move.
|
|
78
|
+
const COMPOSED_EFFECTS = Object.fromEntries(types_js_1.CUBE3_MOVES.map((move) => {
|
|
79
|
+
const face = move[0];
|
|
80
|
+
const suffix = move.slice(1);
|
|
81
|
+
const turns = suffix === "2" ? 2 : suffix === "'" ? 3 : 1;
|
|
82
|
+
return [move, powerEffect(face, turns)];
|
|
83
|
+
}));
|
|
84
|
+
function effectForMove(move) {
|
|
85
|
+
const hit = COMPOSED_EFFECTS[move];
|
|
86
|
+
if (hit !== undefined)
|
|
87
|
+
return hit;
|
|
39
88
|
const { face, turns } = parseMoveParts(move);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
89
|
+
return powerEffect(face, turns);
|
|
90
|
+
}
|
|
91
|
+
function applyCube3Move(state, move) {
|
|
92
|
+
const effect = effectForMove(move);
|
|
93
|
+
const cornerPermutation = new Array(8);
|
|
94
|
+
const cornerOrientation = new Array(8);
|
|
95
|
+
const edgePermutation = new Array(12);
|
|
96
|
+
const edgeOrientation = new Array(12);
|
|
97
|
+
for (let i = 0; i < 8; i++) {
|
|
98
|
+
const source = effect.cornerPermutation[i];
|
|
99
|
+
cornerPermutation[i] = state.corners.permutation[source];
|
|
100
|
+
cornerOrientation[i] =
|
|
101
|
+
(state.corners.orientation[source] + effect.cornerOrientation[i]) % 3;
|
|
43
102
|
}
|
|
44
|
-
|
|
103
|
+
for (let i = 0; i < 12; i++) {
|
|
104
|
+
const source = effect.edgePermutation[i];
|
|
105
|
+
edgePermutation[i] = state.edges.permutation[source];
|
|
106
|
+
edgeOrientation[i] =
|
|
107
|
+
(state.edges.orientation[source] + effect.edgeOrientation[i]) % 2;
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
corners: { permutation: cornerPermutation, orientation: cornerOrientation },
|
|
111
|
+
edges: { permutation: edgePermutation, orientation: edgeOrientation }
|
|
112
|
+
};
|
|
45
113
|
}
|
|
46
114
|
function applyCube3Algorithm(state, moves) {
|
|
47
|
-
|
|
115
|
+
if (moves.length === 0)
|
|
116
|
+
return (0, state_js_1.cloneCube3State)(state);
|
|
117
|
+
let curCp = [...state.corners.permutation];
|
|
118
|
+
let curCo = [...state.corners.orientation];
|
|
119
|
+
let curEp = [...state.edges.permutation];
|
|
120
|
+
let curEo = [...state.edges.orientation];
|
|
121
|
+
let nxtCp = new Array(8);
|
|
122
|
+
let nxtCo = new Array(8);
|
|
123
|
+
let nxtEp = new Array(12);
|
|
124
|
+
let nxtEo = new Array(12);
|
|
125
|
+
for (const move of moves) {
|
|
126
|
+
const effect = effectForMove(move);
|
|
127
|
+
for (let i = 0; i < 8; i++) {
|
|
128
|
+
const source = effect.cornerPermutation[i];
|
|
129
|
+
nxtCp[i] = curCp[source];
|
|
130
|
+
nxtCo[i] = (curCo[source] + effect.cornerOrientation[i]) % 3;
|
|
131
|
+
}
|
|
132
|
+
for (let i = 0; i < 12; i++) {
|
|
133
|
+
const source = effect.edgePermutation[i];
|
|
134
|
+
nxtEp[i] = curEp[source];
|
|
135
|
+
nxtEo[i] = (curEo[source] + effect.edgeOrientation[i]) % 2;
|
|
136
|
+
}
|
|
137
|
+
[curCp, nxtCp] = [nxtCp, curCp];
|
|
138
|
+
[curCo, nxtCo] = [nxtCo, curCo];
|
|
139
|
+
[curEp, nxtEp] = [nxtEp, curEp];
|
|
140
|
+
[curEo, nxtEo] = [nxtEo, curEo];
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
corners: { permutation: curCp, orientation: curCo },
|
|
144
|
+
edges: { permutation: curEp, orientation: curEo }
|
|
145
|
+
};
|
|
48
146
|
}
|
|
49
147
|
function invertCube3Algorithm(moves) {
|
|
50
148
|
return [...moves].reverse().map(invertCube3Move);
|
|
@@ -199,18 +297,6 @@ function invertCube3Move(move) {
|
|
|
199
297
|
}
|
|
200
298
|
return `${face}${suffix === "'" ? "" : "'"}`;
|
|
201
299
|
}
|
|
202
|
-
function applyFaceMove(state, table) {
|
|
203
|
-
return {
|
|
204
|
-
corners: {
|
|
205
|
-
permutation: table.corners.permutation.map((source) => state.corners.permutation[source]),
|
|
206
|
-
orientation: table.corners.permutation.map((source, target) => ((state.corners.orientation[source] + table.corners.orientation[target]) % 3))
|
|
207
|
-
},
|
|
208
|
-
edges: {
|
|
209
|
-
permutation: table.edges.permutation.map((source) => state.edges.permutation[source]),
|
|
210
|
-
orientation: table.edges.permutation.map((source, target) => ((state.edges.orientation[source] + table.edges.orientation[target]) % 2))
|
|
211
|
-
}
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
300
|
function parseMoveParts(move) {
|
|
215
301
|
const face = move[0];
|
|
216
302
|
const suffix = move.slice(1);
|
|
@@ -22,7 +22,7 @@ exports.cube3ScrambleGenerator = {
|
|
|
22
22
|
};
|
|
23
23
|
async function generateCube3Scramble(options = {}) {
|
|
24
24
|
const state = (0, random_state_js_1.generateRandomCube3State)(options);
|
|
25
|
-
const solution = await (0, index_js_1.solveCube3State)(state);
|
|
25
|
+
const solution = await (0, index_js_1.solveCube3State)(state, options.solver ?? {});
|
|
26
26
|
const scrambleMoves = (0, moves_js_1.invertCube3Algorithm)(solution.moves);
|
|
27
27
|
if (!options.skipValidation) {
|
|
28
28
|
const scrambledState = (0, moves_js_1.applyCube3Algorithm)((0, state_js_1.createSolvedCube3State)(), scrambleMoves);
|
|
@@ -100,10 +100,14 @@ async function fetchMoveTable(baseUrl, entry) {
|
|
|
100
100
|
if (buffer.byteLength !== expectedBytes) {
|
|
101
101
|
throw new Error(`Invalid byte size for ${entry.coordinate} move table.`);
|
|
102
102
|
}
|
|
103
|
-
const values = new
|
|
103
|
+
const values = new Uint16Array(entry.range * entry.moves.length);
|
|
104
104
|
const view = new DataView(buffer);
|
|
105
105
|
for (let index = 0; index < values.length; index += 1) {
|
|
106
|
-
|
|
106
|
+
const value = view.getInt32(index * 4, true);
|
|
107
|
+
if (value < 0 || value > 0xffff) {
|
|
108
|
+
throw new Error(`Move table value out of range for ${entry.coordinate}: ${value}.`);
|
|
109
|
+
}
|
|
110
|
+
values[index] = value;
|
|
107
111
|
}
|
|
108
112
|
return {
|
|
109
113
|
coordinate: entry.coordinate,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.lookupCube3MoveTable = exports.loadCube3SolverTables = exports.loadCube3Phase2Tables = exports.loadCube3Phase1Tables = exports.generateCube3SolverTables = exports.generateCube3PruningTable = exports.generateCube3MoveTable = exports.generateCube3Phase2Tables = exports.generateCube3Phase1Tables = exports.checksum = exports.CUBE3_PHASE2_MOVES = exports.CUBE3_PHASE1_MOVES = exports.CUBE3_SOLVER_TABLE_METADATA = exports.moveCoordinate = exports.encodeUdSlice = exports.encodeUdEdgePermutation = exports.encodeSlicePermutation = exports.encodePermutation = exports.encodeEdgeOrientation = exports.encodeCube3Coordinates = exports.encodeCornerOrientation = exports.encodeCube3Coordinate = exports.decodeUdSlice = exports.decodeUdEdgePermutation = exports.decodeSlicePermutation = exports.decodePermutation = exports.decodeEdgeOrientation = exports.decodeCornerOrientation = exports.createRepresentativeStateForCoordinate = exports.CUBE3_COORDINATE_RANGES = exports.Cube3Phase1SearchError = exports.solveCube3Phase1 = exports.isCube3Phase1Solved = exports.encodeCube3Phase1Coordinates = exports.applyCube3Phase1Moves = exports.Cube3Phase2SearchError = exports.solveCube3State = exports.solveCube3Phase2 = exports.isCube3Phase2Solved = exports.encodeCube3Phase2Coordinates = exports.applyCube3Phase2Moves = void 0;
|
|
3
|
+
exports.lookupCube3MoveTable = exports.loadCube3SolverTables = exports.loadCube3Phase2Tables = exports.loadCube3Phase1Tables = exports.generateCube3SolverTables = exports.generateCube3PruningTable = exports.generateCube3MoveTable = exports.generateCube3Phase2Tables = exports.generateCube3Phase1Tables = exports.checksum = exports.CUBE3_PHASE2_MOVES = exports.CUBE3_PHASE1_MOVES = exports.CUBE3_SOLVER_TABLE_METADATA = exports.moveCoordinate = exports.encodeUdSlice = exports.encodeUdEdgePermutation = exports.encodeSlicePermutation = exports.encodePermutation = exports.encodeEdgeOrientation = exports.encodeCube3Coordinates = exports.encodeCornerOrientation = exports.encodeCube3Coordinate = exports.decodeUdSlice = exports.decodeUdEdgePermutation = exports.decodeSlicePermutation = exports.decodePermutation = exports.decodeEdgeOrientation = exports.decodeCornerOrientation = exports.createRepresentativeStateForCoordinate = exports.CUBE3_COORDINATE_RANGES = exports.rotateCube3AlgorithmBySequence = exports.rotateCube3Algorithm = exports.invertCube3RotationSequence = exports.CUBE3_ROTATION_INVERSE = exports.CUBE3_ORIENTATIONS_24 = exports.Cube3Phase1SearchError = exports.solveCube3Phase1 = exports.isCube3Phase1Solved = exports.encodeCube3Phase1Coordinates = exports.collectCube3Phase1Solutions = exports.applyCube3Phase1Moves = exports.Cube3Phase2SearchError = exports.solveCube3State = exports.solveCube3Phase2 = exports.isCube3Phase2Solved = exports.encodeCube3Phase2Coordinates = exports.applyCube3Phase2Moves = void 0;
|
|
4
4
|
var phase2_js_1 = require("./phase2.js");
|
|
5
5
|
Object.defineProperty(exports, "applyCube3Phase2Moves", { enumerable: true, get: function () { return phase2_js_1.applyCube3Phase2Moves; } });
|
|
6
6
|
Object.defineProperty(exports, "encodeCube3Phase2Coordinates", { enumerable: true, get: function () { return phase2_js_1.encodeCube3Phase2Coordinates; } });
|
|
@@ -10,10 +10,17 @@ Object.defineProperty(exports, "solveCube3State", { enumerable: true, get: funct
|
|
|
10
10
|
Object.defineProperty(exports, "Cube3Phase2SearchError", { enumerable: true, get: function () { return phase2_js_1.Cube3Phase2SearchError; } });
|
|
11
11
|
var phase1_js_1 = require("./phase1.js");
|
|
12
12
|
Object.defineProperty(exports, "applyCube3Phase1Moves", { enumerable: true, get: function () { return phase1_js_1.applyCube3Phase1Moves; } });
|
|
13
|
+
Object.defineProperty(exports, "collectCube3Phase1Solutions", { enumerable: true, get: function () { return phase1_js_1.collectCube3Phase1Solutions; } });
|
|
13
14
|
Object.defineProperty(exports, "encodeCube3Phase1Coordinates", { enumerable: true, get: function () { return phase1_js_1.encodeCube3Phase1Coordinates; } });
|
|
14
15
|
Object.defineProperty(exports, "isCube3Phase1Solved", { enumerable: true, get: function () { return phase1_js_1.isCube3Phase1Solved; } });
|
|
15
16
|
Object.defineProperty(exports, "solveCube3Phase1", { enumerable: true, get: function () { return phase1_js_1.solveCube3Phase1; } });
|
|
16
17
|
Object.defineProperty(exports, "Cube3Phase1SearchError", { enumerable: true, get: function () { return phase1_js_1.Cube3Phase1SearchError; } });
|
|
18
|
+
var symmetry_js_1 = require("./symmetry.js");
|
|
19
|
+
Object.defineProperty(exports, "CUBE3_ORIENTATIONS_24", { enumerable: true, get: function () { return symmetry_js_1.CUBE3_ORIENTATIONS_24; } });
|
|
20
|
+
Object.defineProperty(exports, "CUBE3_ROTATION_INVERSE", { enumerable: true, get: function () { return symmetry_js_1.CUBE3_ROTATION_INVERSE; } });
|
|
21
|
+
Object.defineProperty(exports, "invertCube3RotationSequence", { enumerable: true, get: function () { return symmetry_js_1.invertCube3RotationSequence; } });
|
|
22
|
+
Object.defineProperty(exports, "rotateCube3Algorithm", { enumerable: true, get: function () { return symmetry_js_1.rotateCube3Algorithm; } });
|
|
23
|
+
Object.defineProperty(exports, "rotateCube3AlgorithmBySequence", { enumerable: true, get: function () { return symmetry_js_1.rotateCube3AlgorithmBySequence; } });
|
|
17
24
|
var coordinates_js_1 = require("./coordinates.js");
|
|
18
25
|
Object.defineProperty(exports, "CUBE3_COORDINATE_RANGES", { enumerable: true, get: function () { return coordinates_js_1.CUBE3_COORDINATE_RANGES; } });
|
|
19
26
|
Object.defineProperty(exports, "createRepresentativeStateForCoordinate", { enumerable: true, get: function () { return coordinates_js_1.createRepresentativeStateForCoordinate; } });
|
|
@@ -71,9 +71,16 @@ function readMoveTable(assetDirectory, entry) {
|
|
|
71
71
|
if (buffer.length !== expectedBytes) {
|
|
72
72
|
throw new Error(`Invalid byte size for ${entry.coordinate} move table.`);
|
|
73
73
|
}
|
|
74
|
-
|
|
74
|
+
// Every coordinate fits in 16 bits (max range 40320): store half the
|
|
75
|
+
// bytes the int32 wire format uses. Checksums are order-sensitive FNV
|
|
76
|
+
// over the same numeric sequence, so they still match the manifest.
|
|
77
|
+
const values = new Uint16Array(entry.range * entry.moves.length);
|
|
75
78
|
for (let index = 0; index < values.length; index += 1) {
|
|
76
|
-
|
|
79
|
+
const value = buffer.readInt32LE(index * 4);
|
|
80
|
+
if (value < 0 || value > 0xffff) {
|
|
81
|
+
throw new Error(`Move table value out of range for ${entry.coordinate}: ${value}.`);
|
|
82
|
+
}
|
|
83
|
+
values[index] = value;
|
|
77
84
|
}
|
|
78
85
|
const table = {
|
|
79
86
|
coordinate: entry.coordinate,
|
|
@@ -5,6 +5,7 @@ exports.solveCube3Phase1 = solveCube3Phase1;
|
|
|
5
5
|
exports.encodeCube3Phase1Coordinates = encodeCube3Phase1Coordinates;
|
|
6
6
|
exports.isCube3Phase1Solved = isCube3Phase1Solved;
|
|
7
7
|
exports.applyCube3Phase1Moves = applyCube3Phase1Moves;
|
|
8
|
+
exports.collectCube3Phase1Solutions = collectCube3Phase1Solutions;
|
|
8
9
|
const moves_js_1 = require("../moves.js");
|
|
9
10
|
const coordinates_js_1 = require("./coordinates.js");
|
|
10
11
|
const tables_js_1 = require("./tables.js");
|
|
@@ -36,14 +37,7 @@ async function solveCube3Phase1(state, options = {}) {
|
|
|
36
37
|
const maxDepth = options.maxDepth ?? 12;
|
|
37
38
|
const start = encodeCube3Phase1Coordinates(state);
|
|
38
39
|
const deadline = options.timeoutMs === undefined ? undefined : performance.now() + options.timeoutMs;
|
|
39
|
-
const context =
|
|
40
|
-
tables,
|
|
41
|
-
jointTables,
|
|
42
|
-
maxDepth,
|
|
43
|
-
deadline,
|
|
44
|
-
visitedNodes: 0,
|
|
45
|
-
timedOut: false
|
|
46
|
-
};
|
|
40
|
+
const context = createPhase1SearchContext(tables, jointTables, maxDepth, deadline);
|
|
47
41
|
if (await isCube3Phase1Solved(start, tables)) {
|
|
48
42
|
return {
|
|
49
43
|
moves: [],
|
|
@@ -124,6 +118,66 @@ function searchPhase1(cornerOrientation, edgeOrientation, udSlice, previousFace,
|
|
|
124
118
|
edgeOrientation === 0 &&
|
|
125
119
|
udSlice === context.tables.moveTables.udSlice.solvedCoordinate;
|
|
126
120
|
}
|
|
121
|
+
const moveCount = context.tables.phase1Moves.length;
|
|
122
|
+
const childCount = collectPhase1Children(cornerOrientation, edgeOrientation, udSlice, previousFace, previousPreviousFace, remainingDepth, context);
|
|
123
|
+
const slot = remainingDepth * moveCount;
|
|
124
|
+
const scratchMove = context.scratchMove;
|
|
125
|
+
const scratchCo = context.scratchCo;
|
|
126
|
+
const scratchEo = context.scratchEo;
|
|
127
|
+
const scratchUd = context.scratchUd;
|
|
128
|
+
for (let i = 0; i < childCount; i += 1) {
|
|
129
|
+
const moveIndex = scratchMove[slot + i];
|
|
130
|
+
const face = PHASE1_MOVE_FACES[moveIndex];
|
|
131
|
+
path.push(context.tables.phase1Moves[moveIndex]);
|
|
132
|
+
if (searchPhase1(scratchCo[slot + i], scratchEo[slot + i], scratchUd[slot + i], face, previousFace, remainingDepth - 1, path, context)) {
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
path.pop();
|
|
136
|
+
if (context.timedOut) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Enumerates up to `limit` phase-1 solutions of exactly `depth` moves for
|
|
144
|
+
* the given state, most promising first (same ordered DFS as the search).
|
|
145
|
+
* Used to try several G1 landing states and keep the best phase-1+phase-2
|
|
146
|
+
* total — a slightly longer phase 1 often collapses phase 2.
|
|
147
|
+
*/
|
|
148
|
+
async function collectCube3Phase1Solutions(state, depth, options = {}) {
|
|
149
|
+
const tables = options.tables ?? await (0, tables_js_1.loadCube3Phase1Tables)();
|
|
150
|
+
const jointTables = options.jointTables ?? tables.jointTables;
|
|
151
|
+
const limit = options.limit ?? 32;
|
|
152
|
+
const deadline = options.timeoutMs === undefined ? undefined : performance.now() + options.timeoutMs;
|
|
153
|
+
const context = createPhase1SearchContext(tables, jointTables, depth, deadline);
|
|
154
|
+
const start = encodeCube3Phase1Coordinates(state);
|
|
155
|
+
const solutions = [];
|
|
156
|
+
searchPhase1Collect(start.cornerOrientation, start.edgeOrientation, start.udSlice, -1, -1, depth, [], solutions, limit, context);
|
|
157
|
+
return { solutions, visitedNodes: context.visitedNodes };
|
|
158
|
+
}
|
|
159
|
+
function createPhase1SearchContext(tables, jointTables, maxDepth, deadline) {
|
|
160
|
+
const scratchLevels = maxDepth + 1;
|
|
161
|
+
const scratchSize = scratchLevels * tables_js_1.CUBE3_PHASE1_MOVES.length;
|
|
162
|
+
return {
|
|
163
|
+
tables,
|
|
164
|
+
jointTables,
|
|
165
|
+
maxDepth,
|
|
166
|
+
deadline,
|
|
167
|
+
visitedNodes: 0,
|
|
168
|
+
timedOut: false,
|
|
169
|
+
scratchMove: new Int32Array(scratchSize),
|
|
170
|
+
scratchBound: new Int32Array(scratchSize),
|
|
171
|
+
scratchCo: new Int32Array(scratchSize),
|
|
172
|
+
scratchEo: new Int32Array(scratchSize),
|
|
173
|
+
scratchUd: new Int32Array(scratchSize)
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Fills the scratch slot for `remainingDepth` with the surviving children
|
|
178
|
+
* in ascending lower-bound order (stable). Returns the child count.
|
|
179
|
+
*/
|
|
180
|
+
function collectPhase1Children(cornerOrientation, edgeOrientation, udSlice, previousFace, previousPreviousFace, remainingDepth, context) {
|
|
127
181
|
const moveCount = context.tables.phase1Moves.length;
|
|
128
182
|
const validMoves = PHASE1_VALID_MOVES[previousFace + 1][previousPreviousFace + 1];
|
|
129
183
|
const coTable = context.tables.moveTables.cornerOrientation.values;
|
|
@@ -131,9 +185,17 @@ function searchPhase1(cornerOrientation, edgeOrientation, udSlice, previousFace,
|
|
|
131
185
|
const udTable = context.tables.moveTables.udSlice.values;
|
|
132
186
|
const coeoTable = context.tables.pruningTables.cornerOrientationEdgeOrientation.values;
|
|
133
187
|
const udPruningTable = context.tables.pruningTables.udSlice.values;
|
|
188
|
+
const jointTables = context.jointTables;
|
|
134
189
|
const coOffset = cornerOrientation * moveCount;
|
|
135
190
|
const eoOffset = edgeOrientation * moveCount;
|
|
136
191
|
const udOffset = udSlice * moveCount;
|
|
192
|
+
const slot = remainingDepth * moveCount;
|
|
193
|
+
const scratchMove = context.scratchMove;
|
|
194
|
+
const scratchBound = context.scratchBound;
|
|
195
|
+
const scratchCo = context.scratchCo;
|
|
196
|
+
const scratchEo = context.scratchEo;
|
|
197
|
+
const scratchUd = context.scratchUd;
|
|
198
|
+
let childCount = 0;
|
|
137
199
|
for (let i = 0; i < validMoves.length; i += 1) {
|
|
138
200
|
const moveIndex = validMoves[i];
|
|
139
201
|
const nextCornerOrientation = coTable[coOffset + moveIndex];
|
|
@@ -146,7 +208,6 @@ function searchPhase1(cornerOrientation, edgeOrientation, udSlice, previousFace,
|
|
|
146
208
|
if (nextLowerBound > remainingDepth - 1) {
|
|
147
209
|
continue;
|
|
148
210
|
}
|
|
149
|
-
const jointTables = context.jointTables;
|
|
150
211
|
if (jointTables) {
|
|
151
212
|
const udCoIndex = nextUdSlice * 2187 + nextCornerOrientation;
|
|
152
213
|
const jc = (jointTables.jointUdCo[udCoIndex >> 1] >> ((udCoIndex & 1) << 2)) & 0x0f;
|
|
@@ -162,14 +223,72 @@ function searchPhase1(cornerOrientation, edgeOrientation, udSlice, previousFace,
|
|
|
162
223
|
continue;
|
|
163
224
|
}
|
|
164
225
|
}
|
|
226
|
+
scratchMove[slot + childCount] = moveIndex;
|
|
227
|
+
scratchBound[slot + childCount] = nextLowerBound;
|
|
228
|
+
scratchCo[slot + childCount] = nextCornerOrientation;
|
|
229
|
+
scratchEo[slot + childCount] = nextEdgeOrientation;
|
|
230
|
+
scratchUd[slot + childCount] = nextUdSlice;
|
|
231
|
+
childCount += 1;
|
|
232
|
+
}
|
|
233
|
+
// Stable insertion sort by bound (n <= 18; ties keep reference order).
|
|
234
|
+
for (let i = 1; i < childCount; i += 1) {
|
|
235
|
+
const move = scratchMove[slot + i];
|
|
236
|
+
const bound = scratchBound[slot + i];
|
|
237
|
+
const co = scratchCo[slot + i];
|
|
238
|
+
const eo = scratchEo[slot + i];
|
|
239
|
+
const ud = scratchUd[slot + i];
|
|
240
|
+
let j = i - 1;
|
|
241
|
+
while (j >= 0 && scratchBound[slot + j] > bound) {
|
|
242
|
+
scratchMove[slot + j + 1] = scratchMove[slot + j];
|
|
243
|
+
scratchBound[slot + j + 1] = scratchBound[slot + j];
|
|
244
|
+
scratchCo[slot + j + 1] = scratchCo[slot + j];
|
|
245
|
+
scratchEo[slot + j + 1] = scratchEo[slot + j];
|
|
246
|
+
scratchUd[slot + j + 1] = scratchUd[slot + j];
|
|
247
|
+
j -= 1;
|
|
248
|
+
}
|
|
249
|
+
scratchMove[slot + j + 1] = move;
|
|
250
|
+
scratchBound[slot + j + 1] = bound;
|
|
251
|
+
scratchCo[slot + j + 1] = co;
|
|
252
|
+
scratchEo[slot + j + 1] = eo;
|
|
253
|
+
scratchUd[slot + j + 1] = ud;
|
|
254
|
+
}
|
|
255
|
+
return childCount;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Ordered exact-depth DFS that collects solutions instead of stopping at
|
|
259
|
+
* the first one. Returns true when collection should stop (limit reached
|
|
260
|
+
* or timed out).
|
|
261
|
+
*/
|
|
262
|
+
function searchPhase1Collect(cornerOrientation, edgeOrientation, udSlice, previousFace, previousPreviousFace, remainingDepth, path, solutions, limit, context) {
|
|
263
|
+
context.visitedNodes += 1;
|
|
264
|
+
if (context.deadline !== undefined && (context.visitedNodes & 1023) === 0 && performance.now() > context.deadline) {
|
|
265
|
+
context.timedOut = true;
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
if (remainingDepth === 0) {
|
|
269
|
+
if (cornerOrientation === 0 &&
|
|
270
|
+
edgeOrientation === 0 &&
|
|
271
|
+
udSlice === context.tables.moveTables.udSlice.solvedCoordinate) {
|
|
272
|
+
solutions.push([...path]);
|
|
273
|
+
return solutions.length >= limit;
|
|
274
|
+
}
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
const moveCount = context.tables.phase1Moves.length;
|
|
278
|
+
const childCount = collectPhase1Children(cornerOrientation, edgeOrientation, udSlice, previousFace, previousPreviousFace, remainingDepth, context);
|
|
279
|
+
const slot = remainingDepth * moveCount;
|
|
280
|
+
const scratchMove = context.scratchMove;
|
|
281
|
+
const scratchCo = context.scratchCo;
|
|
282
|
+
const scratchEo = context.scratchEo;
|
|
283
|
+
const scratchUd = context.scratchUd;
|
|
284
|
+
for (let i = 0; i < childCount; i += 1) {
|
|
285
|
+
const moveIndex = scratchMove[slot + i];
|
|
165
286
|
const face = PHASE1_MOVE_FACES[moveIndex];
|
|
166
287
|
path.push(context.tables.phase1Moves[moveIndex]);
|
|
167
|
-
|
|
168
|
-
return true;
|
|
169
|
-
}
|
|
288
|
+
const stop = searchPhase1Collect(scratchCo[slot + i], scratchEo[slot + i], scratchUd[slot + i], face, previousFace, remainingDepth - 1, path, solutions, limit, context);
|
|
170
289
|
path.pop();
|
|
171
|
-
if (
|
|
172
|
-
return
|
|
290
|
+
if (stop) {
|
|
291
|
+
return true;
|
|
173
292
|
}
|
|
174
293
|
}
|
|
175
294
|
return false;
|