@cubingcenter/scrambler 0.3.0 → 0.4.1
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 +46 -3
- package/dist/core/api.d.ts.map +1 -1
- package/dist/core/api.js +13 -6
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/types.d.ts +7 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/puzzles/clock/index.d.ts +1 -0
- package/dist/puzzles/clock/index.d.ts.map +1 -1
- package/dist/puzzles/clock/index.js +4 -0
- package/dist/puzzles/clock/scrambler.js +2 -2
- package/dist/puzzles/cube2/index.d.ts +1 -0
- package/dist/puzzles/cube2/index.d.ts.map +1 -1
- package/dist/puzzles/cube2/index.js +4 -0
- package/dist/puzzles/cube2/scrambler.d.ts.map +1 -1
- package/dist/puzzles/cube2/scrambler.js +4 -6
- package/dist/puzzles/cube2/solver/browser-loader.d.ts.map +1 -1
- package/dist/puzzles/cube2/solver/browser-loader.js +4 -12
- package/dist/puzzles/cube2/solver/index.d.ts +1 -1
- package/dist/puzzles/cube2/solver/index.d.ts.map +1 -1
- package/dist/puzzles/cube2/solver/index.js +1 -1
- package/dist/puzzles/cube2/solver/search.d.ts +2 -0
- package/dist/puzzles/cube2/solver/search.d.ts.map +1 -1
- package/dist/puzzles/cube2/solver/search.js +26 -0
- package/dist/puzzles/cube2/validation.d.ts.map +1 -1
- package/dist/puzzles/cube2/validation.js +15 -3
- package/dist/puzzles/cube3/index.d.ts +1 -0
- package/dist/puzzles/cube3/index.d.ts.map +1 -1
- package/dist/puzzles/cube3/index.js +4 -0
- package/dist/puzzles/cube3/solver/browser-loader.d.ts.map +1 -1
- package/dist/puzzles/cube3/solver/browser-loader.js +5 -29
- package/dist/puzzles/cube4/index.d.ts +1 -0
- package/dist/puzzles/cube4/index.d.ts.map +1 -1
- package/dist/puzzles/cube4/index.js +4 -0
- package/dist/puzzles/cube5x5/index.d.ts +1 -0
- package/dist/puzzles/cube5x5/index.d.ts.map +1 -1
- package/dist/puzzles/cube5x5/index.js +4 -0
- package/dist/puzzles/cube6x6/index.d.ts +1 -0
- package/dist/puzzles/cube6x6/index.d.ts.map +1 -1
- package/dist/puzzles/cube6x6/index.js +4 -0
- package/dist/puzzles/cube7x7/index.d.ts +1 -0
- package/dist/puzzles/cube7x7/index.d.ts.map +1 -1
- package/dist/puzzles/cube7x7/index.js +4 -0
- package/dist/puzzles/megaminx/index.d.ts +1 -0
- package/dist/puzzles/megaminx/index.d.ts.map +1 -1
- package/dist/puzzles/megaminx/index.js +4 -0
- package/dist/puzzles/megaminx/scrambler.js +2 -2
- package/dist/puzzles/pyraminx/index.d.ts +3 -1
- package/dist/puzzles/pyraminx/index.d.ts.map +1 -1
- package/dist/puzzles/pyraminx/index.js +6 -1
- package/dist/puzzles/pyraminx/scrambler.d.ts.map +1 -1
- package/dist/puzzles/pyraminx/scrambler.js +26 -22
- package/dist/puzzles/pyraminx/solver/browser-loader.d.ts +14 -0
- package/dist/puzzles/pyraminx/solver/browser-loader.d.ts.map +1 -0
- package/dist/puzzles/pyraminx/solver/browser-loader.js +63 -0
- package/dist/puzzles/pyraminx/solver/generated/centers-table.bin +0 -0
- package/dist/puzzles/pyraminx/solver/generated/manifest.json +26 -0
- package/dist/puzzles/pyraminx/solver/generated/orientation-table.bin +0 -0
- package/dist/puzzles/pyraminx/solver/generated/permutation-table.bin +0 -0
- package/dist/puzzles/pyraminx/solver/generated/policy.bin +0 -0
- package/dist/puzzles/pyraminx/solver/generated-assets.d.ts +7 -0
- package/dist/puzzles/pyraminx/solver/generated-assets.d.ts.map +1 -0
- package/dist/puzzles/pyraminx/solver/generated-assets.js +24 -0
- package/dist/puzzles/pyraminx/solver/index.d.ts +5 -0
- package/dist/puzzles/pyraminx/solver/index.d.ts.map +1 -0
- package/dist/puzzles/pyraminx/solver/index.js +4 -0
- package/dist/puzzles/pyraminx/solver/node-loader.d.ts +7 -0
- package/dist/puzzles/pyraminx/solver/node-loader.d.ts.map +1 -0
- package/dist/puzzles/pyraminx/solver/node-loader.js +73 -0
- package/dist/puzzles/pyraminx/solver/search.d.ts +10 -0
- package/dist/puzzles/pyraminx/solver/search.d.ts.map +1 -0
- package/dist/puzzles/pyraminx/solver/search.js +82 -0
- package/dist/puzzles/pyraminx/solver/table-metadata.d.ts +20 -0
- package/dist/puzzles/pyraminx/solver/table-metadata.d.ts.map +1 -0
- package/dist/puzzles/pyraminx/solver/table-metadata.js +19 -0
- package/dist/puzzles/pyraminx/solver/tables.d.ts +43 -0
- package/dist/puzzles/pyraminx/solver/tables.d.ts.map +1 -0
- package/dist/puzzles/pyraminx/solver/tables.js +300 -0
- package/dist/puzzles/shared/compressed-fetch.d.ts +12 -0
- package/dist/puzzles/shared/compressed-fetch.d.ts.map +1 -0
- package/dist/puzzles/shared/compressed-fetch.js +68 -0
- package/dist/puzzles/skewb/index.d.ts +1 -0
- package/dist/puzzles/skewb/index.d.ts.map +1 -1
- package/dist/puzzles/skewb/index.js +4 -0
- package/dist/puzzles/skewb/scrambler.js +2 -2
- package/dist-cjs/core/api.js +46 -6
- package/dist-cjs/puzzles/clock/index.js +13 -4
- package/dist-cjs/puzzles/clock/scrambler.js +2 -2
- package/dist-cjs/puzzles/cube2/index.js +13 -4
- package/dist-cjs/puzzles/cube2/scrambler.js +2 -4
- package/dist-cjs/puzzles/cube2/solver/browser-loader.js +4 -12
- package/dist-cjs/puzzles/cube2/solver/index.js +3 -1
- package/dist-cjs/puzzles/cube2/solver/search.js +28 -0
- package/dist-cjs/puzzles/cube2/validation.js +14 -2
- package/dist-cjs/puzzles/cube3/index.js +13 -4
- package/dist-cjs/puzzles/cube3/solver/browser-loader.js +5 -29
- package/dist-cjs/puzzles/cube4/index.js +13 -4
- package/dist-cjs/puzzles/cube5x5/index.js +13 -4
- package/dist-cjs/puzzles/cube6x6/index.js +13 -4
- package/dist-cjs/puzzles/cube7x7/index.js +13 -4
- package/dist-cjs/puzzles/megaminx/index.js +13 -4
- package/dist-cjs/puzzles/megaminx/scrambler.js +2 -2
- package/dist-cjs/puzzles/pyraminx/index.js +50 -6
- package/dist-cjs/puzzles/pyraminx/scrambler.js +25 -21
- package/dist-cjs/puzzles/pyraminx/solver/browser-loader.js +68 -0
- package/dist-cjs/puzzles/pyraminx/solver/generated/centers-table.bin +0 -0
- package/dist-cjs/puzzles/pyraminx/solver/generated/manifest.json +26 -0
- package/dist-cjs/puzzles/pyraminx/solver/generated/orientation-table.bin +0 -0
- package/dist-cjs/puzzles/pyraminx/solver/generated/permutation-table.bin +0 -0
- package/dist-cjs/puzzles/pyraminx/solver/generated/policy.bin +0 -0
- package/dist-cjs/puzzles/pyraminx/solver/generated-assets.js +60 -0
- package/dist-cjs/puzzles/pyraminx/solver/index.js +23 -0
- package/dist-cjs/puzzles/pyraminx/solver/node-loader.js +79 -0
- package/dist-cjs/puzzles/pyraminx/solver/search.js +88 -0
- package/dist-cjs/puzzles/pyraminx/solver/table-metadata.js +22 -0
- package/dist-cjs/puzzles/pyraminx/solver/tables.js +327 -0
- package/dist-cjs/puzzles/shared/compressed-fetch.js +71 -0
- package/dist-cjs/puzzles/skewb/index.js +13 -4
- package/dist-cjs/puzzles/skewb/scrambler.js +2 -2
- package/package.json +17 -6
- package/dist/puzzles/pyraminx/solver.d.ts +0 -10
- package/dist/puzzles/pyraminx/solver.d.ts.map +0 -1
- package/dist/puzzles/pyraminx/solver.js +0 -160
- package/dist-cjs/puzzles/pyraminx/solver.js +0 -170
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.loadGeneratedPyraminxSolverTables = loadGeneratedPyraminxSolverTables;
|
|
37
|
+
const browser_loader_js_1 = require("./browser-loader.js");
|
|
38
|
+
let cachedTables;
|
|
39
|
+
function isBrowser() {
|
|
40
|
+
return typeof self !== "undefined";
|
|
41
|
+
}
|
|
42
|
+
async function loadGeneratedPyraminxSolverTables() {
|
|
43
|
+
if (isBrowser()) {
|
|
44
|
+
const browserTables = (0, browser_loader_js_1.getCachedBrowserTables)();
|
|
45
|
+
if (browserTables)
|
|
46
|
+
return browserTables;
|
|
47
|
+
throw new Error("Pyraminx solver tables are not loaded. " +
|
|
48
|
+
"In browser environments, call `initializeScrambler(baseUrl)` " +
|
|
49
|
+
"before generating scrambles.");
|
|
50
|
+
}
|
|
51
|
+
cachedTables ??= await loadNodeTables();
|
|
52
|
+
return cachedTables;
|
|
53
|
+
}
|
|
54
|
+
async function loadNodeTables() {
|
|
55
|
+
const loader = await Promise.resolve().then(() => __importStar(require(
|
|
56
|
+
/* webpackIgnore: true */
|
|
57
|
+
/* @vite-ignore */
|
|
58
|
+
"./node-loader.js")));
|
|
59
|
+
return loader.readGeneratedPyraminxSolverTables();
|
|
60
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.loadGeneratedPyraminxSolverTables = exports.PYRAMINX_SOLVER_TABLE_METADATA = void 0;
|
|
18
|
+
__exportStar(require("./tables.js"), exports);
|
|
19
|
+
__exportStar(require("./search.js"), exports);
|
|
20
|
+
var table_metadata_js_1 = require("./table-metadata.js");
|
|
21
|
+
Object.defineProperty(exports, "PYRAMINX_SOLVER_TABLE_METADATA", { enumerable: true, get: function () { return table_metadata_js_1.PYRAMINX_SOLVER_TABLE_METADATA; } });
|
|
22
|
+
var generated_assets_js_1 = require("./generated-assets.js");
|
|
23
|
+
Object.defineProperty(exports, "loadGeneratedPyraminxSolverTables", { enumerable: true, get: function () { return generated_assets_js_1.loadGeneratedPyraminxSolverTables; } });
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.readGeneratedPyraminxSolverTables = readGeneratedPyraminxSolverTables;
|
|
7
|
+
const node_fs_1 = require("node:fs");
|
|
8
|
+
const node_module_1 = require("node:module");
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const table_metadata_js_1 = require("./table-metadata.js");
|
|
11
|
+
const GENERATED_DIRECTORY = "puzzles/pyraminx/solver/generated";
|
|
12
|
+
function readGeneratedPyraminxSolverTables() {
|
|
13
|
+
const assetDirectory = resolveGeneratedAssetDirectory();
|
|
14
|
+
const manifest = JSON.parse((0, node_fs_1.readFileSync)(node_path_1.default.join(assetDirectory, "manifest.json"), "utf8"));
|
|
15
|
+
if (manifest.version !== 1)
|
|
16
|
+
throw new Error(`Unsupported pyraminx solver table manifest version: ${manifest.version}`);
|
|
17
|
+
const centersTable = readUint8Table(assetDirectory, manifest.files.centersTable, table_metadata_js_1.PYRAMINX_SOLVER_TABLE_METADATA.centersTable.checksum);
|
|
18
|
+
const permutationTable = readUint16Table(assetDirectory, manifest.files.permutationTable, table_metadata_js_1.PYRAMINX_SOLVER_TABLE_METADATA.permutationTable.checksum);
|
|
19
|
+
const orientationTable = readUint8Table(assetDirectory, manifest.files.orientationTable, table_metadata_js_1.PYRAMINX_SOLVER_TABLE_METADATA.orientationTable.checksum);
|
|
20
|
+
const policyTable = readUint8Table(assetDirectory, manifest.files.policyTable, table_metadata_js_1.PYRAMINX_SOLVER_TABLE_METADATA.policyTable.checksum);
|
|
21
|
+
return { centersTable, permutationTable, orientationTable, policyTable };
|
|
22
|
+
}
|
|
23
|
+
function readUint8Table(assetDirectory, entry, expectedChecksum) {
|
|
24
|
+
const buffer = (0, node_fs_1.readFileSync)(node_path_1.default.join(assetDirectory, entry.file));
|
|
25
|
+
if (buffer.length !== entry.entries)
|
|
26
|
+
throw new Error(`Invalid byte size for ${entry.file}`);
|
|
27
|
+
const values = new Uint8Array(buffer);
|
|
28
|
+
assertChecksum(entry.file, checksum(values), expectedChecksum);
|
|
29
|
+
return values;
|
|
30
|
+
}
|
|
31
|
+
function readUint16Table(assetDirectory, entry, expectedChecksum) {
|
|
32
|
+
const buffer = (0, node_fs_1.readFileSync)(node_path_1.default.join(assetDirectory, entry.file));
|
|
33
|
+
if (buffer.length !== entry.entries * 2)
|
|
34
|
+
throw new Error(`Invalid byte size for ${entry.file}`);
|
|
35
|
+
const values = new Uint16Array(entry.entries);
|
|
36
|
+
for (let i = 0; i < values.length; i++)
|
|
37
|
+
values[i] = buffer.readUInt16LE(i * 2);
|
|
38
|
+
assertChecksum(entry.file, checksum(values), expectedChecksum);
|
|
39
|
+
return values;
|
|
40
|
+
}
|
|
41
|
+
function resolveGeneratedAssetDirectory() {
|
|
42
|
+
const envDirectory = process.env.CUBINGCENTER_SCRAMBLER_TABLES_DIR;
|
|
43
|
+
if (envDirectory && (0, node_fs_1.existsSync)(node_path_1.default.join(envDirectory, "manifest.json")))
|
|
44
|
+
return envDirectory;
|
|
45
|
+
for (const candidate of getAssetDirectoryCandidates()) {
|
|
46
|
+
if ((0, node_fs_1.existsSync)(node_path_1.default.join(candidate, "manifest.json")))
|
|
47
|
+
return candidate;
|
|
48
|
+
}
|
|
49
|
+
throw new Error("Unable to find generated pyraminx solver tables. Run pnpm pyraminx:tables.");
|
|
50
|
+
}
|
|
51
|
+
function getAssetDirectoryCandidates() {
|
|
52
|
+
const cwd = process.cwd();
|
|
53
|
+
const candidates = [
|
|
54
|
+
node_path_1.default.join(cwd, "src", GENERATED_DIRECTORY),
|
|
55
|
+
node_path_1.default.join(cwd, "dist", GENERATED_DIRECTORY),
|
|
56
|
+
node_path_1.default.join(cwd, "dist-cjs", GENERATED_DIRECTORY)
|
|
57
|
+
];
|
|
58
|
+
try {
|
|
59
|
+
const requireFromCwd = (0, node_module_1.createRequire)(node_path_1.default.join(cwd, "package.json"));
|
|
60
|
+
const packageEntry = requireFromCwd.resolve("@cubingcenter/scrambler");
|
|
61
|
+
candidates.push(node_path_1.default.join(node_path_1.default.dirname(packageEntry), GENERATED_DIRECTORY));
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// source-tree
|
|
65
|
+
}
|
|
66
|
+
return candidates;
|
|
67
|
+
}
|
|
68
|
+
function assertChecksum(file, actual, expected) {
|
|
69
|
+
if (actual !== expected)
|
|
70
|
+
throw new Error(`Checksum mismatch for ${file}: expected ${expected}, received ${actual}.`);
|
|
71
|
+
}
|
|
72
|
+
function checksum(values) {
|
|
73
|
+
let hash = 0x811c9dc5;
|
|
74
|
+
for (let i = 0; i < values.length; i++) {
|
|
75
|
+
hash ^= values[i];
|
|
76
|
+
hash = Math.imul(hash, 0x01000193);
|
|
77
|
+
}
|
|
78
|
+
return hash >>> 0;
|
|
79
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.solvePyraminx = solvePyraminx;
|
|
4
|
+
exports.solvePyraminxWithInfo = solvePyraminxWithInfo;
|
|
5
|
+
exports.solvePyraminxSync = solvePyraminxSync;
|
|
6
|
+
exports.verifyPyraminxScramble = verifyPyraminxScramble;
|
|
7
|
+
const tables_js_1 = require("./tables.js");
|
|
8
|
+
async function solvePyraminx(state) {
|
|
9
|
+
await (0, tables_js_1.ensureTablesLoaded)();
|
|
10
|
+
return solveWithTables(state).moves;
|
|
11
|
+
}
|
|
12
|
+
async function solvePyraminxWithInfo(state) {
|
|
13
|
+
await (0, tables_js_1.ensureTablesLoaded)();
|
|
14
|
+
return solveWithTables(state);
|
|
15
|
+
}
|
|
16
|
+
function solveWithTables(state) {
|
|
17
|
+
const centersTable = tables_js_1.CENTERS_TABLE;
|
|
18
|
+
const permTable = tables_js_1.PERMUTATION_TABLE;
|
|
19
|
+
const orientTable = tables_js_1.ORIENTATION_TABLE;
|
|
20
|
+
const policy = tables_js_1.POLICY_TABLE;
|
|
21
|
+
let c = (0, tables_js_1.getCentersIndex)(state.centers);
|
|
22
|
+
let p = (0, tables_js_1.getPermutationCompactIndex)(state.edgesPerm);
|
|
23
|
+
let o = (0, tables_js_1.getOrientationIndex)(state.edgesOrient);
|
|
24
|
+
let stateIndex = c + 81 * (p + tables_js_1.PERMUTATION_COUNT * o);
|
|
25
|
+
if (stateIndex === 0)
|
|
26
|
+
return { moves: [], depth: 0 };
|
|
27
|
+
const moves = [];
|
|
28
|
+
while (stateIndex !== 0) {
|
|
29
|
+
const m = (0, tables_js_1.getPolicyMoveFromPacked)(policy, stateIndex);
|
|
30
|
+
moves.push(tables_js_1.PYRAMINX_MOVES[m]);
|
|
31
|
+
c = centersTable[c * tables_js_1.MOVE_COUNT + m];
|
|
32
|
+
p = permTable[p * tables_js_1.MOVE_COUNT + m];
|
|
33
|
+
o = orientTable[o * tables_js_1.MOVE_COUNT + m];
|
|
34
|
+
stateIndex = c + 81 * (p + tables_js_1.PERMUTATION_COUNT * o);
|
|
35
|
+
}
|
|
36
|
+
return { moves, depth: moves.length };
|
|
37
|
+
}
|
|
38
|
+
// Synchronous variant for scripts that have already ensured tables are loaded
|
|
39
|
+
function solvePyraminxSync(state) {
|
|
40
|
+
if (!tables_js_1.POLICY_TABLE)
|
|
41
|
+
throw new Error("Pyraminx tables not loaded");
|
|
42
|
+
return solveWithTables(state).moves;
|
|
43
|
+
}
|
|
44
|
+
const MOVE_INDEX = {
|
|
45
|
+
"U": 0, "U'": 1,
|
|
46
|
+
"L": 2, "L'": 3,
|
|
47
|
+
"R": 4, "R'": 5,
|
|
48
|
+
"B": 6, "B'": 7
|
|
49
|
+
};
|
|
50
|
+
const FACE_TIP = { U: 0, L: 1, R: 2, B: 3 };
|
|
51
|
+
const TIP_MOVE = { u: 0, l: 1, r: 2, b: 3 };
|
|
52
|
+
// Applies a full scramble (core + tips) over compact coordinate indexes and
|
|
53
|
+
// compares against the target state. No full state clones; runs in O(moves).
|
|
54
|
+
function verifyPyraminxScramble(state, finalMoves) {
|
|
55
|
+
const centersTable = tables_js_1.CENTERS_TABLE;
|
|
56
|
+
const permTable = tables_js_1.PERMUTATION_TABLE;
|
|
57
|
+
const orientTable = tables_js_1.ORIENTATION_TABLE;
|
|
58
|
+
if (!centersTable || !permTable || !orientTable) {
|
|
59
|
+
throw new Error("Pyraminx solver tables not loaded. Call ensureTablesLoaded() first.");
|
|
60
|
+
}
|
|
61
|
+
let c = 0;
|
|
62
|
+
let p = 0;
|
|
63
|
+
let o = 0;
|
|
64
|
+
const tips = [0, 0, 0, 0];
|
|
65
|
+
for (const move of finalMoves) {
|
|
66
|
+
const isInverse = move.endsWith("'");
|
|
67
|
+
const base = isInverse ? move.slice(0, -1) : move;
|
|
68
|
+
const faceTip = FACE_TIP[base];
|
|
69
|
+
if (faceTip !== undefined) {
|
|
70
|
+
const m = MOVE_INDEX[move];
|
|
71
|
+
c = centersTable[c * tables_js_1.MOVE_COUNT + m];
|
|
72
|
+
p = permTable[p * tables_js_1.MOVE_COUNT + m];
|
|
73
|
+
o = orientTable[o * tables_js_1.MOVE_COUNT + m];
|
|
74
|
+
tips[faceTip] = (tips[faceTip] + (isInverse ? 2 : 1)) % 3;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
const tipIndex = TIP_MOVE[base];
|
|
78
|
+
tips[tipIndex] = (tips[tipIndex] + (isInverse ? 2 : 1)) % 3;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return (c === (0, tables_js_1.getCentersIndex)(state.centers) &&
|
|
82
|
+
p === (0, tables_js_1.getPermutationCompactIndex)(state.edgesPerm) &&
|
|
83
|
+
o === (0, tables_js_1.getOrientationIndex)(state.edgesOrient) &&
|
|
84
|
+
tips[0] === state.tips[0] &&
|
|
85
|
+
tips[1] === state.tips[1] &&
|
|
86
|
+
tips[2] === state.tips[2] &&
|
|
87
|
+
tips[3] === state.tips[3]);
|
|
88
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PYRAMINX_SOLVER_TABLE_METADATA = void 0;
|
|
4
|
+
exports.PYRAMINX_SOLVER_TABLE_METADATA = {
|
|
5
|
+
centersTable: {
|
|
6
|
+
entries: 648,
|
|
7
|
+
checksum: 4058180805
|
|
8
|
+
},
|
|
9
|
+
permutationTable: {
|
|
10
|
+
entries: 2880,
|
|
11
|
+
checksum: 4161414597
|
|
12
|
+
},
|
|
13
|
+
orientationTable: {
|
|
14
|
+
entries: 256,
|
|
15
|
+
checksum: 3608176069
|
|
16
|
+
},
|
|
17
|
+
policyTable: {
|
|
18
|
+
entries: 349920,
|
|
19
|
+
checksum: 4047562730,
|
|
20
|
+
maxDepth: 11
|
|
21
|
+
}
|
|
22
|
+
};
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"use strict";
|
|
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;
|
|
4
|
+
exports.getPermutationIndex = getPermutationIndex;
|
|
5
|
+
exports.getPermutationFromIndex = getPermutationFromIndex;
|
|
6
|
+
exports.getOrientationIndex = getOrientationIndex;
|
|
7
|
+
exports.getOrientationFromIndex = getOrientationFromIndex;
|
|
8
|
+
exports.getCentersIndex = getCentersIndex;
|
|
9
|
+
exports.getCentersFromIndex = getCentersFromIndex;
|
|
10
|
+
exports.permutationParity = permutationParity;
|
|
11
|
+
exports.getPermutationCompactIndex = getPermutationCompactIndex;
|
|
12
|
+
exports.getPermutationCompactFromFull = getPermutationCompactFromFull;
|
|
13
|
+
exports.getPermutationFullFromCompact = getPermutationFullFromCompact;
|
|
14
|
+
exports.getStateIndex = getStateIndex;
|
|
15
|
+
exports.getPyraminxStateFromIndex = getPyraminxStateFromIndex;
|
|
16
|
+
exports.getPolicyMoveFromPacked = getPolicyMoveFromPacked;
|
|
17
|
+
exports.generateCentersTable = generateCentersTable;
|
|
18
|
+
exports.generatePermutationTable = generatePermutationTable;
|
|
19
|
+
exports.generateOrientationTable = generateOrientationTable;
|
|
20
|
+
exports.generatePolicyTable = generatePolicyTable;
|
|
21
|
+
exports.checksum = checksum;
|
|
22
|
+
exports.ensureTablesLoaded = ensureTablesLoaded;
|
|
23
|
+
exports.getPolicyMove = getPolicyMove;
|
|
24
|
+
exports.getCentersTransition = getCentersTransition;
|
|
25
|
+
exports.getPermutationTransition = getPermutationTransition;
|
|
26
|
+
exports.getOrientationTransition = getOrientationTransition;
|
|
27
|
+
exports.resetTablesForTests = resetTablesForTests;
|
|
28
|
+
const generated_assets_js_1 = require("./generated-assets.js");
|
|
29
|
+
const moves_js_1 = require("../moves.js");
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Coordinate encoding / decoding (kept from original solver.ts)
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
const FACT = [1, 1, 2, 6, 24, 120, 720];
|
|
34
|
+
function getPermutationIndex(perm) {
|
|
35
|
+
let index = 0;
|
|
36
|
+
const n = perm.length;
|
|
37
|
+
for (let i = 0; i < n; i++) {
|
|
38
|
+
let count = 0;
|
|
39
|
+
for (let j = i + 1; j < n; j++) {
|
|
40
|
+
if (perm[j] < perm[i])
|
|
41
|
+
count++;
|
|
42
|
+
}
|
|
43
|
+
index += count * FACT[n - 1 - i];
|
|
44
|
+
}
|
|
45
|
+
return index;
|
|
46
|
+
}
|
|
47
|
+
function getPermutationFromIndex(index, n) {
|
|
48
|
+
const perm = Array(n).fill(0);
|
|
49
|
+
const temp = Array.from({ length: n }, (_, i) => i);
|
|
50
|
+
let val = index;
|
|
51
|
+
for (let i = 0; i < n; i++) {
|
|
52
|
+
const f = FACT[n - 1 - i];
|
|
53
|
+
const choice = Math.floor(val / f);
|
|
54
|
+
val %= f;
|
|
55
|
+
perm[i] = temp[choice];
|
|
56
|
+
temp.splice(choice, 1);
|
|
57
|
+
}
|
|
58
|
+
return perm;
|
|
59
|
+
}
|
|
60
|
+
function getOrientationIndex(orient) {
|
|
61
|
+
let index = 0;
|
|
62
|
+
for (let i = 0; i < 5; i++) {
|
|
63
|
+
index = (index << 1) | orient[i];
|
|
64
|
+
}
|
|
65
|
+
return index;
|
|
66
|
+
}
|
|
67
|
+
function getOrientationFromIndex(index) {
|
|
68
|
+
const orient = Array(6).fill(0);
|
|
69
|
+
let temp = index;
|
|
70
|
+
let sum = 0;
|
|
71
|
+
for (let i = 4; i >= 0; i--) {
|
|
72
|
+
const bit = temp & 1;
|
|
73
|
+
orient[i] = bit;
|
|
74
|
+
sum += bit;
|
|
75
|
+
temp >>= 1;
|
|
76
|
+
}
|
|
77
|
+
orient[5] = sum % 2 === 0 ? 0 : 1;
|
|
78
|
+
return orient;
|
|
79
|
+
}
|
|
80
|
+
function getCentersIndex(centers) {
|
|
81
|
+
return centers[0] + 3 * centers[1] + 9 * centers[2] + 27 * centers[3];
|
|
82
|
+
}
|
|
83
|
+
function getCentersFromIndex(index) {
|
|
84
|
+
let temp = index;
|
|
85
|
+
const centers = Array(4).fill(0);
|
|
86
|
+
for (let i = 0; i < 4; i++) {
|
|
87
|
+
centers[i] = temp % 3;
|
|
88
|
+
temp = Math.floor(temp / 3);
|
|
89
|
+
}
|
|
90
|
+
return centers;
|
|
91
|
+
}
|
|
92
|
+
function permutationParity(perm) {
|
|
93
|
+
let inv = 0;
|
|
94
|
+
for (let i = 0; i < perm.length; i++)
|
|
95
|
+
for (let j = i + 1; j < perm.length; j++)
|
|
96
|
+
if (perm[i] > perm[j])
|
|
97
|
+
inv++;
|
|
98
|
+
return inv % 2;
|
|
99
|
+
}
|
|
100
|
+
// Compact mapping: only even permutations (360) are reachable
|
|
101
|
+
exports.PERMUTATION_FULL_COUNT = 720;
|
|
102
|
+
exports.PERMUTATION_COUNT = 360;
|
|
103
|
+
exports.PERM_720_TO_360 = new Int16Array(exports.PERMUTATION_FULL_COUNT).fill(-1);
|
|
104
|
+
exports.PERM_360_TO_720 = new Uint16Array(exports.PERMUTATION_COUNT);
|
|
105
|
+
(function initPermMapping() {
|
|
106
|
+
let compact = 0;
|
|
107
|
+
for (let full = 0; full < exports.PERMUTATION_FULL_COUNT; full++) {
|
|
108
|
+
const perm = getPermutationFromIndex(full, 6);
|
|
109
|
+
if (permutationParity(perm) === 0) {
|
|
110
|
+
exports.PERM_720_TO_360[full] = compact;
|
|
111
|
+
exports.PERM_360_TO_720[compact] = full;
|
|
112
|
+
compact++;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
})();
|
|
116
|
+
function getPermutationCompactIndex(perm) {
|
|
117
|
+
const full = getPermutationIndex(perm);
|
|
118
|
+
const compact = exports.PERM_720_TO_360[full];
|
|
119
|
+
if (compact === -1)
|
|
120
|
+
throw new Error("Unreachable pyraminx permutation (odd parity)");
|
|
121
|
+
return compact;
|
|
122
|
+
}
|
|
123
|
+
function getPermutationCompactFromFull(full) {
|
|
124
|
+
const c = exports.PERM_720_TO_360[full];
|
|
125
|
+
if (c === -1)
|
|
126
|
+
throw new Error(`Full perm index ${full} is odd parity`);
|
|
127
|
+
return c;
|
|
128
|
+
}
|
|
129
|
+
function getPermutationFullFromCompact(compact) {
|
|
130
|
+
return exports.PERM_360_TO_720[compact];
|
|
131
|
+
}
|
|
132
|
+
function getStateIndex(state) {
|
|
133
|
+
const c = getCentersIndex(state.centers);
|
|
134
|
+
const p = getPermutationCompactIndex(state.edgesPerm);
|
|
135
|
+
const o = getOrientationIndex(state.edgesOrient);
|
|
136
|
+
return c + 81 * (p + exports.PERMUTATION_COUNT * o);
|
|
137
|
+
}
|
|
138
|
+
function getPyraminxStateFromIndex(index) {
|
|
139
|
+
let val = index;
|
|
140
|
+
const centersIdx = val % 81;
|
|
141
|
+
val = Math.floor(val / 81);
|
|
142
|
+
const permCompact = val % exports.PERMUTATION_COUNT;
|
|
143
|
+
const orientIdx = Math.floor(val / exports.PERMUTATION_COUNT);
|
|
144
|
+
const permFull = getPermutationFullFromCompact(permCompact);
|
|
145
|
+
return {
|
|
146
|
+
centers: getCentersFromIndex(centersIdx),
|
|
147
|
+
edgesPerm: getPermutationFromIndex(permFull, 6),
|
|
148
|
+
edgesOrient: getOrientationFromIndex(orientIdx),
|
|
149
|
+
tips: [0, 0, 0, 0]
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
// Table constants
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
exports.CENTERS_COUNT = 81;
|
|
156
|
+
exports.ORIENTATION_COUNT = 32;
|
|
157
|
+
exports.MOVE_COUNT = 8;
|
|
158
|
+
exports.STATE_COUNT = 81 * exports.PERMUTATION_COUNT * exports.ORIENTATION_COUNT; // 933_120
|
|
159
|
+
exports.POLICY_PACKED_ENTRIES = Math.ceil((exports.STATE_COUNT * 3) / 8); // 349_920
|
|
160
|
+
// 3-bit little-endian unpacking of the packed policy table.
|
|
161
|
+
function getPolicyMoveFromPacked(packed, stateIndex) {
|
|
162
|
+
const bits = stateIndex * 3;
|
|
163
|
+
const byteIndex = bits >> 3;
|
|
164
|
+
const offset = bits & 7;
|
|
165
|
+
const word = (packed[byteIndex] | ((packed[byteIndex + 1] ?? 0) << 8)) >> offset;
|
|
166
|
+
return word & 0x07;
|
|
167
|
+
}
|
|
168
|
+
exports.PYRAMINX_MOVES = [
|
|
169
|
+
"U", "U'", "L", "L'", "R", "R'", "B", "B'"
|
|
170
|
+
];
|
|
171
|
+
// ---------------------------------------------------------------------------
|
|
172
|
+
// Table generation (used by generate script)
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
function generateCentersTable() {
|
|
175
|
+
const table = new Uint8Array(exports.CENTERS_COUNT * exports.MOVE_COUNT);
|
|
176
|
+
for (let c = 0; c < exports.CENTERS_COUNT; c++) {
|
|
177
|
+
const centers = getCentersFromIndex(c);
|
|
178
|
+
for (let m = 0; m < exports.MOVE_COUNT; m++) {
|
|
179
|
+
const state = {
|
|
180
|
+
centers: [...centers],
|
|
181
|
+
edgesPerm: [0, 1, 2, 3, 4, 5],
|
|
182
|
+
edgesOrient: [0, 0, 0, 0, 0, 0],
|
|
183
|
+
tips: [0, 0, 0, 0]
|
|
184
|
+
};
|
|
185
|
+
const next = (0, moves_js_1.applyPyraminxMove)(state, exports.PYRAMINX_MOVES[m]);
|
|
186
|
+
table[c * exports.MOVE_COUNT + m] = getCentersIndex(next.centers);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return table;
|
|
190
|
+
}
|
|
191
|
+
function generatePermutationTable() {
|
|
192
|
+
const table = new Uint16Array(exports.PERMUTATION_COUNT * exports.MOVE_COUNT);
|
|
193
|
+
for (let pCompact = 0; pCompact < exports.PERMUTATION_COUNT; pCompact++) {
|
|
194
|
+
const full = getPermutationFullFromCompact(pCompact);
|
|
195
|
+
const perm = getPermutationFromIndex(full, 6);
|
|
196
|
+
for (let m = 0; m < exports.MOVE_COUNT; m++) {
|
|
197
|
+
const state = {
|
|
198
|
+
centers: [0, 0, 0, 0],
|
|
199
|
+
edgesPerm: [...perm],
|
|
200
|
+
edgesOrient: [0, 0, 0, 0, 0, 0],
|
|
201
|
+
tips: [0, 0, 0, 0]
|
|
202
|
+
};
|
|
203
|
+
const next = (0, moves_js_1.applyPyraminxMove)(state, exports.PYRAMINX_MOVES[m]);
|
|
204
|
+
const nextFull = getPermutationIndex(next.edgesPerm);
|
|
205
|
+
table[pCompact * exports.MOVE_COUNT + m] = getPermutationCompactFromFull(nextFull);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return table;
|
|
209
|
+
}
|
|
210
|
+
function generateOrientationTable() {
|
|
211
|
+
const table = new Uint8Array(exports.ORIENTATION_COUNT * exports.MOVE_COUNT);
|
|
212
|
+
for (let o = 0; o < exports.ORIENTATION_COUNT; o++) {
|
|
213
|
+
const orient = getOrientationFromIndex(o);
|
|
214
|
+
for (let m = 0; m < exports.MOVE_COUNT; m++) {
|
|
215
|
+
const state = {
|
|
216
|
+
centers: [0, 0, 0, 0],
|
|
217
|
+
edgesPerm: [0, 1, 2, 3, 4, 5],
|
|
218
|
+
edgesOrient: [...orient],
|
|
219
|
+
tips: [0, 0, 0, 0]
|
|
220
|
+
};
|
|
221
|
+
const next = (0, moves_js_1.applyPyraminxMove)(state, exports.PYRAMINX_MOVES[m]);
|
|
222
|
+
table[o * exports.MOVE_COUNT + m] = getOrientationIndex(next.edgesOrient);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return table;
|
|
226
|
+
}
|
|
227
|
+
// BFS from solved stores, for each state, the inverse of the move that first
|
|
228
|
+
// discovered it: an optimal move toward solved. Packed 3 bits per state
|
|
229
|
+
// (8 moves; the solved entry is never read, the solve loop exits first).
|
|
230
|
+
function generatePolicyTable(centersTable, permTable, orientTable) {
|
|
231
|
+
const distances = new Uint8Array(exports.STATE_COUNT).fill(255);
|
|
232
|
+
const policy = new Uint8Array(exports.STATE_COUNT);
|
|
233
|
+
const solvedIdx = 0; // c=0,p=0,o=0 => 0
|
|
234
|
+
distances[solvedIdx] = 0;
|
|
235
|
+
const queue = new Uint32Array(exports.STATE_COUNT);
|
|
236
|
+
let read = 0;
|
|
237
|
+
let write = 1;
|
|
238
|
+
queue[0] = solvedIdx;
|
|
239
|
+
let maxDepth = 0;
|
|
240
|
+
while (read < write) {
|
|
241
|
+
const curr = queue[read++];
|
|
242
|
+
const dist = distances[curr];
|
|
243
|
+
const c = curr % 81;
|
|
244
|
+
const rem = Math.floor(curr / 81);
|
|
245
|
+
const p = rem % exports.PERMUTATION_COUNT;
|
|
246
|
+
const o = Math.floor(rem / exports.PERMUTATION_COUNT);
|
|
247
|
+
const nextDist = dist + 1;
|
|
248
|
+
for (let m = 0; m < exports.MOVE_COUNT; m++) {
|
|
249
|
+
const nc = centersTable[c * exports.MOVE_COUNT + m];
|
|
250
|
+
const np = permTable[p * exports.MOVE_COUNT + m];
|
|
251
|
+
const no = orientTable[o * exports.MOVE_COUNT + m];
|
|
252
|
+
const next = nc + 81 * (np + exports.PERMUTATION_COUNT * no);
|
|
253
|
+
if (distances[next] === 255) {
|
|
254
|
+
distances[next] = nextDist;
|
|
255
|
+
policy[next] = m ^ 1; // inverse move of the discovery edge
|
|
256
|
+
if (nextDist > maxDepth)
|
|
257
|
+
maxDepth = nextDist;
|
|
258
|
+
queue[write++] = next;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
const packed = new Uint8Array(exports.POLICY_PACKED_ENTRIES);
|
|
263
|
+
for (let i = 0; i < exports.STATE_COUNT; i++) {
|
|
264
|
+
const bits = i * 3;
|
|
265
|
+
const byteIndex = bits >> 3;
|
|
266
|
+
const offset = bits & 7;
|
|
267
|
+
const value = policy[i] & 0x07;
|
|
268
|
+
packed[byteIndex] |= (value << offset) & 0xff;
|
|
269
|
+
if (offset > 5) {
|
|
270
|
+
packed[byteIndex + 1] |= value >> (8 - offset);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return { packed, maxDepth };
|
|
274
|
+
}
|
|
275
|
+
function checksum(values) {
|
|
276
|
+
let hash = 0x811c9dc5;
|
|
277
|
+
for (let i = 0; i < values.length; i++) {
|
|
278
|
+
hash ^= values[i];
|
|
279
|
+
hash = Math.imul(hash, 0x01000193);
|
|
280
|
+
}
|
|
281
|
+
return hash >>> 0;
|
|
282
|
+
}
|
|
283
|
+
// ---------------------------------------------------------------------------
|
|
284
|
+
// Runtime table loading (mirrors cube2/cube3)
|
|
285
|
+
// ---------------------------------------------------------------------------
|
|
286
|
+
let tablesLoaded = false;
|
|
287
|
+
exports.CENTERS_TABLE = null;
|
|
288
|
+
exports.PERMUTATION_TABLE = null;
|
|
289
|
+
exports.ORIENTATION_TABLE = null;
|
|
290
|
+
exports.POLICY_TABLE = null;
|
|
291
|
+
async function ensureTablesLoaded() {
|
|
292
|
+
if (tablesLoaded)
|
|
293
|
+
return;
|
|
294
|
+
const tables = await (0, generated_assets_js_1.loadGeneratedPyraminxSolverTables)();
|
|
295
|
+
exports.CENTERS_TABLE = tables.centersTable;
|
|
296
|
+
exports.PERMUTATION_TABLE = tables.permutationTable;
|
|
297
|
+
exports.ORIENTATION_TABLE = tables.orientationTable;
|
|
298
|
+
exports.POLICY_TABLE = tables.policyTable;
|
|
299
|
+
tablesLoaded = true;
|
|
300
|
+
}
|
|
301
|
+
function getPolicyMove(stateIndex) {
|
|
302
|
+
if (!exports.POLICY_TABLE)
|
|
303
|
+
throw new Error("Pyraminx solver tables not loaded. Call ensureTablesLoaded() first.");
|
|
304
|
+
return getPolicyMoveFromPacked(exports.POLICY_TABLE, stateIndex);
|
|
305
|
+
}
|
|
306
|
+
function getCentersTransition(centerIdx, move) {
|
|
307
|
+
if (!exports.CENTERS_TABLE)
|
|
308
|
+
throw new Error("Pyraminx solver tables not loaded.");
|
|
309
|
+
return exports.CENTERS_TABLE[centerIdx * exports.MOVE_COUNT + move];
|
|
310
|
+
}
|
|
311
|
+
function getPermutationTransition(permIdx, move) {
|
|
312
|
+
if (!exports.PERMUTATION_TABLE)
|
|
313
|
+
throw new Error("Pyraminx solver tables not loaded.");
|
|
314
|
+
return exports.PERMUTATION_TABLE[permIdx * exports.MOVE_COUNT + move];
|
|
315
|
+
}
|
|
316
|
+
function getOrientationTransition(orientIdx, move) {
|
|
317
|
+
if (!exports.ORIENTATION_TABLE)
|
|
318
|
+
throw new Error("Pyraminx solver tables not loaded.");
|
|
319
|
+
return exports.ORIENTATION_TABLE[orientIdx * exports.MOVE_COUNT + move];
|
|
320
|
+
}
|
|
321
|
+
function resetTablesForTests() {
|
|
322
|
+
tablesLoaded = false;
|
|
323
|
+
exports.CENTERS_TABLE = null;
|
|
324
|
+
exports.PERMUTATION_TABLE = null;
|
|
325
|
+
exports.ORIENTATION_TABLE = null;
|
|
326
|
+
exports.POLICY_TABLE = null;
|
|
327
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Shared browser-only helper to fetch solver tables, preferring the
|
|
3
|
+
// precompressed .br/.gz variants (decompressed with DecompressionStream)
|
|
4
|
+
// and falling back to the raw .bin file.
|
|
5
|
+
//
|
|
6
|
+
// Node never uses this module: node-loader.ts reads the .bin files directly
|
|
7
|
+
// from the filesystem.
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.fetchTableBuffer = fetchTableBuffer;
|
|
10
|
+
// DecompressionStream only guarantees "gzip"/"deflate"; "br" support varies,
|
|
11
|
+
// so probe once and never attempt formats the runtime cannot decompress.
|
|
12
|
+
const SUPPORTED_FORMATS = (() => {
|
|
13
|
+
if (typeof DecompressionStream === "undefined")
|
|
14
|
+
return [];
|
|
15
|
+
const supported = [];
|
|
16
|
+
for (const [suffix, format] of [["br", "br"], ["gz", "gzip"]]) {
|
|
17
|
+
try {
|
|
18
|
+
new DecompressionStream(format);
|
|
19
|
+
supported.push([suffix, format]);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// format not supported by this runtime
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return supported;
|
|
26
|
+
})();
|
|
27
|
+
async function fetchTableBuffer(baseUrl, dir, file, options = {}) {
|
|
28
|
+
const urlFor = (name) => `${baseUrl}/${dir}/${name}`;
|
|
29
|
+
const cachedFetch = options.cacheName === undefined ? undefined : makeCachedFetch(options.cacheName);
|
|
30
|
+
if (options.compressed !== false && SUPPORTED_FORMATS.length > 0) {
|
|
31
|
+
for (const [suffix, format] of SUPPORTED_FORMATS) {
|
|
32
|
+
try {
|
|
33
|
+
const response = await (cachedFetch ? cachedFetch(urlFor(`${file}.${suffix}`)) : fetch(urlFor(`${file}.${suffix}`)));
|
|
34
|
+
if (response.ok && response.body) {
|
|
35
|
+
const stream = response.body.pipeThrough(new DecompressionStream(format));
|
|
36
|
+
return await new Response(stream).arrayBuffer();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// try the next candidate
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const response = await (cachedFetch ? cachedFetch(urlFor(file)) : fetch(urlFor(file)));
|
|
45
|
+
if (!response.ok) {
|
|
46
|
+
throw new Error(`Failed to load ${file}: ${response.status} ${response.statusText}`);
|
|
47
|
+
}
|
|
48
|
+
return response.arrayBuffer();
|
|
49
|
+
}
|
|
50
|
+
function makeCachedFetch(cacheName) {
|
|
51
|
+
return async (url) => {
|
|
52
|
+
if (typeof caches === "undefined") {
|
|
53
|
+
return fetch(url);
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const cache = await caches.open(cacheName);
|
|
57
|
+
const cached = await cache.match(url);
|
|
58
|
+
if (cached) {
|
|
59
|
+
return cached;
|
|
60
|
+
}
|
|
61
|
+
const response = await fetch(url);
|
|
62
|
+
if (response.ok) {
|
|
63
|
+
await cache.put(url, response.clone());
|
|
64
|
+
}
|
|
65
|
+
return response;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return fetch(url);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|