@cubingcenter/scrambler 0.5.2 → 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 (78) hide show
  1. package/dist/puzzles/cube2/random-state.d.ts.map +1 -1
  2. package/dist/puzzles/cube2/random-state.js +4 -10
  3. package/dist/puzzles/cube2/solver/browser-loader.d.ts +2 -2
  4. package/dist/puzzles/cube2/solver/browser-loader.d.ts.map +1 -1
  5. package/dist/puzzles/cube2/solver/browser-loader.js +2 -2
  6. package/dist/puzzles/cube2/solver/generated/manifest.json +3 -3
  7. package/dist/puzzles/cube2/solver/generated/policy.bin +0 -0
  8. package/dist/puzzles/cube2/solver/generated-assets.d.ts +1 -1
  9. package/dist/puzzles/cube2/solver/generated-assets.d.ts.map +1 -1
  10. package/dist/puzzles/cube2/solver/index.d.ts +1 -1
  11. package/dist/puzzles/cube2/solver/index.d.ts.map +1 -1
  12. package/dist/puzzles/cube2/solver/index.js +1 -1
  13. package/dist/puzzles/cube2/solver/node-loader.d.ts +1 -1
  14. package/dist/puzzles/cube2/solver/node-loader.d.ts.map +1 -1
  15. package/dist/puzzles/cube2/solver/node-loader.js +2 -2
  16. package/dist/puzzles/cube2/solver/search.d.ts.map +1 -1
  17. package/dist/puzzles/cube2/solver/search.js +12 -32
  18. package/dist/puzzles/cube2/solver/table-metadata.d.ts +2 -2
  19. package/dist/puzzles/cube2/solver/table-metadata.js +2 -2
  20. package/dist/puzzles/cube2/solver/tables.d.ts +1 -1
  21. package/dist/puzzles/cube2/solver/tables.d.ts.map +1 -1
  22. package/dist/puzzles/cube2/solver/tables.js +36 -13
  23. package/dist/puzzles/cube2/state.d.ts.map +1 -1
  24. package/dist/puzzles/cube2/state.js +3 -1
  25. package/dist/puzzles/cube2/validation.d.ts +1 -0
  26. package/dist/puzzles/cube2/validation.d.ts.map +1 -1
  27. package/dist/puzzles/cube2/validation.js +38 -7
  28. package/dist/puzzles/cube3/facelets.d.ts.map +1 -1
  29. package/dist/puzzles/cube3/facelets.js +53 -28
  30. package/dist/puzzles/cube3/random-state.d.ts.map +1 -1
  31. package/dist/puzzles/cube3/random-state.js +9 -2
  32. package/dist/puzzles/cube3/scrambler.js +1 -1
  33. package/dist/puzzles/cube3/state.d.ts.map +1 -1
  34. package/dist/puzzles/cube3/state.js +4 -1
  35. package/dist/puzzles/cube3/validation.d.ts.map +1 -1
  36. package/dist/puzzles/cube3/validation.js +5 -2
  37. package/dist/puzzles/skewb/random-state.d.ts +1 -0
  38. package/dist/puzzles/skewb/random-state.d.ts.map +1 -1
  39. package/dist/puzzles/skewb/random-state.js +9 -1
  40. package/dist/puzzles/skewb/scrambler.d.ts.map +1 -1
  41. package/dist/puzzles/skewb/scrambler.js +6 -2
  42. package/dist/puzzles/skewb/solver/tables.d.ts +1 -0
  43. package/dist/puzzles/skewb/solver/tables.d.ts.map +1 -1
  44. package/dist/puzzles/skewb/solver/tables.js +11 -5
  45. package/dist-cjs/puzzles/cube2/random-state.js +3 -9
  46. package/dist-cjs/puzzles/cube2/solver/browser-loader.js +2 -2
  47. package/dist-cjs/puzzles/cube2/solver/generated/manifest.json +3 -3
  48. package/dist-cjs/puzzles/cube2/solver/generated/policy.bin +0 -0
  49. package/dist-cjs/puzzles/cube2/solver/index.js +2 -2
  50. package/dist-cjs/puzzles/cube2/solver/node-loader.js +2 -2
  51. package/dist-cjs/puzzles/cube2/solver/search.js +11 -31
  52. package/dist-cjs/puzzles/cube2/solver/table-metadata.js +2 -2
  53. package/dist-cjs/puzzles/cube2/solver/tables.js +37 -14
  54. package/dist-cjs/puzzles/cube2/state.js +3 -1
  55. package/dist-cjs/puzzles/cube2/validation.js +39 -7
  56. package/dist-cjs/puzzles/cube3/facelets.js +53 -28
  57. package/dist-cjs/puzzles/cube3/random-state.js +8 -1
  58. package/dist-cjs/puzzles/cube3/scrambler.js +1 -1
  59. package/dist-cjs/puzzles/cube3/state.js +4 -1
  60. package/dist-cjs/puzzles/cube3/validation.js +5 -2
  61. package/dist-cjs/puzzles/skewb/random-state.js +9 -0
  62. package/dist-cjs/puzzles/skewb/scrambler.js +5 -1
  63. package/dist-cjs/puzzles/skewb/solver/tables.js +12 -6
  64. package/package.json +1 -1
  65. package/dist/puzzles/cube2/solver/generated/distances.bin +0 -1
  66. package/dist/puzzles/cube3/solver/browser-worker-pool.d.ts +0 -31
  67. package/dist/puzzles/cube3/solver/browser-worker-pool.d.ts.map +0 -1
  68. package/dist/puzzles/cube3/solver/browser-worker-pool.js +0 -118
  69. package/dist/puzzles/cube3/solver/browser-worker.d.ts +0 -2
  70. package/dist/puzzles/cube3/solver/browser-worker.d.ts.map +0 -1
  71. package/dist/puzzles/cube3/solver/browser-worker.js +0 -71
  72. package/dist/puzzles/cube3/solver/worker-pool.d.ts +0 -18
  73. package/dist/puzzles/cube3/solver/worker-pool.d.ts.map +0 -1
  74. package/dist/puzzles/cube3/solver/worker-pool.js +0 -116
  75. package/dist/puzzles/cube3/solver/worker.d.ts +0 -2
  76. package/dist/puzzles/cube3/solver/worker.d.ts.map +0 -1
  77. package/dist/puzzles/cube3/solver/worker.js +0 -73
  78. package/dist-cjs/puzzles/cube2/solver/generated/distances.bin +0 -1
@@ -1 +1 @@
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;AAItE,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,uBAA4B,GAAG,UAAU,CAsB1F"}
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,6 +1,5 @@
1
1
  import { createRandomSource } from "../../random/index.js";
2
- import { isTrivialSamplingCube2State } from "./validation.js";
3
- import { encodePermutation, encodeOrientation } from "./solver/tables.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;
@@ -8,15 +7,10 @@ export function generateRandomCube2State(options = {}) {
8
7
  // The state is legal by construction (DBL corner fixed, even
9
8
  // permutation, orientation sum divisible by 3, values in range), so the
10
9
  // only meaningful rejection is the trivial set: solved + the 18
11
- // single-move states (scrambles shorter than 2 moves). Coordinates are
12
- // encoded once for both checks.
10
+ // single-move states (scrambles shorter than 2 moves), compared by
11
+ // arrays no coordinate encodings in this hot path.
13
12
  const state = createLegalRandomState(random);
14
- if (!rejectTrivial) {
15
- return state;
16
- }
17
- const p = encodePermutation(state.corners.permutation);
18
- const o = encodeOrientation(state.corners.orientation);
19
- if (!isTrivialSamplingCube2State(p, o)) {
13
+ if (!rejectTrivial || !isTrivialCube2State(state)) {
20
14
  return state;
21
15
  }
22
16
  }
@@ -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,4 +1,4 @@
1
1
  export { solveCube2State, solveCube2StateIndices, solveCube2StateIndicesSync, verifyCube2Scramble, verifyCube2Solution, verifyCube2SolutionIndices } from "./search.js";
2
2
  export type { Cube2Solution } from "./search.js";
3
- export { areCube2TablesLoaded, BFS_MOVES, DISTANCES, ensureTablesLoaded, encodePermutation, encodeOrientation, getDistance, getPermutationTransition, getOrientationTransition, MOVE_COUNT, ORIENTATION_COUNT, ORIENTATION_TABLE, PERMUTATION_TABLE } from "./tables.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";
4
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,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,SAAS,EACT,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,WAAW,EACX,wBAAwB,EACxB,wBAAwB,EACxB,UAAU,EACV,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EAClB,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,2 +1,2 @@
1
1
  export { solveCube2State, solveCube2StateIndices, solveCube2StateIndicesSync, verifyCube2Scramble, verifyCube2Solution, verifyCube2SolutionIndices } from "./search.js";
2
- export { areCube2TablesLoaded, BFS_MOVES, DISTANCES, ensureTablesLoaded, encodePermutation, encodeOrientation, getDistance, getPermutationTransition, getOrientationTransition, MOVE_COUNT, ORIENTATION_COUNT, ORIENTATION_TABLE, PERMUTATION_TABLE } from "./tables.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) {
@@ -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,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;AA6ED,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
+ {"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,5 +1,5 @@
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
5
  const indices = solveWithTablesIndices(state);
@@ -17,7 +17,7 @@ export async function solveCube2StateIndices(state) {
17
17
  return solveWithTablesIndices(state);
18
18
  }
19
19
  export function solveCube2StateIndicesSync(state) {
20
- if (!DISTANCES) {
20
+ if (!POLICY_TABLE) {
21
21
  throw new Error("Cube2 solver tables not loaded. Call ensureTablesLoaded() first.");
22
22
  }
23
23
  return solveWithTablesIndices(state);
@@ -29,39 +29,19 @@ function solveWithTablesIndices(state) {
29
29
  }
30
30
  const permTable = PERMUTATION_TABLE;
31
31
  const orientTable = ORIENTATION_TABLE;
32
- const distances = DISTANCES;
32
+ const policy = POLICY_TABLE;
33
33
  let permIndex = encodePermutation(state.corners.permutation);
34
34
  let orientIndex = encodeOrientation(state.corners.orientation);
35
35
  const moves = [];
36
- // High-performance branchless inlined lookup
37
- const initialCombined = permIndex * ORIENTATION_COUNT + orientIndex;
38
- let distance = (distances[initialCombined >> 1] >> ((initialCombined & 1) << 2)) & 0x0f;
39
- if (distance === 0) {
40
- return moves;
41
- }
42
- while (distance > 0) {
43
- let foundNext = false;
44
- const permOffset = permIndex * MOVE_COUNT;
45
- const orientOffset = orientIndex * MOVE_COUNT;
46
- const nextDistance = distance - 1;
47
- for (let m = 0; m < BFS_MOVES.length; m++) {
48
- const nextPerm = permTable[permOffset + m];
49
- const nextOrient = orientTable[orientOffset + m];
50
- // High-performance branchless inlined lookup
51
- const combined = nextPerm * ORIENTATION_COUNT + nextOrient;
52
- const nextDist = (distances[combined >> 1] >> ((combined & 1) << 2)) & 0x0f;
53
- if (nextDist === nextDistance) {
54
- moves.push(m);
55
- permIndex = nextPerm;
56
- orientIndex = nextOrient;
57
- distance = nextDistance;
58
- foundNext = true;
59
- break;
60
- }
61
- }
62
- if (!foundNext) {
63
- throw new Error("BFS distance table corrupted or unreachable state.");
64
- }
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;
65
45
  }
66
46
  return moves;
67
47
  }
@@ -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,7 +5,7 @@ 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
10
  export declare function areCube2TablesLoaded(): boolean;
11
11
  export declare function getDistance(permIndex: 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;AAED,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;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,7 +61,7 @@ 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
  }
55
67
  export function areCube2TablesLoaded() {
@@ -59,12 +71,23 @@ export function areCube2TablesLoaded() {
59
71
  // Public API
60
72
  // ---------------------------------------------------------------------------
61
73
  export function getDistance(permIndex, orientIndex) {
62
- if (!DISTANCES) {
74
+ if (!POLICY_TABLE) {
63
75
  throw new Error("Cube2 solver tables not loaded. Call ensureTablesLoaded() first.");
64
76
  }
65
- const combined = permIndex * ORIENTATION_COUNT + orientIndex;
66
- const byteValue = DISTANCES[combined >> 1];
67
- 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;
68
91
  }
69
92
  export function getPermutationTransition(move, permIndex) {
70
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"}
@@ -19,8 +19,10 @@ export function cube2StatesEqual(left, right) {
19
19
  return (arraysEqual(left.corners.permutation, right.corners.permutation) &&
20
20
  arraysEqual(left.corners.orientation, right.corners.orientation));
21
21
  }
22
+ // Read-only canonical solved state for comparisons (never mutated).
23
+ const SOLVED_STATE = createSolvedCube2State();
22
24
  export function isSolvedCube2State(state) {
23
- return cube2StatesEqual(state, createSolvedCube2State());
25
+ return cube2StatesEqual(state, SOLVED_STATE);
24
26
  }
25
27
  function range(length) {
26
28
  return Array.from({ length }, (_, index) => index);
@@ -1,4 +1,5 @@
1
1
  import type { Cube2State, Cube2ValidationResult } from "./types.js";
2
+ export declare function isTrivialCube2State(state: Cube2State): boolean;
2
3
  export declare function validateCube2State(state: Cube2State): Cube2ValidationResult;
3
4
  export declare function isOneMoveFromSolvedCube2State(state: Cube2State): boolean;
4
5
  export declare function isTrivialSamplingCube2State(p: number, o: number): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube2/validation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAgBpE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,qBAAqB,CAkB3E;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAIxE;AAKD,wBAAgB,2BAA2B,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAEzE;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAIpE;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAYvE"}
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube2/validation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAwBpE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAuB9D;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,qBAAqB,CAkB3E;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAIxE;AAKD,wBAAgB,2BAA2B,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAEzE;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAIpE;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAYvE"}
@@ -2,16 +2,47 @@ import { CUBE2_MOVES, CORNER_COUNT } from "./types.js";
2
2
  import { applyCube2Move } from "./moves.js";
3
3
  import { createSolvedCube2State, isSolvedCube2State } from "./state.js";
4
4
  import { encodeOrientation, encodePermutation } from "./solver/tables.js";
5
+ // The 18 single-move states, precomputed once so the hot random-state path
6
+ // checks "solved or one move from solved" with pure array comparisons
7
+ // (no coordinate encodings per generated state).
8
+ const ONE_MOVE_FROM_SOLVED_STATES = CUBE2_MOVES.map((move) => applyCube2Move(createSolvedCube2State(), move));
5
9
  // Precomputed coordinate encodings of every single-move state, so the
6
10
  // "one move from solved" check is a couple of O(n) encodings + 18 comparisons
7
11
  // instead of 18 full state applications.
8
- const ONE_MOVE_FROM_SOLVED_ENCODED = CUBE2_MOVES.map((move) => {
9
- const state = applyCube2Move(createSolvedCube2State(), move);
10
- return {
11
- p: encodePermutation(state.corners.permutation),
12
- o: encodeOrientation(state.corners.orientation)
13
- };
14
- });
12
+ const ONE_MOVE_FROM_SOLVED_ENCODED = ONE_MOVE_FROM_SOLVED_STATES.map((state) => ({
13
+ p: encodePermutation(state.corners.permutation),
14
+ o: encodeOrientation(state.corners.orientation)
15
+ }));
16
+ // Cheap trivial check for random-state sampling: solved + the 18 single-move
17
+ // states, compared by arrays with early exit (a random state differs from
18
+ // each candidate within the first couple of permutation entries, so the
19
+ // typical cost is tens of comparisons — no coordinate encodings).
20
+ export function isTrivialCube2State(state) {
21
+ const cp = state.corners.permutation;
22
+ const co = state.corners.orientation;
23
+ for (const candidate of ONE_MOVE_FROM_SOLVED_STATES) {
24
+ const p = candidate.corners.permutation;
25
+ const o = candidate.corners.orientation;
26
+ let match = true;
27
+ for (let i = 0; i < 8; i += 1) {
28
+ if (cp[i] !== p[i]) {
29
+ match = false;
30
+ break;
31
+ }
32
+ }
33
+ if (!match)
34
+ continue;
35
+ for (let i = 0; i < 8; i += 1) {
36
+ if (co[i] !== o[i]) {
37
+ match = false;
38
+ break;
39
+ }
40
+ }
41
+ if (match)
42
+ return true;
43
+ }
44
+ return isSolvedCube2State(state);
45
+ }
15
46
  export function validateCube2State(state) {
16
47
  const errors = [
17
48
  ...validatePermutation("corner", state.corners.permutation, CORNER_COUNT),
@@ -1 +1 @@
1
- {"version":3,"file":"facelets.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/facelets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAgB,MAAM,YAAY,CAAC;AA0DrF,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,aAAa,CA8BhE;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/cube3/facelets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAgB,MAAM,YAAY,CAAC;AAyGrF,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,aAAa,CA6BhE;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"],
@@ -47,20 +49,60 @@ const EDGE_POSITION_SLOTS = [
47
49
  [["B", 5], ["L", 3]],
48
50
  [["B", 3], ["R", 5]]
49
51
  ];
52
+ const CORNER_CELLS = buildCells(CORNER_POSITION_SLOTS, CORNER_PIECE_STICKERS, 3);
53
+ const EDGE_CELLS = buildCells(EDGE_POSITION_SLOTS, EDGE_PIECE_STICKERS, 2);
54
+ function buildCells(slots, stickersTable, orientationCount) {
55
+ const cells = [];
56
+ for (let position = 0; position < slots.length; position += 1) {
57
+ const byPiece = [];
58
+ for (let piece = 0; piece < stickersTable.length; piece += 1) {
59
+ const byOrientation = [];
60
+ for (let orientation = 0; orientation < orientationCount; orientation += 1) {
61
+ const stickers = stickersTable[piece];
62
+ const slotList = slots[position];
63
+ const cell = [];
64
+ for (let index = 0; index < slotList.length; index += 1) {
65
+ const [face, stickerIndex] = slotList[index];
66
+ cell.push(FACE_ORDER.indexOf(face));
67
+ cell.push(stickerIndex);
68
+ cell.push(STICKER_INDEX[stickers[(index - orientation + stickers.length) % stickers.length]]);
69
+ }
70
+ byOrientation.push(cell);
71
+ }
72
+ byPiece.push(byOrientation);
73
+ }
74
+ cells.push(byPiece);
75
+ }
76
+ return cells;
77
+ }
78
+ function placeCells(faces, cells) {
79
+ for (let cellIndex = 0; cellIndex < cells.length; cellIndex += 3) {
80
+ faces[cells[cellIndex]][cells[cellIndex + 1]] = STICKERS[cells[cellIndex + 2]];
81
+ }
82
+ }
50
83
  export function toCube3Facelets(state) {
51
- const facelets = createEmptyFacelets();
52
- for (const face of FACE_ORDER) {
53
- facelets[face][4] = face;
84
+ const facelets = {
85
+ U: Array(9),
86
+ R: Array(9),
87
+ F: Array(9),
88
+ D: Array(9),
89
+ L: Array(9),
90
+ B: Array(9)
91
+ };
92
+ const faces = [facelets.U, facelets.R, facelets.F, facelets.D, facelets.L, facelets.B];
93
+ // Centers (fixed, since no center permutation on the 3x3)
94
+ for (let face = 0; face < 6; face += 1) {
95
+ faces[face][4] = STICKERS[face];
54
96
  }
55
- for (let position = 0; position < CORNER_POSITION_SLOTS.length; position += 1) {
56
- const piece = state.corners.permutation[position];
57
- const orientation = state.corners.orientation[position];
58
- placeOrientedStickers(facelets, CORNER_POSITION_SLOTS[position], CORNER_PIECE_STICKERS[piece], orientation);
97
+ const cornerPermutation = state.corners.permutation;
98
+ const cornerOrientation = state.corners.orientation;
99
+ for (let position = 0; position < CORNER_CELLS.length; position += 1) {
100
+ placeCells(faces, CORNER_CELLS[position][cornerPermutation[position]][cornerOrientation[position]]);
59
101
  }
60
- for (let position = 0; position < EDGE_POSITION_SLOTS.length; position += 1) {
61
- const piece = state.edges.permutation[position];
62
- const orientation = state.edges.orientation[position];
63
- placeOrientedStickers(facelets, EDGE_POSITION_SLOTS[position], EDGE_PIECE_STICKERS[piece], orientation);
102
+ const edgePermutation = state.edges.permutation;
103
+ const edgeOrientation = state.edges.orientation;
104
+ for (let position = 0; position < EDGE_CELLS.length; position += 1) {
105
+ placeCells(faces, EDGE_CELLS[position][edgePermutation[position]][edgeOrientation[position]]);
64
106
  }
65
107
  return facelets;
66
108
  }
@@ -70,20 +112,3 @@ export function formatCube3Facelets(facelets) {
70
112
  export function getCube3FaceOrder() {
71
113
  return FACE_ORDER;
72
114
  }
73
- function createEmptyFacelets() {
74
- return {
75
- U: Array(9).fill("U"),
76
- R: Array(9).fill("R"),
77
- F: Array(9).fill("F"),
78
- D: Array(9).fill("D"),
79
- L: Array(9).fill("L"),
80
- B: Array(9).fill("B")
81
- };
82
- }
83
- function placeOrientedStickers(facelets, slots, stickers, orientation) {
84
- for (let index = 0; index < slots.length; index += 1) {
85
- const [face, stickerIndex] = slots[index];
86
- const sticker = stickers[(index - orientation + stickers.length) % stickers.length];
87
- facelets[face][stickerIndex] = sticker;
88
- }
89
- }
@@ -1 +1 @@
1
- {"version":3,"file":"random-state.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/random-state.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAItE,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,uBAA4B,GAAG,UAAU,CAiB1F"}
1
+ {"version":3,"file":"random-state.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/random-state.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAKtE,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,uBAA4B,GAAG,UAAU,CAsB1F"}
@@ -1,6 +1,7 @@
1
1
  import { createRandomSource } from "../../random/index.js";
2
2
  import { CORNER_COUNT, EDGE_COUNT } from "./types.js";
3
- import { isAllowedRandomCube3State, permutationParity } from "./validation.js";
3
+ import { isOneMoveFromSolvedCube3State, permutationParity } from "./validation.js";
4
+ import { isSolvedCube3State } from "./state.js";
4
5
  import { generateSubsetCube3State } from "./subset-state.js";
5
6
  export function generateRandomCube3State(options = {}) {
6
7
  const random = createRandomSource(options.seed);
@@ -9,8 +10,14 @@ export function generateRandomCube3State(options = {}) {
9
10
  return generateSubsetCube3State(options.subset, random);
10
11
  }
11
12
  for (let attempt = 0; attempt < 100; attempt += 1) {
13
+ // The state is legal by construction (permutations shuffled, orientation
14
+ // sums fixed, parity matched), so the only meaningful rejection is the
15
+ // trivial set (solved + 18 single-move states) — no full re-validation.
12
16
  const state = createLegalRandomState(random);
13
- if (!rejectTrivial || isAllowedRandomCube3State(state)) {
17
+ if (!rejectTrivial) {
18
+ return state;
19
+ }
20
+ if (!isSolvedCube3State(state) && !isOneMoveFromSolvedCube3State(state)) {
14
21
  return state;
15
22
  }
16
23
  }
@@ -44,7 +44,7 @@ export async function generateCube3Scramble(options = {}) {
44
44
  generatorVersion: "1.0.0",
45
45
  facelets,
46
46
  faceletString: formatCube3Facelets(facelets),
47
- state: JSON.parse(JSON.stringify(state)),
47
+ state,
48
48
  solutionMoves: solution.moves,
49
49
  solutionDepth: solution.depth,
50
50
  phase1Depth: solution.phase1.depth,
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,wBAAgB,sBAAsB,IAAI,UAAU,CAWnD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAW7D;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAO7E;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAE7D"}
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,wBAAgB,sBAAsB,IAAI,UAAU,CAWnD;AAMD,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAW7D;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAO7E;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAE7D"}
@@ -11,6 +11,9 @@ export function createSolvedCube3State() {
11
11
  }
12
12
  };
13
13
  }
14
+ // Read-only canonical solved state for comparisons (never mutated, so a
15
+ // single shared instance is safe and avoids allocating per call).
16
+ const SOLVED_STATE = createSolvedCube3State();
14
17
  export function cloneCube3State(state) {
15
18
  return {
16
19
  corners: {
@@ -30,7 +33,7 @@ export function cube3StatesEqual(left, right) {
30
33
  arraysEqual(left.edges.orientation, right.edges.orientation));
31
34
  }
32
35
  export function isSolvedCube3State(state) {
33
- return cube3StatesEqual(state, createSolvedCube3State());
36
+ return cube3StatesEqual(state, SOLVED_STATE);
34
37
  }
35
38
  function range(length) {
36
39
  return Array.from({ length }, (_, index) => index);
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/validation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAIpE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,qBAAqB,CA+B3E;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAGxE;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAIpE;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAYvE"}
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/validation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAIpE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,qBAAqB,CA+B3E;AASD,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAExE;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAIpE;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAYvE"}