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