@cubingcenter/scrambler 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/dist/core/api.d.ts.map +1 -1
- package/dist/core/api.js +6 -1
- package/dist/puzzles/skewb/facelets.d.ts.map +1 -1
- package/dist/puzzles/skewb/facelets.js +46 -23
- package/dist/puzzles/skewb/index.d.ts +2 -1
- package/dist/puzzles/skewb/index.d.ts.map +1 -1
- package/dist/puzzles/skewb/index.js +2 -1
- package/dist/puzzles/skewb/random-state.d.ts +1 -1
- package/dist/puzzles/skewb/random-state.d.ts.map +1 -1
- package/dist/puzzles/skewb/random-state.js +25 -18
- package/dist/puzzles/skewb/scrambler.d.ts.map +1 -1
- package/dist/puzzles/skewb/scrambler.js +10 -13
- package/dist/puzzles/skewb/solver/browser-loader.d.ts +20 -0
- package/dist/puzzles/skewb/solver/browser-loader.d.ts.map +1 -0
- package/dist/puzzles/skewb/solver/browser-loader.js +86 -0
- package/dist/puzzles/skewb/solver/generated/centers-table.bin +0 -0
- package/dist/puzzles/skewb/solver/generated/helpers.bin +0 -0
- package/dist/puzzles/skewb/solver/generated/manifest.json +31 -0
- package/dist/puzzles/skewb/solver/generated/orientation-table.bin +0 -0
- package/dist/puzzles/skewb/solver/generated/permutation-table.bin +0 -0
- package/dist/puzzles/skewb/solver/generated/policy.bin +0 -0
- package/dist/puzzles/skewb/solver/generated-assets.d.ts +10 -0
- package/dist/puzzles/skewb/solver/generated-assets.d.ts.map +1 -0
- package/dist/puzzles/skewb/solver/generated-assets.js +24 -0
- package/dist/puzzles/skewb/solver/index.d.ts +5 -0
- package/dist/puzzles/skewb/solver/index.d.ts.map +1 -0
- package/dist/puzzles/skewb/solver/index.js +4 -0
- package/dist/puzzles/skewb/solver/node-loader.d.ts +10 -0
- package/dist/puzzles/skewb/solver/node-loader.d.ts.map +1 -0
- package/dist/puzzles/skewb/solver/node-loader.js +91 -0
- package/dist/puzzles/skewb/solver/search.d.ts +11 -0
- package/dist/puzzles/skewb/solver/search.d.ts.map +1 -0
- package/dist/puzzles/skewb/solver/search.js +80 -0
- package/dist/puzzles/skewb/solver/table-metadata.d.ts +27 -0
- package/dist/puzzles/skewb/solver/table-metadata.d.ts.map +1 -0
- package/dist/puzzles/skewb/solver/table-metadata.js +26 -0
- package/dist/puzzles/skewb/solver/tables.d.ts +68 -0
- package/dist/puzzles/skewb/solver/tables.d.ts.map +1 -0
- package/dist/puzzles/skewb/solver/tables.js +508 -0
- package/dist/random/prng.js +10 -1
- package/dist-cjs/core/api.js +6 -1
- package/dist-cjs/puzzles/skewb/facelets.js +46 -23
- package/dist-cjs/puzzles/skewb/index.js +49 -11
- package/dist-cjs/puzzles/skewb/random-state.js +25 -18
- package/dist-cjs/puzzles/skewb/scrambler.js +9 -12
- package/dist-cjs/puzzles/skewb/solver/browser-loader.js +91 -0
- package/dist-cjs/puzzles/skewb/solver/generated/centers-table.bin +0 -0
- package/dist-cjs/puzzles/skewb/solver/generated/helpers.bin +0 -0
- package/dist-cjs/puzzles/skewb/solver/generated/manifest.json +31 -0
- package/dist-cjs/puzzles/skewb/solver/generated/orientation-table.bin +0 -0
- package/dist-cjs/puzzles/skewb/solver/generated/permutation-table.bin +0 -0
- package/dist-cjs/puzzles/skewb/solver/generated/policy.bin +0 -0
- package/dist-cjs/puzzles/skewb/solver/generated-assets.js +60 -0
- package/dist-cjs/puzzles/skewb/solver/index.js +23 -0
- package/dist-cjs/puzzles/skewb/solver/node-loader.js +97 -0
- package/dist-cjs/puzzles/skewb/solver/search.js +87 -0
- package/dist-cjs/puzzles/skewb/solver/table-metadata.js +29 -0
- package/dist-cjs/puzzles/skewb/solver/tables.js +539 -0
- package/dist-cjs/random/prng.js +10 -1
- package/package.json +6 -3
- package/dist/puzzles/skewb/solver.d.ts +0 -11
- package/dist/puzzles/skewb/solver.d.ts.map +0 -1
- package/dist/puzzles/skewb/solver.js +0 -149
- package/dist-cjs/puzzles/skewb/solver.js +0 -160
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ The package currently ships random-state scramblers for `222` (2x2x2), `333` (3x
|
|
|
15
15
|
- 2x2 quality: random-state target + optimal table-guided solver + verified scramble inversion
|
|
16
16
|
- 3x3 quality: random-state target + two-phase solver + verified scramble inversion
|
|
17
17
|
- Pyraminx quality: random-state target + optimal policy-table solver (all 933,120 core states verified)
|
|
18
|
+
- Skewb quality: random-state target + optimal policy-table solver (all 3,149,280 states; exact uniform sampling)
|
|
18
19
|
- 4x4 quality: reduced 4x4 random-state target + existing 3x3 two-phase finish; full center/wing random-state reduction is isolated under `cube4.reduction`
|
|
19
20
|
|
|
20
21
|
This library is intended for practice, training, and internal CubingCenter use. Do not present it as an official WCA competition scrambler; sanctioned competitions must use the official program required by the delegate/organization.
|
|
@@ -141,6 +142,26 @@ const solution = await pyraminx.solvePyraminx(state); // optimal, policy-table b
|
|
|
141
142
|
|
|
142
143
|
The solver only covers the core (centers + edges); tips are appended by the scrambler. The `pyraminx.solver` namespace exposes the compact coordinates and the policy table; its optimality is verified exhaustively over all 933,120 core states (`pnpm verify:pyraminx:exhaustive`). Normal consumers should prefer `generateScramble("pymx")`.
|
|
143
144
|
|
|
145
|
+
## Skewb Technical Module
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
import { skewb } from "@cubingcenter/scrambler";
|
|
149
|
+
|
|
150
|
+
const solved = skewb.createSolvedSkewbState();
|
|
151
|
+
const moved = skewb.applySkewbAlgorithm(solved, ["R", "U", "R'"]);
|
|
152
|
+
const state = await skewb.generateRandomSkewbState({ seed: "demo" });
|
|
153
|
+
const facelets = skewb.toSkewbFacelets(state);
|
|
154
|
+
const faceletString = skewb.formatSkewbFacelets(facelets);
|
|
155
|
+
const validation = skewb.validateSkewbState(state);
|
|
156
|
+
const solution = await skewb.solveSkewb(state); // optimal, policy-table based
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`generateRandomSkewbState` returns a legal random state, not a scramble sequence. It samples the compact group rank uniformly, so every one of the 3,149,280 reachable states is exactly as likely as any other.
|
|
160
|
+
|
|
161
|
+
`toSkewbFacelets` returns six faces with five stickers each. `formatSkewbFacelets` produces a stable 30-character string.
|
|
162
|
+
|
|
163
|
+
Both `generateRandomSkewbState` and `solveSkewb` are async: they need the solver tables, which load lazily in Node and are prefetched in the browser via `initializeScrambler(baseUrl, "skwb")` (or `skewb.solveSkewbSync` after preloading). The `skewb.solver` namespace exposes the compact rank coordinates and the 3-bit policy table (all 3,149,280 states, God's number 11). Normal consumers should prefer `generateScramble("skwb")`.
|
|
164
|
+
|
|
144
165
|
## 4x4 Technical Module
|
|
145
166
|
|
|
146
167
|
```ts
|
package/dist/core/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/core/api.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,OAAO,EACP,SAAS,EACT,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/core/api.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,OAAO,EACP,SAAS,EACT,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BrG;AAED,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,iBAAiB,EAC5B,QAAQ,GAAE,iBAAmC,GAC5C,IAAI,CAEN;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,GAAE,iBAAmC,GAAG,SAAS,EAAE,CAE7F;AAED,wBAAgB,YAAY,CAC1B,OAAO,EAAE,OAAO,EAChB,QAAQ,GAAE,iBAAmC,GAC5C,SAAS,CAQX;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,eAAoB,EAC7B,QAAQ,GAAE,iBAAmC,GAC5C,OAAO,CAAC,cAAc,CAAC,CAiBzB;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,eAAoB,EAC7B,QAAQ,GAAE,iBAAmC,GAC5C,OAAO,CAAC,cAAc,EAAE,CAAC,CAU3B"}
|
package/dist/core/api.js
CHANGED
|
@@ -30,11 +30,16 @@ export async function initializeScrambler(baseUrl, eventId) {
|
|
|
30
30
|
const { loadBrowserTables } = await import("../puzzles/pyraminx/solver/browser-loader.js");
|
|
31
31
|
await loadBrowserTables(baseUrl);
|
|
32
32
|
}
|
|
33
|
+
else if (eventId === "skwb") {
|
|
34
|
+
const { loadBrowserTables } = await import("../puzzles/skewb/solver/browser-loader.js");
|
|
35
|
+
await loadBrowserTables(baseUrl);
|
|
36
|
+
}
|
|
33
37
|
else if (!eventId) {
|
|
34
38
|
await Promise.all([
|
|
35
39
|
import("../puzzles/cube2/solver/browser-loader.js").then((m) => m.loadBrowserTables(baseUrl)),
|
|
36
40
|
import("../puzzles/cube3/solver/browser-loader.js").then((m) => m.loadBrowserTables(baseUrl)),
|
|
37
|
-
import("../puzzles/pyraminx/solver/browser-loader.js").then((m) => m.loadBrowserTables(baseUrl))
|
|
41
|
+
import("../puzzles/pyraminx/solver/browser-loader.js").then((m) => m.loadBrowserTables(baseUrl)),
|
|
42
|
+
import("../puzzles/skewb/solver/browser-loader.js").then((m) => m.loadBrowserTables(baseUrl))
|
|
38
43
|
]);
|
|
39
44
|
}
|
|
40
45
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"facelets.d.ts","sourceRoot":"","sources":["../../../src/puzzles/skewb/facelets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"facelets.d.ts","sourceRoot":"","sources":["../../../src/puzzles/skewb/facelets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AA0D5D,wBAAgB,yBAAyB,IAAI,aAAa,CASzD;AAaD,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,aAAa,CAqBhE;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,CAEnE;AAED,wBAAgB,iBAAiB,IAAI,SAAS,MAAM,EAAE,CAErD"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Facelets model matching cubingcenter-viz/src/puzzles/skewb.ts
|
|
2
2
|
// Each face has 5 stickers: 0=center, 1=TL,2=TR,3=BL,4=BR (net layout)
|
|
3
3
|
const FACE_ORDER = ["U", "R", "F", "D", "L", "B"];
|
|
4
|
+
const FACE_INDEX = { U: 0, R: 1, F: 2, D: 3, L: 4, B: 5 };
|
|
4
5
|
// Canonical sticker faces per corner position: [UD sticker, RL sticker, FB sticker]
|
|
5
6
|
const CORNER_STICKERS = [
|
|
6
7
|
["U", "R", "F"],
|
|
@@ -23,43 +24,65 @@ const CORNER_SLOTS = [
|
|
|
23
24
|
[["D", 4], ["R", 4], ["B", 3]],
|
|
24
25
|
[["D", 3], ["L", 3], ["B", 4]],
|
|
25
26
|
];
|
|
27
|
+
// For each (position, piece, orientation) the three facelet writes, flattened:
|
|
28
|
+
// face index, sticker slot index and sticker color index, so the hot loop is
|
|
29
|
+
// pure table lookups (no tuple destructuring, no modulo).
|
|
30
|
+
const CORNER_WRITE_COUNT = 8 * 8 * 3 * 3;
|
|
31
|
+
const WRITE_FACE = new Int8Array(CORNER_WRITE_COUNT);
|
|
32
|
+
const WRITE_SLOT = new Int8Array(CORNER_WRITE_COUNT);
|
|
33
|
+
const WRITE_COLOR = new Int8Array(CORNER_WRITE_COUNT);
|
|
34
|
+
for (let pos = 0; pos < 8; pos++) {
|
|
35
|
+
const slots = CORNER_SLOTS[pos];
|
|
36
|
+
for (let piece = 0; piece < 8; piece++) {
|
|
37
|
+
const stickers = CORNER_STICKERS[piece];
|
|
38
|
+
for (let ori = 0; ori < 3; ori++) {
|
|
39
|
+
const base = ((pos * 8 + piece) * 3 + ori) * 3;
|
|
40
|
+
for (let i = 0; i < 3; i++) {
|
|
41
|
+
const [face, idx] = slots[i];
|
|
42
|
+
const color = stickers[(i + 3 - ori) % 3];
|
|
43
|
+
WRITE_FACE[base + i] = FACE_INDEX[face];
|
|
44
|
+
WRITE_SLOT[base + i] = idx;
|
|
45
|
+
WRITE_COLOR[base + i] = FACE_INDEX[color];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
26
50
|
export function createSolvedSkewbFacelets() {
|
|
27
51
|
return {
|
|
28
|
-
U:
|
|
29
|
-
R:
|
|
30
|
-
F:
|
|
31
|
-
D:
|
|
32
|
-
L:
|
|
33
|
-
B:
|
|
52
|
+
U: ["U", "U", "U", "U", "U"],
|
|
53
|
+
R: ["R", "R", "R", "R", "R"],
|
|
54
|
+
F: ["F", "F", "F", "F", "F"],
|
|
55
|
+
D: ["D", "D", "D", "D", "D"],
|
|
56
|
+
L: ["L", "L", "L", "L", "L"],
|
|
57
|
+
B: ["B", "B", "B", "B", "B"],
|
|
34
58
|
};
|
|
35
59
|
}
|
|
36
60
|
function createEmptySkewbFacelets() {
|
|
37
61
|
return {
|
|
38
|
-
U:
|
|
39
|
-
R:
|
|
40
|
-
F:
|
|
41
|
-
D:
|
|
42
|
-
L:
|
|
43
|
-
B:
|
|
62
|
+
U: ["?", "?", "?", "?", "?"],
|
|
63
|
+
R: ["?", "?", "?", "?", "?"],
|
|
64
|
+
F: ["?", "?", "?", "?", "?"],
|
|
65
|
+
D: ["?", "?", "?", "?", "?"],
|
|
66
|
+
L: ["?", "?", "?", "?", "?"],
|
|
67
|
+
B: ["?", "?", "?", "?", "?"],
|
|
44
68
|
};
|
|
45
69
|
}
|
|
46
70
|
export function toSkewbFacelets(state) {
|
|
47
71
|
const facelets = createEmptySkewbFacelets();
|
|
72
|
+
const faces = [facelets.U, facelets.R, facelets.F, facelets.D, facelets.L, facelets.B];
|
|
73
|
+
const perm = state.corners.permutation;
|
|
74
|
+
const orient = state.corners.orientation;
|
|
75
|
+
const centers = state.centers;
|
|
48
76
|
// centers
|
|
49
77
|
for (let face = 0; face < 6; face++) {
|
|
50
|
-
|
|
51
|
-
facelets[targetFace][0] = FACE_ORDER[state.centers[face]];
|
|
78
|
+
faces[face][0] = FACE_ORDER[centers[face]];
|
|
52
79
|
}
|
|
53
|
-
// corners
|
|
80
|
+
// corners (unrolled 3 writes per position)
|
|
54
81
|
for (let pos = 0; pos < 8; pos++) {
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
for (let i = 0; i < 3; i++) {
|
|
60
|
-
const [face, idx] = slots[i];
|
|
61
|
-
facelets[face][idx] = stickers[(i + 3 - ori) % 3];
|
|
62
|
-
}
|
|
82
|
+
const base = ((pos * 8 + perm[pos]) * 3 + orient[pos]) * 3;
|
|
83
|
+
faces[WRITE_FACE[base]][WRITE_SLOT[base]] = FACE_ORDER[WRITE_COLOR[base]];
|
|
84
|
+
faces[WRITE_FACE[base + 1]][WRITE_SLOT[base + 1]] = FACE_ORDER[WRITE_COLOR[base + 1]];
|
|
85
|
+
faces[WRITE_FACE[base + 2]][WRITE_SLOT[base + 2]] = FACE_ORDER[WRITE_COLOR[base + 2]];
|
|
63
86
|
}
|
|
64
87
|
return facelets;
|
|
65
88
|
}
|
|
@@ -5,7 +5,8 @@ export { cloneSkewbState, createSolvedSkewbState, isSolvedSkewbState, skewbState
|
|
|
5
5
|
export { permutationParity, validateSkewbState, isAllowedRandomSkewbState, isOneMoveFromSolvedSkewbState } from "./validation.js";
|
|
6
6
|
export { generateRandomSkewbState } from "./random-state.js";
|
|
7
7
|
export { createSolvedSkewbFacelets, formatSkewbFacelets, getSkewbFaceOrder, toSkewbFacelets } from "./facelets.js";
|
|
8
|
-
export { solveSkewb, getStateIndex, getStateFromIndex } from "./solver.js";
|
|
8
|
+
export { solveSkewb, solveSkewbWithInfo, solveSkewbSync, verifySkewbScramble, verifySkewbSolution, getStateIndex, getStateFromIndex, } from "./solver/index.js";
|
|
9
|
+
export * as solver from "./solver/index.js";
|
|
9
10
|
export { generateSkewbScramble, skewbScrambleGenerator } from "./scrambler.js";
|
|
10
11
|
export { generateScramble, generateScrambles, getEventInfo, getSupportedEvents, initializeScrambler } from "../../core/index.js";
|
|
11
12
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/puzzles/skewb/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACtD,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC/G,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,6BAA6B,EAAE,MAAM,iBAAiB,CAAC;AAClI,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACnH,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/puzzles/skewb/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACtD,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC/G,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,6BAA6B,EAAE,MAAM,iBAAiB,CAAC;AAClI,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACnH,OAAO,EACL,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EACnB,mBAAmB,EACnB,aAAa,EACb,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAE/E,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,qBAAqB,CAAC"}
|
|
@@ -7,6 +7,7 @@ export { cloneSkewbState, createSolvedSkewbState, isSolvedSkewbState, skewbState
|
|
|
7
7
|
export { permutationParity, validateSkewbState, isAllowedRandomSkewbState, isOneMoveFromSolvedSkewbState } from "./validation.js";
|
|
8
8
|
export { generateRandomSkewbState } from "./random-state.js";
|
|
9
9
|
export { createSolvedSkewbFacelets, formatSkewbFacelets, getSkewbFaceOrder, toSkewbFacelets } from "./facelets.js";
|
|
10
|
-
export { solveSkewb, getStateIndex, getStateFromIndex } from "./solver.js";
|
|
10
|
+
export { solveSkewb, solveSkewbWithInfo, solveSkewbSync, verifySkewbScramble, verifySkewbSolution, getStateIndex, getStateFromIndex, } from "./solver/index.js";
|
|
11
|
+
export * as solver from "./solver/index.js";
|
|
11
12
|
export { generateSkewbScramble, skewbScrambleGenerator } from "./scrambler.js";
|
|
12
13
|
export { generateScramble, generateScrambles, getEventInfo, getSupportedEvents, initializeScrambler } from "../../core/index.js";
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { SkewbRandomStateOptions, SkewbState } from "./types.js";
|
|
2
|
-
export declare function generateRandomSkewbState(options?: SkewbRandomStateOptions): SkewbState
|
|
2
|
+
export declare function generateRandomSkewbState(options?: SkewbRandomStateOptions): Promise<SkewbState>;
|
|
3
3
|
//# 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":"
|
|
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,27 +1,34 @@
|
|
|
1
1
|
import { createRandomSource } from "../../random/index.js";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import { SKEWB_MOVES } from "./types.js";
|
|
6
|
-
export function generateRandomSkewbState(options = {}) {
|
|
2
|
+
import { ensureTablesLoaded, getCentersTransition, getOrientationTransition, getPermutationTransition, getStateFromRank, MOVE_COUNT, rankFromComponents, SOLVED_RANK, STATE_COUNT, } from "./solver/index.js";
|
|
3
|
+
export async function generateRandomSkewbState(options = {}) {
|
|
4
|
+
await ensureTablesLoaded();
|
|
7
5
|
const random = createRandomSource(options.seed);
|
|
8
6
|
const rejectTrivial = options.rejectTrivial ?? true;
|
|
7
|
+
const trivialRanks = rejectTrivial ? getTrivialRanks() : null;
|
|
8
|
+
// Exact uniform sampling: the compact rank is a bijection over the
|
|
9
|
+
// 3,149,280 reachable states, so a uniform rank gives every state exactly
|
|
10
|
+
// the same probability (unlike a finite random walk, which only approaches
|
|
11
|
+
// uniformity). Trivial states (solved / one move from solved) are rejected
|
|
12
|
+
// and resampled, which keeps the conditional distribution uniform.
|
|
9
13
|
for (let attempt = 0; attempt < 100; attempt++) {
|
|
10
|
-
const
|
|
11
|
-
if (
|
|
12
|
-
|
|
14
|
+
const rank = random.nextInt(STATE_COUNT);
|
|
15
|
+
if (trivialRanks && trivialRanks.has(rank))
|
|
16
|
+
continue;
|
|
17
|
+
return getStateFromRank(rank);
|
|
13
18
|
}
|
|
14
19
|
throw new Error("Unable to generate a non-trivial random Skewb state.");
|
|
15
20
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
let trivialRanks = null;
|
|
22
|
+
function getTrivialRanks() {
|
|
23
|
+
if (!trivialRanks) {
|
|
24
|
+
const ranks = new Set([SOLVED_RANK]);
|
|
25
|
+
for (let m = 0; m < MOVE_COUNT; m++) {
|
|
26
|
+
const r7 = getPermutationTransition(0, m);
|
|
27
|
+
const rO = getOrientationTransition(0, m);
|
|
28
|
+
const rC = getCentersTransition(0, m);
|
|
29
|
+
ranks.add(rankFromComponents(r7, rO, rC));
|
|
30
|
+
}
|
|
31
|
+
trivialRanks = ranks;
|
|
25
32
|
}
|
|
26
|
-
return
|
|
33
|
+
return trivialRanks;
|
|
27
34
|
}
|
|
@@ -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;
|
|
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;AAM9F,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,CA+BzB"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { generateRandomSkewbState } from "./random-state.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { solveSkewb } from "./solver.js";
|
|
2
|
+
import { formatSkewbAlgorithm, invertSkewbAlgorithm } from "./moves.js";
|
|
3
|
+
import { solveSkewb, verifySkewbScramble } from "./solver/index.js";
|
|
5
4
|
import { formatSkewbFacelets, toSkewbFacelets } from "./facelets.js";
|
|
6
5
|
export const skewbScrambleGenerator = {
|
|
7
6
|
info: {
|
|
@@ -9,25 +8,23 @@ export const skewbScrambleGenerator = {
|
|
|
9
8
|
name: "Skewb",
|
|
10
9
|
puzzle: "skewb",
|
|
11
10
|
quality: "random-state",
|
|
12
|
-
generator: "skewb-
|
|
13
|
-
description: "Random-state Skewb scramble generated by an optimal
|
|
11
|
+
generator: "skewb-optimal",
|
|
12
|
+
description: "Random-state Skewb scramble generated by an optimal solver with precalculated tables.",
|
|
14
13
|
},
|
|
15
14
|
generate(options = {}) {
|
|
16
15
|
return generateSkewbScramble(options);
|
|
17
16
|
},
|
|
18
17
|
};
|
|
19
18
|
export async function generateSkewbScramble(options = {}) {
|
|
20
|
-
const state = generateRandomSkewbState(options);
|
|
21
|
-
const solution = solveSkewb(state);
|
|
19
|
+
const state = await generateRandomSkewbState(options);
|
|
20
|
+
const solution = await solveSkewb(state);
|
|
22
21
|
const scrambleMoves = invertSkewbAlgorithm(solution);
|
|
23
22
|
if (!options.skipValidation) {
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
// If the scramble reproduces the target state, its inverse (the
|
|
24
|
+
// solution) solves it by construction, so a single walk suffices.
|
|
25
|
+
if (!verifySkewbScramble(state, scrambleMoves)) {
|
|
26
26
|
throw new Error("Generated Skewb scramble does not reproduce the target random state.");
|
|
27
27
|
}
|
|
28
|
-
if (!isSolvedSkewbState(applySkewbAlgorithm(state, solution))) {
|
|
29
|
-
throw new Error("Generated Skewb solution does not solve the target random state.");
|
|
30
|
-
}
|
|
31
28
|
if (scrambleMoves.length < 2)
|
|
32
29
|
throw new Error("Generated Skewb scramble is too short.");
|
|
33
30
|
}
|
|
@@ -37,7 +34,7 @@ export async function generateSkewbScramble(options = {}) {
|
|
|
37
34
|
scramble: formatSkewbAlgorithm(scrambleMoves),
|
|
38
35
|
moves: scrambleMoves,
|
|
39
36
|
quality: "random-state",
|
|
40
|
-
generator: "skewb-
|
|
37
|
+
generator: "skewb-optimal",
|
|
41
38
|
metadata: {
|
|
42
39
|
state,
|
|
43
40
|
facelets,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare function getCachedBrowserTables(): {
|
|
2
|
+
permutationTable: Uint16Array;
|
|
3
|
+
orientationTable: Uint16Array;
|
|
4
|
+
centersTable: Uint16Array;
|
|
5
|
+
policyTable: Uint8Array;
|
|
6
|
+
evenCenterRank: Uint16Array;
|
|
7
|
+
orientClass: Uint8Array;
|
|
8
|
+
r7Sets: Uint16Array;
|
|
9
|
+
} | undefined;
|
|
10
|
+
export declare function clearCachedBrowserTables(): void;
|
|
11
|
+
export declare function loadBrowserTables(baseUrl?: string): Promise<{
|
|
12
|
+
permutationTable: Uint16Array;
|
|
13
|
+
orientationTable: Uint16Array;
|
|
14
|
+
centersTable: Uint16Array;
|
|
15
|
+
policyTable: Uint8Array;
|
|
16
|
+
evenCenterRank: Uint16Array;
|
|
17
|
+
orientClass: Uint8Array;
|
|
18
|
+
r7Sets: Uint16Array;
|
|
19
|
+
}>;
|
|
20
|
+
//# sourceMappingURL=browser-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-loader.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/skewb/solver/browser-loader.ts"],"names":[],"mappings":"AAeA,wBAAgB,sBAAsB;sBATlB,WAAW;sBACX,WAAW;kBACf,WAAW;iBACZ,UAAU;oBACP,WAAW;iBACd,UAAU;YACf,WAAW;cAKpB;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,YAAY,EAAE,WAAW,CAAC;IAC1B,WAAW,EAAE,UAAU,CAAC;IACxB,cAAc,EAAE,WAAW,CAAC;IAC5B,WAAW,EAAE,UAAU,CAAC;IACxB,MAAM,EAAE,WAAW,CAAC;CACrB,CAAC,CAwCD"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { SKEWB_SOLVER_TABLE_METADATA } from "./table-metadata.js";
|
|
2
|
+
import { fetchTableBuffer } from "../../shared/compressed-fetch.js";
|
|
3
|
+
const TABLES_DIR = "puzzles/skewb/solver/generated";
|
|
4
|
+
let cachedBrowserTables;
|
|
5
|
+
export function getCachedBrowserTables() {
|
|
6
|
+
return cachedBrowserTables;
|
|
7
|
+
}
|
|
8
|
+
export function clearCachedBrowserTables() {
|
|
9
|
+
cachedBrowserTables = undefined;
|
|
10
|
+
}
|
|
11
|
+
export async function loadBrowserTables(baseUrl) {
|
|
12
|
+
if (cachedBrowserTables) {
|
|
13
|
+
return cachedBrowserTables;
|
|
14
|
+
}
|
|
15
|
+
const resolved = resolveBaseUrl(baseUrl);
|
|
16
|
+
const manifest = await fetchManifest(resolved);
|
|
17
|
+
const [permTable, orientTable, centersTable, helpers, policy] = await Promise.all([
|
|
18
|
+
fetchUint16Table(resolved, manifest.files.permutationTable, SKEWB_SOLVER_TABLE_METADATA.permutationTable.entries),
|
|
19
|
+
fetchUint16Table(resolved, manifest.files.orientationTable, SKEWB_SOLVER_TABLE_METADATA.orientationTable.entries),
|
|
20
|
+
fetchUint16Table(resolved, manifest.files.centersTable, SKEWB_SOLVER_TABLE_METADATA.centersTable.entries),
|
|
21
|
+
fetchUint8Table(resolved, manifest.files.helpers, SKEWB_SOLVER_TABLE_METADATA.helpers.entries),
|
|
22
|
+
fetchUint8Table(resolved, manifest.files.policyTable, SKEWB_SOLVER_TABLE_METADATA.policyTable.entries)
|
|
23
|
+
]);
|
|
24
|
+
const helpersOffset = SKEWB_SOLVER_TABLE_METADATA.helpers;
|
|
25
|
+
const evenCenterRank = new Uint16Array(helpersOffset.evenCenterRankBytes / 2);
|
|
26
|
+
const rankView = new DataView(helpers.buffer, helpers.byteOffset);
|
|
27
|
+
for (let i = 0; i < evenCenterRank.length; i++) {
|
|
28
|
+
evenCenterRank[i] = rankView.getUint16(i * 2, true);
|
|
29
|
+
}
|
|
30
|
+
const orientClass = helpers.subarray(helpersOffset.evenCenterRankBytes, helpersOffset.evenCenterRankBytes + helpersOffset.orientClassBytes);
|
|
31
|
+
const r7Sets = new Uint16Array(helpersOffset.r7SetsEntries);
|
|
32
|
+
const view = new DataView(helpers.buffer, helpers.byteOffset + helpersOffset.evenCenterRankBytes + helpersOffset.orientClassBytes);
|
|
33
|
+
for (let i = 0; i < r7Sets.length; i++) {
|
|
34
|
+
r7Sets[i] = view.getUint16(i * 2, true);
|
|
35
|
+
}
|
|
36
|
+
cachedBrowserTables = {
|
|
37
|
+
permutationTable: permTable,
|
|
38
|
+
orientationTable: orientTable,
|
|
39
|
+
centersTable,
|
|
40
|
+
policyTable: policy,
|
|
41
|
+
evenCenterRank,
|
|
42
|
+
orientClass,
|
|
43
|
+
r7Sets
|
|
44
|
+
};
|
|
45
|
+
return cachedBrowserTables;
|
|
46
|
+
}
|
|
47
|
+
function resolveBaseUrl(provided) {
|
|
48
|
+
if (provided) {
|
|
49
|
+
return provided.replace(/\/$/, "");
|
|
50
|
+
}
|
|
51
|
+
if (typeof document !== "undefined") {
|
|
52
|
+
const script = document.currentScript;
|
|
53
|
+
if (script?.src) {
|
|
54
|
+
const base = new URL(".", script.src).href;
|
|
55
|
+
return base.replace(/\/$/, "");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return ".";
|
|
59
|
+
}
|
|
60
|
+
async function fetchManifest(baseUrl) {
|
|
61
|
+
const url = `${baseUrl}/puzzles/skewb/solver/generated/manifest.json`;
|
|
62
|
+
const response = await fetch(url);
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
throw new Error(`Failed to load skewb solver table manifest from ${url}: ${response.status} ${response.statusText}`);
|
|
65
|
+
}
|
|
66
|
+
return await response.json();
|
|
67
|
+
}
|
|
68
|
+
async function fetchUint16Table(baseUrl, entry, expectedEntries) {
|
|
69
|
+
const buffer = await fetchTableBuffer(baseUrl, TABLES_DIR, entry.file, { compressed: entry.compressed !== false });
|
|
70
|
+
if (buffer.byteLength !== expectedEntries * 2) {
|
|
71
|
+
throw new Error(`Invalid byte size for ${entry.file}`);
|
|
72
|
+
}
|
|
73
|
+
const values = new Uint16Array(expectedEntries);
|
|
74
|
+
const view = new DataView(buffer);
|
|
75
|
+
for (let i = 0; i < values.length; i++) {
|
|
76
|
+
values[i] = view.getUint16(i * 2, true);
|
|
77
|
+
}
|
|
78
|
+
return values;
|
|
79
|
+
}
|
|
80
|
+
async function fetchUint8Table(baseUrl, entry, expectedEntries) {
|
|
81
|
+
const buffer = await fetchTableBuffer(baseUrl, TABLES_DIR, entry.file, { compressed: entry.compressed !== false });
|
|
82
|
+
if (buffer.byteLength !== expectedEntries) {
|
|
83
|
+
throw new Error(`Invalid byte size for ${entry.file}`);
|
|
84
|
+
}
|
|
85
|
+
return new Uint8Array(buffer);
|
|
86
|
+
}
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"files": {
|
|
4
|
+
"permutationTable": {
|
|
5
|
+
"file": "permutation-table.bin",
|
|
6
|
+
"entries": 40320,
|
|
7
|
+
"checksum": 3073532981
|
|
8
|
+
},
|
|
9
|
+
"orientationTable": {
|
|
10
|
+
"file": "orientation-table.bin",
|
|
11
|
+
"entries": 17496,
|
|
12
|
+
"checksum": 2128852053
|
|
13
|
+
},
|
|
14
|
+
"centersTable": {
|
|
15
|
+
"file": "centers-table.bin",
|
|
16
|
+
"entries": 5760,
|
|
17
|
+
"checksum": 360885765
|
|
18
|
+
},
|
|
19
|
+
"helpers": {
|
|
20
|
+
"file": "helpers.bin",
|
|
21
|
+
"entries": 29547,
|
|
22
|
+
"checksum": 4284555735
|
|
23
|
+
},
|
|
24
|
+
"policyTable": {
|
|
25
|
+
"file": "policy.bin",
|
|
26
|
+
"entries": 1180980,
|
|
27
|
+
"checksum": 1294485891,
|
|
28
|
+
"maxDepth": 11
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function loadGeneratedSkewbSolverTables(): Promise<{
|
|
2
|
+
permutationTable: Uint16Array;
|
|
3
|
+
orientationTable: Uint16Array;
|
|
4
|
+
centersTable: Uint16Array;
|
|
5
|
+
policyTable: Uint8Array;
|
|
6
|
+
evenCenterRank: Uint16Array;
|
|
7
|
+
orientClass: Uint8Array;
|
|
8
|
+
r7Sets: Uint16Array;
|
|
9
|
+
}>;
|
|
10
|
+
//# sourceMappingURL=generated-assets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generated-assets.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/skewb/solver/generated-assets.ts"],"names":[],"mappings":"AAgBA,wBAAsB,8BAA8B,IAAI,OAAO,CAAC;IAC9D,gBAAgB,EAAE,WAAW,CAAC;IAC9B,gBAAgB,EAAE,WAAW,CAAC;IAC9B,YAAY,EAAE,WAAW,CAAC;IAC1B,WAAW,EAAE,UAAU,CAAC;IACxB,cAAc,EAAE,WAAW,CAAC;IAC5B,WAAW,EAAE,UAAU,CAAC;IACxB,MAAM,EAAE,WAAW,CAAC;CACrB,CAAC,CAYD"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { getCachedBrowserTables } from "./browser-loader.js";
|
|
2
|
+
let cachedTables;
|
|
3
|
+
function isBrowser() {
|
|
4
|
+
return typeof self !== "undefined";
|
|
5
|
+
}
|
|
6
|
+
export async function loadGeneratedSkewbSolverTables() {
|
|
7
|
+
if (isBrowser()) {
|
|
8
|
+
const browserTables = getCachedBrowserTables();
|
|
9
|
+
if (browserTables)
|
|
10
|
+
return browserTables;
|
|
11
|
+
throw new Error("Skewb solver tables are not loaded. " +
|
|
12
|
+
"In browser environments, call `initializeScrambler(baseUrl)` " +
|
|
13
|
+
"before generating scrambles.");
|
|
14
|
+
}
|
|
15
|
+
cachedTables ??= await loadNodeTables();
|
|
16
|
+
return cachedTables;
|
|
17
|
+
}
|
|
18
|
+
async function loadNodeTables() {
|
|
19
|
+
const loader = await import(
|
|
20
|
+
/* webpackIgnore: true */
|
|
21
|
+
/* @vite-ignore */
|
|
22
|
+
"./node-loader.js");
|
|
23
|
+
return loader.readGeneratedSkewbSolverTables();
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/skewb/solver/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,8BAA8B,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function readGeneratedSkewbSolverTables(): {
|
|
2
|
+
permutationTable: Uint16Array;
|
|
3
|
+
orientationTable: Uint16Array;
|
|
4
|
+
centersTable: Uint16Array;
|
|
5
|
+
policyTable: Uint8Array;
|
|
6
|
+
evenCenterRank: Uint16Array;
|
|
7
|
+
orientClass: Uint8Array;
|
|
8
|
+
r7Sets: Uint16Array;
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=node-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-loader.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/skewb/solver/node-loader.ts"],"names":[],"mappings":"AAkBA,wBAAgB,8BAA8B,IAAI;IAChD,gBAAgB,EAAE,WAAW,CAAC;IAC9B,gBAAgB,EAAE,WAAW,CAAC;IAC9B,YAAY,EAAE,WAAW,CAAC;IAC1B,WAAW,EAAE,UAAU,CAAC;IACxB,cAAc,EAAE,WAAW,CAAC;IAC5B,WAAW,EAAE,UAAU,CAAC;IACxB,MAAM,EAAE,WAAW,CAAC;CACrB,CA6DA"}
|