@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
@@ -28,9 +28,12 @@ export function validateCube3State(state) {
28
28
  errors
29
29
  };
30
30
  }
31
+ // The 18 single-move states, precomputed once so the hot random-state path
32
+ // checks "one move from solved" with pure comparisons instead of 18 full
33
+ // move applications per generated state.
34
+ const ONE_MOVE_FROM_SOLVED_STATES = CUBE3_MOVES.map((move) => applyCube3Move(createSolvedCube3State(), move));
31
35
  export function isOneMoveFromSolvedCube3State(state) {
32
- const solved = createSolvedCube3State();
33
- return CUBE3_MOVES.some((move) => cube3StatesEqual(applyCube3Move(solved, move), state));
36
+ return ONE_MOVE_FROM_SOLVED_STATES.some((candidate) => cube3StatesEqual(candidate, state));
34
37
  }
35
38
  export function isAllowedRandomCube3State(state) {
36
39
  return validateCube3State(state).valid &&
@@ -1,3 +1,4 @@
1
1
  import type { SkewbRandomStateOptions, SkewbState } from "./types.js";
2
2
  export declare function generateRandomSkewbState(options?: SkewbRandomStateOptions): Promise<SkewbState>;
3
+ export declare function generateRandomSkewbStateSync(options?: SkewbRandomStateOptions): SkewbState;
3
4
  //# sourceMappingURL=random-state.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"random-state.d.ts","sourceRoot":"","sources":["../../../src/puzzles/skewb/random-state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAatE,wBAAsB,wBAAwB,CAC5C,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,UAAU,CAAC,CAiBrB"}
1
+ {"version":3,"file":"random-state.d.ts","sourceRoot":"","sources":["../../../src/puzzles/skewb/random-state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AActE,wBAAsB,wBAAwB,CAC5C,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,UAAU,CAAC,CAGrB;AAID,wBAAgB,4BAA4B,CAAC,OAAO,GAAE,uBAA4B,GAAG,UAAU,CAmB9F"}
@@ -1,7 +1,15 @@
1
1
  import { createRandomSource } from "../../random/index.js";
2
- import { ensureTablesLoaded, getCentersTransition, getOrientationTransition, getPermutationTransition, getStateFromRank, MOVE_COUNT, rankFromComponents, SOLVED_RANK, STATE_COUNT, } from "./solver/index.js";
2
+ import { ensureTablesLoaded, areSkewbTablesLoaded, getCentersTransition, getOrientationTransition, getPermutationTransition, getStateFromRank, MOVE_COUNT, rankFromComponents, SOLVED_RANK, STATE_COUNT, } from "./solver/index.js";
3
3
  export async function generateRandomSkewbState(options = {}) {
4
4
  await ensureTablesLoaded();
5
+ return generateRandomSkewbStateSync(options);
6
+ }
7
+ // Synchronous variant for callers that have already ensured the tables are
8
+ // loaded (e.g. the scrambler after the first call): no per-call await hop.
9
+ export function generateRandomSkewbStateSync(options = {}) {
10
+ if (!areSkewbTablesLoaded()) {
11
+ throw new Error("Skewb solver tables not loaded. Call ensureTablesLoaded() first.");
12
+ }
5
13
  const random = createRandomSource(options.seed);
6
14
  const rejectTrivial = options.rejectTrivial ?? true;
7
15
  const trivialRanks = rejectTrivial ? getTrivialRanks() : null;
@@ -1 +1 @@
1
- {"version":3,"file":"scrambler.d.ts","sourceRoot":"","sources":["../../../src/puzzles/skewb/scrambler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAa9F,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,CAgDzB"}
1
+ {"version":3,"file":"scrambler.d.ts","sourceRoot":"","sources":["../../../src/puzzles/skewb/scrambler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAa9F,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,CAoDzB"}
@@ -1,4 +1,4 @@
1
- import { generateRandomSkewbState } from "./random-state.js";
1
+ import { generateRandomSkewbState, generateRandomSkewbStateSync } from "./random-state.js";
2
2
  import { formatSkewbAlgorithm } from "./moves.js";
3
3
  import { areSkewbTablesLoaded, solveSkewbIndices, solveSkewbIndicesSync, verifySkewbScrambleIndices, SKEWB_MOVES } from "./solver/index.js";
4
4
  import { formatSkewbFacelets, toSkewbFacelets } from "./facelets.js";
@@ -16,7 +16,11 @@ export const skewbScrambleGenerator = {
16
16
  },
17
17
  };
18
18
  export async function generateSkewbScramble(options = {}) {
19
- const state = await generateRandomSkewbState(options);
19
+ // generateRandomSkewbStateSync guarantees the tables are loaded, so use
20
+ // the sync state generation on every call after the first (no await hop).
21
+ const state = areSkewbTablesLoaded()
22
+ ? generateRandomSkewbStateSync(options)
23
+ : await generateRandomSkewbState(options);
20
24
  // generateRandomSkewbState guarantees the tables are loaded, so use the
21
25
  // sync solve on every call after the first (no extra microtask).
22
26
  const solution = areSkewbTablesLoaded()
@@ -29,6 +29,7 @@ export declare let ORIENT_CLASS: Uint8Array | null;
29
29
  export declare let R7_SETS: Uint16Array | null;
30
30
  export declare let PERM7_UNRANK: Uint8Array | null;
31
31
  export declare let CENTER_UNRANK: Uint8Array | null;
32
+ export declare let ORIENT_UNRANK: Uint8Array | null;
32
33
  export declare let SOLVED_RANK: number;
33
34
  export declare function ensureTablesLoaded(): Promise<void>;
34
35
  export declare function areSkewbTablesLoaded(): boolean;
@@ -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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"}
@@ -149,6 +149,7 @@ export let ORIENT_CLASS = null; // rO -> class 0..8
149
149
  export let R7_SETS = null; // (rC_even * 9 + class) * 4 + i
150
150
  export let PERM7_UNRANK = null; // r7 * 7 + i -> piece at corner i+1 (derived at load)
151
151
  export let CENTER_UNRANK = null; // rC * 6 + i -> center piece (derived at load)
152
+ export let ORIENT_UNRANK = null; // rO * 7 + i -> orientation of corner i+1 (derived at load)
152
153
  export let SOLVED_RANK = 0;
153
154
  export async function ensureTablesLoaded() {
154
155
  if (tablesLoaded)
@@ -185,6 +186,12 @@ export function assignTables(tables) {
185
186
  for (let i = 0; i < 6; i++)
186
187
  CENTER_UNRANK[rC * 6 + i] = centers[i];
187
188
  }
189
+ ORIENT_UNRANK = new Uint8Array(ORIENTATION_COUNT * 7);
190
+ for (let rO = 0; rO < ORIENTATION_COUNT; rO++) {
191
+ const orient = getOrientationFromIndex(rO);
192
+ for (let i = 0; i < 7; i++)
193
+ ORIENT_UNRANK[rO * 7 + i] = orient[i + 1];
194
+ }
188
195
  SOLVED_RANK = rankFromComponents(0, 0, 0);
189
196
  tablesLoaded = true;
190
197
  }
@@ -200,6 +207,7 @@ export function resetTablesForTests() {
200
207
  R7_SETS = null;
201
208
  PERM7_UNRANK = null;
202
209
  CENTER_UNRANK = null;
210
+ ORIENT_UNRANK = null;
203
211
  }
204
212
  // ---------------------------------------------------------------------------
205
213
  // Compact rank helpers
@@ -245,11 +253,9 @@ export function getStateFromRank(rank) {
245
253
  perm[i + 1] = PERM7_UNRANK[permBase + i];
246
254
  const orient = new Array(8);
247
255
  orient[0] = 0;
248
- let val = rO;
249
- for (let i = 1; i < 8; i++) {
250
- orient[i] = val % 3;
251
- val = Math.floor(val / 3);
252
- }
256
+ const orientBase = rO * 7;
257
+ for (let i = 1; i < 8; i++)
258
+ orient[i] = ORIENT_UNRANK[orientBase + i - 1];
253
259
  const centers = new Array(6);
254
260
  const centerBase = EVEN_CENTER_FULL[even] * 6;
255
261
  for (let i = 0; i < 6; i++)
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.generateRandomCube2State = generateRandomCube2State;
4
4
  const index_js_1 = require("../../random/index.js");
5
5
  const validation_js_1 = require("./validation.js");
6
- const tables_js_1 = require("./solver/tables.js");
7
6
  function generateRandomCube2State(options = {}) {
8
7
  const random = (0, index_js_1.createRandomSource)(options.seed);
9
8
  const rejectTrivial = options.rejectTrivial ?? true;
@@ -11,15 +10,10 @@ function generateRandomCube2State(options = {}) {
11
10
  // The state is legal by construction (DBL corner fixed, even
12
11
  // permutation, orientation sum divisible by 3, values in range), so the
13
12
  // only meaningful rejection is the trivial set: solved + the 18
14
- // single-move states (scrambles shorter than 2 moves). Coordinates are
15
- // encoded once for both checks.
13
+ // single-move states (scrambles shorter than 2 moves), compared by
14
+ // arrays no coordinate encodings in this hot path.
16
15
  const state = createLegalRandomState(random);
17
- if (!rejectTrivial) {
18
- return state;
19
- }
20
- const p = (0, tables_js_1.encodePermutation)(state.corners.permutation);
21
- const o = (0, tables_js_1.encodeOrientation)(state.corners.orientation);
22
- if (!(0, validation_js_1.isTrivialSamplingCube2State)(p, o)) {
16
+ if (!rejectTrivial || !(0, validation_js_1.isTrivialCube2State)(state)) {
23
17
  return state;
24
18
  }
25
19
  }
@@ -22,12 +22,12 @@ async function loadBrowserTables(baseUrl) {
22
22
  const [permTable, orientTable, dist] = await Promise.all([
23
23
  fetchUint16Table(resolved, manifest.files.permutationTable, table_metadata_js_1.CUBE2_SOLVER_TABLE_METADATA.permutationTable.entries),
24
24
  fetchUint16Table(resolved, manifest.files.orientationTable, table_metadata_js_1.CUBE2_SOLVER_TABLE_METADATA.orientationTable.entries),
25
- fetchUint8Table(resolved, manifest.files.distances, table_metadata_js_1.CUBE2_SOLVER_TABLE_METADATA.distances.entries)
25
+ fetchUint8Table(resolved, manifest.files.policy, table_metadata_js_1.CUBE2_SOLVER_TABLE_METADATA.policy.entries)
26
26
  ]);
27
27
  cachedBrowserTables = {
28
28
  permutationTable: permTable,
29
29
  orientationTable: orientTable,
30
- distances: dist
30
+ policyTable: dist
31
31
  };
32
32
  return cachedBrowserTables;
33
33
  }
@@ -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
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PERMUTATION_TABLE = exports.ORIENTATION_TABLE = exports.ORIENTATION_COUNT = exports.MOVE_COUNT = exports.getOrientationTransition = exports.getPermutationTransition = exports.getDistance = exports.encodeOrientation = exports.encodePermutation = exports.ensureTablesLoaded = exports.DISTANCES = exports.BFS_MOVES = exports.areCube2TablesLoaded = exports.verifyCube2SolutionIndices = exports.verifyCube2Solution = exports.verifyCube2Scramble = exports.solveCube2StateIndicesSync = exports.solveCube2StateIndices = exports.solveCube2State = void 0;
3
+ exports.PERMUTATION_TABLE = exports.ORIENTATION_TABLE = exports.ORIENTATION_COUNT = exports.MOVE_COUNT = exports.getOrientationTransition = exports.getPermutationTransition = exports.getDistance = exports.encodeOrientation = exports.encodePermutation = exports.ensureTablesLoaded = exports.POLICY_TABLE = exports.BFS_MOVES = exports.areCube2TablesLoaded = exports.verifyCube2SolutionIndices = exports.verifyCube2Solution = exports.verifyCube2Scramble = exports.solveCube2StateIndicesSync = exports.solveCube2StateIndices = exports.solveCube2State = void 0;
4
4
  var search_js_1 = require("./search.js");
5
5
  Object.defineProperty(exports, "solveCube2State", { enumerable: true, get: function () { return search_js_1.solveCube2State; } });
6
6
  Object.defineProperty(exports, "solveCube2StateIndices", { enumerable: true, get: function () { return search_js_1.solveCube2StateIndices; } });
@@ -11,7 +11,7 @@ Object.defineProperty(exports, "verifyCube2SolutionIndices", { enumerable: true,
11
11
  var tables_js_1 = require("./tables.js");
12
12
  Object.defineProperty(exports, "areCube2TablesLoaded", { enumerable: true, get: function () { return tables_js_1.areCube2TablesLoaded; } });
13
13
  Object.defineProperty(exports, "BFS_MOVES", { enumerable: true, get: function () { return tables_js_1.BFS_MOVES; } });
14
- Object.defineProperty(exports, "DISTANCES", { enumerable: true, get: function () { return tables_js_1.DISTANCES; } });
14
+ Object.defineProperty(exports, "POLICY_TABLE", { enumerable: true, get: function () { return tables_js_1.POLICY_TABLE; } });
15
15
  Object.defineProperty(exports, "ensureTablesLoaded", { enumerable: true, get: function () { return tables_js_1.ensureTablesLoaded; } });
16
16
  Object.defineProperty(exports, "encodePermutation", { enumerable: true, get: function () { return tables_js_1.encodePermutation; } });
17
17
  Object.defineProperty(exports, "encodeOrientation", { enumerable: true, get: function () { return tables_js_1.encodeOrientation; } });
@@ -17,11 +17,11 @@ function readGeneratedCube2SolverTables() {
17
17
  }
18
18
  const permutationTable = readUint16Table(assetDirectory, manifest.files.permutationTable, table_metadata_js_1.CUBE2_SOLVER_TABLE_METADATA.permutationTable.checksum);
19
19
  const orientationTable = readUint16Table(assetDirectory, manifest.files.orientationTable, table_metadata_js_1.CUBE2_SOLVER_TABLE_METADATA.orientationTable.checksum);
20
- const distances = readUint8Table(assetDirectory, manifest.files.distances, table_metadata_js_1.CUBE2_SOLVER_TABLE_METADATA.distances.checksum);
20
+ const policyTable = readUint8Table(assetDirectory, manifest.files.policy, table_metadata_js_1.CUBE2_SOLVER_TABLE_METADATA.policy.checksum);
21
21
  return {
22
22
  permutationTable,
23
23
  orientationTable,
24
- distances
24
+ policyTable
25
25
  };
26
26
  }
27
27
  function readUint16Table(assetDirectory, entry, expectedChecksum) {
@@ -25,7 +25,7 @@ async function solveCube2StateIndices(state) {
25
25
  return solveWithTablesIndices(state);
26
26
  }
27
27
  function solveCube2StateIndicesSync(state) {
28
- if (!tables_js_1.DISTANCES) {
28
+ if (!tables_js_1.POLICY_TABLE) {
29
29
  throw new Error("Cube2 solver tables not loaded. Call ensureTablesLoaded() first.");
30
30
  }
31
31
  return solveWithTablesIndices(state);
@@ -37,39 +37,19 @@ 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 distances = tables_js_1.DISTANCES;
40
+ const policy = tables_js_1.POLICY_TABLE;
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 = [];
44
- // High-performance branchless inlined lookup
45
- const initialCombined = permIndex * tables_js_1.ORIENTATION_COUNT + orientIndex;
46
- let distance = (distances[initialCombined >> 1] >> ((initialCombined & 1) << 2)) & 0x0f;
47
- if (distance === 0) {
48
- return moves;
49
- }
50
- while (distance > 0) {
51
- let foundNext = false;
52
- const permOffset = permIndex * tables_js_1.MOVE_COUNT;
53
- const orientOffset = orientIndex * tables_js_1.MOVE_COUNT;
54
- const nextDistance = distance - 1;
55
- for (let m = 0; m < tables_js_1.BFS_MOVES.length; m++) {
56
- const nextPerm = permTable[permOffset + m];
57
- const nextOrient = orientTable[orientOffset + m];
58
- // High-performance branchless inlined lookup
59
- const combined = nextPerm * tables_js_1.ORIENTATION_COUNT + nextOrient;
60
- const nextDist = (distances[combined >> 1] >> ((combined & 1) << 2)) & 0x0f;
61
- if (nextDist === nextDistance) {
62
- moves.push(m);
63
- permIndex = nextPerm;
64
- orientIndex = nextOrient;
65
- distance = nextDistance;
66
- foundNext = true;
67
- break;
68
- }
69
- }
70
- if (!foundNext) {
71
- throw new Error("BFS distance table corrupted or unreachable state.");
72
- }
44
+ // Direct policy walk: the precomputed optimal move per state, so no
45
+ // candidate scanning (the old BFS-distance gradient walk) is needed.
46
+ let combined = permIndex * tables_js_1.ORIENTATION_COUNT + orientIndex;
47
+ while (combined !== 0) {
48
+ const m = (policy[combined >> 1] >> ((combined & 1) << 2)) & 0x0f;
49
+ moves.push(m);
50
+ permIndex = permTable[permIndex * tables_js_1.MOVE_COUNT + m];
51
+ orientIndex = orientTable[orientIndex * tables_js_1.MOVE_COUNT + m];
52
+ combined = permIndex * tables_js_1.ORIENTATION_COUNT + orientIndex;
73
53
  }
74
54
  return moves;
75
55
  }
@@ -10,9 +10,9 @@ exports.CUBE2_SOLVER_TABLE_METADATA = {
10
10
  entries: 6561,
11
11
  checksum: 1566738619
12
12
  },
13
- distances: {
13
+ policy: {
14
14
  entries: 1837080,
15
- checksum: 2959532459,
15
+ checksum: 2042005723,
16
16
  maxDepth: 11
17
17
  }
18
18
  };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.BFS_MOVES = exports.DISTANCES = exports.ORIENTATION_TABLE = exports.PERMUTATION_TABLE = exports.MOVE_COUNT = exports.ORIENTATION_COUNT = exports.PERMUTATION_COUNT = void 0;
3
+ exports.BFS_MOVES = 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;
@@ -15,19 +15,31 @@ exports.MOVE_COUNT = 9;
15
15
  // ---------------------------------------------------------------------------
16
16
  // Coordinate encoding / decoding
17
17
  // ---------------------------------------------------------------------------
18
+ // Lehmer encoding of the 7 free corners (DBL at index 6 is fixed), with a
19
+ // bitmask instead of array removal: available values tracked as bits, the
20
+ // rank of a value = the popcount of the bits below it.
21
+ const PERM7_FACT = [720, 120, 24, 6, 2, 1, 1];
18
22
  function encodePermutation(permutation) {
19
- const perm7 = permutation.filter((_, i) => i !== 6);
20
- const mapped = perm7.map((x) => (x > 6 ? x - 1 : x));
21
23
  let index = 0;
22
- const available = [0, 1, 2, 3, 4, 5, 6];
24
+ let available = 0b1111111;
23
25
  for (let position = 0; position < 7; position += 1) {
24
- const value = mapped[position];
25
- const selected = available.indexOf(value);
26
- index += selected * factorialSmall(6 - position);
27
- available.splice(selected, 1);
26
+ const permPosition = position < 6 ? position : position + 1;
27
+ const piece = permutation[permPosition];
28
+ const value = piece > 6 ? piece - 1 : piece;
29
+ const rank = popcount(available & ((1 << value) - 1));
30
+ index += rank * PERM7_FACT[position];
31
+ available &= ~(1 << value);
28
32
  }
29
33
  return index;
30
34
  }
35
+ function popcount(x) {
36
+ let count = 0;
37
+ while (x !== 0) {
38
+ x &= x - 1;
39
+ count += 1;
40
+ }
41
+ return count;
42
+ }
31
43
  function encodeOrientation(orientation) {
32
44
  const orient7 = orientation.filter((_, i) => i !== 6);
33
45
  let index = 0;
@@ -51,7 +63,7 @@ function factorialSmall(n) {
51
63
  let tablesLoaded = false;
52
64
  exports.PERMUTATION_TABLE = null;
53
65
  exports.ORIENTATION_TABLE = null;
54
- exports.DISTANCES = null;
66
+ exports.POLICY_TABLE = null;
55
67
  async function ensureTablesLoaded() {
56
68
  if (tablesLoaded) {
57
69
  return;
@@ -59,7 +71,7 @@ async function ensureTablesLoaded() {
59
71
  const tables = await (0, generated_assets_js_1.loadGeneratedCube2SolverTables)();
60
72
  exports.PERMUTATION_TABLE = tables.permutationTable;
61
73
  exports.ORIENTATION_TABLE = tables.orientationTable;
62
- exports.DISTANCES = tables.distances;
74
+ exports.POLICY_TABLE = tables.policyTable;
63
75
  tablesLoaded = true;
64
76
  }
65
77
  function areCube2TablesLoaded() {
@@ -69,12 +81,23 @@ function areCube2TablesLoaded() {
69
81
  // Public API
70
82
  // ---------------------------------------------------------------------------
71
83
  function getDistance(permIndex, orientIndex) {
72
- if (!exports.DISTANCES) {
84
+ if (!exports.POLICY_TABLE) {
73
85
  throw new Error("Cube2 solver tables not loaded. Call ensureTablesLoaded() first.");
74
86
  }
75
- const combined = permIndex * exports.ORIENTATION_COUNT + orientIndex;
76
- const byteValue = exports.DISTANCES[combined >> 1];
77
- return (combined & 1) === 0 ? (byteValue & 0x0f) : (byteValue >> 4);
87
+ const permTable = exports.PERMUTATION_TABLE;
88
+ const orientTable = exports.ORIENTATION_TABLE;
89
+ let p = permIndex;
90
+ let o = orientIndex;
91
+ let combined = p * exports.ORIENTATION_COUNT + o;
92
+ let distance = 0;
93
+ while (combined !== 0) {
94
+ const m = (exports.POLICY_TABLE[combined >> 1] >> ((combined & 1) << 2)) & 0x0f;
95
+ p = permTable[p * exports.MOVE_COUNT + m];
96
+ o = orientTable[o * exports.MOVE_COUNT + m];
97
+ combined = p * exports.ORIENTATION_COUNT + o;
98
+ distance += 1;
99
+ }
100
+ return distance;
78
101
  }
79
102
  function getPermutationTransition(move, permIndex) {
80
103
  if (!exports.PERMUTATION_TABLE) {
@@ -25,8 +25,10 @@ function cube2StatesEqual(left, right) {
25
25
  return (arraysEqual(left.corners.permutation, right.corners.permutation) &&
26
26
  arraysEqual(left.corners.orientation, right.corners.orientation));
27
27
  }
28
+ // Read-only canonical solved state for comparisons (never mutated).
29
+ const SOLVED_STATE = createSolvedCube2State();
28
30
  function isSolvedCube2State(state) {
29
- return cube2StatesEqual(state, createSolvedCube2State());
31
+ return cube2StatesEqual(state, SOLVED_STATE);
30
32
  }
31
33
  function range(length) {
32
34
  return Array.from({ length }, (_, index) => index);
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isTrivialCube2State = isTrivialCube2State;
3
4
  exports.validateCube2State = validateCube2State;
4
5
  exports.isOneMoveFromSolvedCube2State = isOneMoveFromSolvedCube2State;
5
6
  exports.isTrivialSamplingCube2State = isTrivialSamplingCube2State;
@@ -9,16 +10,47 @@ const types_js_1 = require("./types.js");
9
10
  const moves_js_1 = require("./moves.js");
10
11
  const state_js_1 = require("./state.js");
11
12
  const tables_js_1 = require("./solver/tables.js");
13
+ // The 18 single-move states, precomputed once so the hot random-state path
14
+ // checks "solved or one move from solved" with pure array comparisons
15
+ // (no coordinate encodings per generated state).
16
+ const ONE_MOVE_FROM_SOLVED_STATES = types_js_1.CUBE2_MOVES.map((move) => (0, moves_js_1.applyCube2Move)((0, state_js_1.createSolvedCube2State)(), move));
12
17
  // Precomputed coordinate encodings of every single-move state, so the
13
18
  // "one move from solved" check is a couple of O(n) encodings + 18 comparisons
14
19
  // instead of 18 full state applications.
15
- const ONE_MOVE_FROM_SOLVED_ENCODED = types_js_1.CUBE2_MOVES.map((move) => {
16
- const state = (0, moves_js_1.applyCube2Move)((0, state_js_1.createSolvedCube2State)(), move);
17
- return {
18
- p: (0, tables_js_1.encodePermutation)(state.corners.permutation),
19
- o: (0, tables_js_1.encodeOrientation)(state.corners.orientation)
20
- };
21
- });
20
+ const ONE_MOVE_FROM_SOLVED_ENCODED = ONE_MOVE_FROM_SOLVED_STATES.map((state) => ({
21
+ p: (0, tables_js_1.encodePermutation)(state.corners.permutation),
22
+ o: (0, tables_js_1.encodeOrientation)(state.corners.orientation)
23
+ }));
24
+ // Cheap trivial check for random-state sampling: solved + the 18 single-move
25
+ // states, compared by arrays with early exit (a random state differs from
26
+ // each candidate within the first couple of permutation entries, so the
27
+ // typical cost is tens of comparisons — no coordinate encodings).
28
+ function isTrivialCube2State(state) {
29
+ const cp = state.corners.permutation;
30
+ const co = state.corners.orientation;
31
+ for (const candidate of ONE_MOVE_FROM_SOLVED_STATES) {
32
+ const p = candidate.corners.permutation;
33
+ const o = candidate.corners.orientation;
34
+ let match = true;
35
+ for (let i = 0; i < 8; i += 1) {
36
+ if (cp[i] !== p[i]) {
37
+ match = false;
38
+ break;
39
+ }
40
+ }
41
+ if (!match)
42
+ continue;
43
+ for (let i = 0; i < 8; i += 1) {
44
+ if (co[i] !== o[i]) {
45
+ match = false;
46
+ break;
47
+ }
48
+ }
49
+ if (match)
50
+ return true;
51
+ }
52
+ return (0, state_js_1.isSolvedCube2State)(state);
53
+ }
22
54
  function validateCube2State(state) {
23
55
  const errors = [
24
56
  ...validatePermutation("corner", state.corners.permutation, types_js_1.CORNER_COUNT),
@@ -4,6 +4,8 @@ exports.toCube3Facelets = toCube3Facelets;
4
4
  exports.formatCube3Facelets = formatCube3Facelets;
5
5
  exports.getCube3FaceOrder = getCube3FaceOrder;
6
6
  const FACE_ORDER = ["U", "R", "F", "D", "L", "B"];
7
+ const STICKERS = ["U", "R", "F", "D", "L", "B"];
8
+ const STICKER_INDEX = { U: 0, R: 1, F: 2, D: 3, L: 4, B: 5 };
7
9
  const CORNER_PIECE_STICKERS = [
8
10
  ["U", "R", "F"],
9
11
  ["U", "F", "L"],
@@ -52,20 +54,60 @@ const EDGE_POSITION_SLOTS = [
52
54
  [["B", 5], ["L", 3]],
53
55
  [["B", 3], ["R", 5]]
54
56
  ];
57
+ const CORNER_CELLS = buildCells(CORNER_POSITION_SLOTS, CORNER_PIECE_STICKERS, 3);
58
+ const EDGE_CELLS = buildCells(EDGE_POSITION_SLOTS, EDGE_PIECE_STICKERS, 2);
59
+ function buildCells(slots, stickersTable, orientationCount) {
60
+ const cells = [];
61
+ for (let position = 0; position < slots.length; position += 1) {
62
+ const byPiece = [];
63
+ for (let piece = 0; piece < stickersTable.length; piece += 1) {
64
+ const byOrientation = [];
65
+ for (let orientation = 0; orientation < orientationCount; orientation += 1) {
66
+ const stickers = stickersTable[piece];
67
+ const slotList = slots[position];
68
+ const cell = [];
69
+ for (let index = 0; index < slotList.length; index += 1) {
70
+ const [face, stickerIndex] = slotList[index];
71
+ cell.push(FACE_ORDER.indexOf(face));
72
+ cell.push(stickerIndex);
73
+ cell.push(STICKER_INDEX[stickers[(index - orientation + stickers.length) % stickers.length]]);
74
+ }
75
+ byOrientation.push(cell);
76
+ }
77
+ byPiece.push(byOrientation);
78
+ }
79
+ cells.push(byPiece);
80
+ }
81
+ return cells;
82
+ }
83
+ function placeCells(faces, cells) {
84
+ for (let cellIndex = 0; cellIndex < cells.length; cellIndex += 3) {
85
+ faces[cells[cellIndex]][cells[cellIndex + 1]] = STICKERS[cells[cellIndex + 2]];
86
+ }
87
+ }
55
88
  function toCube3Facelets(state) {
56
- const facelets = createEmptyFacelets();
57
- for (const face of FACE_ORDER) {
58
- facelets[face][4] = face;
89
+ const facelets = {
90
+ U: Array(9),
91
+ R: Array(9),
92
+ F: Array(9),
93
+ D: Array(9),
94
+ L: Array(9),
95
+ B: Array(9)
96
+ };
97
+ const faces = [facelets.U, facelets.R, facelets.F, facelets.D, facelets.L, facelets.B];
98
+ // Centers (fixed, since no center permutation on the 3x3)
99
+ for (let face = 0; face < 6; face += 1) {
100
+ faces[face][4] = STICKERS[face];
59
101
  }
60
- for (let position = 0; position < CORNER_POSITION_SLOTS.length; position += 1) {
61
- const piece = state.corners.permutation[position];
62
- const orientation = state.corners.orientation[position];
63
- placeOrientedStickers(facelets, CORNER_POSITION_SLOTS[position], CORNER_PIECE_STICKERS[piece], orientation);
102
+ const cornerPermutation = state.corners.permutation;
103
+ const cornerOrientation = state.corners.orientation;
104
+ for (let position = 0; position < CORNER_CELLS.length; position += 1) {
105
+ placeCells(faces, CORNER_CELLS[position][cornerPermutation[position]][cornerOrientation[position]]);
64
106
  }
65
- for (let position = 0; position < EDGE_POSITION_SLOTS.length; position += 1) {
66
- const piece = state.edges.permutation[position];
67
- const orientation = state.edges.orientation[position];
68
- placeOrientedStickers(facelets, EDGE_POSITION_SLOTS[position], EDGE_PIECE_STICKERS[piece], orientation);
107
+ const edgePermutation = state.edges.permutation;
108
+ const edgeOrientation = state.edges.orientation;
109
+ for (let position = 0; position < EDGE_CELLS.length; position += 1) {
110
+ placeCells(faces, EDGE_CELLS[position][edgePermutation[position]][edgeOrientation[position]]);
69
111
  }
70
112
  return facelets;
71
113
  }
@@ -75,20 +117,3 @@ function formatCube3Facelets(facelets) {
75
117
  function getCube3FaceOrder() {
76
118
  return FACE_ORDER;
77
119
  }
78
- function createEmptyFacelets() {
79
- return {
80
- U: Array(9).fill("U"),
81
- R: Array(9).fill("R"),
82
- F: Array(9).fill("F"),
83
- D: Array(9).fill("D"),
84
- L: Array(9).fill("L"),
85
- B: Array(9).fill("B")
86
- };
87
- }
88
- function placeOrientedStickers(facelets, slots, stickers, orientation) {
89
- for (let index = 0; index < slots.length; index += 1) {
90
- const [face, stickerIndex] = slots[index];
91
- const sticker = stickers[(index - orientation + stickers.length) % stickers.length];
92
- facelets[face][stickerIndex] = sticker;
93
- }
94
- }
@@ -4,6 +4,7 @@ exports.generateRandomCube3State = generateRandomCube3State;
4
4
  const index_js_1 = require("../../random/index.js");
5
5
  const types_js_1 = require("./types.js");
6
6
  const validation_js_1 = require("./validation.js");
7
+ const state_js_1 = require("./state.js");
7
8
  const subset_state_js_1 = require("./subset-state.js");
8
9
  function generateRandomCube3State(options = {}) {
9
10
  const random = (0, index_js_1.createRandomSource)(options.seed);
@@ -12,8 +13,14 @@ function generateRandomCube3State(options = {}) {
12
13
  return (0, subset_state_js_1.generateSubsetCube3State)(options.subset, random);
13
14
  }
14
15
  for (let attempt = 0; attempt < 100; attempt += 1) {
16
+ // The state is legal by construction (permutations shuffled, orientation
17
+ // sums fixed, parity matched), so the only meaningful rejection is the
18
+ // trivial set (solved + 18 single-move states) — no full re-validation.
15
19
  const state = createLegalRandomState(random);
16
- if (!rejectTrivial || (0, validation_js_1.isAllowedRandomCube3State)(state)) {
20
+ if (!rejectTrivial) {
21
+ return state;
22
+ }
23
+ if (!(0, state_js_1.isSolvedCube3State)(state) && !(0, validation_js_1.isOneMoveFromSolvedCube3State)(state)) {
17
24
  return state;
18
25
  }
19
26
  }
@@ -48,7 +48,7 @@ async function generateCube3Scramble(options = {}) {
48
48
  generatorVersion: "1.0.0",
49
49
  facelets,
50
50
  faceletString: (0, facelets_js_1.formatCube3Facelets)(facelets),
51
- state: JSON.parse(JSON.stringify(state)),
51
+ state,
52
52
  solutionMoves: solution.moves,
53
53
  solutionDepth: solution.depth,
54
54
  phase1Depth: solution.phase1.depth,
@@ -17,6 +17,9 @@ function createSolvedCube3State() {
17
17
  }
18
18
  };
19
19
  }
20
+ // Read-only canonical solved state for comparisons (never mutated, so a
21
+ // single shared instance is safe and avoids allocating per call).
22
+ const SOLVED_STATE = createSolvedCube3State();
20
23
  function cloneCube3State(state) {
21
24
  return {
22
25
  corners: {
@@ -36,7 +39,7 @@ function cube3StatesEqual(left, right) {
36
39
  arraysEqual(left.edges.orientation, right.edges.orientation));
37
40
  }
38
41
  function isSolvedCube3State(state) {
39
- return cube3StatesEqual(state, createSolvedCube3State());
42
+ return cube3StatesEqual(state, SOLVED_STATE);
40
43
  }
41
44
  function range(length) {
42
45
  return Array.from({ length }, (_, index) => index);