@cubingcenter/scrambler 0.4.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.
Files changed (118) hide show
  1. package/README.md +46 -3
  2. package/dist/core/api.d.ts.map +1 -1
  3. package/dist/core/api.js +13 -6
  4. package/dist/core/index.d.ts +1 -1
  5. package/dist/core/index.d.ts.map +1 -1
  6. package/dist/core/types.d.ts +7 -0
  7. package/dist/core/types.d.ts.map +1 -1
  8. package/dist/puzzles/clock/index.d.ts +1 -0
  9. package/dist/puzzles/clock/index.d.ts.map +1 -1
  10. package/dist/puzzles/clock/index.js +4 -0
  11. package/dist/puzzles/cube2/index.d.ts +1 -0
  12. package/dist/puzzles/cube2/index.d.ts.map +1 -1
  13. package/dist/puzzles/cube2/index.js +4 -0
  14. package/dist/puzzles/cube2/scrambler.d.ts.map +1 -1
  15. package/dist/puzzles/cube2/scrambler.js +4 -6
  16. package/dist/puzzles/cube2/solver/browser-loader.d.ts.map +1 -1
  17. package/dist/puzzles/cube2/solver/browser-loader.js +4 -12
  18. package/dist/puzzles/cube2/solver/index.d.ts +1 -1
  19. package/dist/puzzles/cube2/solver/index.d.ts.map +1 -1
  20. package/dist/puzzles/cube2/solver/index.js +1 -1
  21. package/dist/puzzles/cube2/solver/search.d.ts +2 -0
  22. package/dist/puzzles/cube2/solver/search.d.ts.map +1 -1
  23. package/dist/puzzles/cube2/solver/search.js +26 -0
  24. package/dist/puzzles/cube2/validation.d.ts.map +1 -1
  25. package/dist/puzzles/cube2/validation.js +15 -3
  26. package/dist/puzzles/cube3/index.d.ts +1 -0
  27. package/dist/puzzles/cube3/index.d.ts.map +1 -1
  28. package/dist/puzzles/cube3/index.js +4 -0
  29. package/dist/puzzles/cube3/solver/browser-loader.d.ts.map +1 -1
  30. package/dist/puzzles/cube3/solver/browser-loader.js +5 -29
  31. package/dist/puzzles/cube4/index.d.ts +1 -0
  32. package/dist/puzzles/cube4/index.d.ts.map +1 -1
  33. package/dist/puzzles/cube4/index.js +4 -0
  34. package/dist/puzzles/cube5x5/index.d.ts +1 -0
  35. package/dist/puzzles/cube5x5/index.d.ts.map +1 -1
  36. package/dist/puzzles/cube5x5/index.js +4 -0
  37. package/dist/puzzles/cube6x6/index.d.ts +1 -0
  38. package/dist/puzzles/cube6x6/index.d.ts.map +1 -1
  39. package/dist/puzzles/cube6x6/index.js +4 -0
  40. package/dist/puzzles/cube7x7/index.d.ts +1 -0
  41. package/dist/puzzles/cube7x7/index.d.ts.map +1 -1
  42. package/dist/puzzles/cube7x7/index.js +4 -0
  43. package/dist/puzzles/megaminx/index.d.ts +1 -0
  44. package/dist/puzzles/megaminx/index.d.ts.map +1 -1
  45. package/dist/puzzles/megaminx/index.js +4 -0
  46. package/dist/puzzles/pyraminx/index.d.ts +3 -1
  47. package/dist/puzzles/pyraminx/index.d.ts.map +1 -1
  48. package/dist/puzzles/pyraminx/index.js +6 -1
  49. package/dist/puzzles/pyraminx/scrambler.d.ts.map +1 -1
  50. package/dist/puzzles/pyraminx/scrambler.js +24 -20
  51. package/dist/puzzles/pyraminx/solver/browser-loader.d.ts +14 -0
  52. package/dist/puzzles/pyraminx/solver/browser-loader.d.ts.map +1 -0
  53. package/dist/puzzles/pyraminx/solver/browser-loader.js +63 -0
  54. package/dist/puzzles/pyraminx/solver/generated/centers-table.bin +0 -0
  55. package/dist/puzzles/pyraminx/solver/generated/manifest.json +26 -0
  56. package/dist/puzzles/pyraminx/solver/generated/orientation-table.bin +0 -0
  57. package/dist/puzzles/pyraminx/solver/generated/permutation-table.bin +0 -0
  58. package/dist/puzzles/pyraminx/solver/generated/policy.bin +0 -0
  59. package/dist/puzzles/pyraminx/solver/generated-assets.d.ts +7 -0
  60. package/dist/puzzles/pyraminx/solver/generated-assets.d.ts.map +1 -0
  61. package/dist/puzzles/pyraminx/solver/generated-assets.js +24 -0
  62. package/dist/puzzles/pyraminx/solver/index.d.ts +5 -0
  63. package/dist/puzzles/pyraminx/solver/index.d.ts.map +1 -0
  64. package/dist/puzzles/pyraminx/solver/index.js +4 -0
  65. package/dist/puzzles/pyraminx/solver/node-loader.d.ts +7 -0
  66. package/dist/puzzles/pyraminx/solver/node-loader.d.ts.map +1 -0
  67. package/dist/puzzles/pyraminx/solver/node-loader.js +73 -0
  68. package/dist/puzzles/pyraminx/solver/search.d.ts +10 -0
  69. package/dist/puzzles/pyraminx/solver/search.d.ts.map +1 -0
  70. package/dist/puzzles/pyraminx/solver/search.js +82 -0
  71. package/dist/puzzles/pyraminx/solver/table-metadata.d.ts +20 -0
  72. package/dist/puzzles/pyraminx/solver/table-metadata.d.ts.map +1 -0
  73. package/dist/puzzles/pyraminx/solver/table-metadata.js +19 -0
  74. package/dist/puzzles/pyraminx/solver/tables.d.ts +43 -0
  75. package/dist/puzzles/pyraminx/solver/tables.d.ts.map +1 -0
  76. package/dist/puzzles/pyraminx/solver/tables.js +300 -0
  77. package/dist/puzzles/shared/compressed-fetch.d.ts +12 -0
  78. package/dist/puzzles/shared/compressed-fetch.d.ts.map +1 -0
  79. package/dist/puzzles/shared/compressed-fetch.js +68 -0
  80. package/dist/puzzles/skewb/index.d.ts +1 -0
  81. package/dist/puzzles/skewb/index.d.ts.map +1 -1
  82. package/dist/puzzles/skewb/index.js +4 -0
  83. package/dist-cjs/core/api.js +46 -6
  84. package/dist-cjs/puzzles/clock/index.js +13 -4
  85. package/dist-cjs/puzzles/cube2/index.js +13 -4
  86. package/dist-cjs/puzzles/cube2/scrambler.js +2 -4
  87. package/dist-cjs/puzzles/cube2/solver/browser-loader.js +4 -12
  88. package/dist-cjs/puzzles/cube2/solver/index.js +3 -1
  89. package/dist-cjs/puzzles/cube2/solver/search.js +28 -0
  90. package/dist-cjs/puzzles/cube2/validation.js +14 -2
  91. package/dist-cjs/puzzles/cube3/index.js +13 -4
  92. package/dist-cjs/puzzles/cube3/solver/browser-loader.js +5 -29
  93. package/dist-cjs/puzzles/cube4/index.js +13 -4
  94. package/dist-cjs/puzzles/cube5x5/index.js +13 -4
  95. package/dist-cjs/puzzles/cube6x6/index.js +13 -4
  96. package/dist-cjs/puzzles/cube7x7/index.js +13 -4
  97. package/dist-cjs/puzzles/megaminx/index.js +13 -4
  98. package/dist-cjs/puzzles/pyraminx/index.js +50 -6
  99. package/dist-cjs/puzzles/pyraminx/scrambler.js +23 -19
  100. package/dist-cjs/puzzles/pyraminx/solver/browser-loader.js +68 -0
  101. package/dist-cjs/puzzles/pyraminx/solver/generated/centers-table.bin +0 -0
  102. package/dist-cjs/puzzles/pyraminx/solver/generated/manifest.json +26 -0
  103. package/dist-cjs/puzzles/pyraminx/solver/generated/orientation-table.bin +0 -0
  104. package/dist-cjs/puzzles/pyraminx/solver/generated/permutation-table.bin +0 -0
  105. package/dist-cjs/puzzles/pyraminx/solver/generated/policy.bin +0 -0
  106. package/dist-cjs/puzzles/pyraminx/solver/generated-assets.js +60 -0
  107. package/dist-cjs/puzzles/pyraminx/solver/index.js +23 -0
  108. package/dist-cjs/puzzles/pyraminx/solver/node-loader.js +79 -0
  109. package/dist-cjs/puzzles/pyraminx/solver/search.js +88 -0
  110. package/dist-cjs/puzzles/pyraminx/solver/table-metadata.js +22 -0
  111. package/dist-cjs/puzzles/pyraminx/solver/tables.js +327 -0
  112. package/dist-cjs/puzzles/shared/compressed-fetch.js +71 -0
  113. package/dist-cjs/puzzles/skewb/index.js +13 -4
  114. package/package.json +17 -6
  115. package/dist/puzzles/pyraminx/solver.d.ts +0 -10
  116. package/dist/puzzles/pyraminx/solver.d.ts.map +0 -1
  117. package/dist/puzzles/pyraminx/solver.js +0 -160
  118. package/dist-cjs/puzzles/pyraminx/solver.js +0 -170
@@ -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
+ }
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.skewbScrambleGenerator = exports.generateSkewbScramble = exports.getStateFromIndex = exports.getStateIndex = exports.solveSkewb = exports.toSkewbFacelets = exports.getSkewbFaceOrder = exports.formatSkewbFacelets = exports.createSolvedSkewbFacelets = exports.generateRandomSkewbState = exports.isOneMoveFromSolvedSkewbState = exports.isAllowedRandomSkewbState = exports.validateSkewbState = exports.permutationParity = exports.skewbStatesEqual = exports.isSolvedSkewbState = exports.createSolvedSkewbState = exports.cloneSkewbState = exports.parseSkewbMove = exports.parseSkewbAlgorithm = exports.invertSkewbMove = exports.invertSkewbAlgorithm = exports.formatSkewbAlgorithm = exports.applySkewbMove = exports.applySkewbAlgorithm = exports.SKEWB_FACES = exports.SKEWB_MOVES = void 0;
3
+ exports.initializeScrambler = exports.getSupportedEvents = exports.getEventInfo = exports.generateScrambles = exports.generateScramble = exports.skewbScrambleGenerator = exports.generateSkewbScramble = exports.getStateFromIndex = exports.getStateIndex = exports.solveSkewb = exports.toSkewbFacelets = exports.getSkewbFaceOrder = exports.formatSkewbFacelets = exports.createSolvedSkewbFacelets = exports.generateRandomSkewbState = exports.isOneMoveFromSolvedSkewbState = exports.isAllowedRandomSkewbState = exports.validateSkewbState = exports.permutationParity = exports.skewbStatesEqual = exports.isSolvedSkewbState = exports.createSolvedSkewbState = exports.cloneSkewbState = exports.parseSkewbMove = exports.parseSkewbAlgorithm = exports.invertSkewbMove = exports.invertSkewbAlgorithm = exports.formatSkewbAlgorithm = exports.applySkewbMove = exports.applySkewbAlgorithm = exports.SKEWB_FACES = exports.SKEWB_MOVES = void 0;
4
+ const index_js_1 = require("../../core/index.js");
5
+ const scrambler_js_1 = require("./scrambler.js");
6
+ (0, index_js_1.registerScrambleGenerator)(scrambler_js_1.skewbScrambleGenerator);
4
7
  var types_js_1 = require("./types.js");
5
8
  Object.defineProperty(exports, "SKEWB_MOVES", { enumerable: true, get: function () { return types_js_1.SKEWB_MOVES; } });
6
9
  Object.defineProperty(exports, "SKEWB_FACES", { enumerable: true, get: function () { return types_js_1.SKEWB_FACES; } });
@@ -33,6 +36,12 @@ var solver_js_1 = require("./solver.js");
33
36
  Object.defineProperty(exports, "solveSkewb", { enumerable: true, get: function () { return solver_js_1.solveSkewb; } });
34
37
  Object.defineProperty(exports, "getStateIndex", { enumerable: true, get: function () { return solver_js_1.getStateIndex; } });
35
38
  Object.defineProperty(exports, "getStateFromIndex", { enumerable: true, get: function () { return solver_js_1.getStateFromIndex; } });
36
- var scrambler_js_1 = require("./scrambler.js");
37
- Object.defineProperty(exports, "generateSkewbScramble", { enumerable: true, get: function () { return scrambler_js_1.generateSkewbScramble; } });
38
- Object.defineProperty(exports, "skewbScrambleGenerator", { enumerable: true, get: function () { return scrambler_js_1.skewbScrambleGenerator; } });
39
+ var scrambler_js_2 = require("./scrambler.js");
40
+ Object.defineProperty(exports, "generateSkewbScramble", { enumerable: true, get: function () { return scrambler_js_2.generateSkewbScramble; } });
41
+ Object.defineProperty(exports, "skewbScrambleGenerator", { enumerable: true, get: function () { return scrambler_js_2.skewbScrambleGenerator; } });
42
+ var index_js_2 = require("../../core/index.js");
43
+ Object.defineProperty(exports, "generateScramble", { enumerable: true, get: function () { return index_js_2.generateScramble; } });
44
+ Object.defineProperty(exports, "generateScrambles", { enumerable: true, get: function () { return index_js_2.generateScrambles; } });
45
+ Object.defineProperty(exports, "getEventInfo", { enumerable: true, get: function () { return index_js_2.getEventInfo; } });
46
+ Object.defineProperty(exports, "getSupportedEvents", { enumerable: true, get: function () { return index_js_2.getSupportedEvents; } });
47
+ Object.defineProperty(exports, "initializeScrambler", { enumerable: true, get: function () { return index_js_2.initializeScrambler; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cubingcenter/scrambler",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Extensible scramble generation toolkit for CubingCenter projects.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -13,7 +13,13 @@
13
13
  "types": "./dist/index.d.ts",
14
14
  "import": "./dist/index.js",
15
15
  "require": "./dist-cjs/index.js"
16
- }
16
+ },
17
+ "./puzzles/*": {
18
+ "types": "./dist/puzzles/*/index.d.ts",
19
+ "import": "./dist/puzzles/*/index.js",
20
+ "require": "./dist-cjs/puzzles/*/index.js"
21
+ },
22
+ "./package.json": "./package.json"
17
23
  },
18
24
  "types": "./dist/index.d.ts",
19
25
  "files": [
@@ -28,17 +34,22 @@
28
34
  "test": "node --test --import tsx test/*.test.ts",
29
35
  "lint": "tsc -p tsconfig.json --noEmit",
30
36
  "package:check": "pnpm build && node scripts/check-package.cjs",
31
- "verify": "pnpm lint && pnpm test && pnpm build && pnpm cube3:tables:check && pnpm cube4:tables:check && pnpm cube2:tables:check && pnpm package:check",
37
+ "verify": "pnpm lint && pnpm test && pnpm build && pnpm cube3:tables:check && pnpm cube2:tables:check && pnpm pyraminx:tables:check && pnpm package:check",
32
38
  "dev": "tsx src/cli/index.ts",
33
39
  "cube3:tables": "node --import tsx scripts/generate-cube3-solver-tables.ts",
34
40
  "cube3:tables:check": "node --import tsx scripts/check-cube3-solver-tables.ts",
35
- "cube4:tables": "node --import tsx scripts/generate-cube4-reduction-tables.ts",
36
- "cube4:tables:check": "node --import tsx scripts/check-cube4-reduction-tables.ts",
37
41
  "cube2:tables": "node --import tsx scripts/generate-cube2-solver-tables.ts",
38
42
  "cube2:tables:check": "node --import tsx scripts/check-cube2-solver-tables.ts",
43
+ "pyraminx:tables": "node --import tsx scripts/generate-pyraminx-solver-tables.ts",
44
+ "pyraminx:tables:check": "node --import tsx scripts/check-pyraminx-solver-tables.ts",
45
+ "tables:web:sync": "node --import tsx scripts/sync-web-tables.ts",
46
+ "verify:scrambles": "node --import tsx scripts/verify-scrambles.ts",
47
+ "verify:pyraminx:exhaustive": "node --import tsx scripts/verify-pyraminx-exhaustive.ts",
39
48
  "bench": "node --import tsx bench.ts",
40
49
  "bench:cube2": "node --import tsx bench-cube2.ts",
41
- "bench:cube3": "node --import tsx bench-cube3.ts"
50
+ "bench:cube3": "node --import tsx bench-cube3.ts",
51
+ "bench:skewb": "node --import tsx bench-skewb.ts",
52
+ "bench:pyraminx": "node --import tsx bench-pyraminx.ts"
42
53
  },
43
54
  "devDependencies": {
44
55
  "@cubingcenter/viz": "link:../cubingcenter-viz",