@cubingcenter/scrambler 0.4.1 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +21 -0
  2. package/dist/core/api.d.ts.map +1 -1
  3. package/dist/core/api.js +6 -1
  4. package/dist/puzzles/pyraminx/facelets.d.ts.map +1 -1
  5. package/dist/puzzles/pyraminx/facelets.js +43 -17
  6. package/dist/puzzles/pyraminx/random-state.d.ts.map +1 -1
  7. package/dist/puzzles/pyraminx/random-state.js +63 -29
  8. package/dist/puzzles/pyraminx/scrambler.d.ts.map +1 -1
  9. package/dist/puzzles/pyraminx/scrambler.js +35 -26
  10. package/dist/puzzles/pyraminx/solver/search.d.ts +3 -0
  11. package/dist/puzzles/pyraminx/solver/search.d.ts.map +1 -1
  12. package/dist/puzzles/pyraminx/solver/search.js +60 -5
  13. package/dist/puzzles/pyraminx/solver/tables.d.ts +1 -0
  14. package/dist/puzzles/pyraminx/solver/tables.d.ts.map +1 -1
  15. package/dist/puzzles/pyraminx/solver/tables.js +3 -0
  16. package/dist/puzzles/skewb/facelets.d.ts.map +1 -1
  17. package/dist/puzzles/skewb/facelets.js +46 -23
  18. package/dist/puzzles/skewb/index.d.ts +2 -1
  19. package/dist/puzzles/skewb/index.d.ts.map +1 -1
  20. package/dist/puzzles/skewb/index.js +2 -1
  21. package/dist/puzzles/skewb/random-state.d.ts +1 -1
  22. package/dist/puzzles/skewb/random-state.d.ts.map +1 -1
  23. package/dist/puzzles/skewb/random-state.js +25 -18
  24. package/dist/puzzles/skewb/scrambler.d.ts.map +1 -1
  25. package/dist/puzzles/skewb/scrambler.js +27 -16
  26. package/dist/puzzles/skewb/solver/browser-loader.d.ts +20 -0
  27. package/dist/puzzles/skewb/solver/browser-loader.d.ts.map +1 -0
  28. package/dist/puzzles/skewb/solver/browser-loader.js +86 -0
  29. package/dist/puzzles/skewb/solver/generated/centers-table.bin +0 -0
  30. package/dist/puzzles/skewb/solver/generated/helpers.bin +0 -0
  31. package/dist/puzzles/skewb/solver/generated/manifest.json +31 -0
  32. package/dist/puzzles/skewb/solver/generated/orientation-table.bin +0 -0
  33. package/dist/puzzles/skewb/solver/generated/permutation-table.bin +0 -0
  34. package/dist/puzzles/skewb/solver/generated/policy.bin +0 -0
  35. package/dist/puzzles/skewb/solver/generated-assets.d.ts +10 -0
  36. package/dist/puzzles/skewb/solver/generated-assets.d.ts.map +1 -0
  37. package/dist/puzzles/skewb/solver/generated-assets.js +24 -0
  38. package/dist/puzzles/skewb/solver/index.d.ts +5 -0
  39. package/dist/puzzles/skewb/solver/index.d.ts.map +1 -0
  40. package/dist/puzzles/skewb/solver/index.js +4 -0
  41. package/dist/puzzles/skewb/solver/node-loader.d.ts +10 -0
  42. package/dist/puzzles/skewb/solver/node-loader.d.ts.map +1 -0
  43. package/dist/puzzles/skewb/solver/node-loader.js +91 -0
  44. package/dist/puzzles/skewb/solver/search.d.ts +14 -0
  45. package/dist/puzzles/skewb/solver/search.d.ts.map +1 -0
  46. package/dist/puzzles/skewb/solver/search.js +120 -0
  47. package/dist/puzzles/skewb/solver/table-metadata.d.ts +27 -0
  48. package/dist/puzzles/skewb/solver/table-metadata.d.ts.map +1 -0
  49. package/dist/puzzles/skewb/solver/table-metadata.js +26 -0
  50. package/dist/puzzles/skewb/solver/tables.d.ts +69 -0
  51. package/dist/puzzles/skewb/solver/tables.d.ts.map +1 -0
  52. package/dist/puzzles/skewb/solver/tables.js +511 -0
  53. package/dist/random/prng.d.ts.map +1 -1
  54. package/dist/random/prng.js +25 -2
  55. package/dist-cjs/core/api.js +6 -1
  56. package/dist-cjs/puzzles/pyraminx/facelets.js +43 -17
  57. package/dist-cjs/puzzles/pyraminx/random-state.js +63 -29
  58. package/dist-cjs/puzzles/pyraminx/scrambler.js +34 -25
  59. package/dist-cjs/puzzles/pyraminx/solver/search.js +62 -4
  60. package/dist-cjs/puzzles/pyraminx/solver/tables.js +4 -0
  61. package/dist-cjs/puzzles/skewb/facelets.js +46 -23
  62. package/dist-cjs/puzzles/skewb/index.js +49 -11
  63. package/dist-cjs/puzzles/skewb/random-state.js +25 -18
  64. package/dist-cjs/puzzles/skewb/scrambler.js +26 -15
  65. package/dist-cjs/puzzles/skewb/solver/browser-loader.js +91 -0
  66. package/dist-cjs/puzzles/skewb/solver/generated/centers-table.bin +0 -0
  67. package/dist-cjs/puzzles/skewb/solver/generated/helpers.bin +0 -0
  68. package/dist-cjs/puzzles/skewb/solver/generated/manifest.json +31 -0
  69. package/dist-cjs/puzzles/skewb/solver/generated/orientation-table.bin +0 -0
  70. package/dist-cjs/puzzles/skewb/solver/generated/permutation-table.bin +0 -0
  71. package/dist-cjs/puzzles/skewb/solver/generated/policy.bin +0 -0
  72. package/dist-cjs/puzzles/skewb/solver/generated-assets.js +60 -0
  73. package/dist-cjs/puzzles/skewb/solver/index.js +23 -0
  74. package/dist-cjs/puzzles/skewb/solver/node-loader.js +97 -0
  75. package/dist-cjs/puzzles/skewb/solver/search.js +130 -0
  76. package/dist-cjs/puzzles/skewb/solver/table-metadata.js +29 -0
  77. package/dist-cjs/puzzles/skewb/solver/tables.js +543 -0
  78. package/dist-cjs/random/prng.js +25 -2
  79. package/package.json +6 -3
  80. package/dist/puzzles/skewb/solver.d.ts +0 -11
  81. package/dist/puzzles/skewb/solver.d.ts.map +0 -1
  82. package/dist/puzzles/skewb/solver.js +0 -149
  83. package/dist-cjs/puzzles/skewb/solver.js +0 -160
@@ -0,0 +1,27 @@
1
+ export declare const SKEWB_SOLVER_TABLE_METADATA: {
2
+ readonly permutationTable: {
3
+ readonly entries: 40320;
4
+ readonly checksum: 3073532981;
5
+ };
6
+ readonly orientationTable: {
7
+ readonly entries: 17496;
8
+ readonly checksum: 2128852053;
9
+ };
10
+ readonly centersTable: {
11
+ readonly entries: 5760;
12
+ readonly checksum: 360885765;
13
+ };
14
+ readonly helpers: {
15
+ readonly evenCenterRankBytes: 1440;
16
+ readonly orientClassBytes: 2187;
17
+ readonly r7SetsEntries: 12960;
18
+ readonly entries: number;
19
+ readonly checksum: 4284555735;
20
+ };
21
+ readonly policyTable: {
22
+ readonly entries: 1180980;
23
+ readonly checksum: 1294485891;
24
+ readonly maxDepth: 11;
25
+ };
26
+ };
27
+ //# sourceMappingURL=table-metadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-metadata.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/skewb/solver/table-metadata.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;CAyB9B,CAAC"}
@@ -0,0 +1,26 @@
1
+ export const SKEWB_SOLVER_TABLE_METADATA = {
2
+ permutationTable: {
3
+ entries: 40320, // PERMUTATION_COUNT (5040) * MOVE_COUNT (8)
4
+ checksum: 3073532981
5
+ },
6
+ orientationTable: {
7
+ entries: 17496, // ORIENTATION_COUNT (2187) * MOVE_COUNT (8)
8
+ checksum: 2128852053
9
+ },
10
+ centersTable: {
11
+ entries: 5760, // CENTER_COUNT (720) * MOVE_COUNT (8)
12
+ checksum: 360885765
13
+ },
14
+ helpers: {
15
+ evenCenterRankBytes: 1440, // 720 * 2 (Uint16, sentinel 65535)
16
+ orientClassBytes: 2187,
17
+ r7SetsEntries: 12960, // EVEN_CENTER_COUNT (360) * ORIENT_CLASS_COUNT (9) * PER_SET_SIZE (4)
18
+ entries: 1440 + 2187 + 12960 * 2,
19
+ checksum: 4284555735
20
+ },
21
+ policyTable: {
22
+ entries: 1180980, // ceil(STATE_COUNT (3_149_280) * 3 / 8)
23
+ checksum: 1294485891,
24
+ maxDepth: 11
25
+ }
26
+ };
@@ -0,0 +1,69 @@
1
+ import type { SkewbMove, SkewbState } from "../types.js";
2
+ export declare function getPermutationIndex(perm: readonly number[]): number;
3
+ export declare function getPermutationFromIndex(index: number, n: number): number[];
4
+ export declare function getOrientationIndex(orient: readonly number[]): number;
5
+ export declare function getOrientationFromIndex(index: number): number[];
6
+ export declare function getCenterIndex(centers: readonly number[]): number;
7
+ export declare function getCenterFromIndex(index: number): number[];
8
+ export declare function getStateIndex(state: SkewbState): number;
9
+ export declare function getStateFromIndex(index: number): SkewbState;
10
+ export declare const PERMUTATION_COUNT = 5040;
11
+ export declare const ORIENTATION_COUNT = 2187;
12
+ export declare const CENTER_COUNT = 720;
13
+ export declare const EVEN_CENTER_COUNT = 360;
14
+ export declare const ORIENT_CLASS_COUNT = 9;
15
+ export declare const PER_SET_SIZE = 4;
16
+ export declare const MOVE_COUNT = 8;
17
+ export declare const STATE_COUNT: number;
18
+ export declare const POLICY_PACKED_ENTRIES: number;
19
+ export declare const SKEWB_MOVES: readonly SkewbMove[];
20
+ export declare function getPerm7Index(perm: readonly number[]): number;
21
+ export declare function getPerm7FromIndex(index: number): number[];
22
+ export declare let PERMUTATION_TABLE: Uint16Array | null;
23
+ export declare let ORIENTATION_TABLE: Uint16Array | null;
24
+ export declare let CENTERS_TABLE: Uint16Array | null;
25
+ export declare let POLICY_TABLE: Uint8Array | null;
26
+ export declare let EVEN_CENTER_RANK: Uint16Array | null;
27
+ export declare let EVEN_CENTER_FULL: Uint16Array | null;
28
+ export declare let ORIENT_CLASS: Uint8Array | null;
29
+ export declare let R7_SETS: Uint16Array | null;
30
+ export declare let PERM7_UNRANK: Uint8Array | null;
31
+ export declare let CENTER_UNRANK: Uint8Array | null;
32
+ export declare let SOLVED_RANK: number;
33
+ export declare function ensureTablesLoaded(): Promise<void>;
34
+ export declare function areSkewbTablesLoaded(): boolean;
35
+ export declare function assignTables(tables: {
36
+ permutationTable: Uint16Array;
37
+ orientationTable: Uint16Array;
38
+ centersTable: Uint16Array;
39
+ policyTable: Uint8Array;
40
+ evenCenterRank: Uint16Array;
41
+ orientClass: Uint8Array;
42
+ r7Sets: Uint16Array;
43
+ }): void;
44
+ export declare function resetTablesForTests(): void;
45
+ export declare function rankFromComponents(r7: number, rO: number, rC: number): number;
46
+ export declare function getStateRank(state: SkewbState): number;
47
+ export declare function getStateFromRank(rank: number): SkewbState;
48
+ export declare function getPolicyMoveFromPacked(packed: Uint8Array, stateRank: number): number;
49
+ export declare function getPolicyMove(stateRank: number): number;
50
+ export declare function getPermutationTransition(permIdx: number, move: number): number;
51
+ export declare function getOrientationTransition(orientIdx: number, move: number): number;
52
+ export declare function getCentersTransition(centerIdx: number, move: number): number;
53
+ export declare function generatePermutationTable(): Uint16Array;
54
+ export declare function generateOrientationTable(): Uint16Array;
55
+ export declare function generateCentersTable(): Uint16Array;
56
+ export declare function permutationParity(perm: readonly number[]): number;
57
+ export interface SkewbHelperTables {
58
+ evenCenterRank: Uint16Array;
59
+ orientClass: Uint8Array;
60
+ r7Sets: Uint16Array;
61
+ evenCenterFull: Uint16Array;
62
+ }
63
+ export declare function generateHelperTables(permTable: Uint16Array, orientTable: Uint16Array, centersTable: Uint16Array): SkewbHelperTables;
64
+ export declare function generatePolicyTable(permTable: Uint16Array, orientTable: Uint16Array, centersTable: Uint16Array, helpers: SkewbHelperTables): {
65
+ packed: Uint8Array;
66
+ maxDepth: number;
67
+ };
68
+ export declare function checksum(values: ArrayLike<number>): number;
69
+ //# sourceMappingURL=tables.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/skewb/solver/tables.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,511 @@
1
+ import { applySkewbMove } from "../moves.js";
2
+ import { loadGeneratedSkewbSolverTables } from "./generated-assets.js";
3
+ // ---------------------------------------------------------------------------
4
+ // Coordinate encoding / decoding (kept from the original solver.ts)
5
+ // ---------------------------------------------------------------------------
6
+ const FACT = [1, 1, 2, 6, 24, 120, 720, 5040, 40320];
7
+ // Popcount lookup (0..255) for the allocation-free Lehmer encoders below.
8
+ const POPCOUNT = new Uint8Array(256);
9
+ for (let i = 1; i < 256; i++)
10
+ POPCOUNT[i] = POPCOUNT[i >> 1] + (i & 1);
11
+ export function getPermutationIndex(perm) {
12
+ // perm length 8, but pos0 is always 0 for WCA reachable states.
13
+ // We encode full 8-perm via Lehmer code (0..40319), but for fixed corner it will be in subset.
14
+ let index = 0;
15
+ let used = 0;
16
+ const n = perm.length;
17
+ for (let i = 0; i < n; i++) {
18
+ const v = perm[i];
19
+ const digit = v - POPCOUNT[used & ((1 << v) - 1)];
20
+ used |= 1 << v;
21
+ index += digit * FACT[n - 1 - i];
22
+ }
23
+ return index;
24
+ }
25
+ export function getPermutationFromIndex(index, n) {
26
+ const perm = Array(n).fill(0);
27
+ const temp = Array.from({ length: n }, (_, i) => i);
28
+ let val = index;
29
+ for (let i = 0; i < n; i++) {
30
+ const f = FACT[n - 1 - i];
31
+ const choice = Math.floor(val / f);
32
+ val %= f;
33
+ perm[i] = temp[choice];
34
+ temp.splice(choice, 1);
35
+ }
36
+ return perm;
37
+ }
38
+ export function getOrientationIndex(orient) {
39
+ // orient length 8, index 0 is fixed 0, encode remaining 7 base3
40
+ let index = 0;
41
+ for (let i = 7; i >= 1; i--) {
42
+ index = index * 3 + orient[i];
43
+ }
44
+ return index;
45
+ }
46
+ export function getOrientationFromIndex(index) {
47
+ const orient = Array(8).fill(0);
48
+ orient[0] = 0;
49
+ let val = index;
50
+ for (let i = 1; i < 8; i++) {
51
+ orient[i] = val % 3;
52
+ val = Math.floor(val / 3);
53
+ }
54
+ return orient;
55
+ }
56
+ export function getCenterIndex(centers) {
57
+ let index = 0;
58
+ let used = 0;
59
+ const n = centers.length;
60
+ for (let i = 0; i < n; i++) {
61
+ const v = centers[i];
62
+ const digit = v - POPCOUNT[used & ((1 << v) - 1)];
63
+ used |= 1 << v;
64
+ index += digit * FACT[n - 1 - i];
65
+ }
66
+ return index;
67
+ }
68
+ export function getCenterFromIndex(index) {
69
+ return getPermutationFromIndex(index, 6);
70
+ }
71
+ export function getStateIndex(state) {
72
+ const p = getPermutationIndex(state.corners.permutation);
73
+ const c = getCenterIndex(state.centers);
74
+ const o = getOrientationIndex(state.corners.orientation);
75
+ // p in 0..40319, c in 0..719, o in 0..2186
76
+ // Combine: ((p * 720 + c) * 2187 + o) -> up to 63B fits in JS number (53-bit)
77
+ return ((p * 720 + c) * 2187 + o);
78
+ }
79
+ export function getStateFromIndex(index) {
80
+ const o = index % 2187;
81
+ let val = Math.floor(index / 2187);
82
+ const c = val % 720;
83
+ const p = Math.floor(val / 720);
84
+ return {
85
+ corners: {
86
+ permutation: getPermutationFromIndex(p, 8),
87
+ orientation: getOrientationFromIndex(o),
88
+ },
89
+ centers: getCenterFromIndex(c),
90
+ };
91
+ }
92
+ // ---------------------------------------------------------------------------
93
+ // Compact encoding over the WCA-reachable group (3,149,280 states)
94
+ // ---------------------------------------------------------------------------
95
+ // With the UFR corner fixed (position 0, orientation 0), the reachable group
96
+ // factors as:
97
+ // rC: even center permutations only -> 360
98
+ // rO: orientations of corners 1..7 -> 2187, partitioned into 9 classes
99
+ // r7: corner permutations of 1..7 -> exactly 4 values per (rC, rO-class)
100
+ // so rank = (rC_even * 2187 + rO) * 4 + k is a bijection over the reachable set.
101
+ export const PERMUTATION_COUNT = 5040; // r7: corners 1..7 Lehmer
102
+ export const ORIENTATION_COUNT = 2187; // rO: corners 1..7 base-3
103
+ export const CENTER_COUNT = 720; // rC: centers Lehmer
104
+ export const EVEN_CENTER_COUNT = 360; // reachable center arrangements
105
+ export const ORIENT_CLASS_COUNT = 9; // orientation classes
106
+ export const PER_SET_SIZE = 4; // corner permutations per (rC, rO-class)
107
+ export const MOVE_COUNT = 8;
108
+ export const STATE_COUNT = EVEN_CENTER_COUNT * ORIENTATION_COUNT * PER_SET_SIZE; // 3_149_280
109
+ export const POLICY_PACKED_ENTRIES = Math.ceil((STATE_COUNT * 3) / 8);
110
+ export const SKEWB_MOVES = ["R", "R'", "U", "U'", "L", "L'", "B", "B'"];
111
+ export function getPerm7Index(perm) {
112
+ // Lehmer rank of the positions 1..7 (values 1..7, offset by -1).
113
+ // Allocation-free: digit = count of unused values below the current one,
114
+ // computed with a used-bitmask and a popcount lookup.
115
+ let index = 0;
116
+ let used = 0;
117
+ for (let i = 1; i < 8; i++) {
118
+ const v = perm[i] - 1;
119
+ const digit = v - POPCOUNT[used & ((1 << v) - 1)];
120
+ used |= 1 << v;
121
+ index += digit * FACT[7 - i];
122
+ }
123
+ return index;
124
+ }
125
+ export function getPerm7FromIndex(index) {
126
+ const perm = Array(8).fill(0);
127
+ const available = [1, 2, 3, 4, 5, 6, 7];
128
+ let val = index;
129
+ for (let i = 1; i < 8; i++) {
130
+ const f = FACT[7 - i];
131
+ const choice = Math.floor(val / f);
132
+ val %= f;
133
+ perm[i] = available[choice];
134
+ available.splice(choice, 1);
135
+ }
136
+ return perm;
137
+ }
138
+ // ---------------------------------------------------------------------------
139
+ // Table loading / runtime state
140
+ // ---------------------------------------------------------------------------
141
+ let tablesLoaded = false;
142
+ export let PERMUTATION_TABLE = null; // r7 * MOVE_COUNT + m
143
+ export let ORIENTATION_TABLE = null; // rO * MOVE_COUNT + m
144
+ export let CENTERS_TABLE = null; // rC * MOVE_COUNT + m
145
+ export let POLICY_TABLE = null; // optimal move per rank, 3-bit packed
146
+ export let EVEN_CENTER_RANK = null; // rC -> compact 0..359 (65535 = odd)
147
+ export let EVEN_CENTER_FULL = null; // compact 0..359 -> rC (derived at load)
148
+ export let ORIENT_CLASS = null; // rO -> class 0..8
149
+ export let R7_SETS = null; // (rC_even * 9 + class) * 4 + i
150
+ export let PERM7_UNRANK = null; // r7 * 7 + i -> piece at corner i+1 (derived at load)
151
+ export let CENTER_UNRANK = null; // rC * 6 + i -> center piece (derived at load)
152
+ export let SOLVED_RANK = 0;
153
+ export async function ensureTablesLoaded() {
154
+ if (tablesLoaded)
155
+ return;
156
+ const tables = await loadGeneratedSkewbSolverTables();
157
+ assignTables(tables);
158
+ }
159
+ export function areSkewbTablesLoaded() {
160
+ return tablesLoaded;
161
+ }
162
+ export function assignTables(tables) {
163
+ PERMUTATION_TABLE = tables.permutationTable;
164
+ ORIENTATION_TABLE = tables.orientationTable;
165
+ CENTERS_TABLE = tables.centersTable;
166
+ POLICY_TABLE = tables.policyTable;
167
+ EVEN_CENTER_RANK = tables.evenCenterRank;
168
+ ORIENT_CLASS = tables.orientClass;
169
+ R7_SETS = tables.r7Sets;
170
+ EVEN_CENTER_FULL = new Uint16Array(EVEN_CENTER_COUNT);
171
+ for (let rC = 0; rC < CENTER_COUNT; rC++) {
172
+ const compactIdx = tables.evenCenterRank[rC];
173
+ if (compactIdx !== 65535)
174
+ EVEN_CENTER_FULL[compactIdx] = rC;
175
+ }
176
+ PERM7_UNRANK = new Uint8Array(PERMUTATION_COUNT * 7);
177
+ for (let r7 = 0; r7 < PERMUTATION_COUNT; r7++) {
178
+ const perm = getPerm7FromIndex(r7);
179
+ for (let i = 0; i < 7; i++)
180
+ PERM7_UNRANK[r7 * 7 + i] = perm[i + 1];
181
+ }
182
+ CENTER_UNRANK = new Uint8Array(CENTER_COUNT * 6);
183
+ for (let rC = 0; rC < CENTER_COUNT; rC++) {
184
+ const centers = getCenterFromIndex(rC);
185
+ for (let i = 0; i < 6; i++)
186
+ CENTER_UNRANK[rC * 6 + i] = centers[i];
187
+ }
188
+ SOLVED_RANK = rankFromComponents(0, 0, 0);
189
+ tablesLoaded = true;
190
+ }
191
+ export function resetTablesForTests() {
192
+ tablesLoaded = false;
193
+ PERMUTATION_TABLE = null;
194
+ ORIENTATION_TABLE = null;
195
+ CENTERS_TABLE = null;
196
+ POLICY_TABLE = null;
197
+ EVEN_CENTER_RANK = null;
198
+ EVEN_CENTER_FULL = null;
199
+ ORIENT_CLASS = null;
200
+ R7_SETS = null;
201
+ PERM7_UNRANK = null;
202
+ CENTER_UNRANK = null;
203
+ }
204
+ // ---------------------------------------------------------------------------
205
+ // Compact rank helpers
206
+ // ---------------------------------------------------------------------------
207
+ export function rankFromComponents(r7, rO, rC) {
208
+ const evenRank = EVEN_CENTER_RANK[rC];
209
+ if (evenRank === 65535) {
210
+ throw new Error("Unreachable Skewb state: odd center permutation parity.");
211
+ }
212
+ const cls = ORIENT_CLASS[rO];
213
+ const set = R7_SETS;
214
+ const base = (evenRank * ORIENT_CLASS_COUNT + cls) * PER_SET_SIZE;
215
+ for (let i = 0; i < PER_SET_SIZE; i++) {
216
+ if (set[base + i] === r7) {
217
+ return (evenRank * ORIENTATION_COUNT + rO) * PER_SET_SIZE + i;
218
+ }
219
+ }
220
+ throw new Error("Unreachable Skewb state: corner permutation not in reachable set.");
221
+ }
222
+ export function getStateRank(state) {
223
+ if (state.corners.permutation[0] !== 0) {
224
+ throw new Error("The Skewb solver requires the UFR corner (index 0) to be fixed " +
225
+ "as the frame of reference. Please normalize the state before solving.");
226
+ }
227
+ const r7 = getPerm7Index(state.corners.permutation);
228
+ const rO = getOrientationIndex(state.corners.orientation);
229
+ const rC = getCenterIndex(state.centers);
230
+ return rankFromComponents(r7, rO, rC);
231
+ }
232
+ // Inverse of getStateRank: decodes a reachable rank (0..3_149_279) back into
233
+ // a full state. Used for exact uniform random-state sampling.
234
+ export function getStateFromRank(rank) {
235
+ const even = Math.floor(rank / (ORIENTATION_COUNT * PER_SET_SIZE));
236
+ const rest = rank % (ORIENTATION_COUNT * PER_SET_SIZE);
237
+ const rO = Math.floor(rest / PER_SET_SIZE);
238
+ const k = rest % PER_SET_SIZE;
239
+ const cls = ORIENT_CLASS[rO];
240
+ const r7 = R7_SETS[(even * ORIENT_CLASS_COUNT + cls) * PER_SET_SIZE + k];
241
+ const perm = new Array(8);
242
+ perm[0] = 0;
243
+ const permBase = r7 * 7;
244
+ for (let i = 0; i < 7; i++)
245
+ perm[i + 1] = PERM7_UNRANK[permBase + i];
246
+ const orient = new Array(8);
247
+ 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
+ }
253
+ const centers = new Array(6);
254
+ const centerBase = EVEN_CENTER_FULL[even] * 6;
255
+ for (let i = 0; i < 6; i++)
256
+ centers[i] = CENTER_UNRANK[centerBase + i];
257
+ return { corners: { permutation: perm, orientation: orient }, centers };
258
+ }
259
+ // 3-bit little-endian unpacking of the packed policy table.
260
+ export function getPolicyMoveFromPacked(packed, stateRank) {
261
+ const bits = stateRank * 3;
262
+ const byteIndex = bits >> 3;
263
+ const offset = bits & 7;
264
+ const word = (packed[byteIndex] | ((packed[byteIndex + 1] ?? 0) << 8)) >> offset;
265
+ return word & 0x07;
266
+ }
267
+ export function getPolicyMove(stateRank) {
268
+ if (!POLICY_TABLE) {
269
+ throw new Error("Skewb solver tables not loaded. Call ensureTablesLoaded() first.");
270
+ }
271
+ return getPolicyMoveFromPacked(POLICY_TABLE, stateRank);
272
+ }
273
+ export function getPermutationTransition(permIdx, move) {
274
+ if (!PERMUTATION_TABLE) {
275
+ throw new Error("Skewb solver tables not loaded. Call ensureTablesLoaded() first.");
276
+ }
277
+ return PERMUTATION_TABLE[permIdx * MOVE_COUNT + move];
278
+ }
279
+ export function getOrientationTransition(orientIdx, move) {
280
+ if (!ORIENTATION_TABLE) {
281
+ throw new Error("Skewb solver tables not loaded. Call ensureTablesLoaded() first.");
282
+ }
283
+ return ORIENTATION_TABLE[orientIdx * MOVE_COUNT + move];
284
+ }
285
+ export function getCentersTransition(centerIdx, move) {
286
+ if (!CENTERS_TABLE) {
287
+ throw new Error("Skewb solver tables not loaded. Call ensureTablesLoaded() first.");
288
+ }
289
+ return CENTERS_TABLE[centerIdx * MOVE_COUNT + move];
290
+ }
291
+ // ---------------------------------------------------------------------------
292
+ // Table generation (used by the generate script)
293
+ // ---------------------------------------------------------------------------
294
+ export function generatePermutationTable() {
295
+ const table = new Uint16Array(PERMUTATION_COUNT * MOVE_COUNT);
296
+ for (let r7 = 0; r7 < PERMUTATION_COUNT; r7++) {
297
+ const perm = getPerm7FromIndex(r7);
298
+ for (let m = 0; m < MOVE_COUNT; m++) {
299
+ const state = {
300
+ corners: { permutation: [...perm], orientation: [0, 0, 0, 0, 0, 0, 0, 0] },
301
+ centers: [0, 1, 2, 3, 4, 5],
302
+ };
303
+ const next = applySkewbMove(state, SKEWB_MOVES[m]);
304
+ table[r7 * MOVE_COUNT + m] = getPerm7Index(next.corners.permutation);
305
+ }
306
+ }
307
+ return table;
308
+ }
309
+ export function generateOrientationTable() {
310
+ const table = new Uint16Array(ORIENTATION_COUNT * MOVE_COUNT);
311
+ for (let rO = 0; rO < ORIENTATION_COUNT; rO++) {
312
+ const orient = getOrientationFromIndex(rO);
313
+ for (let m = 0; m < MOVE_COUNT; m++) {
314
+ const state = {
315
+ corners: { permutation: [0, 1, 2, 3, 4, 5, 6, 7], orientation: [...orient] },
316
+ centers: [0, 1, 2, 3, 4, 5],
317
+ };
318
+ const next = applySkewbMove(state, SKEWB_MOVES[m]);
319
+ table[rO * MOVE_COUNT + m] = getOrientationIndex(next.corners.orientation);
320
+ }
321
+ }
322
+ return table;
323
+ }
324
+ export function generateCentersTable() {
325
+ const table = new Uint16Array(CENTER_COUNT * MOVE_COUNT);
326
+ for (let rC = 0; rC < CENTER_COUNT; rC++) {
327
+ const centers = getCenterFromIndex(rC);
328
+ for (let m = 0; m < MOVE_COUNT; m++) {
329
+ const state = {
330
+ corners: { permutation: [0, 1, 2, 3, 4, 5, 6, 7], orientation: [0, 0, 0, 0, 0, 0, 0, 0] },
331
+ centers: [...centers],
332
+ };
333
+ const next = applySkewbMove(state, SKEWB_MOVES[m]);
334
+ table[rC * MOVE_COUNT + m] = getCenterIndex(next.centers);
335
+ }
336
+ }
337
+ return table;
338
+ }
339
+ export function permutationParity(perm) {
340
+ let inversions = 0;
341
+ for (let i = 0; i < perm.length; i++) {
342
+ for (let j = i + 1; j < perm.length; j++) {
343
+ if (perm[i] > perm[j])
344
+ inversions++;
345
+ }
346
+ }
347
+ return inversions % 2;
348
+ }
349
+ // Discovers the reachable-state structure with a BFS over the full product
350
+ // coordinate (r7, rO, rC) and derives the compact-rank helper tables:
351
+ // - even center permutations only (360 of 720)
352
+ // - rO partitioned into 9 classes by the 4-corner-permutation sets
353
+ export function generateHelperTables(permTable, orientTable, centersTable) {
354
+ const evenCenterRank = new Uint16Array(CENTER_COUNT).fill(65535);
355
+ const evenCenterFull = new Uint16Array(EVEN_CENTER_COUNT);
356
+ let compact = 0;
357
+ for (let rC = 0; rC < CENTER_COUNT; rC++) {
358
+ if (permutationParity(getCenterFromIndex(rC)) === 0) {
359
+ evenCenterRank[rC] = compact;
360
+ evenCenterFull[compact] = rC;
361
+ compact++;
362
+ }
363
+ }
364
+ if (compact !== EVEN_CENTER_COUNT) {
365
+ throw new Error(`Expected ${EVEN_CENTER_COUNT} even center permutations, found ${compact}.`);
366
+ }
367
+ const pack = (r7, rO, rC) => (r7 * ORIENTATION_COUNT + rO) * CENTER_COUNT + rC;
368
+ const pairSets = new Map();
369
+ const seen = new Set([0]);
370
+ const queue = [0];
371
+ let head = 0;
372
+ while (head < queue.length) {
373
+ const cur = queue[head++];
374
+ const rC = cur % CENTER_COUNT;
375
+ const rest = Math.floor(cur / CENTER_COUNT);
376
+ const rO = rest % ORIENTATION_COUNT;
377
+ const r7 = Math.floor(rest / ORIENTATION_COUNT);
378
+ const pairKey = rC * ORIENTATION_COUNT + rO;
379
+ let set = pairSets.get(pairKey);
380
+ if (!set) {
381
+ set = new Set();
382
+ pairSets.set(pairKey, set);
383
+ }
384
+ set.add(r7);
385
+ for (let m = 0; m < MOVE_COUNT; m++) {
386
+ const n7 = permTable[r7 * MOVE_COUNT + m];
387
+ const nO = orientTable[rO * MOVE_COUNT + m];
388
+ const nC = centersTable[rC * MOVE_COUNT + m];
389
+ const next = pack(n7, nO, nC);
390
+ if (!seen.has(next)) {
391
+ seen.add(next);
392
+ queue.push(next);
393
+ }
394
+ }
395
+ }
396
+ if (seen.size !== STATE_COUNT) {
397
+ throw new Error(`Reachability BFS covered ${seen.size} states, expected ${STATE_COUNT}.`);
398
+ }
399
+ const setKey = (values) => [...values].sort((a, b) => a - b).join(",");
400
+ // Reference partition from rC = 0 (verified identical for all even rC).
401
+ const orientClass = new Uint8Array(ORIENTATION_COUNT);
402
+ const classOfSet = new Map();
403
+ for (let rO = 0; rO < ORIENTATION_COUNT; rO++) {
404
+ const set = pairSets.get(rO);
405
+ const key = setKey(set);
406
+ if (!classOfSet.has(key)) {
407
+ classOfSet.set(key, classOfSet.size);
408
+ }
409
+ orientClass[rO] = classOfSet.get(key);
410
+ }
411
+ if (classOfSet.size !== ORIENT_CLASS_COUNT) {
412
+ throw new Error(`Expected ${ORIENT_CLASS_COUNT} orientation classes, found ${classOfSet.size}.`);
413
+ }
414
+ // Verify the partition is identical for every even rC and that each
415
+ // (rC, class) has exactly PER_SET_SIZE corner permutations.
416
+ const r7Sets = new Uint16Array(EVEN_CENTER_COUNT * ORIENT_CLASS_COUNT * PER_SET_SIZE);
417
+ for (let rC = 0; rC < CENTER_COUNT; rC++) {
418
+ const rCCompact = evenCenterRank[rC];
419
+ if (rCCompact === 65535)
420
+ continue;
421
+ for (let rO = 0; rO < ORIENTATION_COUNT; rO++) {
422
+ const set = pairSets.get(rC * ORIENTATION_COUNT + rO);
423
+ if (set.size !== PER_SET_SIZE) {
424
+ throw new Error(`Expected ${PER_SET_SIZE} permutations for (rC=${rC}, rO=${rO}), found ${set.size}.`);
425
+ }
426
+ const cls = orientClass[rO];
427
+ if (classOfSet.get(setKey(set)) !== cls) {
428
+ throw new Error(`Orientation class partition differs at rC=${rC}, rO=${rO}.`);
429
+ }
430
+ const stored = [...set].sort((a, b) => a - b);
431
+ const base = (rCCompact * ORIENT_CLASS_COUNT + cls) * PER_SET_SIZE;
432
+ for (let i = 0; i < PER_SET_SIZE; i++)
433
+ r7Sets[base + i] = stored[i];
434
+ }
435
+ }
436
+ return { evenCenterRank, orientClass, r7Sets, evenCenterFull };
437
+ }
438
+ // BFS from solved stores, for each state, the inverse of the move that first
439
+ // discovered it: an optimal move toward solved. Packed 3 bits per state
440
+ // (8 moves; the solved entry is never read, the solve loop exits first).
441
+ export function generatePolicyTable(permTable, orientTable, centersTable, helpers) {
442
+ // Install the compact-rank tables for the duration of the build.
443
+ const saved = { evenCenterRank: EVEN_CENTER_RANK, orientClass: ORIENT_CLASS, r7Sets: R7_SETS };
444
+ EVEN_CENTER_RANK = helpers.evenCenterRank;
445
+ ORIENT_CLASS = helpers.orientClass;
446
+ R7_SETS = helpers.r7Sets;
447
+ try {
448
+ const solvedRank = rankFromComponents(0, 0, 0);
449
+ const distances = new Uint8Array(STATE_COUNT).fill(255);
450
+ const policy = new Uint8Array(STATE_COUNT);
451
+ distances[solvedRank] = 0;
452
+ const queue = new Uint32Array(STATE_COUNT);
453
+ let read = 0;
454
+ let write = 1;
455
+ queue[0] = solvedRank;
456
+ let maxDepth = 0;
457
+ while (read < write) {
458
+ const curr = queue[read++];
459
+ const dist = distances[curr];
460
+ const even = Math.floor(curr / (ORIENTATION_COUNT * PER_SET_SIZE));
461
+ const rest = curr % (ORIENTATION_COUNT * PER_SET_SIZE);
462
+ const rO = Math.floor(rest / PER_SET_SIZE);
463
+ const k = rest % PER_SET_SIZE;
464
+ const cls = ORIENT_CLASS[rO];
465
+ const r7 = R7_SETS[(even * ORIENT_CLASS_COUNT + cls) * PER_SET_SIZE + k];
466
+ const rC = helpers.evenCenterFull[even];
467
+ const nextDist = dist + 1;
468
+ for (let m = 0; m < MOVE_COUNT; m++) {
469
+ const n7 = permTable[r7 * MOVE_COUNT + m];
470
+ const nO = orientTable[rO * MOVE_COUNT + m];
471
+ const nC = centersTable[rC * MOVE_COUNT + m];
472
+ const next = rankFromComponents(n7, nO, nC);
473
+ if (distances[next] === 255) {
474
+ distances[next] = nextDist;
475
+ policy[next] = m ^ 1; // inverse move of the discovery edge
476
+ if (nextDist > maxDepth)
477
+ maxDepth = nextDist;
478
+ queue[write++] = next;
479
+ }
480
+ }
481
+ }
482
+ if (write !== STATE_COUNT) {
483
+ throw new Error(`Policy BFS covered ${write} states, expected ${STATE_COUNT}.`);
484
+ }
485
+ const packed = new Uint8Array(POLICY_PACKED_ENTRIES);
486
+ for (let i = 0; i < STATE_COUNT; i++) {
487
+ const bits = i * 3;
488
+ const byteIndex = bits >> 3;
489
+ const offset = bits & 7;
490
+ const value = policy[i] & 0x07;
491
+ packed[byteIndex] |= (value << offset) & 0xff;
492
+ if (offset > 5) {
493
+ packed[byteIndex + 1] |= value >> (8 - offset);
494
+ }
495
+ }
496
+ return { packed, maxDepth };
497
+ }
498
+ finally {
499
+ EVEN_CENTER_RANK = saved.evenCenterRank;
500
+ ORIENT_CLASS = saved.orientClass;
501
+ R7_SETS = saved.r7Sets;
502
+ }
503
+ }
504
+ export function checksum(values) {
505
+ let hash = 0x811c9dc5;
506
+ for (let i = 0; i < values.length; i++) {
507
+ hash ^= values[i];
508
+ hash = Math.imul(hash, 0x01000193);
509
+ }
510
+ return hash >>> 0;
511
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"prng.d.ts","sourceRoot":"","sources":["../../src/random/prng.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B,IAAI,IAAI,MAAM,CAAC;IACf,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAAC;IACtC,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,CAAC;CACnC;AAED,wBAAgB,kBAAkB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,YAAY,CAMvE"}
1
+ {"version":3,"file":"prng.d.ts","sourceRoot":"","sources":["../../src/random/prng.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,YAAY;IAC3B,IAAI,IAAI,MAAM,CAAC;IACf,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAAC;IACtC,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,CAAC;CACnC;AAED,wBAAgB,kBAAkB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,YAAY,CAOvE"}