@cubingcenter/scrambler 0.5.1 → 0.5.3

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 (85) hide show
  1. package/dist/puzzles/cube2/facelets.d.ts.map +1 -1
  2. package/dist/puzzles/cube2/facelets.js +43 -22
  3. package/dist/puzzles/cube2/random-state.d.ts.map +1 -1
  4. package/dist/puzzles/cube2/random-state.js +38 -34
  5. package/dist/puzzles/cube2/scrambler.d.ts.map +1 -1
  6. package/dist/puzzles/cube2/scrambler.js +24 -10
  7. package/dist/puzzles/cube2/solver/browser-loader.d.ts +2 -2
  8. package/dist/puzzles/cube2/solver/browser-loader.d.ts.map +1 -1
  9. package/dist/puzzles/cube2/solver/browser-loader.js +2 -2
  10. package/dist/puzzles/cube2/solver/generated/manifest.json +3 -3
  11. package/dist/puzzles/cube2/solver/generated/policy.bin +0 -0
  12. package/dist/puzzles/cube2/solver/generated-assets.d.ts +1 -1
  13. package/dist/puzzles/cube2/solver/generated-assets.d.ts.map +1 -1
  14. package/dist/puzzles/cube2/solver/index.d.ts +2 -1
  15. package/dist/puzzles/cube2/solver/index.d.ts.map +1 -1
  16. package/dist/puzzles/cube2/solver/index.js +2 -1
  17. package/dist/puzzles/cube2/solver/node-loader.d.ts +1 -1
  18. package/dist/puzzles/cube2/solver/node-loader.d.ts.map +1 -1
  19. package/dist/puzzles/cube2/solver/node-loader.js +2 -2
  20. package/dist/puzzles/cube2/solver/search.d.ts +3 -0
  21. package/dist/puzzles/cube2/solver/search.d.ts.map +1 -1
  22. package/dist/puzzles/cube2/solver/search.js +51 -36
  23. package/dist/puzzles/cube2/solver/table-metadata.d.ts +2 -2
  24. package/dist/puzzles/cube2/solver/table-metadata.js +2 -2
  25. package/dist/puzzles/cube2/solver/tables.d.ts +2 -1
  26. package/dist/puzzles/cube2/solver/tables.d.ts.map +1 -1
  27. package/dist/puzzles/cube2/solver/tables.js +39 -13
  28. package/dist/puzzles/cube2/state.d.ts.map +1 -1
  29. package/dist/puzzles/cube2/state.js +3 -1
  30. package/dist/puzzles/cube2/validation.d.ts +2 -0
  31. package/dist/puzzles/cube2/validation.d.ts.map +1 -1
  32. package/dist/puzzles/cube2/validation.js +45 -8
  33. package/dist/puzzles/cube3/facelets.d.ts.map +1 -1
  34. package/dist/puzzles/cube3/facelets.js +53 -28
  35. package/dist/puzzles/cube3/random-state.d.ts.map +1 -1
  36. package/dist/puzzles/cube3/random-state.js +9 -2
  37. package/dist/puzzles/cube3/scrambler.js +1 -1
  38. package/dist/puzzles/cube3/state.d.ts.map +1 -1
  39. package/dist/puzzles/cube3/state.js +4 -1
  40. package/dist/puzzles/cube3/validation.d.ts.map +1 -1
  41. package/dist/puzzles/cube3/validation.js +5 -2
  42. package/dist/puzzles/skewb/random-state.d.ts +1 -0
  43. package/dist/puzzles/skewb/random-state.d.ts.map +1 -1
  44. package/dist/puzzles/skewb/random-state.js +9 -1
  45. package/dist/puzzles/skewb/scrambler.d.ts.map +1 -1
  46. package/dist/puzzles/skewb/scrambler.js +6 -2
  47. package/dist/puzzles/skewb/solver/tables.d.ts +1 -0
  48. package/dist/puzzles/skewb/solver/tables.d.ts.map +1 -1
  49. package/dist/puzzles/skewb/solver/tables.js +11 -5
  50. package/dist-cjs/puzzles/cube2/facelets.js +43 -22
  51. package/dist-cjs/puzzles/cube2/random-state.js +37 -33
  52. package/dist-cjs/puzzles/cube2/scrambler.js +22 -8
  53. package/dist-cjs/puzzles/cube2/solver/browser-loader.js +2 -2
  54. package/dist-cjs/puzzles/cube2/solver/generated/manifest.json +3 -3
  55. package/dist-cjs/puzzles/cube2/solver/generated/policy.bin +0 -0
  56. package/dist-cjs/puzzles/cube2/solver/index.js +18 -1
  57. package/dist-cjs/puzzles/cube2/solver/node-loader.js +2 -2
  58. package/dist-cjs/puzzles/cube2/solver/search.js +53 -35
  59. package/dist-cjs/puzzles/cube2/solver/table-metadata.js +2 -2
  60. package/dist-cjs/puzzles/cube2/solver/tables.js +41 -14
  61. package/dist-cjs/puzzles/cube2/state.js +3 -1
  62. package/dist-cjs/puzzles/cube2/validation.js +47 -8
  63. package/dist-cjs/puzzles/cube3/facelets.js +53 -28
  64. package/dist-cjs/puzzles/cube3/random-state.js +8 -1
  65. package/dist-cjs/puzzles/cube3/scrambler.js +1 -1
  66. package/dist-cjs/puzzles/cube3/state.js +4 -1
  67. package/dist-cjs/puzzles/cube3/validation.js +5 -2
  68. package/dist-cjs/puzzles/skewb/random-state.js +9 -0
  69. package/dist-cjs/puzzles/skewb/scrambler.js +5 -1
  70. package/dist-cjs/puzzles/skewb/solver/tables.js +12 -6
  71. package/package.json +1 -1
  72. package/dist/puzzles/cube2/solver/generated/distances.bin +0 -1
  73. package/dist/puzzles/cube3/solver/browser-worker-pool.d.ts +0 -31
  74. package/dist/puzzles/cube3/solver/browser-worker-pool.d.ts.map +0 -1
  75. package/dist/puzzles/cube3/solver/browser-worker-pool.js +0 -118
  76. package/dist/puzzles/cube3/solver/browser-worker.d.ts +0 -2
  77. package/dist/puzzles/cube3/solver/browser-worker.d.ts.map +0 -1
  78. package/dist/puzzles/cube3/solver/browser-worker.js +0 -71
  79. package/dist/puzzles/cube3/solver/worker-pool.d.ts +0 -18
  80. package/dist/puzzles/cube3/solver/worker-pool.d.ts.map +0 -1
  81. package/dist/puzzles/cube3/solver/worker-pool.js +0 -116
  82. package/dist/puzzles/cube3/solver/worker.d.ts +0 -2
  83. package/dist/puzzles/cube3/solver/worker.d.ts.map +0 -1
  84. package/dist/puzzles/cube3/solver/worker.js +0 -73
  85. package/dist-cjs/puzzles/cube2/solver/generated/distances.bin +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"facelets.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube2/facelets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAgB,MAAM,YAAY,CAAC;AA4BrF,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,aAAa,CAehE;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,CAEnE;AAED,wBAAgB,iBAAiB,IAAI,SAAS,SAAS,EAAE,CAExD"}
1
+ {"version":3,"file":"facelets.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube2/facelets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAgB,MAAM,YAAY,CAAC;AAgErF,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,aAAa,CAqBhE;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,CAEnE;AAED,wBAAgB,iBAAiB,IAAI,SAAS,SAAS,EAAE,CAExD"}
@@ -1,4 +1,6 @@
1
1
  const FACE_ORDER = ["U", "R", "F", "D", "L", "B"];
2
+ const STICKERS = ["U", "R", "F", "D", "L", "B"];
3
+ const STICKER_INDEX = { U: 0, R: 1, F: 2, D: 3, L: 4, B: 5 };
2
4
  const CORNER_PIECE_STICKERS = [
3
5
  ["U", "R", "F"],
4
6
  ["U", "F", "L"],
@@ -19,12 +21,48 @@ const CORNER_POSITION_SLOTS = [
19
21
  [["D", 2], ["B", 3], ["L", 2]],
20
22
  [["D", 3], ["R", 3], ["B", 2]]
21
23
  ];
22
- export function toCube2Facelets(state) {
23
- const facelets = createEmptyFacelets();
24
+ const CORNER_CELLS = buildCells();
25
+ function buildCells() {
26
+ const cells = [];
24
27
  for (let position = 0; position < CORNER_POSITION_SLOTS.length; position += 1) {
25
- const piece = state.corners.permutation[position];
26
- const orientation = state.corners.orientation[position];
27
- placeOrientedStickers(facelets, CORNER_POSITION_SLOTS[position], CORNER_PIECE_STICKERS[piece], orientation);
28
+ const byPiece = [];
29
+ for (let piece = 0; piece < CORNER_PIECE_STICKERS.length; piece += 1) {
30
+ const byOrientation = [];
31
+ for (let orientation = 0; orientation < 3; orientation += 1) {
32
+ const stickers = CORNER_PIECE_STICKERS[piece];
33
+ const slotList = CORNER_POSITION_SLOTS[position];
34
+ const cell = [];
35
+ for (let index = 0; index < slotList.length; index += 1) {
36
+ const [face, stickerIndex] = slotList[index];
37
+ cell.push(FACE_ORDER.indexOf(face));
38
+ cell.push(stickerIndex);
39
+ cell.push(STICKER_INDEX[stickers[(index - orientation + stickers.length) % stickers.length]]);
40
+ }
41
+ byOrientation.push(cell);
42
+ }
43
+ byPiece.push(byOrientation);
44
+ }
45
+ cells.push(byPiece);
46
+ }
47
+ return cells;
48
+ }
49
+ export function toCube2Facelets(state) {
50
+ const facelets = {
51
+ U: Array(4),
52
+ R: Array(4),
53
+ F: Array(4),
54
+ D: Array(4),
55
+ L: Array(4),
56
+ B: Array(4)
57
+ };
58
+ const faces = [facelets.U, facelets.R, facelets.F, facelets.D, facelets.L, facelets.B];
59
+ const permutation = state.corners.permutation;
60
+ const orientation = state.corners.orientation;
61
+ for (let position = 0; position < CORNER_CELLS.length; position += 1) {
62
+ const cells = CORNER_CELLS[position][permutation[position]][orientation[position]];
63
+ for (let cellIndex = 0; cellIndex < cells.length; cellIndex += 3) {
64
+ faces[cells[cellIndex]][cells[cellIndex + 1]] = STICKERS[cells[cellIndex + 2]];
65
+ }
28
66
  }
29
67
  return facelets;
30
68
  }
@@ -34,20 +72,3 @@ export function formatCube2Facelets(facelets) {
34
72
  export function getCube2FaceOrder() {
35
73
  return FACE_ORDER;
36
74
  }
37
- function createEmptyFacelets() {
38
- return {
39
- U: Array(4).fill("U"),
40
- R: Array(4).fill("R"),
41
- F: Array(4).fill("F"),
42
- D: Array(4).fill("D"),
43
- L: Array(4).fill("L"),
44
- B: Array(4).fill("B")
45
- };
46
- }
47
- function placeOrientedStickers(facelets, slots, stickers, orientation) {
48
- for (let index = 0; index < slots.length; index += 1) {
49
- const [face, stickerIndex] = slots[index];
50
- const sticker = stickers[(index - orientation + stickers.length) % stickers.length];
51
- facelets[face][stickerIndex] = sticker;
52
- }
53
- }
@@ -1 +1 @@
1
- {"version":3,"file":"random-state.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube2/random-state.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAGtE,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,uBAA4B,GAAG,UAAU,CAa1F"}
1
+ {"version":3,"file":"random-state.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube2/random-state.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAGtE,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,uBAA4B,GAAG,UAAU,CAiB1F"}
@@ -1,51 +1,55 @@
1
1
  import { createRandomSource } from "../../random/index.js";
2
- import { CORNER_COUNT } from "./types.js";
3
- import { isAllowedRandomCube2State } from "./validation.js";
2
+ import { isTrivialCube2State } from "./validation.js";
4
3
  export function generateRandomCube2State(options = {}) {
5
4
  const random = createRandomSource(options.seed);
6
5
  const rejectTrivial = options.rejectTrivial ?? true;
7
6
  for (let attempt = 0; attempt < 100; attempt += 1) {
7
+ // The state is legal by construction (DBL corner fixed, even
8
+ // permutation, orientation sum divisible by 3, values in range), so the
9
+ // only meaningful rejection is the trivial set: solved + the 18
10
+ // single-move states (scrambles shorter than 2 moves), compared by
11
+ // arrays — no coordinate encodings in this hot path.
8
12
  const state = createLegalRandomState(random);
9
- if (!rejectTrivial || isAllowedRandomCube2State(state)) {
13
+ if (!rejectTrivial || !isTrivialCube2State(state)) {
10
14
  return state;
11
15
  }
12
16
  }
13
17
  throw new Error("Unable to generate a non-trivial random 2x2 state.");
14
18
  }
15
19
  function createLegalRandomState(random) {
16
- const perm7 = shuffledRange(CORNER_COUNT - 1, random);
17
- const mappedPerm = perm7.map((val) => (val >= 6 ? val + 1 : val));
18
- mappedPerm.splice(6, 0, 6);
19
- const orient7 = randomOrientations(CORNER_COUNT - 1, 3, random);
20
- orient7.splice(6, 0, 0);
21
- return {
22
- corners: {
23
- permutation: mappedPerm,
24
- orientation: orient7
25
- }
26
- };
27
- }
28
- function shuffledRange(length, random) {
29
- const values = Array.from({ length }, (_, index) => index);
30
- for (let index = values.length - 1; index > 0; index -= 1) {
20
+ // 7 free corners (piece 6 = DBL stays fixed at position 6)
21
+ const perm7 = [0, 1, 2, 3, 4, 5, 6];
22
+ for (let index = 6; index > 0; index -= 1) {
31
23
  const swapIndex = random.nextInt(index + 1);
32
- swap(values, index, swapIndex);
24
+ if (swapIndex < index) {
25
+ const temporary = perm7[index];
26
+ perm7[index] = perm7[swapIndex];
27
+ perm7[swapIndex] = temporary;
28
+ }
33
29
  }
34
- return values;
35
- }
36
- function randomOrientations(length, modulo, random) {
37
- const orientations = Array(length).fill(0);
30
+ // Map the shuffle domain {0..6} to {0..5,7} and insert 6 at position 6
31
+ // (DBL fixed). Single allocation, no intermediate arrays.
32
+ const last = perm7[6] >= 6 ? perm7[6] + 1 : perm7[6];
33
+ const permutation = new Array(8);
34
+ for (let index = 0; index < 6; index += 1) {
35
+ const value = perm7[index];
36
+ permutation[index] = value >= 6 ? value + 1 : value;
37
+ }
38
+ permutation[6] = 6;
39
+ permutation[7] = last;
40
+ // 7 free orientations with sum divisible by 3; DBL stays oriented
41
+ const orientation = [0, 0, 0, 0, 0, 0, 0, 0];
38
42
  let total = 0;
39
- for (let index = 0; index < length - 1; index += 1) {
40
- const orientation = random.nextInt(modulo);
41
- orientations[index] = orientation;
42
- total += orientation;
43
+ for (let index = 0; index < 6; index += 1) {
44
+ const value = random.nextInt(3);
45
+ orientation[index] = value;
46
+ total += value;
43
47
  }
44
- orientations[length - 1] = (modulo - (total % modulo)) % modulo;
45
- return orientations;
46
- }
47
- function swap(values, left, right) {
48
- const temporary = values[left];
49
- values[left] = values[right];
50
- values[right] = temporary;
48
+ orientation[7] = (3 - (total % 3)) % 3;
49
+ return {
50
+ corners: {
51
+ permutation,
52
+ orientation
53
+ }
54
+ };
51
55
  }
@@ -1 +1 @@
1
- {"version":3,"file":"scrambler.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube2/scrambler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAS9F,eAAO,MAAM,sBAAsB,EAAE,iBAYpC,CAAC;AAEF,wBAAsB,qBAAqB,CACzC,OAAO,GAAE,eAAe,GAAG;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAO,GAC3D,OAAO,CAAC,cAAc,CAAC,CAwCzB"}
1
+ {"version":3,"file":"scrambler.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube2/scrambler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAiB9F,eAAO,MAAM,sBAAsB,EAAE,iBAYpC,CAAC;AAEF,wBAAsB,qBAAqB,CACzC,OAAO,GAAE,eAAe,GAAG;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAO,GAC3D,OAAO,CAAC,cAAc,CAAC,CAqDzB"}
@@ -1,7 +1,10 @@
1
- import { formatCube2Algorithm, invertCube2Algorithm } from "./moves.js";
1
+ import { formatCube2Algorithm } from "./moves.js";
2
2
  import { generateRandomCube2State } from "./random-state.js";
3
3
  import { formatCube2Facelets, toCube2Facelets } from "./facelets.js";
4
- import { solveCube2State, verifyCube2Scramble, verifyCube2Solution } from "./solver/index.js";
4
+ import { areCube2TablesLoaded, solveCube2StateIndices, solveCube2StateIndicesSync, verifyCube2SolutionIndices, BFS_MOVES } from "./solver/index.js";
5
+ // BFS_MOVES = [U, U2, U', R, R2, R', F, F2, F'] — the inverse of index m is
6
+ // CUBE2_MOVE_INVERSE[m] (U2/R2/F2 are self-inverse, so m ^ 1 does not apply).
7
+ const CUBE2_MOVE_INVERSE = [2, 1, 0, 5, 4, 3, 8, 7, 6];
5
8
  export const cube2ScrambleGenerator = {
6
9
  info: {
7
10
  eventId: "222",
@@ -17,20 +20,31 @@ export const cube2ScrambleGenerator = {
17
20
  };
18
21
  export async function generateCube2Scramble(options = {}) {
19
22
  const state = generateRandomCube2State(options);
20
- const solution = await solveCube2State(state);
21
- const scrambleMoves = invertCube2Algorithm(solution.moves);
23
+ const solution = areCube2TablesLoaded()
24
+ ? solveCube2StateIndicesSync(state)
25
+ : await solveCube2StateIndices(state);
26
+ const depth = solution.length;
27
+ // Scramble = inverse of the solution, reversed.
28
+ const scrambleMoves = new Array(depth);
29
+ for (let index = 0; index < depth; index += 1) {
30
+ scrambleMoves[index] = BFS_MOVES[CUBE2_MOVE_INVERSE[solution[depth - 1 - index]]];
31
+ }
22
32
  if (!options.skipValidation) {
23
- if (!verifyCube2Scramble(state, scrambleMoves)) {
33
+ // The scramble is the exact inverse of the solution, so verifying that
34
+ // the solution solves the target state is equivalent to verifying that
35
+ // the scramble reproduces it — a single numeric walk suffices.
36
+ if (!verifyCube2SolutionIndices(state, solution)) {
24
37
  throw new Error("Generated 2x2 scramble does not reproduce the target random state.");
25
38
  }
26
- if (!verifyCube2Solution(state, solution.moves)) {
27
- throw new Error("Generated 2x2 solution does not solve the target random state.");
28
- }
29
39
  if (scrambleMoves.length < 2) {
30
40
  throw new Error("Generated 2x2 scramble is too short.");
31
41
  }
32
42
  }
33
43
  const facelets = toCube2Facelets(state);
44
+ const solutionMoves = new Array(depth);
45
+ for (let index = 0; index < depth; index += 1) {
46
+ solutionMoves[index] = BFS_MOVES[solution[index]];
47
+ }
34
48
  const result = {
35
49
  eventId: "222",
36
50
  scramble: formatCube2Algorithm(scrambleMoves),
@@ -41,8 +55,8 @@ export async function generateCube2Scramble(options = {}) {
41
55
  state,
42
56
  facelets,
43
57
  faceletString: formatCube2Facelets(facelets),
44
- solutionMoves: solution.moves,
45
- solutionDepth: solution.depth
58
+ solutionMoves,
59
+ solutionDepth: depth
46
60
  }
47
61
  };
48
62
  if (options.seed !== undefined) {
@@ -1,12 +1,12 @@
1
1
  export declare function getCachedBrowserTables(): {
2
2
  permutationTable: Uint16Array;
3
3
  orientationTable: Uint16Array;
4
- distances: Uint8Array;
4
+ policyTable: Uint8Array;
5
5
  } | undefined;
6
6
  export declare function clearCachedBrowserTables(): void;
7
7
  export declare function loadBrowserTables(baseUrl?: string): Promise<{
8
8
  permutationTable: Uint16Array;
9
9
  orientationTable: Uint16Array;
10
- distances: Uint8Array;
10
+ policyTable: Uint8Array;
11
11
  }>;
12
12
  //# sourceMappingURL=browser-loader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"browser-loader.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube2/solver/browser-loader.ts"],"names":[],"mappings":"AAWA,wBAAgB,sBAAsB;sBALlB,WAAW;sBACX,WAAW;eAClB,UAAU;cAKtB;AAED,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAED,wBAAsB,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACjE,gBAAgB,EAAE,WAAW,CAAC;IAC9B,gBAAgB,EAAE,WAAW,CAAC;IAC9B,SAAS,EAAE,UAAU,CAAC;CACvB,CAAC,CAqBD"}
1
+ {"version":3,"file":"browser-loader.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube2/solver/browser-loader.ts"],"names":[],"mappings":"AAWA,wBAAgB,sBAAsB;sBALlB,WAAW;sBACX,WAAW;iBAChB,UAAU;cAKxB;AAED,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAED,wBAAsB,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACjE,gBAAgB,EAAE,WAAW,CAAC;IAC9B,gBAAgB,EAAE,WAAW,CAAC;IAC9B,WAAW,EAAE,UAAU,CAAC;CACzB,CAAC,CAqBD"}
@@ -17,12 +17,12 @@ export async function loadBrowserTables(baseUrl) {
17
17
  const [permTable, orientTable, dist] = await Promise.all([
18
18
  fetchUint16Table(resolved, manifest.files.permutationTable, CUBE2_SOLVER_TABLE_METADATA.permutationTable.entries),
19
19
  fetchUint16Table(resolved, manifest.files.orientationTable, CUBE2_SOLVER_TABLE_METADATA.orientationTable.entries),
20
- fetchUint8Table(resolved, manifest.files.distances, CUBE2_SOLVER_TABLE_METADATA.distances.entries)
20
+ fetchUint8Table(resolved, manifest.files.policy, CUBE2_SOLVER_TABLE_METADATA.policy.entries)
21
21
  ]);
22
22
  cachedBrowserTables = {
23
23
  permutationTable: permTable,
24
24
  orientationTable: orientTable,
25
- distances: dist
25
+ policyTable: dist
26
26
  };
27
27
  return cachedBrowserTables;
28
28
  }
@@ -11,10 +11,10 @@
11
11
  "entries": 6561,
12
12
  "checksum": 1566738619
13
13
  },
14
- "distances": {
15
- "file": "distances.bin",
14
+ "policy": {
15
+ "file": "policy.bin",
16
16
  "entries": 1837080,
17
- "checksum": 2959532459,
17
+ "checksum": 2042005723,
18
18
  "maxDepth": 11
19
19
  }
20
20
  }
@@ -1,6 +1,6 @@
1
1
  export declare function loadGeneratedCube2SolverTables(): Promise<{
2
2
  permutationTable: Uint16Array;
3
3
  orientationTable: Uint16Array;
4
- distances: Uint8Array;
4
+ policyTable: Uint8Array;
5
5
  }>;
6
6
  //# sourceMappingURL=generated-assets.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"generated-assets.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube2/solver/generated-assets.ts"],"names":[],"mappings":"AAYA,wBAAsB,8BAA8B,IAAI,OAAO,CAAC;IAC9D,gBAAgB,EAAE,WAAW,CAAC;IAC9B,gBAAgB,EAAE,WAAW,CAAC;IAC9B,SAAS,EAAE,UAAU,CAAC;CACvB,CAAC,CAeD"}
1
+ {"version":3,"file":"generated-assets.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube2/solver/generated-assets.ts"],"names":[],"mappings":"AAYA,wBAAsB,8BAA8B,IAAI,OAAO,CAAC;IAC9D,gBAAgB,EAAE,WAAW,CAAC;IAC9B,gBAAgB,EAAE,WAAW,CAAC;IAC9B,WAAW,EAAE,UAAU,CAAC;CACzB,CAAC,CAeD"}
@@ -1,3 +1,4 @@
1
- export { solveCube2State, verifyCube2Scramble, verifyCube2Solution } from "./search.js";
1
+ export { solveCube2State, solveCube2StateIndices, solveCube2StateIndicesSync, verifyCube2Scramble, verifyCube2Solution, verifyCube2SolutionIndices } from "./search.js";
2
2
  export type { Cube2Solution } from "./search.js";
3
+ export { areCube2TablesLoaded, BFS_MOVES, POLICY_TABLE, ensureTablesLoaded, encodePermutation, encodeOrientation, getDistance, getPermutationTransition, getOrientationTransition, MOVE_COUNT, ORIENTATION_COUNT, ORIENTATION_TABLE, PERMUTATION_TABLE } from "./tables.js";
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube2/solver/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACxF,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube2/solver/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,0BAA0B,EAC1B,mBAAmB,EACnB,mBAAmB,EACnB,0BAA0B,EAC3B,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EACL,oBAAoB,EACpB,SAAS,EACT,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,WAAW,EACX,wBAAwB,EACxB,wBAAwB,EACxB,UAAU,EACV,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,aAAa,CAAC"}
@@ -1 +1,2 @@
1
- export { solveCube2State, verifyCube2Scramble, verifyCube2Solution } from "./search.js";
1
+ export { solveCube2State, solveCube2StateIndices, solveCube2StateIndicesSync, verifyCube2Scramble, verifyCube2Solution, verifyCube2SolutionIndices } from "./search.js";
2
+ export { areCube2TablesLoaded, BFS_MOVES, POLICY_TABLE, ensureTablesLoaded, encodePermutation, encodeOrientation, getDistance, getPermutationTransition, getOrientationTransition, MOVE_COUNT, ORIENTATION_COUNT, ORIENTATION_TABLE, PERMUTATION_TABLE } from "./tables.js";
@@ -1,6 +1,6 @@
1
1
  export declare function readGeneratedCube2SolverTables(): {
2
2
  permutationTable: Uint16Array;
3
3
  orientationTable: Uint16Array;
4
- distances: Uint8Array;
4
+ policyTable: Uint8Array;
5
5
  };
6
6
  //# sourceMappingURL=node-loader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"node-loader.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube2/solver/node-loader.ts"],"names":[],"mappings":"AAgBA,wBAAgB,8BAA8B,IAAI;IAChD,gBAAgB,EAAE,WAAW,CAAC;IAC9B,gBAAgB,EAAE,WAAW,CAAC;IAC9B,SAAS,EAAE,UAAU,CAAC;CACvB,CAmBA"}
1
+ {"version":3,"file":"node-loader.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube2/solver/node-loader.ts"],"names":[],"mappings":"AAgBA,wBAAgB,8BAA8B,IAAI;IAChD,gBAAgB,EAAE,WAAW,CAAC;IAC9B,gBAAgB,EAAE,WAAW,CAAC;IAC9B,WAAW,EAAE,UAAU,CAAC;CACzB,CAmBA"}
@@ -11,11 +11,11 @@ export function readGeneratedCube2SolverTables() {
11
11
  }
12
12
  const permutationTable = readUint16Table(assetDirectory, manifest.files.permutationTable, CUBE2_SOLVER_TABLE_METADATA.permutationTable.checksum);
13
13
  const orientationTable = readUint16Table(assetDirectory, manifest.files.orientationTable, CUBE2_SOLVER_TABLE_METADATA.orientationTable.checksum);
14
- const distances = readUint8Table(assetDirectory, manifest.files.distances, CUBE2_SOLVER_TABLE_METADATA.distances.checksum);
14
+ const policyTable = readUint8Table(assetDirectory, manifest.files.policy, CUBE2_SOLVER_TABLE_METADATA.policy.checksum);
15
15
  return {
16
16
  permutationTable,
17
17
  orientationTable,
18
- distances
18
+ policyTable
19
19
  };
20
20
  }
21
21
  function readUint16Table(assetDirectory, entry, expectedChecksum) {
@@ -5,6 +5,9 @@ export interface Cube2Solution {
5
5
  state: Cube2State;
6
6
  }
7
7
  export declare function solveCube2State(state: Cube2State): Promise<Cube2Solution>;
8
+ export declare function solveCube2StateIndices(state: Cube2State): Promise<number[]>;
9
+ export declare function solveCube2StateIndicesSync(state: Cube2State): number[];
8
10
  export declare function verifyCube2Scramble(state: Cube2State, scrambleMoves: readonly Cube2Move[]): boolean;
9
11
  export declare function verifyCube2Solution(state: Cube2State, solutionMoves: readonly Cube2Move[]): boolean;
12
+ export declare function verifyCube2SolutionIndices(state: Cube2State, solution: readonly number[]): boolean;
10
13
  //# sourceMappingURL=search.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube2/solver/search.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAczD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,UAAU,CAAC;CACnB;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,CA6D/E;AAoBD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,SAAS,EAAE,GAAG,OAAO,CAUnG;AAGD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,SAAS,EAAE,GAAG,OAAO,CAOnG"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube2/solver/search.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAczD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,UAAU,CAAC;CACnB;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,CAQ/E;AAKD,wBAAsB,sBAAsB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAGjF;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,EAAE,CAKtE;AAoDD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,SAAS,EAAE,GAAG,OAAO,CAUnG;AAGD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,SAAS,EAAE,GAAG,OAAO,CAOnG;AAMD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAclG"}
@@ -1,52 +1,49 @@
1
1
  import { createSolvedCube2State } from "../state.js";
2
- import { encodeOrientation, encodePermutation, ensureTablesLoaded, PERMUTATION_TABLE, ORIENTATION_TABLE, DISTANCES, ORIENTATION_COUNT, BFS_MOVES, MOVE_COUNT } from "./tables.js";
2
+ import { encodeOrientation, encodePermutation, ensureTablesLoaded, PERMUTATION_TABLE, ORIENTATION_TABLE, POLICY_TABLE, ORIENTATION_COUNT, BFS_MOVES, MOVE_COUNT } from "./tables.js";
3
3
  export async function solveCube2State(state) {
4
4
  await ensureTablesLoaded();
5
+ const indices = solveWithTablesIndices(state);
6
+ return {
7
+ moves: indices.map((index) => BFS_MOVES[index]),
8
+ depth: indices.length,
9
+ state: createSolvedCube2State()
10
+ };
11
+ }
12
+ // Index-based solve for the hot scramble path: returns the solution as move
13
+ // indices (0..8) in walk order. The inverse of index m is CUBE2_MOVE_INVERSE[m]
14
+ // (U2/R2/F2 are self-inverse, so m ^ 1 does not apply here).
15
+ export async function solveCube2StateIndices(state) {
16
+ await ensureTablesLoaded();
17
+ return solveWithTablesIndices(state);
18
+ }
19
+ export function solveCube2StateIndicesSync(state) {
20
+ if (!POLICY_TABLE) {
21
+ throw new Error("Cube2 solver tables not loaded. Call ensureTablesLoaded() first.");
22
+ }
23
+ return solveWithTablesIndices(state);
24
+ }
25
+ function solveWithTablesIndices(state) {
5
26
  if (state.corners.permutation[6] !== 6 || state.corners.orientation[6] !== 0) {
6
27
  throw new Error("The 2x2 solver requires the DBL corner (index 6) to be solved " +
7
28
  "as the frame of reference. Please normalize the state before solving.");
8
29
  }
9
30
  const permTable = PERMUTATION_TABLE;
10
31
  const orientTable = ORIENTATION_TABLE;
11
- const distances = DISTANCES;
32
+ const policy = POLICY_TABLE;
12
33
  let permIndex = encodePermutation(state.corners.permutation);
13
34
  let orientIndex = encodeOrientation(state.corners.orientation);
14
35
  const moves = [];
15
- // High-performance branchless inlined lookup
16
- const initialCombined = permIndex * ORIENTATION_COUNT + orientIndex;
17
- let distance = (distances[initialCombined >> 1] >> ((initialCombined & 1) << 2)) & 0x0f;
18
- if (distance === 0) {
19
- return { moves, depth: 0, state: createSolvedCube2State() };
20
- }
21
- while (distance > 0) {
22
- let foundNext = false;
23
- const permOffset = permIndex * MOVE_COUNT;
24
- const orientOffset = orientIndex * MOVE_COUNT;
25
- const nextDistance = distance - 1;
26
- for (let m = 0; m < BFS_MOVES.length; m++) {
27
- const nextPerm = permTable[permOffset + m];
28
- const nextOrient = orientTable[orientOffset + m];
29
- // High-performance branchless inlined lookup
30
- const combined = nextPerm * ORIENTATION_COUNT + nextOrient;
31
- const nextDist = (distances[combined >> 1] >> ((combined & 1) << 2)) & 0x0f;
32
- if (nextDist === nextDistance) {
33
- moves.push(BFS_MOVES[m]);
34
- permIndex = nextPerm;
35
- orientIndex = nextOrient;
36
- distance = nextDistance;
37
- foundNext = true;
38
- break;
39
- }
40
- }
41
- if (!foundNext) {
42
- throw new Error("BFS distance table corrupted or unreachable state.");
43
- }
36
+ // Direct policy walk: the precomputed optimal move per state, so no
37
+ // candidate scanning (the old BFS-distance gradient walk) is needed.
38
+ let combined = permIndex * ORIENTATION_COUNT + orientIndex;
39
+ while (combined !== 0) {
40
+ const m = (policy[combined >> 1] >> ((combined & 1) << 2)) & 0x0f;
41
+ moves.push(m);
42
+ permIndex = permTable[permIndex * MOVE_COUNT + m];
43
+ orientIndex = orientTable[orientIndex * MOVE_COUNT + m];
44
+ combined = permIndex * ORIENTATION_COUNT + orientIndex;
44
45
  }
45
- return {
46
- moves,
47
- depth: moves.length,
48
- state: createSolvedCube2State()
49
- };
46
+ return moves;
50
47
  }
51
48
  const MOVE_INDEX = {};
52
49
  for (let i = 0; i < BFS_MOVES.length; i++) {
@@ -74,3 +71,21 @@ export function verifyCube2Solution(state, solutionMoves) {
74
71
  const { permIndex, orientIndex } = walkCube2Coordinates(encodePermutation(state.corners.permutation), encodeOrientation(state.corners.orientation), solutionMoves);
75
72
  return permIndex === 0 && orientIndex === 0;
76
73
  }
74
+ // Numeric verification for the hot scramble path: walks the solution (move
75
+ // indices, walk order) from the target state to solved. The scramble is the
76
+ // exact inverse of the solution, so this is equivalent to verifying that the
77
+ // scramble reproduces the target state — one walk, no string parsing.
78
+ export function verifyCube2SolutionIndices(state, solution) {
79
+ const permTable = PERMUTATION_TABLE;
80
+ const orientTable = ORIENTATION_TABLE;
81
+ if (!permTable || !orientTable) {
82
+ throw new Error("Cube2 solver tables not loaded. Call ensureTablesLoaded() first.");
83
+ }
84
+ let permIndex = encodePermutation(state.corners.permutation);
85
+ let orientIndex = encodeOrientation(state.corners.orientation);
86
+ for (const m of solution) {
87
+ permIndex = permTable[permIndex * MOVE_COUNT + m];
88
+ orientIndex = orientTable[orientIndex * MOVE_COUNT + m];
89
+ }
90
+ return permIndex === 0 && orientIndex === 0;
91
+ }
@@ -7,9 +7,9 @@ export declare const CUBE2_SOLVER_TABLE_METADATA: {
7
7
  readonly entries: 6561;
8
8
  readonly checksum: 1566738619;
9
9
  };
10
- readonly distances: {
10
+ readonly policy: {
11
11
  readonly entries: 1837080;
12
- readonly checksum: 2959532459;
12
+ readonly checksum: 2042005723;
13
13
  readonly maxDepth: 11;
14
14
  };
15
15
  };
@@ -7,9 +7,9 @@ export const CUBE2_SOLVER_TABLE_METADATA = {
7
7
  entries: 6561,
8
8
  checksum: 1566738619
9
9
  },
10
- distances: {
10
+ policy: {
11
11
  entries: 1837080,
12
- checksum: 2959532459,
12
+ checksum: 2042005723,
13
13
  maxDepth: 11
14
14
  }
15
15
  };
@@ -5,8 +5,9 @@ export declare function encodePermutation(permutation: readonly number[]): numbe
5
5
  export declare function encodeOrientation(orientation: readonly number[]): number;
6
6
  export declare let PERMUTATION_TABLE: Uint16Array | null;
7
7
  export declare let ORIENTATION_TABLE: Uint16Array | null;
8
- export declare let DISTANCES: Uint8Array | null;
8
+ export declare let POLICY_TABLE: Uint8Array | null;
9
9
  export declare function ensureTablesLoaded(): Promise<void>;
10
+ export declare function areCube2TablesLoaded(): boolean;
10
11
  export declare function getDistance(permIndex: number, orientIndex: number): number;
11
12
  export declare function getPermutationTransition(move: number, permIndex: number): number;
12
13
  export declare function getOrientationTransition(move: number, orientIndex: number): number;
@@ -1 +1 @@
1
- {"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube2/solver/tables.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,UAAU,IAAI,CAAC;AAM5B,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAexE;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAWxE;AAeD,eAAO,IAAI,iBAAiB,EAAE,WAAW,GAAG,IAAW,CAAC;AACxD,eAAO,IAAI,iBAAiB,EAAE,WAAW,GAAG,IAAW,CAAC;AACxD,eAAO,IAAI,SAAS,EAAE,UAAU,GAAG,IAAW,CAAC;AAE/C,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAUxD;AAMD,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAO1E;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAKhF;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAKlF;AAED,eAAO,MAAM,SAAS,EAAE,SAAS,MAAM,EAItC,CAAC"}
1
+ {"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/cube2/solver/tables.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,UAAU,IAAI,CAAC;AAW5B,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAYxE;AAWD,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAWxE;AAeD,eAAO,IAAI,iBAAiB,EAAE,WAAW,GAAG,IAAW,CAAC;AACxD,eAAO,IAAI,iBAAiB,EAAE,WAAW,GAAG,IAAW,CAAC;AACxD,eAAO,IAAI,YAAY,EAAE,UAAU,GAAG,IAAW,CAAC;AAElD,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAUxD;AAED,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAMD,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAkB1E;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAKhF;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAKlF;AAED,eAAO,MAAM,SAAS,EAAE,SAAS,MAAM,EAItC,CAAC"}
@@ -5,19 +5,31 @@ export const MOVE_COUNT = 9;
5
5
  // ---------------------------------------------------------------------------
6
6
  // Coordinate encoding / decoding
7
7
  // ---------------------------------------------------------------------------
8
+ // Lehmer encoding of the 7 free corners (DBL at index 6 is fixed), with a
9
+ // bitmask instead of array removal: available values tracked as bits, the
10
+ // rank of a value = the popcount of the bits below it.
11
+ const PERM7_FACT = [720, 120, 24, 6, 2, 1, 1];
8
12
  export function encodePermutation(permutation) {
9
- const perm7 = permutation.filter((_, i) => i !== 6);
10
- const mapped = perm7.map((x) => (x > 6 ? x - 1 : x));
11
13
  let index = 0;
12
- const available = [0, 1, 2, 3, 4, 5, 6];
14
+ let available = 0b1111111;
13
15
  for (let position = 0; position < 7; position += 1) {
14
- const value = mapped[position];
15
- const selected = available.indexOf(value);
16
- index += selected * factorialSmall(6 - position);
17
- available.splice(selected, 1);
16
+ const permPosition = position < 6 ? position : position + 1;
17
+ const piece = permutation[permPosition];
18
+ const value = piece > 6 ? piece - 1 : piece;
19
+ const rank = popcount(available & ((1 << value) - 1));
20
+ index += rank * PERM7_FACT[position];
21
+ available &= ~(1 << value);
18
22
  }
19
23
  return index;
20
24
  }
25
+ function popcount(x) {
26
+ let count = 0;
27
+ while (x !== 0) {
28
+ x &= x - 1;
29
+ count += 1;
30
+ }
31
+ return count;
32
+ }
21
33
  export function encodeOrientation(orientation) {
22
34
  const orient7 = orientation.filter((_, i) => i !== 6);
23
35
  let index = 0;
@@ -41,7 +53,7 @@ function factorialSmall(n) {
41
53
  let tablesLoaded = false;
42
54
  export let PERMUTATION_TABLE = null;
43
55
  export let ORIENTATION_TABLE = null;
44
- export let DISTANCES = null;
56
+ export let POLICY_TABLE = null;
45
57
  export async function ensureTablesLoaded() {
46
58
  if (tablesLoaded) {
47
59
  return;
@@ -49,19 +61,33 @@ export async function ensureTablesLoaded() {
49
61
  const tables = await loadGeneratedCube2SolverTables();
50
62
  PERMUTATION_TABLE = tables.permutationTable;
51
63
  ORIENTATION_TABLE = tables.orientationTable;
52
- DISTANCES = tables.distances;
64
+ POLICY_TABLE = tables.policyTable;
53
65
  tablesLoaded = true;
54
66
  }
67
+ export function areCube2TablesLoaded() {
68
+ return tablesLoaded;
69
+ }
55
70
  // ---------------------------------------------------------------------------
56
71
  // Public API
57
72
  // ---------------------------------------------------------------------------
58
73
  export function getDistance(permIndex, orientIndex) {
59
- if (!DISTANCES) {
74
+ if (!POLICY_TABLE) {
60
75
  throw new Error("Cube2 solver tables not loaded. Call ensureTablesLoaded() first.");
61
76
  }
62
- const combined = permIndex * ORIENTATION_COUNT + orientIndex;
63
- const byteValue = DISTANCES[combined >> 1];
64
- return (combined & 1) === 0 ? (byteValue & 0x0f) : (byteValue >> 4);
77
+ const permTable = PERMUTATION_TABLE;
78
+ const orientTable = ORIENTATION_TABLE;
79
+ let p = permIndex;
80
+ let o = orientIndex;
81
+ let combined = p * ORIENTATION_COUNT + o;
82
+ let distance = 0;
83
+ while (combined !== 0) {
84
+ const m = (POLICY_TABLE[combined >> 1] >> ((combined & 1) << 2)) & 0x0f;
85
+ p = permTable[p * MOVE_COUNT + m];
86
+ o = orientTable[o * MOVE_COUNT + m];
87
+ combined = p * ORIENTATION_COUNT + o;
88
+ distance += 1;
89
+ }
90
+ return distance;
65
91
  }
66
92
  export function getPermutationTransition(move, permIndex) {
67
93
  if (!PERMUTATION_TABLE) {
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube2/state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,wBAAgB,sBAAsB,IAAI,UAAU,CAOnD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAO7D;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAK7E;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAE7D"}
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube2/state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,wBAAgB,sBAAsB,IAAI,UAAU,CAOnD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAO7D;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAK7E;AAKD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAE7D"}