@cubingcenter/scrambler 0.6.3 → 0.8.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 +17 -4
- package/dist/core/api.d.ts.map +1 -1
- package/dist/core/api.js +10 -2
- package/dist/puzzles/cube2/solver/browser-loader.d.ts +4 -7
- package/dist/puzzles/cube2/solver/browser-loader.d.ts.map +1 -1
- package/dist/puzzles/cube2/solver/browser-loader.js +42 -14
- package/dist/puzzles/cube2/solver/generated-assets.d.ts.map +1 -1
- package/dist/puzzles/cube2/solver/generated-assets.js +8 -3
- 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.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/browser-loader.js +48 -22
- package/dist/puzzles/cube3/solver/generated-assets.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/generated-assets.js +9 -3
- 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 +37 -0
- package/dist/puzzles/cube3/solver/phase1.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/phase1.js +156 -15
- package/dist/puzzles/cube3/solver/phase2.d.ts +75 -0
- package/dist/puzzles/cube3/solver/phase2.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/phase2.js +279 -68
- 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/solver/tables.js +14 -12
- package/dist/puzzles/cube3/validation.d.ts.map +1 -1
- package/dist/puzzles/cube3/validation.js +15 -7
- package/dist/puzzles/cube4/browser-worker-pool.d.ts +82 -0
- package/dist/puzzles/cube4/browser-worker-pool.d.ts.map +1 -0
- package/dist/puzzles/cube4/browser-worker-pool.js +205 -0
- package/dist/puzzles/cube4/browser-worker.d.ts +2 -0
- package/dist/puzzles/cube4/browser-worker.d.ts.map +1 -0
- package/dist/puzzles/cube4/browser-worker.js +31 -0
- package/dist/puzzles/cube4/index.d.ts +10 -2
- package/dist/puzzles/cube4/index.d.ts.map +1 -1
- package/dist/puzzles/cube4/index.js +6 -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 +212 -11
- package/dist/puzzles/cube4/reduction/browser-loader.d.ts +29 -1
- package/dist/puzzles/cube4/reduction/browser-loader.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/browser-loader.js +227 -32
- package/dist/puzzles/cube4/reduction/center2.d.ts +44 -3
- package/dist/puzzles/cube4/reduction/center2.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/center2.js +152 -10
- 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/finish3.d.ts +53 -0
- package/dist/puzzles/cube4/reduction/finish3.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/finish3.js +393 -0
- 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 +44 -0
- package/dist/puzzles/cube4/reduction/generated/hc-move.bin +0 -0
- package/dist/puzzles/cube4/reduction/hc-pdb.d.ts +52 -0
- package/dist/puzzles/cube4/reduction/hc-pdb.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/hc-pdb.js +220 -0
- package/dist/puzzles/cube4/reduction/node-loader.d.ts +15 -1
- package/dist/puzzles/cube4/reduction/node-loader.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/node-loader.js +199 -25
- package/dist/puzzles/cube4/reduction/pair-transversal.d.ts +28 -0
- package/dist/puzzles/cube4/reduction/pair-transversal.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/pair-transversal.js +107 -0
- package/dist/puzzles/cube4/reduction/pairing-coordinate.js +5 -5
- package/dist/puzzles/cube4/reduction/phase3-search.d.ts +116 -7
- package/dist/puzzles/cube4/reduction/phase3-search.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/phase3-search.js +458 -32
- package/dist/puzzles/cube4/reduction/rotation-fix.d.ts +28 -0
- package/dist/puzzles/cube4/reduction/rotation-fix.d.ts.map +1 -0
- package/dist/puzzles/cube4/reduction/rotation-fix.js +212 -0
- package/dist/puzzles/cube4/reduction/solve.d.ts +172 -2
- package/dist/puzzles/cube4/reduction/solve.d.ts.map +1 -1
- package/dist/puzzles/cube4/reduction/solve.js +1201 -180
- package/dist/puzzles/cube4/scramble-cache.d.ts +93 -0
- package/dist/puzzles/cube4/scramble-cache.d.ts.map +1 -0
- package/dist/puzzles/cube4/scramble-cache.js +150 -0
- package/dist/puzzles/cube4/scrambler.d.ts +201 -6
- package/dist/puzzles/cube4/scrambler.d.ts.map +1 -1
- package/dist/puzzles/cube4/scrambler.js +414 -176
- package/dist/puzzles/cube4/state.d.ts.map +1 -1
- package/dist/puzzles/cube4/state.js +4 -1
- package/dist/puzzles/cube4/worker-pool.d.ts +139 -0
- package/dist/puzzles/cube4/worker-pool.d.ts.map +1 -0
- package/dist/puzzles/cube4/worker-pool.js +405 -0
- package/dist/puzzles/cube4/worker-protocol.d.ts +58 -0
- package/dist/puzzles/cube4/worker-protocol.d.ts.map +1 -0
- package/dist/puzzles/cube4/worker-protocol.js +58 -0
- package/dist/puzzles/cube4/worker.d.ts +26 -0
- package/dist/puzzles/cube4/worker.d.ts.map +1 -0
- package/dist/puzzles/cube4/worker.js +49 -0
- package/dist/puzzles/pyraminx/solver/browser-loader.d.ts +4 -8
- package/dist/puzzles/pyraminx/solver/browser-loader.d.ts.map +1 -1
- package/dist/puzzles/pyraminx/solver/browser-loader.js +44 -15
- package/dist/puzzles/pyraminx/solver/generated-assets.d.ts.map +1 -1
- package/dist/puzzles/pyraminx/solver/generated-assets.js +8 -3
- 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/shared/compressed-fetch.d.ts +37 -1
- package/dist/puzzles/shared/compressed-fetch.d.ts.map +1 -1
- package/dist/puzzles/shared/compressed-fetch.js +226 -31
- package/dist/puzzles/shared/load-once.d.ts +16 -0
- package/dist/puzzles/shared/load-once.d.ts.map +1 -0
- package/dist/puzzles/shared/load-once.js +11 -0
- package/dist/puzzles/skewb/solver/browser-loader.d.ts +4 -11
- package/dist/puzzles/skewb/solver/browser-loader.d.ts.map +1 -1
- package/dist/puzzles/skewb/solver/browser-loader.js +44 -16
- package/dist/puzzles/skewb/solver/generated-assets.d.ts.map +1 -1
- package/dist/puzzles/skewb/solver/generated-assets.js +8 -3
- 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/puzzles/sq1/solver/browser-loader.d.ts.map +1 -1
- package/dist/puzzles/sq1/solver/browser-loader.js +58 -26
- package/dist-cjs/core/api.js +10 -2
- package/dist-cjs/puzzles/cube2/solver/browser-loader.js +42 -14
- package/dist-cjs/puzzles/cube2/solver/generated-assets.js +8 -3
- 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 +48 -22
- package/dist-cjs/puzzles/cube3/solver/generated-assets.js +9 -3
- 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 +157 -15
- package/dist-cjs/puzzles/cube3/solver/phase2.js +276 -65
- package/dist-cjs/puzzles/cube3/solver/symmetry.js +80 -0
- package/dist-cjs/puzzles/cube3/solver/tables.js +14 -12
- package/dist-cjs/puzzles/cube3/validation.js +15 -7
- package/dist-cjs/puzzles/cube4/browser-worker-pool.js +211 -0
- package/dist-cjs/puzzles/cube4/index.js +23 -1
- package/dist-cjs/puzzles/cube4/moves.js +213 -11
- package/dist-cjs/puzzles/cube4/reduction/browser-loader.js +227 -32
- package/dist-cjs/puzzles/cube4/reduction/center2.js +155 -10
- package/dist-cjs/puzzles/cube4/reduction/edge3.js +24 -3
- package/dist-cjs/puzzles/cube4/reduction/finish3.js +398 -0
- 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 +44 -0
- package/dist-cjs/puzzles/cube4/reduction/generated/hc-move.bin +0 -0
- package/dist-cjs/puzzles/cube4/reduction/hc-pdb.js +232 -0
- package/dist-cjs/puzzles/cube4/reduction/node-loader.js +200 -25
- package/dist-cjs/puzzles/cube4/reduction/pair-transversal.js +112 -0
- package/dist-cjs/puzzles/cube4/reduction/pairing-coordinate.js +5 -5
- package/dist-cjs/puzzles/cube4/reduction/phase3-search.js +460 -33
- package/dist-cjs/puzzles/cube4/reduction/rotation-fix.js +216 -0
- package/dist-cjs/puzzles/cube4/reduction/solve.js +1203 -172
- package/dist-cjs/puzzles/cube4/scramble-cache.js +156 -0
- package/dist-cjs/puzzles/cube4/scrambler.js +416 -174
- package/dist-cjs/puzzles/cube4/state.js +4 -1
- package/dist-cjs/puzzles/cube4/worker-pool.js +447 -0
- package/dist-cjs/puzzles/cube4/worker-protocol.js +62 -0
- package/dist-cjs/puzzles/pyraminx/solver/browser-loader.js +44 -15
- package/dist-cjs/puzzles/pyraminx/solver/generated-assets.js +8 -3
- package/dist-cjs/puzzles/pyraminx/solver/search.js +2 -5
- package/dist-cjs/puzzles/pyraminx/solver/tables.js +18 -1
- package/dist-cjs/puzzles/shared/compressed-fetch.js +229 -31
- package/dist-cjs/puzzles/shared/load-once.js +14 -0
- package/dist-cjs/puzzles/skewb/solver/browser-loader.js +44 -16
- package/dist-cjs/puzzles/skewb/solver/generated-assets.js +8 -3
- package/dist-cjs/puzzles/skewb/solver/search.js +2 -2
- package/dist-cjs/puzzles/skewb/solver/tables.js +18 -1
- package/dist-cjs/puzzles/sq1/solver/browser-loader.js +58 -26
- package/docs/development.md +19 -7
- package/package.json +17 -7
|
@@ -116,12 +116,12 @@ function applyPairingMoveToArray(arr24, move) {
|
|
|
116
116
|
* and preserved by the phase-3 move set (verified).
|
|
117
117
|
*/
|
|
118
118
|
function hasPairingTransversal(wp) {
|
|
119
|
-
|
|
120
|
-
for (
|
|
121
|
-
const
|
|
122
|
-
if (
|
|
119
|
+
let mask = 0;
|
|
120
|
+
for (let i = 0; i < 12; i++) {
|
|
121
|
+
const bit = 1 << (wp[exports.PAIRING_SECOND_HALVES[i]] >> 1);
|
|
122
|
+
if ((mask & bit) !== 0)
|
|
123
123
|
return false;
|
|
124
|
-
|
|
124
|
+
mask |= bit;
|
|
125
125
|
}
|
|
126
126
|
return true;
|
|
127
127
|
}
|
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
* and all 12 wing pairs paired (sym-index == 0), using the 17-move exploration set.
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.FastPhase3Solver = exports.skipAxis3 = exports.ckmv3 = exports.SEARCH_MOVES = void 0;
|
|
9
|
+
exports.proofStats = exports.FastPhase3Solver = exports.skipAxis3 = exports.ckmv3 = exports.SEARCH_MOVES = void 0;
|
|
10
|
+
exports.resetProofStats = resetProofStats;
|
|
10
11
|
exports.solvePhase3 = solvePhase3;
|
|
11
12
|
const center3_js_1 = require("./center3.js");
|
|
12
13
|
const edge3_js_1 = require("./edge3.js");
|
|
14
|
+
const hc_pdb_js_1 = require("./hc-pdb.js");
|
|
13
15
|
/** The 17 exploration moves (the reference's m < 17 bound). */
|
|
14
16
|
exports.SEARCH_MOVES = center3_js_1.CENTER3_MOVES.slice(0, 17);
|
|
15
17
|
// Commutation / move-cancellation pruning tables
|
|
@@ -37,86 +39,511 @@ for (let i = 0; i < 20; i++) {
|
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
41
|
}
|
|
40
|
-
/**
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
/** Popcount of 12-bit masks (the inlined sym-coordinate rank). */
|
|
43
|
+
const POPC12 = new Uint8Array(4096);
|
|
44
|
+
for (let i = 1; i < 4096; i++)
|
|
45
|
+
POPC12[i] = POPC12[i >> 1] + (i & 1);
|
|
46
|
+
/** Flat row-major ckmv3 (21x20) + typed skipAxis3 for the hot recursion. */
|
|
47
|
+
const ckmv3Flat = new Uint8Array(21 * 20);
|
|
48
|
+
for (let i = 0; i < 21; i++) {
|
|
49
|
+
for (let j = 0; j < 20; j++)
|
|
50
|
+
ckmv3Flat[i * 20 + j] = exports.ckmv3[i][j] ? 1 : 0;
|
|
51
|
+
}
|
|
52
|
+
const skipAxis3Fast = Int32Array.from(exports.skipAxis3);
|
|
53
|
+
const F_SLOT_MAP = [
|
|
54
|
+
[4, 5, 2, 3, 1, 0, 6, 7, 8, 9, 10, 11],
|
|
55
|
+
[1, 0, 2, 3, 5, 4, 6, 7, 8, 9, 10, 11],
|
|
56
|
+
[5, 4, 2, 3, 0, 1, 6, 7, 8, 9, 10, 11],
|
|
57
|
+
[0, 1, 2, 3, 4, 6, 5, 7, 8, 9, 11, 10],
|
|
58
|
+
[11, 1, 2, 8, 4, 5, 6, 7, 0, 9, 10, 3],
|
|
59
|
+
[3, 1, 2, 0, 4, 5, 6, 7, 11, 9, 10, 8],
|
|
60
|
+
[8, 1, 2, 11, 4, 5, 6, 7, 3, 9, 10, 0],
|
|
61
|
+
[0, 1, 7, 6, 4, 5, 2, 3, 8, 9, 10, 11],
|
|
62
|
+
[0, 1, 3, 2, 4, 5, 7, 6, 8, 9, 10, 11],
|
|
63
|
+
[0, 1, 6, 7, 4, 5, 3, 2, 8, 9, 10, 11],
|
|
64
|
+
[0, 1, 2, 3, 7, 5, 6, 4, 9, 8, 10, 11],
|
|
65
|
+
[0, 9, 10, 3, 4, 5, 6, 7, 8, 2, 1, 11],
|
|
66
|
+
[0, 2, 1, 3, 4, 5, 6, 7, 8, 10, 9, 11],
|
|
67
|
+
[0, 10, 9, 3, 4, 5, 6, 7, 8, 1, 2, 11],
|
|
68
|
+
[1, 0, 2, 3, 5, 4, 6, 7, 8, 11, 10, 9],
|
|
69
|
+
[0, 3, 2, 1, 4, 6, 5, 7, 8, 9, 11, 10],
|
|
70
|
+
[3, 1, 2, 0, 4, 7, 6, 5, 11, 9, 10, 8]
|
|
71
|
+
];
|
|
72
|
+
const H_SLOT_MAP = [
|
|
73
|
+
[4, 5, 2, 3, 1, 0, 6, 7, 8, 9, 10, 11],
|
|
74
|
+
[1, 0, 2, 3, 5, 4, 6, 7, 8, 9, 10, 11],
|
|
75
|
+
[5, 4, 2, 3, 0, 1, 6, 7, 8, 9, 10, 11],
|
|
76
|
+
[0, 1, 2, 3, 4, 6, 5, 7, 8, 9, 11, 10],
|
|
77
|
+
[11, 1, 2, 8, 4, 5, 6, 7, 0, 9, 10, 3],
|
|
78
|
+
[3, 1, 2, 0, 4, 5, 6, 7, 11, 9, 10, 8],
|
|
79
|
+
[8, 1, 2, 11, 4, 5, 6, 7, 3, 9, 10, 0],
|
|
80
|
+
[0, 1, 7, 6, 4, 5, 2, 3, 8, 9, 10, 11],
|
|
81
|
+
[0, 1, 3, 2, 4, 5, 7, 6, 8, 9, 10, 11],
|
|
82
|
+
[0, 1, 6, 7, 4, 5, 3, 2, 8, 9, 10, 11],
|
|
83
|
+
[0, 1, 2, 3, 7, 5, 6, 4, 9, 8, 10, 11],
|
|
84
|
+
[0, 9, 10, 3, 4, 5, 6, 7, 8, 2, 1, 11],
|
|
85
|
+
[0, 2, 1, 3, 4, 5, 6, 7, 8, 10, 9, 11],
|
|
86
|
+
[0, 10, 9, 3, 4, 5, 6, 7, 8, 1, 2, 11],
|
|
87
|
+
[1, 0, 2, 3, 5, 4, 6, 7, 10, 9, 8, 11],
|
|
88
|
+
[2, 1, 0, 3, 4, 6, 5, 7, 8, 9, 11, 10],
|
|
89
|
+
[3, 1, 2, 0, 6, 5, 4, 7, 11, 9, 10, 8]
|
|
90
|
+
];
|
|
43
91
|
class FastPhase3Solver {
|
|
44
92
|
tempep;
|
|
45
93
|
moveLog;
|
|
46
94
|
sym;
|
|
95
|
+
/** Transposition Table size: 2^20 = 1,048,576 entries (12 MiB per solve).
|
|
96
|
+
* The previous 2M table used 24 MiB per process, plus a separate 8 MiB
|
|
97
|
+
* generation array. The compact 1M table leaves room for concurrent
|
|
98
|
+
* solvers under the 150 MiB reduction-table budget. */
|
|
99
|
+
static TT_SIZE = 1 << 20;
|
|
100
|
+
static TT_MASK = FastPhase3Solver.TT_SIZE - 1;
|
|
101
|
+
tt = new Uint32Array(FastPhase3Solver.TT_SIZE * 3);
|
|
102
|
+
// TT info packs (call id:20, previous move:5, remaining depth:6), avoiding
|
|
103
|
+
// a separate 8 MiB generation array while retaining exact proof tracking.
|
|
104
|
+
static CALL_ID_LIMIT = 1 << 20;
|
|
105
|
+
callSeq = 1;
|
|
106
|
+
callId = 0;
|
|
107
|
+
contextCenter3;
|
|
108
|
+
contextEdge3;
|
|
109
|
+
/** Reset this solver's cumulative node counter (legacy method name). */
|
|
110
|
+
resetEpochNodes() {
|
|
111
|
+
this.epochNodes = 0;
|
|
112
|
+
}
|
|
113
|
+
clearTranspositionTable() {
|
|
114
|
+
this.tt.fill(0);
|
|
115
|
+
this.callSeq = 1;
|
|
116
|
+
}
|
|
117
|
+
setTableContext(center3, edge3Distances) {
|
|
118
|
+
if (this.contextCenter3 === center3 && this.contextEdge3 === edge3Distances)
|
|
119
|
+
return;
|
|
120
|
+
this.clearTranspositionTable();
|
|
121
|
+
this.contextCenter3 = center3;
|
|
122
|
+
this.contextEdge3 = edge3Distances;
|
|
123
|
+
}
|
|
124
|
+
/** Fresh per-call id for TT-generation tagging. */
|
|
125
|
+
nextCallId() {
|
|
126
|
+
const id = this.callSeq++;
|
|
127
|
+
if (this.callSeq >= FastPhase3Solver.CALL_ID_LIMIT) {
|
|
128
|
+
this.tt.fill(0);
|
|
129
|
+
this.callSeq = 1;
|
|
130
|
+
}
|
|
131
|
+
return id;
|
|
132
|
+
}
|
|
47
133
|
constructor() {
|
|
48
|
-
|
|
49
|
-
|
|
134
|
+
// Preallocated to well above the deepest phase-3 sweep (maxLength3 <= 27):
|
|
135
|
+
// `ensureDepth` stays as a fallback for out-of-band callers.
|
|
136
|
+
this.tempep = Array.from({ length: 48 }, () => new Uint8Array(12));
|
|
137
|
+
this.moveLog = new Array(48);
|
|
50
138
|
this.sym = (0, edge3_js_1.getSymTables)();
|
|
51
139
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
140
|
+
ensureDepth(depth) {
|
|
141
|
+
while (this.tempep.length <= depth) {
|
|
142
|
+
this.tempep.push(new Uint8Array(12));
|
|
143
|
+
this.moveLog.push(0);
|
|
144
|
+
}
|
|
55
145
|
}
|
|
146
|
+
nodeCount = 0;
|
|
147
|
+
/** Cumulative nodes across calls (reset per solve generation). */
|
|
148
|
+
epochNodes = 0;
|
|
149
|
+
/**
|
|
150
|
+
* Proof bookkeeping for IDA* threshold propagation (reset per exact-length
|
|
151
|
+
* call). When a length-L search exhausts its tree WITHOUT hitting any of
|
|
152
|
+
* these flags, no (center, edge) solution exists at any length below
|
|
153
|
+
* `minExcess`, so the caller may skip straight to it:
|
|
154
|
+
* - `capped`: the node budget aborted the search (bounds unknown).
|
|
155
|
+
* - `limitHit`: goal enumeration hit its cap (tree not exhausted).
|
|
156
|
+
* - `sawGoal`: a goal was reached (even if the validator rejected it —
|
|
157
|
+
* a nearby length may still validate, so only step L+1).
|
|
158
|
+
* - `ttTouched`: a transposition-table hit from ANOTHER exact-length call
|
|
159
|
+
* skipped a subtree, and the hit was not certified goal-free across
|
|
160
|
+
* candidate validators.
|
|
161
|
+
* Bound-based axis skips need no flag: the skipped siblings are
|
|
162
|
+
* same-face turns, so noteExcess records their triangle-inequality bound.
|
|
163
|
+
*/
|
|
164
|
+
capped = false;
|
|
165
|
+
limitHit = false;
|
|
166
|
+
sawGoal = false;
|
|
167
|
+
ttTouched = false;
|
|
168
|
+
minExcess = 0;
|
|
56
169
|
/**
|
|
57
|
-
*
|
|
170
|
+
* Root-length coverage of the tightest TT hit this call (depth +
|
|
171
|
+
* storedDepth): the pruned subtree proves no goal at or below that root
|
|
172
|
+
* length under the call's predicate. `Number.MAX_SAFE_INTEGER` when no hit
|
|
173
|
+
* pruned anything. Feeds `validatedThrough`.
|
|
58
174
|
*/
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
175
|
+
ttCoverage = Number.MAX_SAFE_INTEGER;
|
|
176
|
+
/**
|
|
177
|
+
* Recursive IDA* worker on table transitions with Transposition Table pruning.
|
|
178
|
+
*/
|
|
179
|
+
search(eplast, ct, prun, maxl, lm, depth, center3Tables, edge3Distances, maxNodes = 15000, collect, hc, hcCoord = -1, c3PairDist) {
|
|
180
|
+
this.nodeCount++;
|
|
181
|
+
this.epochNodes++;
|
|
182
|
+
if (this.nodeCount > maxNodes) {
|
|
183
|
+
this.capped = true;
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
// Goal test at EVERY node (any length <= maxl), not only at maxl === 0
|
|
187
|
+
// leaves: a goal is (edge paired AND center3 at goal) — both PDB
|
|
188
|
+
// distances zero (the parent's gates guarantee leaves are goals, so this
|
|
189
|
+
// strictly generalizes the old leaf test). Exact-length-only detection
|
|
190
|
+
// made shorter goals on the same path invisible ("a nearby length may
|
|
191
|
+
// still validate"), which both blocked proofs on any raw goal and hid
|
|
192
|
+
// shorter solutions.
|
|
193
|
+
if (prun === 0 && center3Tables.pruning[ct] === 0) {
|
|
194
|
+
this.sawGoal = true;
|
|
195
|
+
if (!collect)
|
|
196
|
+
return true;
|
|
197
|
+
collect.examined++;
|
|
198
|
+
const moves = this.moveLog.slice(0, depth).map((m) => exports.SEARCH_MOVES[m]);
|
|
199
|
+
if (collect.validate(moves))
|
|
200
|
+
return true;
|
|
201
|
+
if (collect.examined >= collect.limit) {
|
|
202
|
+
this.limitHit = true;
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
// A rejected goal is not a goal for the validator's predicate: keep
|
|
206
|
+
// exploring the subtree — validated goals may sit deeper on the same
|
|
207
|
+
// branches. At the fringe (maxl === 0) there is no subtree left, so
|
|
208
|
+
// record the branch's coverage claim here (nothing below `depth` on
|
|
209
|
+
// this branch was searched): "no validated goal at <= depth".
|
|
210
|
+
if (maxl === 0 && depth < this.minExcess)
|
|
211
|
+
this.minExcess = depth + 1;
|
|
212
|
+
}
|
|
213
|
+
else if (maxl === 0) {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
if (depth >= this.tempep.length)
|
|
217
|
+
this.ensureDepth(depth);
|
|
218
|
+
const sym = this.sym;
|
|
219
|
+
const symMvrot = sym.mvrot;
|
|
220
|
+
const symMvroto = sym.mvroto;
|
|
64
221
|
const ep = this.tempep[depth];
|
|
65
222
|
if (lm !== 20) {
|
|
66
223
|
const movoBase = (lm << 3) * 12;
|
|
67
|
-
const movBase = (lm << 3) * 12;
|
|
68
224
|
for (let i = 0; i < 12; i++) {
|
|
69
|
-
ep[i] =
|
|
225
|
+
ep[i] = symMvroto[movoBase + eplast[symMvrot[movoBase + i]]];
|
|
70
226
|
}
|
|
71
227
|
}
|
|
72
228
|
else {
|
|
73
229
|
ep.set(eplast);
|
|
74
230
|
}
|
|
231
|
+
const hasHc = hc !== undefined && hcCoord >= 0 && hc.moves.length > 0;
|
|
232
|
+
// HC pruning depends on a second coordinate not represented in the TT
|
|
233
|
+
// key. The root c3-pair check also varies only at depth zero. Do not reuse
|
|
234
|
+
// entries in those contexts; all other phase-3 state is key-complete.
|
|
235
|
+
const useTt = !hasHc && !(depth === 0 && c3PairDist !== undefined);
|
|
236
|
+
// TT: entries claim "no RAW goal at any length <= maxl from (ep, lm)" —
|
|
237
|
+
// coordinate-complete and predicate-independent (the validator's
|
|
238
|
+
// full-state checks are NOT coordinate-complete, so no validator-scoped
|
|
239
|
+
// claims may be cached). Hits are `<=`-claims and therefore
|
|
240
|
+
// proof-compatible for `validatedThrough`/`provenThrough` coverage.
|
|
241
|
+
const ttActive = useTt && maxl >= 2;
|
|
242
|
+
let keyHigh = 0;
|
|
243
|
+
let keyLow = 0;
|
|
244
|
+
let slot = 0;
|
|
245
|
+
if (ttActive) {
|
|
246
|
+
keyHigh = ((ct << 16) | (ep[8] | (ep[9] << 4) | (ep[10] << 8) | (ep[11] << 12))) >>> 0;
|
|
247
|
+
keyLow = (ep[0] | (ep[1] << 4) | (ep[2] << 8) | (ep[3] << 12) |
|
|
248
|
+
(ep[4] << 16) | (ep[5] << 20) | (ep[6] << 24) | (ep[7] << 28)) >>> 0;
|
|
249
|
+
slot = (((keyLow ^ Math.imul(keyHigh, 0x9e3779b9)) & FastPhase3Solver.TT_MASK) * 3) >>> 0;
|
|
250
|
+
const storedHigh = this.tt[slot];
|
|
251
|
+
const storedLow = this.tt[slot + 1];
|
|
252
|
+
const storedInfo = this.tt[slot + 2];
|
|
253
|
+
if (storedHigh === keyHigh && storedLow === keyLow) {
|
|
254
|
+
const storedCallId = (storedInfo >>> 11) & 0xfffff;
|
|
255
|
+
const storedLm = (storedInfo >>> 6) & 0x1f;
|
|
256
|
+
const storedDepth = storedInfo & 0x3f;
|
|
257
|
+
if (storedDepth >= maxl && storedLm === lm) {
|
|
258
|
+
const coverage = depth + storedDepth;
|
|
259
|
+
if (coverage < this.ttCoverage)
|
|
260
|
+
this.ttCoverage = coverage;
|
|
261
|
+
if (storedCallId !== this.callId)
|
|
262
|
+
this.ttTouched = true;
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
const c3Move = center3Tables.moveTable;
|
|
268
|
+
const c3Prun = center3Tables.pruning;
|
|
269
|
+
const c3Row = ct * center3_js_1.CENTER3_MOVES.length;
|
|
270
|
+
const sym2Raw = sym.sym2Raw;
|
|
271
|
+
const sym2Mask = sym.sym2Mask;
|
|
272
|
+
const raw2Sym = sym.raw2Sym;
|
|
273
|
+
const hcMoves = hasHc ? hc.moves : null;
|
|
274
|
+
const hcDist = hasHc ? hc.distances : null;
|
|
275
|
+
const hcRow = hasHc ? hcCoord * hc_pdb_js_1.HC_PDB_MOVES.length : 0;
|
|
276
|
+
// Byte-expanded (tests/generators) vs nibble-packed (loaders) distances.
|
|
277
|
+
const edgeUnpacked = edge3Distances.length >= 31006080;
|
|
278
|
+
const examined0 = collect === undefined ? 0 : collect.examined;
|
|
279
|
+
const ckmvRow = lm * 20;
|
|
75
280
|
for (let m = 0; m < 17; m++) {
|
|
76
|
-
if (
|
|
77
|
-
m =
|
|
281
|
+
if (ckmv3Flat[ckmvRow + m]) {
|
|
282
|
+
m = skipAxis3Fast[m];
|
|
78
283
|
continue;
|
|
79
284
|
}
|
|
80
|
-
const nc =
|
|
285
|
+
const nc = c3Move[c3Row + m];
|
|
81
286
|
if (nc < 0)
|
|
82
287
|
continue;
|
|
83
|
-
const prun1 =
|
|
288
|
+
const prun1 = c3Prun[nc];
|
|
84
289
|
if (prun1 >= maxl) {
|
|
290
|
+
const ex1 = (prun1 > maxl && m < 14) ? depth + prun1 : depth + 1 + prun1;
|
|
291
|
+
if (ex1 < this.minExcess)
|
|
292
|
+
this.minExcess = ex1;
|
|
85
293
|
if (prun1 > maxl && m < 14)
|
|
86
|
-
m =
|
|
294
|
+
m = skipAxis3Fast[m];
|
|
87
295
|
continue;
|
|
88
296
|
}
|
|
89
|
-
|
|
90
|
-
|
|
297
|
+
let nhc = -1;
|
|
298
|
+
if (hasHc) {
|
|
299
|
+
nhc = hcMoves[hcRow + m];
|
|
300
|
+
if (nhc >= 0) {
|
|
301
|
+
const prunh = hcDist[nhc];
|
|
302
|
+
if (prunh >= maxl) {
|
|
303
|
+
const exH = (prunh > maxl && m < 14) ? depth + prunh : depth + 1 + prunh;
|
|
304
|
+
if (exH < this.minExcess)
|
|
305
|
+
this.minExcess = exH;
|
|
306
|
+
if (prunh > maxl && m < 14)
|
|
307
|
+
m = skipAxis3Fast[m];
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (c3PairDist && depth === 0) {
|
|
313
|
+
const c3Base = nc * 144;
|
|
314
|
+
const fSlot = F_SLOT_MAP[m];
|
|
315
|
+
const hSlot = H_SLOT_MAP[m];
|
|
316
|
+
let prunPair = 0;
|
|
317
|
+
for (let s = 0; s < 12; s++) {
|
|
318
|
+
const idx = c3Base + fSlot[s] * 12 + hSlot[ep[s]];
|
|
319
|
+
const d = (idx & 1) === 0 ? (c3PairDist[idx >> 1] & 0x0f) : (c3PairDist[idx >> 1] >> 4);
|
|
320
|
+
if (d >= maxl) {
|
|
321
|
+
prunPair = d;
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
if (d > prunPair)
|
|
325
|
+
prunPair = d;
|
|
326
|
+
}
|
|
327
|
+
if (prunPair >= maxl) {
|
|
328
|
+
const exP = (prunPair > maxl && m < 14) ? depth + prunPair : depth + 1 + prunPair;
|
|
329
|
+
if (exP < this.minExcess)
|
|
330
|
+
this.minExcess = exP;
|
|
331
|
+
if (prunPair > maxl && m < 14)
|
|
332
|
+
m = skipAxis3Fast[m];
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
// Inlined getMvSymFrom(ep, ep, m, ...) (bit-identical; masks here are
|
|
337
|
+
// 12-bit, so one popcount lookup replaces bitCount's three).
|
|
338
|
+
let mrIdx = m << 3;
|
|
339
|
+
let movoBase = mrIdx * 12;
|
|
340
|
+
let raw = 0;
|
|
341
|
+
let bits = 0;
|
|
342
|
+
for (let i = 0; i < 4; i++) {
|
|
343
|
+
const val = symMvroto[movoBase + ep[symMvrot[movoBase + i]]];
|
|
344
|
+
raw = raw * (12 - i) + val - POPC12[bits & ((1 << val) - 1)];
|
|
345
|
+
bits |= 1 << val;
|
|
346
|
+
}
|
|
347
|
+
const symBase = raw2Sym[raw];
|
|
348
|
+
mrIdx |= symBase & 7;
|
|
349
|
+
let symOut = symBase >> 3;
|
|
350
|
+
bits = sym2Mask[symOut];
|
|
351
|
+
movoBase = mrIdx * 12;
|
|
352
|
+
for (let i = 4; i < 10; i++) {
|
|
353
|
+
const val = symMvroto[movoBase + ep[symMvrot[movoBase + i]]];
|
|
354
|
+
symOut = symOut * (12 - i) + val - POPC12[bits & ((1 << val) - 1)];
|
|
355
|
+
bits |= 1 << val;
|
|
356
|
+
}
|
|
357
|
+
const symIdx = ((symOut << 3) | (mrIdx & 7)) >>> 0;
|
|
358
|
+
// Format-aware read: loaders keep the nibble-packed table (15.5MB);
|
|
359
|
+
// tests and generators may pass byte-expanded buffers instead.
|
|
360
|
+
const eIdx = symIdx >> 3;
|
|
361
|
+
const prunx = edgeUnpacked
|
|
362
|
+
? edge3Distances[eIdx]
|
|
363
|
+
: (edge3Distances[eIdx >> 1] >> ((eIdx & 1) * 4)) & 0x0f;
|
|
91
364
|
if (prunx >= maxl) {
|
|
365
|
+
const exE = (prunx > maxl && m < 14) ? depth + prunx : depth + 1 + prunx;
|
|
366
|
+
if (exE < this.minExcess)
|
|
367
|
+
this.minExcess = exE;
|
|
92
368
|
if (prunx > maxl && m < 14)
|
|
93
|
-
m =
|
|
369
|
+
m = skipAxis3Fast[m];
|
|
94
370
|
continue;
|
|
95
371
|
}
|
|
96
|
-
|
|
97
|
-
|
|
372
|
+
this.moveLog[depth] = m;
|
|
373
|
+
if (this.search(ep, nc, prunx, maxl - 1, m, depth + 1, center3Tables, edge3Distances, maxNodes, collect, hc, nhc, c3PairDist)) {
|
|
98
374
|
return true;
|
|
99
375
|
}
|
|
100
376
|
}
|
|
377
|
+
// Cache only subtrees with NO raw goal reached in them (the examined
|
|
378
|
+
// count is the goal counter — rejections included, since the claim is
|
|
379
|
+
// raw). Goal-free subtrees are safe to reuse across calls, candidates
|
|
380
|
+
// and predicates.
|
|
381
|
+
if (ttActive &&
|
|
382
|
+
this.nodeCount <= maxNodes &&
|
|
383
|
+
(collect?.examined ?? 0) === examined0) {
|
|
384
|
+
this.tt[slot] = keyHigh;
|
|
385
|
+
this.tt[slot + 1] = keyLow;
|
|
386
|
+
this.tt[slot + 2] = ((this.callId << 11) | (lm << 6) | maxl) >>> 0;
|
|
387
|
+
}
|
|
101
388
|
return false;
|
|
102
389
|
}
|
|
103
390
|
/**
|
|
104
|
-
*
|
|
391
|
+
* Record an IDA* bound prune (inlined at the four gate sites): the child at
|
|
392
|
+
* `depth + 1` needs at least `childBound` more moves, so no solution exists
|
|
393
|
+
* through this branch below root threshold `depth + 1 + childBound`. When a
|
|
394
|
+
* bound-based axis skip also fires, the skipped siblings are same-face turns
|
|
395
|
+
* (verified: every non-empty skipAxis3 run under m < 14 is a same-base
|
|
396
|
+
* triple), hence at most 1 legal move apart — their bound is at least
|
|
397
|
+
* `childBound - 1` by the triangle inequality on the exact PDBs, so the
|
|
398
|
+
* recorded excess `depth + childBound` stays a true lower bound.
|
|
399
|
+
*/
|
|
400
|
+
/**
|
|
401
|
+
* Search for a phase-3 solution with exact target length (first found).
|
|
105
402
|
*/
|
|
106
|
-
|
|
403
|
+
solveLength(edge, ct3, edist, length3, center3Tables, edge3Distances, maxNodes = 15000, hc, hcCoord = -1, c3PairDist) {
|
|
404
|
+
this.setTableContext(center3Tables, edge3Distances);
|
|
107
405
|
this.tempep[0].set(edge);
|
|
108
|
-
|
|
406
|
+
this.nodeCount = 0;
|
|
407
|
+
this.callId = this.nextCallId();
|
|
408
|
+
this.ttCoverage = Number.MAX_SAFE_INTEGER;
|
|
409
|
+
if (this.search(this.tempep[0], ct3, edist, length3, 20, 0, center3Tables, edge3Distances, maxNodes, undefined, hc, hcCoord, c3PairDist)) {
|
|
109
410
|
return this.moveLog.slice(0, length3).map((m) => exports.SEARCH_MOVES[m]);
|
|
110
411
|
}
|
|
111
412
|
return null;
|
|
112
413
|
}
|
|
414
|
+
/**
|
|
415
|
+
* Search for a phase-3 solution with exact target length, accepting the
|
|
416
|
+
* first goal sequence that passes `validate` (up to `limit` examined,
|
|
417
|
+
* sharing `maxNodes`). With a validator that enforces the downstream
|
|
418
|
+
* requirements (reduction + standard orientation + 3x3 conversion), hard
|
|
419
|
+
* states whose first-found solutions are unusable get more draws from
|
|
420
|
+
* the same budget instead of an immediate miss.
|
|
421
|
+
*/
|
|
422
|
+
solveLengthValid(edge, ct3, edist, length3, center3Tables, edge3Distances, maxNodes = 15000, limit = 16, validate, hc, hcCoord = -1, c3PairDist) {
|
|
423
|
+
return this.solveLengthProven(edge, ct3, edist, length3, center3Tables, edge3Distances, maxNodes, limit, validate, hc, hcCoord, c3PairDist).moves;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Exact-length validated search that also reports what the call proved.
|
|
427
|
+
* `provenThrough` is the greatest length with provably no (center, edge)
|
|
428
|
+
* solution: set only when the tree was exhaustively explored with no goal
|
|
429
|
+
* reached, no node-cap abort, no TT hit, and complete excess info — the
|
|
430
|
+
* caller may then skip every shorter length for this candidate. Otherwise
|
|
431
|
+
* it is `length3 - 1` at most (only the attempted length is settled).
|
|
432
|
+
*/
|
|
433
|
+
solveLengthProven(edge, ct3, edist, length3, center3Tables, edge3Distances, maxNodes = 15000, limit = 16, validate, hc, hcCoord = -1, c3PairDist) {
|
|
434
|
+
this.setTableContext(center3Tables, edge3Distances);
|
|
435
|
+
if (c3PairDist) {
|
|
436
|
+
const c3Base = ct3 * 144;
|
|
437
|
+
let maxPairDist = 0;
|
|
438
|
+
for (let s = 0; s < 12; s++) {
|
|
439
|
+
const idx = c3Base + s * 12 + edge[s];
|
|
440
|
+
const d = (idx & 1) === 0 ? (c3PairDist[idx >> 1] & 0x0f) : (c3PairDist[idx >> 1] >> 4);
|
|
441
|
+
if (d > maxPairDist)
|
|
442
|
+
maxPairDist = d;
|
|
443
|
+
}
|
|
444
|
+
if (maxPairDist > length3) {
|
|
445
|
+
return { moves: null, provenThrough: maxPairDist - 1, validatedThrough: maxPairDist - 1, nodesUsed: 0 };
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
this.tempep[0].set(edge);
|
|
449
|
+
this.nodeCount = 0;
|
|
450
|
+
this.capped = false;
|
|
451
|
+
this.limitHit = false;
|
|
452
|
+
this.sawGoal = false;
|
|
453
|
+
this.ttTouched = false;
|
|
454
|
+
this.minExcess = Number.MAX_SAFE_INTEGER;
|
|
455
|
+
this.ttCoverage = Number.MAX_SAFE_INTEGER;
|
|
456
|
+
this.callId = this.nextCallId();
|
|
457
|
+
let accepted = null;
|
|
458
|
+
const collect = {
|
|
459
|
+
limit,
|
|
460
|
+
examined: 0,
|
|
461
|
+
validate: (moves) => {
|
|
462
|
+
if (validate(moves)) {
|
|
463
|
+
accepted = moves;
|
|
464
|
+
return true;
|
|
465
|
+
}
|
|
466
|
+
// A rejected goal is not a validated solution. Enumeration callers
|
|
467
|
+
// may collect it themselves, but cannot use it as a search proof.
|
|
468
|
+
return false;
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
this.search(this.tempep[0], ct3, edist, length3, 20, 0, center3Tables, edge3Distances, maxNodes, collect, hc, hcCoord, c3PairDist);
|
|
472
|
+
if (accepted)
|
|
473
|
+
return { moves: accepted, provenThrough: length3, validatedThrough: length3, nodesUsed: this.nodeCount };
|
|
474
|
+
if (typeof process !== "undefined" && process.env?.CUBE4_PROOF_STATS === "1") {
|
|
475
|
+
exports.proofStats.total++;
|
|
476
|
+
if (this.capped)
|
|
477
|
+
exports.proofStats.capped++;
|
|
478
|
+
if (this.limitHit)
|
|
479
|
+
exports.proofStats.limitHit++;
|
|
480
|
+
if (this.sawGoal)
|
|
481
|
+
exports.proofStats.sawGoal++;
|
|
482
|
+
if (this.ttTouched)
|
|
483
|
+
exports.proofStats.ttTouched++;
|
|
484
|
+
}
|
|
485
|
+
// `provenThrough` keeps its conservative raw-goal contract: it may only
|
|
486
|
+
// claim a depth when NO raw goal was reached at all (a rejected goal IS
|
|
487
|
+
// a raw goal), no TT hit intervened, and the bound prunes cover past the
|
|
488
|
+
// attempted length.
|
|
489
|
+
let provenThrough = -1;
|
|
490
|
+
if (!this.capped &&
|
|
491
|
+
!this.limitHit &&
|
|
492
|
+
!this.sawGoal &&
|
|
493
|
+
!this.ttTouched &&
|
|
494
|
+
this.minExcess > length3 &&
|
|
495
|
+
this.minExcess !== Number.MAX_SAFE_INTEGER) {
|
|
496
|
+
if (typeof process !== "undefined" && process.env?.CUBE4_PROOF_STATS === "1") {
|
|
497
|
+
exports.proofStats.clean++;
|
|
498
|
+
exports.proofStats.jumpSum += this.minExcess - 1 - length3;
|
|
499
|
+
}
|
|
500
|
+
provenThrough = this.minExcess - 1;
|
|
501
|
+
}
|
|
502
|
+
// `validatedThrough` is scoped to THIS call's validator predicate (all
|
|
503
|
+
// calls sharing one solver instance use one predicate): rejected raw
|
|
504
|
+
// goals are not goals for it, and TT hits are proof-compatible (entries
|
|
505
|
+
// mean "no goal under the call's predicate at any length <= stored").
|
|
506
|
+
// Every walked branch leaves a claim: bound prunes record their
|
|
507
|
+
// threshold (noteExcess), fringe rejections record `length3 + 1` below,
|
|
508
|
+
// and TT-hit subtrees record `ttCoverage`.
|
|
509
|
+
let validatedThrough = -1;
|
|
510
|
+
if (!this.capped && !this.limitHit) {
|
|
511
|
+
let bound = length3;
|
|
512
|
+
if (this.minExcess !== Number.MAX_SAFE_INTEGER && this.minExcess - 1 > bound) {
|
|
513
|
+
bound = this.minExcess - 1;
|
|
514
|
+
}
|
|
515
|
+
if (this.ttCoverage < bound)
|
|
516
|
+
bound = this.ttCoverage;
|
|
517
|
+
validatedThrough = bound;
|
|
518
|
+
}
|
|
519
|
+
return { moves: null, provenThrough, validatedThrough, nodesUsed: this.nodeCount };
|
|
520
|
+
}
|
|
113
521
|
}
|
|
114
522
|
exports.FastPhase3Solver = FastPhase3Solver;
|
|
115
|
-
|
|
523
|
+
/** Temporary instrumentation for proof-statistics (CUBE4_PROOF_STATS=1). */
|
|
524
|
+
exports.proofStats = {
|
|
525
|
+
total: 0,
|
|
526
|
+
capped: 0,
|
|
527
|
+
limitHit: 0,
|
|
528
|
+
sawGoal: 0,
|
|
529
|
+
ttTouched: 0,
|
|
530
|
+
clean: 0,
|
|
531
|
+
jumpSum: 0
|
|
532
|
+
};
|
|
533
|
+
function resetProofStats() {
|
|
534
|
+
exports.proofStats.total = 0;
|
|
535
|
+
exports.proofStats.capped = 0;
|
|
536
|
+
exports.proofStats.limitHit = 0;
|
|
537
|
+
exports.proofStats.sawGoal = 0;
|
|
538
|
+
exports.proofStats.ttTouched = 0;
|
|
539
|
+
exports.proofStats.clean = 0;
|
|
540
|
+
exports.proofStats.jumpSum = 0;
|
|
541
|
+
}
|
|
116
542
|
/**
|
|
117
543
|
* Solve Phase 3 with iterative deepening on a single Phase3Start.
|
|
118
544
|
*/
|
|
119
|
-
async function solvePhase3(start, center3Tables, edge3Tables, maxLength = 20) {
|
|
545
|
+
async function solvePhase3(start, center3Tables, edge3Tables, maxLength = 20, maxNodes = 80_000) {
|
|
546
|
+
const solver = new FastPhase3Solver();
|
|
120
547
|
const sym = (0, edge3_js_1.getSymTables)();
|
|
121
548
|
const symIdx = (0, edge3_js_1.getMvSymFrom)(start.edge, start.edge, 20, sym.sym2Raw, sym.sym2Mask, sym.raw2Sym, sym.mvrot, sym.mvroto);
|
|
122
549
|
const edist = (edge3Tables.distances[(symIdx >> 3) >> 1] >> (((symIdx >> 3) & 1) * 4)) & 0x0f;
|
|
@@ -125,7 +552,7 @@ async function solvePhase3(start, center3Tables, edge3Tables, maxLength = 20) {
|
|
|
125
552
|
return null;
|
|
126
553
|
const minLength = Math.max(cdist, edist);
|
|
127
554
|
for (let l = minLength; l <= maxLength; l++) {
|
|
128
|
-
const moves =
|
|
555
|
+
const moves = solver.solveLength(start.edge, start.ct3, edist, l, center3Tables, edge3Tables.distances, maxNodes);
|
|
129
556
|
if (moves) {
|
|
130
557
|
return {
|
|
131
558
|
moves,
|