@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
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* the reduction solver (phases 1-4), and invert the solution. The scramble
|
|
5
5
|
* always reproduces the sampled target state and facelets exactly.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Reduction-only, no resampling: each call runs one round of up to `bestOf`
|
|
8
|
+
* independent attempts and returns the shortest success (ties keep the
|
|
9
|
+
* lowest job index). If every attempt fails, the last error is thrown.
|
|
10
10
|
*/
|
|
11
11
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
12
|
if (k2 === undefined) k2 = k;
|
|
@@ -44,100 +44,15 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
45
|
exports.cube4ScrambleGenerator = void 0;
|
|
46
46
|
exports.generateCube4Scramble = generateCube4Scramble;
|
|
47
|
+
exports.generateCube4Attempt = generateCube4Attempt;
|
|
47
48
|
const moves_js_1 = require("./moves.js");
|
|
48
49
|
const random_state_js_1 = require("./random-state.js");
|
|
49
50
|
const state_js_1 = require("./state.js");
|
|
50
51
|
const facelets_js_1 = require("./facelets.js");
|
|
51
52
|
const solve_js_1 = require("./reduction/solve.js");
|
|
52
53
|
const index_js_1 = require("../cube3/solver/index.js");
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
// cubing.js fallback (random-state, WCA-compliant)
|
|
56
|
-
// ────────────────────────────────────────────────────────────
|
|
57
|
-
/**
|
|
58
|
-
* Try to generate a 4x4 scramble using cubing.js `randomScrambleForEvent`.
|
|
59
|
-
* Uses a dynamic import so browser bundles can tree-shake it when unused.
|
|
60
|
-
* Returns null if cubing.js is unavailable.
|
|
61
|
-
*/
|
|
62
|
-
async function generateCube4WithCubingJs() {
|
|
63
|
-
try {
|
|
64
|
-
const { randomScrambleForEvent } = await Promise.resolve().then(() => __importStar(require(
|
|
65
|
-
/* webpackIgnore: true */
|
|
66
|
-
/* @vite-ignore */
|
|
67
|
-
"cubing/scramble")));
|
|
68
|
-
const alg = await randomScrambleForEvent("444");
|
|
69
|
-
const scramble = alg.toString();
|
|
70
|
-
const moves = scramble.trim().split(/\s+/);
|
|
71
|
-
return {
|
|
72
|
-
eventId: "444",
|
|
73
|
-
scramble,
|
|
74
|
-
moves,
|
|
75
|
-
quality: "random-state",
|
|
76
|
-
generator: "cubing-js",
|
|
77
|
-
metadata: {
|
|
78
|
-
generator: "cubing-js",
|
|
79
|
-
distribution: "random-state",
|
|
80
|
-
note: "Generated by cubing.js randomScrambleForEvent as fallback."
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
catch {
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
// ────────────────────────────────────────────────────────────
|
|
89
|
-
// Last-resort: random-moves (no external dep, always works)
|
|
90
|
-
// ────────────────────────────────────────────────────────────
|
|
91
|
-
const CUBE4_FACES = ["U", "R", "F", "D", "L", "B"];
|
|
92
|
-
// Opposite face indices (U↔D, R↔L, F↔B)
|
|
93
|
-
const CUBE4_OPPOSITE = { U: "D", D: "U", R: "L", L: "R", F: "B", B: "F" };
|
|
94
|
-
const CUBE4_SUFFIXES = ["", "2", "'"];
|
|
95
|
-
const CUBE4_WIDE_FACES = ["Uw", "Rw", "Fw", "Dw", "Lw", "Bw"];
|
|
96
|
-
// Opposite wide-move faces
|
|
97
|
-
const CUBE4_WIDE_OPPOSITE = {
|
|
98
|
-
Uw: "Dw", Dw: "Uw", Rw: "Lw", Lw: "Rw", Fw: "Bw", Bw: "Fw"
|
|
99
|
-
};
|
|
100
|
-
// All available move bases (outer + wide). Outer and wide turns are treated
|
|
101
|
-
// independently for the same-face/opposite-face check using their base letter.
|
|
102
|
-
const ALL_MOVE_BASES = [...CUBE4_FACES, ...CUBE4_WIDE_FACES];
|
|
103
|
-
function generateCube4RandomMoves(options = {}) {
|
|
104
|
-
const rng = (0, index_js_2.createRandomSource)(options.seed);
|
|
105
|
-
const moveCount = 40;
|
|
106
|
-
const moves = [];
|
|
107
|
-
let lastFace = null;
|
|
108
|
-
let secondLastFace = null;
|
|
109
|
-
const getBaseLetter = (face) => face.length > 1 ? face[0] : face;
|
|
110
|
-
for (let i = 0; i < moveCount; i++) {
|
|
111
|
-
let face;
|
|
112
|
-
// Reject same face as last or opposite face when the last two moves
|
|
113
|
-
// share the same axis (to avoid redundant move pairs like R L R).
|
|
114
|
-
do {
|
|
115
|
-
face = ALL_MOVE_BASES[rng.nextInt(ALL_MOVE_BASES.length)];
|
|
116
|
-
} while (face === lastFace ||
|
|
117
|
-
(lastFace !== null &&
|
|
118
|
-
(CUBE4_OPPOSITE[face] === lastFace || CUBE4_WIDE_OPPOSITE[face] === lastFace) &&
|
|
119
|
-
secondLastFace !== null &&
|
|
120
|
-
getBaseLetter(secondLastFace) === getBaseLetter(face)));
|
|
121
|
-
const suffix = CUBE4_SUFFIXES[rng.nextInt(3)];
|
|
122
|
-
moves.push(`${face}${suffix}`);
|
|
123
|
-
secondLastFace = lastFace;
|
|
124
|
-
lastFace = face;
|
|
125
|
-
}
|
|
126
|
-
const scramble = moves.join(" ");
|
|
127
|
-
return {
|
|
128
|
-
eventId: "444",
|
|
129
|
-
scramble,
|
|
130
|
-
moves,
|
|
131
|
-
quality: "random-moves",
|
|
132
|
-
generator: "cube4-random-moves-fallback",
|
|
133
|
-
metadata: {
|
|
134
|
-
generator: "cube4-random-moves-fallback",
|
|
135
|
-
distribution: "random-moves",
|
|
136
|
-
note: "Reduction solver and cubing.js both failed; using random-moves.",
|
|
137
|
-
...options.metadata
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
}
|
|
54
|
+
const tables_js_1 = require("../cube3/solver/tables.js");
|
|
55
|
+
const pair_tables_js_1 = require("../cube3/solver/pair-tables.js");
|
|
141
56
|
// ────────────────────────────────────────────────────────────
|
|
142
57
|
let cachedTables;
|
|
143
58
|
async function loadCube4Tables(baseUrl) {
|
|
@@ -177,98 +92,234 @@ exports.cube4ScrambleGenerator = {
|
|
|
177
92
|
puzzle: "cube-4x4",
|
|
178
93
|
quality: "random-state",
|
|
179
94
|
generator: "cube4-reduction",
|
|
180
|
-
description: "Random-state 4x4 scramble via the reduction solver (centers + pairing + 3x3 finish).
|
|
95
|
+
description: "Random-state 4x4 scramble via the reduction solver (centers + pairing + 3x3 finish)."
|
|
181
96
|
},
|
|
182
97
|
generate(options = {}) {
|
|
183
98
|
return generateCube4Scramble(options);
|
|
184
99
|
}
|
|
185
100
|
};
|
|
101
|
+
function invertRotationSequence(rotSeq) {
|
|
102
|
+
return rotSeq.slice().reverse().map((r) => (r.endsWith("'") ? r.slice(0, -1) : r.endsWith("2") ? r : `${r}'`));
|
|
103
|
+
}
|
|
104
|
+
/** Shortest-first pick (ties keep `a`, i.e. the lowest job index). */
|
|
105
|
+
function shorterScramble(a, b) {
|
|
106
|
+
if (!a || b.moves.length < a.moves.length)
|
|
107
|
+
return b;
|
|
108
|
+
return a;
|
|
109
|
+
}
|
|
110
|
+
function seedForBestJob(options, job) {
|
|
111
|
+
if (job === 0)
|
|
112
|
+
return options;
|
|
113
|
+
const next = { ...options };
|
|
114
|
+
if (options.seed !== undefined) {
|
|
115
|
+
next.seed = `${options.seed}:best${job}`;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
delete next.seed;
|
|
119
|
+
}
|
|
120
|
+
return next;
|
|
121
|
+
}
|
|
122
|
+
function isRetryableCube4Error(error) {
|
|
123
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
124
|
+
const isPhase2Error = typeof error?.name === "string" &&
|
|
125
|
+
error.name.includes("Cube3Phase2");
|
|
126
|
+
return (error instanceof Error &&
|
|
127
|
+
(msg.includes("reduction failed") ||
|
|
128
|
+
msg.includes("failed to convert") ||
|
|
129
|
+
msg.includes("no valid phase 2") ||
|
|
130
|
+
msg.includes("timeout") ||
|
|
131
|
+
isPhase2Error ||
|
|
132
|
+
msg.includes("Phase 2 solution not found")));
|
|
133
|
+
}
|
|
186
134
|
async function generateCube4Scramble(options = {}) {
|
|
135
|
+
const bestOf = Math.max(1, Math.floor(options.bestOf ?? 1) || 1);
|
|
187
136
|
let tables;
|
|
137
|
+
let pairTables;
|
|
188
138
|
try {
|
|
189
|
-
|
|
139
|
+
const [loadedTables, cube3P1Tables, loadedPairTables] = await Promise.all([
|
|
140
|
+
loadCube4Tables(),
|
|
141
|
+
(0, tables_js_1.loadCube3Phase1Tables)(),
|
|
142
|
+
(0, pair_tables_js_1.loadCube3PairTables)()
|
|
143
|
+
]);
|
|
144
|
+
tables = loadedTables;
|
|
145
|
+
tables.cube3Phase1 = cube3P1Tables;
|
|
146
|
+
tables.pairTables = loadedPairTables;
|
|
147
|
+
pairTables = loadedPairTables;
|
|
190
148
|
}
|
|
191
149
|
catch (error) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
if (cubingJsResult)
|
|
195
|
-
return cubingJsResult;
|
|
196
|
-
return generateCube4RandomMoves(options);
|
|
150
|
+
throw new Error("[cube4-scrambler] Failed to load reduction tables; run pnpm cube4:center1:tables etc. " +
|
|
151
|
+
`(${error instanceof Error ? error.message : String(error)})`);
|
|
197
152
|
}
|
|
153
|
+
const totalRestarts = Math.max(1, Math.floor(options.restarts ?? options.bestOf ?? 1) || 1);
|
|
198
154
|
let lastError;
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
155
|
+
try {
|
|
156
|
+
return await generateCube4Attempt(tables, pairTables, {
|
|
157
|
+
...options,
|
|
158
|
+
restarts: totalRestarts
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
if (!isRetryableCube4Error(error))
|
|
163
|
+
throw error;
|
|
164
|
+
lastError = error;
|
|
165
|
+
}
|
|
166
|
+
if (!options.deep) {
|
|
167
|
+
try {
|
|
168
|
+
return await generateCube4Attempt(tables, pairTables, {
|
|
169
|
+
...options,
|
|
170
|
+
restarts: totalRestarts,
|
|
171
|
+
deep: true
|
|
172
|
+
});
|
|
206
173
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
else {
|
|
213
|
-
delete attemptOptions.seed;
|
|
214
|
-
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
if (isRetryableCube4Error(error))
|
|
176
|
+
lastError = error;
|
|
177
|
+
else
|
|
178
|
+
throw error;
|
|
215
179
|
}
|
|
216
|
-
|
|
180
|
+
}
|
|
181
|
+
throw lastError instanceof Error
|
|
182
|
+
? lastError
|
|
183
|
+
: new Error(`[cube4-scrambler] Reduction solver failed for restarts=${totalRestarts}: ${String(lastError)}`);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Single-attempt 444 solve: samples ONE state (from `options.seed`) and
|
|
187
|
+
* solves it with the reduction solver. Throws on failure — no resampling.
|
|
188
|
+
* Fan-out primitive for hosts: run with seeds `seed`, `seed:bestN`, ...
|
|
189
|
+
* across workers and keep the shortest valid result.
|
|
190
|
+
*/
|
|
191
|
+
async function generateCube4Attempt(tables, pairTables, options = {}) {
|
|
192
|
+
const attemptState = (0, random_state_js_1.generateRandomCube4State)(options);
|
|
193
|
+
const axes = options.threeAxes ? [[], ["z"], ["y'"]] : [[]];
|
|
194
|
+
let bestScrambleMoves = null;
|
|
195
|
+
let bestSolutionMoves = null;
|
|
196
|
+
let bestPhases;
|
|
197
|
+
let bestNodes;
|
|
198
|
+
let bestTimings;
|
|
199
|
+
let bestReductionLen;
|
|
200
|
+
let lastError;
|
|
201
|
+
const tAttemptStart = performance.now();
|
|
202
|
+
const finishOpts = options.finish ?? {};
|
|
203
|
+
for (const rot of axes) {
|
|
217
204
|
try {
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
205
|
+
const sRot = rot.length === 0 ? attemptState : (0, solve_js_1.applyCubeRotation)(attemptState, rot);
|
|
206
|
+
// The reduction search is bounded by deterministic work caps (see
|
|
207
|
+
// solve.ts), so these timeouts are only wall-clock backstops for
|
|
208
|
+
// pathological cases.
|
|
209
|
+
const solvePromise = (0, solve_js_1.solveCube4State)(sRot, tables, async (cube3State) => {
|
|
210
|
+
// Single solver-side best-of-orientations finish call with
|
|
211
|
+
// extra phase-1 improvement: same lengths as the former
|
|
212
|
+
// hand-rolled UD/X/Z triple at a fraction of the cost.
|
|
213
|
+
const solveFinish = async (timeBudget, phase2Budget) => {
|
|
214
|
+
const solution = await (0, index_js_1.solveCube3State)(cube3State, {
|
|
215
|
+
orientations: finishOpts.orientations ?? 3,
|
|
216
|
+
phase1ExtraDepth: finishOpts.phase1ExtraDepth ?? 1,
|
|
217
|
+
phase1SolutionLimit: finishOpts.phase1SolutionLimit ?? 24,
|
|
218
|
+
targetLength: finishOpts.targetLength ?? 20,
|
|
219
|
+
timeoutMs: timeBudget,
|
|
220
|
+
// The 54MB pair tables are skipped for the 444 finish: pair
|
|
221
|
+
// pruning changes nothing measurable here (identical lengths
|
|
222
|
+
// on a 44-state corpus, zero extra finish retries), and
|
|
223
|
+
// loading them dominates cold-start I/O. Hosts that already
|
|
224
|
+
// hold pairTables can still pass them via generateCube4Attempt.
|
|
225
|
+
phase2: { pairTables, skipPairTables: pairTables === undefined, timeoutMs: phase2Budget }
|
|
226
|
+
});
|
|
227
|
+
return { moves: solution.moves };
|
|
228
|
+
};
|
|
229
|
+
// The first call seeds everything, so a flaky finish gets one
|
|
230
|
+
// budgeted retry before the whole finish is declared failed.
|
|
231
|
+
try {
|
|
232
|
+
return await solveFinish(finishOpts.timeoutMs ?? 150, finishOpts.phase2TimeoutMs ?? 50);
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
return await solveFinish(finishOpts.retryTimeoutMs ?? 300, finishOpts.retryPhase2TimeoutMs ?? 100);
|
|
225
236
|
}
|
|
237
|
+
}, {
|
|
238
|
+
timeoutMs: 75000,
|
|
239
|
+
deep: options.deep === true,
|
|
240
|
+
deepPolish: options.deepPolish ?? options.threeAxes,
|
|
241
|
+
...(options.finishTries !== undefined ? { finishTries: options.finishTries } : {}),
|
|
242
|
+
...(options.centersBeam !== undefined ? { centersBeam: options.centersBeam } : {}),
|
|
243
|
+
...(options.edgesPerCenter !== undefined ? { edgesPerCenter: options.edgesPerCenter } : {}),
|
|
244
|
+
...(options.phase1ExtraDepths !== undefined ? { phase1ExtraDepths: options.phase1ExtraDepths } : {}),
|
|
245
|
+
...(options.phase1Cap !== undefined ? { phase1Cap: options.phase1Cap } : {}),
|
|
246
|
+
...(options.center2Wide !== undefined ? { center2Wide: options.center2Wide } : {}),
|
|
247
|
+
...(options.restarts !== undefined ? { restarts: options.restarts } : {}),
|
|
248
|
+
...(options.prepCap !== undefined ? { prepCap: options.prepCap } : {}),
|
|
249
|
+
...(options.p1CandCap !== undefined ? { p1CandCap: options.p1CandCap } : {}),
|
|
250
|
+
...(options.stagnationCut !== undefined ? { stagnationCut: options.stagnationCut } : {}),
|
|
251
|
+
maxReductionLen: bestReductionLen
|
|
252
|
+
});
|
|
253
|
+
let timeoutHandle;
|
|
254
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
255
|
+
timeoutHandle = setTimeout(() => reject(new Error("4x4 solve: timeout")), 90000);
|
|
256
|
+
});
|
|
257
|
+
let solveOutcome;
|
|
258
|
+
try {
|
|
259
|
+
solveOutcome = await Promise.race([solvePromise, timeoutPromise]);
|
|
260
|
+
}
|
|
261
|
+
finally {
|
|
262
|
+
// The search is work-capped and always settles first: clearing the
|
|
263
|
+
// backstop avoids leaking a live 90s timer per axis (which kept the
|
|
264
|
+
// event loop — and CLI/test processes — alive needlessly).
|
|
265
|
+
clearTimeout(timeoutHandle);
|
|
226
266
|
}
|
|
227
|
-
const
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
distribution: "random-state",
|
|
240
|
-
...options.metadata
|
|
267
|
+
const { scramble: scrambleMoves, moves: solutionMoves, phases, timings, nodes } = solveOutcome;
|
|
268
|
+
const invRot = rot.length === 0 ? [] : invertRotationSequence(rot);
|
|
269
|
+
const fullScramble = rot.length === 0 ? scrambleMoves : (0, solve_js_1.rotateAlgorithm)(scrambleMoves, invRot);
|
|
270
|
+
const fullSolution = rot.length === 0 ? solutionMoves : (0, solve_js_1.rotateAlgorithm)(solutionMoves, invRot);
|
|
271
|
+
if (!bestScrambleMoves || fullScramble.length < bestScrambleMoves.length) {
|
|
272
|
+
bestScrambleMoves = fullScramble;
|
|
273
|
+
bestSolutionMoves = fullSolution;
|
|
274
|
+
bestPhases = phases;
|
|
275
|
+
bestTimings = timings;
|
|
276
|
+
bestNodes = nodes;
|
|
277
|
+
if (phases) {
|
|
278
|
+
bestReductionLen = phases.length12 + phases.length3;
|
|
241
279
|
}
|
|
242
|
-
};
|
|
243
|
-
if (options.seed !== undefined) {
|
|
244
|
-
result.seed = options.seed;
|
|
245
280
|
}
|
|
246
|
-
return result;
|
|
247
281
|
}
|
|
248
|
-
catch (
|
|
249
|
-
lastError =
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
282
|
+
catch (err) {
|
|
283
|
+
lastError = err;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
if (!bestScrambleMoves || !bestSolutionMoves) {
|
|
287
|
+
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
288
|
+
}
|
|
289
|
+
const scrambleMoves = bestScrambleMoves;
|
|
290
|
+
const solutionMoves = bestSolutionMoves;
|
|
291
|
+
const scrambledState = (0, moves_js_1.applyCube4Algorithm)((0, state_js_1.createSolvedCube4State)(), scrambleMoves);
|
|
292
|
+
if (!options.skipValidation) {
|
|
293
|
+
const solvedCheck = (0, moves_js_1.applyCube4Algorithm)(scrambledState, solutionMoves);
|
|
294
|
+
if (!(0, state_js_1.isSolvedCube4State)(solvedCheck)) {
|
|
295
|
+
throw new Error("Generated 4x4 scramble does not solve back to solved state.");
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
const facelets = (0, facelets_js_1.toCube4Facelets)(scrambledState);
|
|
299
|
+
const result = {
|
|
300
|
+
eventId: "444",
|
|
301
|
+
scramble: (0, moves_js_1.formatCube4Algorithm)(scrambleMoves),
|
|
302
|
+
moves: scrambleMoves,
|
|
303
|
+
quality: "random-state",
|
|
304
|
+
generator: "cube4-reduction",
|
|
305
|
+
metadata: {
|
|
306
|
+
generator: "cube4-reduction",
|
|
307
|
+
state: scrambledState,
|
|
308
|
+
facelets,
|
|
309
|
+
faceletString: (0, facelets_js_1.formatCube4Facelets)(facelets),
|
|
310
|
+
distribution: "random-state",
|
|
311
|
+
solutionMoves: (0, moves_js_1.invertCube4Algorithm)(scrambleMoves),
|
|
312
|
+
// Winning-reduction breakdown (unsimplified lengths) + wall-clock
|
|
313
|
+
// split, for tuning and A/B comparisons.
|
|
314
|
+
phases: bestPhases,
|
|
315
|
+
phaseTimings: bestTimings,
|
|
316
|
+
nodes: bestNodes,
|
|
317
|
+
attemptMs: performance.now() - tAttemptStart,
|
|
318
|
+
...options.metadata
|
|
265
319
|
}
|
|
320
|
+
};
|
|
321
|
+
if (options.seed !== undefined) {
|
|
322
|
+
result.seed = options.seed;
|
|
266
323
|
}
|
|
267
|
-
|
|
268
|
-
console.warn("[cube4-scrambler] Reduction solver failed after 5 attempts; trying cubing.js fallback.", lastError);
|
|
269
|
-
const cubingJsResult = await generateCube4WithCubingJs();
|
|
270
|
-
if (cubingJsResult)
|
|
271
|
-
return cubingJsResult;
|
|
272
|
-
console.warn("[cube4-scrambler] cubing.js fallback also failed; using random-moves.");
|
|
273
|
-
return generateCube4RandomMoves(options);
|
|
324
|
+
return result;
|
|
274
325
|
}
|
|
@@ -44,8 +44,11 @@ function cube4StatesEqual(a, b) {
|
|
|
44
44
|
typedArrayEqual(a.wp, b.wp) &&
|
|
45
45
|
typedArrayEqual(a.centers, b.centers));
|
|
46
46
|
}
|
|
47
|
+
// Read-only canonical solved state for comparisons (never mutated, so a
|
|
48
|
+
// single shared instance is safe and avoids allocating 4 arrays per call).
|
|
49
|
+
const SOLVED_STATE = createSolvedCube4State();
|
|
47
50
|
function isSolvedCube4State(state) {
|
|
48
|
-
return cube4StatesEqual(state,
|
|
51
|
+
return cube4StatesEqual(state, SOLVED_STATE);
|
|
49
52
|
}
|
|
50
53
|
// ---------------------------------------------------------------------------
|
|
51
54
|
// Helpers
|
|
@@ -38,7 +38,7 @@ function solveWithTablesIndices(state) {
|
|
|
38
38
|
const centersTable = tables_js_1.CENTERS_TABLE;
|
|
39
39
|
const permTable = tables_js_1.PERMUTATION_TABLE;
|
|
40
40
|
const orientTable = tables_js_1.ORIENTATION_TABLE;
|
|
41
|
-
const policy = tables_js_1.
|
|
41
|
+
const policy = tables_js_1.POLICY_TABLE_EXPANDED;
|
|
42
42
|
let c = (0, tables_js_1.getCentersIndex)(state.centers);
|
|
43
43
|
let p = (0, tables_js_1.getPermutationCompactIndex)(state.edgesPerm);
|
|
44
44
|
let o = (0, tables_js_1.getOrientationIndex)(state.edgesOrient);
|
|
@@ -47,10 +47,7 @@ function solveWithTablesIndices(state) {
|
|
|
47
47
|
return [];
|
|
48
48
|
const moves = [];
|
|
49
49
|
while (stateIndex !== 0) {
|
|
50
|
-
const
|
|
51
|
-
const byteIndex = bits >> 3;
|
|
52
|
-
const offset = bits & 7;
|
|
53
|
-
const m = ((policy[byteIndex] | ((policy[byteIndex + 1] ?? 0) << 8)) >> offset) & 0x07;
|
|
50
|
+
const m = policy[stateIndex];
|
|
54
51
|
moves.push(m);
|
|
55
52
|
c = centersTable[c * tables_js_1.MOVE_COUNT + m];
|
|
56
53
|
p = permTable[p * tables_js_1.MOVE_COUNT + m];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.POLICY_TABLE = exports.ORIENTATION_TABLE = exports.PERMUTATION_TABLE = exports.CENTERS_TABLE = exports.PYRAMINX_MOVES = exports.POLICY_PACKED_ENTRIES = exports.STATE_COUNT = exports.MOVE_COUNT = exports.ORIENTATION_COUNT = exports.CENTERS_COUNT = exports.PERM_360_TO_720 = exports.PERM_720_TO_360 = exports.PERMUTATION_COUNT = exports.PERMUTATION_FULL_COUNT = void 0;
|
|
3
|
+
exports.POLICY_TABLE_EXPANDED = exports.POLICY_TABLE = exports.ORIENTATION_TABLE = exports.PERMUTATION_TABLE = exports.CENTERS_TABLE = exports.PYRAMINX_MOVES = exports.POLICY_PACKED_ENTRIES = exports.STATE_COUNT = exports.MOVE_COUNT = exports.ORIENTATION_COUNT = exports.CENTERS_COUNT = exports.PERM_360_TO_720 = exports.PERM_720_TO_360 = exports.PERMUTATION_COUNT = exports.PERMUTATION_FULL_COUNT = void 0;
|
|
4
4
|
exports.getPermutationIndex = getPermutationIndex;
|
|
5
5
|
exports.getPermutationFromIndex = getPermutationFromIndex;
|
|
6
6
|
exports.getOrientationIndex = getOrientationIndex;
|
|
@@ -289,6 +289,12 @@ exports.CENTERS_TABLE = null;
|
|
|
289
289
|
exports.PERMUTATION_TABLE = null;
|
|
290
290
|
exports.ORIENTATION_TABLE = null;
|
|
291
291
|
exports.POLICY_TABLE = null;
|
|
292
|
+
/**
|
|
293
|
+
* Byte-expanded policy (one optimal move per state, no bit-unpacking in
|
|
294
|
+
* the hot walk). Built at load from the 3-bit packed table: +0.9 MB RAM,
|
|
295
|
+
* zero wire/format change.
|
|
296
|
+
*/
|
|
297
|
+
exports.POLICY_TABLE_EXPANDED = null;
|
|
292
298
|
async function ensureTablesLoaded() {
|
|
293
299
|
if (tablesLoaded)
|
|
294
300
|
return;
|
|
@@ -297,8 +303,18 @@ async function ensureTablesLoaded() {
|
|
|
297
303
|
exports.PERMUTATION_TABLE = tables.permutationTable;
|
|
298
304
|
exports.ORIENTATION_TABLE = tables.orientationTable;
|
|
299
305
|
exports.POLICY_TABLE = tables.policyTable;
|
|
306
|
+
exports.POLICY_TABLE_EXPANDED = expandPolicy3Bit(tables.policyTable, exports.STATE_COUNT);
|
|
300
307
|
tablesLoaded = true;
|
|
301
308
|
}
|
|
309
|
+
function expandPolicy3Bit(packed, count) {
|
|
310
|
+
const expanded = new Uint8Array(count);
|
|
311
|
+
for (let index = 0; index < count; index += 1) {
|
|
312
|
+
const bits = index * 3;
|
|
313
|
+
const byteIndex = bits >> 3;
|
|
314
|
+
expanded[index] = (((packed[byteIndex] | ((packed[byteIndex + 1] ?? 0) << 8)) >> (bits & 7)) & 0x07);
|
|
315
|
+
}
|
|
316
|
+
return expanded;
|
|
317
|
+
}
|
|
302
318
|
function arePyraminxTablesLoaded() {
|
|
303
319
|
return tablesLoaded;
|
|
304
320
|
}
|
|
@@ -328,4 +344,5 @@ function resetTablesForTests() {
|
|
|
328
344
|
exports.PERMUTATION_TABLE = null;
|
|
329
345
|
exports.ORIENTATION_TABLE = null;
|
|
330
346
|
exports.POLICY_TABLE = null;
|
|
347
|
+
exports.POLICY_TABLE_EXPANDED = null;
|
|
331
348
|
}
|
|
@@ -43,7 +43,7 @@ function solveWithTables(state) {
|
|
|
43
43
|
return { moves, depth: moves.length };
|
|
44
44
|
}
|
|
45
45
|
function solveWithTablesIndices(state) {
|
|
46
|
-
const policy = tables_js_1.
|
|
46
|
+
const policy = tables_js_1.POLICY_TABLE_EXPANDED;
|
|
47
47
|
const permTable = tables_js_1.PERMUTATION_TABLE;
|
|
48
48
|
const orientTable = tables_js_1.ORIENTATION_TABLE;
|
|
49
49
|
const centersTable = tables_js_1.CENTERS_TABLE;
|
|
@@ -60,7 +60,7 @@ function solveWithTablesIndices(state) {
|
|
|
60
60
|
}
|
|
61
61
|
const moves = [];
|
|
62
62
|
while (rank !== tables_js_1.SOLVED_RANK) {
|
|
63
|
-
const m =
|
|
63
|
+
const m = policy[rank];
|
|
64
64
|
moves.push(m);
|
|
65
65
|
r7 = permTable[r7 * tables_js_1.MOVE_COUNT + m];
|
|
66
66
|
rO = orientTable[rO * tables_js_1.MOVE_COUNT + m];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SOLVED_RANK = exports.ORIENT_UNRANK = exports.CENTER_UNRANK = exports.PERM7_UNRANK = exports.R7_SETS = exports.ORIENT_CLASS = exports.EVEN_CENTER_FULL = exports.EVEN_CENTER_RANK = exports.POLICY_TABLE = exports.CENTERS_TABLE = exports.ORIENTATION_TABLE = exports.PERMUTATION_TABLE = exports.SKEWB_MOVES = exports.POLICY_PACKED_ENTRIES = exports.STATE_COUNT = exports.MOVE_COUNT = exports.PER_SET_SIZE = exports.ORIENT_CLASS_COUNT = exports.EVEN_CENTER_COUNT = exports.CENTER_COUNT = exports.ORIENTATION_COUNT = exports.PERMUTATION_COUNT = void 0;
|
|
3
|
+
exports.SOLVED_RANK = exports.ORIENT_UNRANK = exports.CENTER_UNRANK = exports.PERM7_UNRANK = exports.R7_SETS = exports.ORIENT_CLASS = exports.EVEN_CENTER_FULL = exports.EVEN_CENTER_RANK = exports.POLICY_TABLE_EXPANDED = exports.POLICY_TABLE = exports.CENTERS_TABLE = exports.ORIENTATION_TABLE = exports.PERMUTATION_TABLE = exports.SKEWB_MOVES = exports.POLICY_PACKED_ENTRIES = exports.STATE_COUNT = exports.MOVE_COUNT = exports.PER_SET_SIZE = exports.ORIENT_CLASS_COUNT = exports.EVEN_CENTER_COUNT = exports.CENTER_COUNT = exports.ORIENTATION_COUNT = exports.PERMUTATION_COUNT = void 0;
|
|
4
4
|
exports.getPermutationIndex = getPermutationIndex;
|
|
5
5
|
exports.getPermutationFromIndex = getPermutationFromIndex;
|
|
6
6
|
exports.getOrientationIndex = getOrientationIndex;
|
|
@@ -175,6 +175,12 @@ exports.PERMUTATION_TABLE = null; // r7 * MOVE_COUNT + m
|
|
|
175
175
|
exports.ORIENTATION_TABLE = null; // rO * MOVE_COUNT + m
|
|
176
176
|
exports.CENTERS_TABLE = null; // rC * MOVE_COUNT + m
|
|
177
177
|
exports.POLICY_TABLE = null; // optimal move per rank, 3-bit packed
|
|
178
|
+
/**
|
|
179
|
+
* Byte-expanded policy (one optimal move per rank, no bit-unpacking in
|
|
180
|
+
* the hot walk). Built at load from the 3-bit packed table: +3.1 MB RAM,
|
|
181
|
+
* zero wire/format change.
|
|
182
|
+
*/
|
|
183
|
+
exports.POLICY_TABLE_EXPANDED = null;
|
|
178
184
|
exports.EVEN_CENTER_RANK = null; // rC -> compact 0..359 (65535 = odd)
|
|
179
185
|
exports.EVEN_CENTER_FULL = null; // compact 0..359 -> rC (derived at load)
|
|
180
186
|
exports.ORIENT_CLASS = null; // rO -> class 0..8
|
|
@@ -224,9 +230,19 @@ function assignTables(tables) {
|
|
|
224
230
|
for (let i = 0; i < 7; i++)
|
|
225
231
|
exports.ORIENT_UNRANK[rO * 7 + i] = orient[i + 1];
|
|
226
232
|
}
|
|
233
|
+
exports.POLICY_TABLE_EXPANDED = expandPolicy3Bit(tables.policyTable, exports.STATE_COUNT);
|
|
227
234
|
exports.SOLVED_RANK = rankFromComponents(0, 0, 0);
|
|
228
235
|
tablesLoaded = true;
|
|
229
236
|
}
|
|
237
|
+
function expandPolicy3Bit(packed, count) {
|
|
238
|
+
const expanded = new Uint8Array(count);
|
|
239
|
+
for (let index = 0; index < count; index += 1) {
|
|
240
|
+
const bits = index * 3;
|
|
241
|
+
const byteIndex = bits >> 3;
|
|
242
|
+
expanded[index] = (((packed[byteIndex] | ((packed[byteIndex + 1] ?? 0) << 8)) >> (bits & 7)) & 0x07);
|
|
243
|
+
}
|
|
244
|
+
return expanded;
|
|
245
|
+
}
|
|
230
246
|
function resetTablesForTests() {
|
|
231
247
|
tablesLoaded = false;
|
|
232
248
|
exports.PERMUTATION_TABLE = null;
|
|
@@ -240,6 +256,7 @@ function resetTablesForTests() {
|
|
|
240
256
|
exports.PERM7_UNRANK = null;
|
|
241
257
|
exports.CENTER_UNRANK = null;
|
|
242
258
|
exports.ORIENT_UNRANK = null;
|
|
259
|
+
exports.POLICY_TABLE_EXPANDED = null;
|
|
243
260
|
}
|
|
244
261
|
// ---------------------------------------------------------------------------
|
|
245
262
|
// Compact rank helpers
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cubingcenter/scrambler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Extensible scramble generation toolkit for CubingCenter projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -57,12 +57,19 @@
|
|
|
57
57
|
"tsx": "^4.20.0",
|
|
58
58
|
"typescript": "^5.9.0"
|
|
59
59
|
},
|
|
60
|
-
"dependencies": {
|
|
61
|
-
"cubing": "^0.63.4"
|
|
62
|
-
},
|
|
63
60
|
"scripts": {
|
|
64
61
|
"build": "node scripts/clean.cjs && tsc -p tsconfig.json && tsc -p tsconfig.cjs.json && node scripts/write-cjs-package.cjs && node scripts/copy-assets.cjs",
|
|
65
|
-
"test": "node --test --import tsx
|
|
62
|
+
"test": "node --test --import tsx",
|
|
63
|
+
"test:all": "node --test --import tsx test/*.test.ts",
|
|
64
|
+
"test:skewb": "node --test --import tsx test/skewb*.test.ts",
|
|
65
|
+
"test:pyraminx": "node --test --import tsx test/pyraminx*.test.ts",
|
|
66
|
+
"test:sq1": "node --test --import tsx test/sq1*.test.ts",
|
|
67
|
+
"test:fto": "node --test --import tsx test/fto*.test.ts",
|
|
68
|
+
"test:cube2": "node --test --import tsx test/cube2*.test.ts",
|
|
69
|
+
"test:cube3": "node --test --import tsx test/cube3*.test.ts",
|
|
70
|
+
"test:cube4": "node --test --import tsx test/cube4*.test.ts test/center*.test.ts test/edge3.test.ts test/pairing-coordinate.test.ts test/phase*.test.ts",
|
|
71
|
+
"test:big": "node --test --import tsx test/big-cubes*.test.ts",
|
|
72
|
+
"test:watch": "node --test --watch --import tsx",
|
|
66
73
|
"lint": "tsc -p tsconfig.json --noEmit",
|
|
67
74
|
"package:check": "pnpm build && node scripts/check-package.cjs",
|
|
68
75
|
"verify": "pnpm lint && pnpm test && pnpm build && pnpm cube3:tables:check && pnpm cube2:tables:check && pnpm pyraminx:tables:check && pnpm skewb:tables:check && pnpm sq1:tables:check && pnpm package:check",
|
|
@@ -83,6 +90,8 @@
|
|
|
83
90
|
"cube4:center2:tables:check": "node --import tsx scripts/check-center2-solver-tables.ts",
|
|
84
91
|
"cube4:center3:tables": "node --import tsx scripts/generate-center3-solver-tables.ts",
|
|
85
92
|
"cube4:center3:tables:check": "node --import tsx scripts/check-center3-solver-tables.ts",
|
|
93
|
+
"cube4:hc:tables": "node --import tsx scripts/generate-hc-pdb-table.ts",
|
|
94
|
+
"cube4:hc:tables:check": "node --import tsx scripts/check-hc-pdb-table.ts",
|
|
86
95
|
"cube4:edge3:tables": "node --import tsx scripts/generate-edge3-solver-tables.ts",
|
|
87
96
|
"cube4:edge3:tables:check": "node --import tsx scripts/check-edge3-solver-tables.ts",
|
|
88
97
|
"tables:web:sync": "node --import tsx scripts/sync-web-tables.ts",
|