@cubingcenter/scrambler 0.2.0 → 0.3.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.
Files changed (43) hide show
  1. package/dist/puzzles/index.d.ts +1 -0
  2. package/dist/puzzles/index.d.ts.map +1 -1
  3. package/dist/puzzles/index.js +3 -0
  4. package/dist/puzzles/pyraminx/scrambler.js +2 -2
  5. package/dist/puzzles/skewb/facelets.d.ts +6 -0
  6. package/dist/puzzles/skewb/facelets.d.ts.map +1 -0
  7. package/dist/puzzles/skewb/facelets.js +71 -0
  8. package/dist/puzzles/skewb/index.d.ts +10 -0
  9. package/dist/puzzles/skewb/index.d.ts.map +1 -0
  10. package/dist/puzzles/skewb/index.js +8 -0
  11. package/dist/puzzles/skewb/moves.d.ts +19 -0
  12. package/dist/puzzles/skewb/moves.d.ts.map +1 -0
  13. package/dist/puzzles/skewb/moves.js +84 -0
  14. package/dist/puzzles/skewb/random-state.d.ts +3 -0
  15. package/dist/puzzles/skewb/random-state.d.ts.map +1 -0
  16. package/dist/puzzles/skewb/random-state.js +27 -0
  17. package/dist/puzzles/skewb/scrambler.d.ts +6 -0
  18. package/dist/puzzles/skewb/scrambler.d.ts.map +1 -0
  19. package/dist/puzzles/skewb/scrambler.js +52 -0
  20. package/dist/puzzles/skewb/solver.d.ts +11 -0
  21. package/dist/puzzles/skewb/solver.d.ts.map +1 -0
  22. package/dist/puzzles/skewb/solver.js +149 -0
  23. package/dist/puzzles/skewb/state.d.ts +6 -0
  24. package/dist/puzzles/skewb/state.d.ts.map +1 -0
  25. package/dist/puzzles/skewb/state.js +31 -0
  26. package/dist/puzzles/skewb/types.d.ts +25 -0
  27. package/dist/puzzles/skewb/types.d.ts.map +1 -0
  28. package/dist/puzzles/skewb/types.js +13 -0
  29. package/dist/puzzles/skewb/validation.d.ts +6 -0
  30. package/dist/puzzles/skewb/validation.d.ts.map +1 -0
  31. package/dist/puzzles/skewb/validation.js +69 -0
  32. package/dist-cjs/puzzles/index.js +4 -1
  33. package/dist-cjs/puzzles/pyraminx/scrambler.js +2 -2
  34. package/dist-cjs/puzzles/skewb/facelets.js +77 -0
  35. package/dist-cjs/puzzles/skewb/index.js +38 -0
  36. package/dist-cjs/puzzles/skewb/moves.js +95 -0
  37. package/dist-cjs/puzzles/skewb/random-state.js +30 -0
  38. package/dist-cjs/puzzles/skewb/scrambler.js +56 -0
  39. package/dist-cjs/puzzles/skewb/solver.js +160 -0
  40. package/dist-cjs/puzzles/skewb/state.js +37 -0
  41. package/dist-cjs/puzzles/skewb/types.js +16 -0
  42. package/dist-cjs/puzzles/skewb/validation.js +75 -0
  43. package/package.json +1 -1
@@ -7,4 +7,5 @@ export * as cube6x6 from "./cube6x6/index.js";
7
7
  export * as cube7x7 from "./cube7x7/index.js";
8
8
  export * as pyraminx from "./pyraminx/index.js";
9
9
  export * as clock from "./clock/index.js";
10
+ export * as skewb from "./skewb/index.js";
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/puzzles/index.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAC;AAC9C,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAC;AAC9C,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAC;AAC9C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/puzzles/index.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAC;AAC9C,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAC;AAC9C,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAC;AAC9C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC"}
@@ -8,6 +8,7 @@ import { cube6ScrambleGenerator } from "./cube6x6/index.js";
8
8
  import { cube7ScrambleGenerator } from "./cube7x7/index.js";
9
9
  import { pyraminxScrambleGenerator } from "./pyraminx/index.js";
10
10
  import { clockScrambleGenerator } from "./clock/index.js";
11
+ import { skewbScrambleGenerator } from "./skewb/index.js";
11
12
  registerScrambleGenerator(cube2ScrambleGenerator);
12
13
  registerScrambleGenerator(cube3ScrambleGenerator);
13
14
  registerScrambleGenerator(cube4ScrambleGenerator);
@@ -17,6 +18,7 @@ registerScrambleGenerator(cube6ScrambleGenerator);
17
18
  registerScrambleGenerator(cube7ScrambleGenerator);
18
19
  registerScrambleGenerator(pyraminxScrambleGenerator);
19
20
  registerScrambleGenerator(clockScrambleGenerator);
21
+ registerScrambleGenerator(skewbScrambleGenerator);
20
22
  export * as cube2 from "./cube2/index.js";
21
23
  export * as cube3 from "./cube3/index.js";
22
24
  export * as cube4 from "./cube4/index.js";
@@ -26,3 +28,4 @@ export * as cube6x6 from "./cube6x6/index.js";
26
28
  export * as cube7x7 from "./cube7x7/index.js";
27
29
  export * as pyraminx from "./pyraminx/index.js";
28
30
  export * as clock from "./clock/index.js";
31
+ export * as skewb from "./skewb/index.js";
@@ -5,7 +5,7 @@ import { solvePyraminx } from "./solver.js";
5
5
  import { formatPyraminxFacelets, toPyraminxFacelets } from "./facelets.js";
6
6
  export const pyraminxScrambleGenerator = {
7
7
  info: {
8
- eventId: "pyrso",
8
+ eventId: "pyra",
9
9
  name: "Pyraminx",
10
10
  puzzle: "pyraminx",
11
11
  quality: "random-state",
@@ -46,7 +46,7 @@ export async function generatePyraminxScramble(options = {}) {
46
46
  }
47
47
  const facelets = toPyraminxFacelets(state);
48
48
  return {
49
- eventId: "pyrso",
49
+ eventId: "pyra",
50
50
  scramble: finalMoves.join(" "),
51
51
  moves: finalMoves,
52
52
  quality: "random-state",
@@ -0,0 +1,6 @@
1
+ import type { SkewbFacelets, SkewbState } from "./types.js";
2
+ export declare function createSolvedSkewbFacelets(): SkewbFacelets;
3
+ export declare function toSkewbFacelets(state: SkewbState): SkewbFacelets;
4
+ export declare function formatSkewbFacelets(facelets: SkewbFacelets): string;
5
+ export declare function getSkewbFaceOrder(): readonly string[];
6
+ //# sourceMappingURL=facelets.d.ts.map
@@ -0,0 +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;AAgC5D,wBAAgB,yBAAyB,IAAI,aAAa,CASzD;AAaD,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,aAAa,CAsBhE;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,CAEnE;AAED,wBAAgB,iBAAiB,IAAI,SAAS,MAAM,EAAE,CAErD"}
@@ -0,0 +1,71 @@
1
+ // Facelets model matching cubingcenter-viz/src/puzzles/skewb.ts
2
+ // Each face has 5 stickers: 0=center, 1=TL,2=TR,3=BL,4=BR (net layout)
3
+ const FACE_ORDER = ["U", "R", "F", "D", "L", "B"];
4
+ // Canonical sticker faces per corner position: [UD sticker, RL sticker, FB sticker]
5
+ const CORNER_STICKERS = [
6
+ ["U", "R", "F"],
7
+ ["U", "L", "F"],
8
+ ["U", "R", "B"],
9
+ ["U", "L", "B"],
10
+ ["D", "R", "F"],
11
+ ["D", "L", "F"],
12
+ ["D", "R", "B"],
13
+ ["D", "L", "B"],
14
+ ];
15
+ // Facelet index of each corner position on each of its 3 faces, aligned with CORNER_STICKERS order.
16
+ const CORNER_SLOTS = [
17
+ [["U", 4], ["R", 1], ["F", 2]],
18
+ [["U", 3], ["L", 2], ["F", 1]],
19
+ [["U", 2], ["R", 2], ["B", 1]],
20
+ [["U", 1], ["L", 1], ["B", 2]],
21
+ [["D", 2], ["R", 3], ["F", 4]],
22
+ [["D", 1], ["L", 4], ["F", 3]],
23
+ [["D", 4], ["R", 4], ["B", 3]],
24
+ [["D", 3], ["L", 3], ["B", 4]],
25
+ ];
26
+ export function createSolvedSkewbFacelets() {
27
+ return {
28
+ U: Array.from({ length: 5 }, () => "U"),
29
+ R: Array.from({ length: 5 }, () => "R"),
30
+ F: Array.from({ length: 5 }, () => "F"),
31
+ D: Array.from({ length: 5 }, () => "D"),
32
+ L: Array.from({ length: 5 }, () => "L"),
33
+ B: Array.from({ length: 5 }, () => "B"),
34
+ };
35
+ }
36
+ function createEmptySkewbFacelets() {
37
+ return {
38
+ U: Array(5).fill("?"),
39
+ R: Array(5).fill("?"),
40
+ F: Array(5).fill("?"),
41
+ D: Array(5).fill("?"),
42
+ L: Array(5).fill("?"),
43
+ B: Array(5).fill("?"),
44
+ };
45
+ }
46
+ export function toSkewbFacelets(state) {
47
+ const facelets = createEmptySkewbFacelets();
48
+ // centers
49
+ for (let face = 0; face < 6; face++) {
50
+ const targetFace = FACE_ORDER[face];
51
+ facelets[targetFace][0] = FACE_ORDER[state.centers[face]];
52
+ }
53
+ // corners
54
+ for (let pos = 0; pos < 8; pos++) {
55
+ const piece = state.corners.permutation[pos];
56
+ const ori = state.corners.orientation[pos];
57
+ const slots = CORNER_SLOTS[pos];
58
+ const stickers = CORNER_STICKERS[piece];
59
+ for (let i = 0; i < 3; i++) {
60
+ const [face, idx] = slots[i];
61
+ facelets[face][idx] = stickers[(i + 3 - ori) % 3];
62
+ }
63
+ }
64
+ return facelets;
65
+ }
66
+ export function formatSkewbFacelets(facelets) {
67
+ return FACE_ORDER.map((face) => facelets[face].join("")).join("");
68
+ }
69
+ export function getSkewbFaceOrder() {
70
+ return FACE_ORDER;
71
+ }
@@ -0,0 +1,10 @@
1
+ export { SKEWB_MOVES, SKEWB_FACES } from "./types.js";
2
+ export type { SkewbMove, SkewbState, SkewbFaceMove, SkewbRandomStateOptions, SkewbFacelets } from "./types.js";
3
+ export { applySkewbAlgorithm, applySkewbMove, formatSkewbAlgorithm, invertSkewbAlgorithm, invertSkewbMove, parseSkewbAlgorithm, parseSkewbMove, } from "./moves.js";
4
+ export { cloneSkewbState, createSolvedSkewbState, isSolvedSkewbState, skewbStatesEqual, } from "./state.js";
5
+ export { permutationParity, validateSkewbState, isAllowedRandomSkewbState, isOneMoveFromSolvedSkewbState } from "./validation.js";
6
+ export { generateRandomSkewbState } from "./random-state.js";
7
+ export { createSolvedSkewbFacelets, formatSkewbFacelets, getSkewbFaceOrder, toSkewbFacelets } from "./facelets.js";
8
+ export { solveSkewb, getStateIndex, getStateFromIndex } from "./solver.js";
9
+ export { generateSkewbScramble, skewbScrambleGenerator } from "./scrambler.js";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/puzzles/skewb/index.ts"],"names":[],"mappings":"AAAA,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,EAAE,UAAU,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC3E,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,8 @@
1
+ export { SKEWB_MOVES, SKEWB_FACES } from "./types.js";
2
+ export { applySkewbAlgorithm, applySkewbMove, formatSkewbAlgorithm, invertSkewbAlgorithm, invertSkewbMove, parseSkewbAlgorithm, parseSkewbMove, } from "./moves.js";
3
+ export { cloneSkewbState, createSolvedSkewbState, isSolvedSkewbState, skewbStatesEqual, } from "./state.js";
4
+ export { permutationParity, validateSkewbState, isAllowedRandomSkewbState, isOneMoveFromSolvedSkewbState } from "./validation.js";
5
+ export { generateRandomSkewbState } from "./random-state.js";
6
+ export { createSolvedSkewbFacelets, formatSkewbFacelets, getSkewbFaceOrder, toSkewbFacelets } from "./facelets.js";
7
+ export { solveSkewb, getStateIndex, getStateFromIndex } from "./solver.js";
8
+ export { generateSkewbScramble, skewbScrambleGenerator } from "./scrambler.js";
@@ -0,0 +1,19 @@
1
+ import type { SkewbMove, SkewbState } from "./types.js";
2
+ interface MoveTable {
3
+ corners: {
4
+ permutation: readonly number[];
5
+ orientation: readonly number[];
6
+ };
7
+ centers: readonly number[];
8
+ }
9
+ declare const MOVE_TABLES: Record<string, MoveTable>;
10
+ export declare function applySkewbMove(state: SkewbState, move: SkewbMove): SkewbState;
11
+ export declare function applySkewbAlgorithm(state: SkewbState, moves: readonly SkewbMove[]): SkewbState;
12
+ export declare function invertSkewbMove(move: SkewbMove): SkewbMove;
13
+ export declare function invertSkewbAlgorithm(moves: readonly SkewbMove[]): SkewbMove[];
14
+ export declare function parseSkewbMove(value: string): SkewbMove;
15
+ export declare function parseSkewbAlgorithm(algorithm: string): SkewbMove[];
16
+ export declare function formatSkewbAlgorithm(moves: readonly SkewbMove[]): string;
17
+ export declare function getAllSkewbMovesForTables(): readonly string[];
18
+ export { MOVE_TABLES };
19
+ //# sourceMappingURL=moves.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"moves.d.ts","sourceRoot":"","sources":["../../../src/puzzles/skewb/moves.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAMxD,UAAU,SAAS;IACjB,OAAO,EAAE;QAAE,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;QAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,CAAC;IAC5E,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B;AAED,QAAA,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAyB1C,CAAC;AAkBF,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,GAAG,UAAU,CAS7E;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,UAAU,CAE9F;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAG1D;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,EAAE,CAE7E;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAGvD;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,CAIlE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,MAAM,CAExE;AAGD,wBAAgB,yBAAyB,IAAI,SAAS,MAAM,EAAE,CAE7D;AAED,OAAO,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,84 @@
1
+ import { SKEWB_MOVES } from "./types.js";
2
+ import { cloneSkewbState } from "./state.js";
3
+ const MOVE_TABLES = {
4
+ F: {
5
+ corners: { permutation: [0, 4, 1, 3, 2, 5, 6, 7], orientation: [1, 1, 1, 0, 1, 0, 0, 0] },
6
+ centers: [2, 0, 1, 3, 4, 5],
7
+ },
8
+ D: {
9
+ corners: { permutation: [5, 1, 2, 3, 4, 6, 0, 7], orientation: [2, 0, 0, 0, 2, 2, 2, 0] },
10
+ centers: [0, 2, 3, 1, 4, 5],
11
+ },
12
+ U: {
13
+ corners: { permutation: [0, 2, 7, 3, 4, 5, 6, 1], orientation: [0, 1, 1, 1, 0, 0, 0, 1] },
14
+ centers: [5, 1, 2, 3, 0, 4],
15
+ },
16
+ R: {
17
+ corners: { permutation: [0, 1, 4, 3, 7, 5, 6, 2], orientation: [0, 0, 1, 0, 1, 0, 1, 1] },
18
+ centers: [0, 3, 2, 5, 4, 1],
19
+ },
20
+ L: {
21
+ corners: { permutation: [0, 7, 2, 3, 1, 5, 6, 4], orientation: [0, 1, 0, 0, 1, 1, 0, 1] },
22
+ centers: [0, 1, 4, 2, 3, 5],
23
+ },
24
+ B: {
25
+ corners: { permutation: [0, 1, 2, 6, 4, 3, 5, 7], orientation: [0, 0, 0, 2, 0, 2, 2, 2] },
26
+ centers: [0, 1, 2, 4, 5, 3],
27
+ },
28
+ };
29
+ function applyFaceMove(state, table) {
30
+ const perm = state.corners.permutation;
31
+ const orient = state.corners.orientation;
32
+ const centers = state.centers;
33
+ const newPerm = table.corners.permutation.map((src) => perm[src]);
34
+ const newOrient = table.corners.permutation.map((src, tgt) => (orient[src] + table.corners.orientation[tgt]) % 3);
35
+ const newCenters = table.centers.map((src) => centers[src]);
36
+ for (let i = 0; i < perm.length; i++)
37
+ perm[i] = newPerm[i];
38
+ for (let i = 0; i < orient.length; i++)
39
+ orient[i] = newOrient[i];
40
+ for (let i = 0; i < centers.length; i++)
41
+ centers[i] = newCenters[i];
42
+ }
43
+ export function applySkewbMove(state, move) {
44
+ const face = move[0].toUpperCase();
45
+ const suffix = move.slice(1);
46
+ const table = MOVE_TABLES[face];
47
+ if (!table)
48
+ throw new Error(`Unknown Skewb move: ${move}`);
49
+ const repetitions = suffix === "'" || suffix === "2" ? 2 : 1;
50
+ const next = cloneSkewbState(state);
51
+ for (let i = 0; i < repetitions; i++)
52
+ applyFaceMove(next, table);
53
+ return next;
54
+ }
55
+ export function applySkewbAlgorithm(state, moves) {
56
+ return moves.reduce((current, move) => applySkewbMove(current, move), state);
57
+ }
58
+ export function invertSkewbMove(move) {
59
+ if (move.endsWith("'"))
60
+ return move.slice(0, -1);
61
+ return `${move}'`;
62
+ }
63
+ export function invertSkewbAlgorithm(moves) {
64
+ return [...moves].reverse().map(invertSkewbMove);
65
+ }
66
+ export function parseSkewbMove(value) {
67
+ if (SKEWB_MOVES.includes(value))
68
+ return value;
69
+ throw new Error(`Invalid Skewb move: ${value}`);
70
+ }
71
+ export function parseSkewbAlgorithm(algorithm) {
72
+ const trimmed = algorithm.trim();
73
+ if (trimmed.length === 0)
74
+ return [];
75
+ return trimmed.split(/\s+/).map(parseSkewbMove);
76
+ }
77
+ export function formatSkewbAlgorithm(moves) {
78
+ return moves.join(" ");
79
+ }
80
+ // For internal use by solver (needs all 6 faces for table generation)
81
+ export function getAllSkewbMovesForTables() {
82
+ return ["R", "U", "L", "B", "F", "D"].flatMap((f) => [f, `${f}'`]);
83
+ }
84
+ export { MOVE_TABLES };
@@ -0,0 +1,3 @@
1
+ import type { SkewbRandomStateOptions, SkewbState } from "./types.js";
2
+ export declare function generateRandomSkewbState(options?: SkewbRandomStateOptions): SkewbState;
3
+ //# sourceMappingURL=random-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"random-state.d.ts","sourceRoot":"","sources":["../../../src/puzzles/skewb/random-state.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAMtE,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,uBAA4B,GAAG,UAAU,CAS1F"}
@@ -0,0 +1,27 @@
1
+ import { createRandomSource } from "../../random/index.js";
2
+ import { isAllowedRandomSkewbState } from "./validation.js";
3
+ import { applySkewbMove } from "./moves.js";
4
+ import { createSolvedSkewbState } from "./state.js";
5
+ import { SKEWB_MOVES } from "./types.js";
6
+ export function generateRandomSkewbState(options = {}) {
7
+ const random = createRandomSource(options.seed);
8
+ const rejectTrivial = options.rejectTrivial ?? true;
9
+ for (let attempt = 0; attempt < 100; attempt++) {
10
+ const state = createLegalRandomState(random);
11
+ if (!rejectTrivial || isAllowedRandomSkewbState(state))
12
+ return state;
13
+ }
14
+ throw new Error("Unable to generate a non-trivial random Skewb state.");
15
+ }
16
+ function createLegalRandomState(random) {
17
+ // Generate a random reachable Skewb state via random walk from solved.
18
+ // Skewb has 3,149,280 reachable states with WCA moves (fixed corner).
19
+ // A walk of 30+ moves is sufficient to approach uniform distribution.
20
+ let state = createSolvedSkewbState();
21
+ const walkLength = 30 + random.nextInt(5); // 30-34 moves
22
+ for (let i = 0; i < walkLength; i++) {
23
+ const move = random.choice([...SKEWB_MOVES]);
24
+ state = applySkewbMove(state, move);
25
+ }
26
+ return state;
27
+ }
@@ -0,0 +1,6 @@
1
+ import type { ScrambleGenerator, ScrambleOptions, ScrambleResult } from "../../core/index.js";
2
+ export declare const skewbScrambleGenerator: ScrambleGenerator;
3
+ export declare function generateSkewbScramble(options?: ScrambleOptions & {
4
+ skipValidation?: boolean;
5
+ }): Promise<ScrambleResult>;
6
+ //# sourceMappingURL=scrambler.d.ts.map
@@ -0,0 +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;AAO9F,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,CAiCzB"}
@@ -0,0 +1,52 @@
1
+ import { generateRandomSkewbState } from "./random-state.js";
2
+ import { createSolvedSkewbState, isSolvedSkewbState, skewbStatesEqual } from "./state.js";
3
+ import { applySkewbAlgorithm, formatSkewbAlgorithm, invertSkewbAlgorithm } from "./moves.js";
4
+ import { solveSkewb } from "./solver.js";
5
+ import { formatSkewbFacelets, toSkewbFacelets } from "./facelets.js";
6
+ export const skewbScrambleGenerator = {
7
+ info: {
8
+ eventId: "skewb",
9
+ name: "Skewb",
10
+ puzzle: "skewb",
11
+ quality: "random-state",
12
+ generator: "skewb-bfs",
13
+ description: "Random-state Skewb scramble generated by an optimal bidirectional BFS solver.",
14
+ },
15
+ generate(options = {}) {
16
+ return generateSkewbScramble(options);
17
+ },
18
+ };
19
+ export async function generateSkewbScramble(options = {}) {
20
+ const state = generateRandomSkewbState(options);
21
+ const solution = solveSkewb(state);
22
+ const scrambleMoves = invertSkewbAlgorithm(solution);
23
+ if (!options.skipValidation) {
24
+ const scrambledState = applySkewbAlgorithm(createSolvedSkewbState(), scrambleMoves);
25
+ if (!skewbStatesEqual(scrambledState, state)) {
26
+ throw new Error("Generated Skewb scramble does not reproduce the target random state.");
27
+ }
28
+ if (!isSolvedSkewbState(applySkewbAlgorithm(state, solution))) {
29
+ throw new Error("Generated Skewb solution does not solve the target random state.");
30
+ }
31
+ if (scrambleMoves.length < 2)
32
+ throw new Error("Generated Skewb scramble is too short.");
33
+ }
34
+ const facelets = toSkewbFacelets(state);
35
+ const result = {
36
+ eventId: "skewb",
37
+ scramble: formatSkewbAlgorithm(scrambleMoves),
38
+ moves: scrambleMoves,
39
+ quality: "random-state",
40
+ generator: "skewb-bfs",
41
+ metadata: {
42
+ state,
43
+ facelets,
44
+ faceletString: formatSkewbFacelets(facelets),
45
+ solutionMoves: solution,
46
+ solutionDepth: solution.length,
47
+ },
48
+ };
49
+ if (options.seed !== undefined)
50
+ result.seed = options.seed;
51
+ return result;
52
+ }
@@ -0,0 +1,11 @@
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 function solveSkewb(state: SkewbState): SkewbMove[];
11
+ //# sourceMappingURL=solver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"solver.d.ts","sourceRoot":"","sources":["../../../src/puzzles/skewb/solver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAMxD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAWnE;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAY1E;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAOrE;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAS/D;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CASjE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAE1D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAOvD;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAY3D;AAID,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,EAAE,CA+CzD"}
@@ -0,0 +1,149 @@
1
+ import { applySkewbMove } from "./moves.js";
2
+ import { createSolvedSkewbState } from "./state.js";
3
+ const FACT = [1, 1, 2, 6, 24, 120, 720, 5040, 40320];
4
+ export function getPermutationIndex(perm) {
5
+ // perm length 8, but pos0 is always 0 for WCA reachable states.
6
+ // We encode full 8-perm via Lehmer code (0..40319), but for fixed corner it will be in subset.
7
+ let index = 0;
8
+ const n = perm.length;
9
+ for (let i = 0; i < n; i++) {
10
+ let count = 0;
11
+ for (let j = i + 1; j < n; j++)
12
+ if (perm[j] < perm[i])
13
+ count++;
14
+ index += count * FACT[n - 1 - i];
15
+ }
16
+ return index;
17
+ }
18
+ export function getPermutationFromIndex(index, n) {
19
+ const perm = Array(n).fill(0);
20
+ const temp = Array.from({ length: n }, (_, i) => i);
21
+ let val = index;
22
+ for (let i = 0; i < n; i++) {
23
+ const f = FACT[n - 1 - i];
24
+ const choice = Math.floor(val / f);
25
+ val %= f;
26
+ perm[i] = temp[choice];
27
+ temp.splice(choice, 1);
28
+ }
29
+ return perm;
30
+ }
31
+ export function getOrientationIndex(orient) {
32
+ // orient length 8, index 0 is fixed 0, encode remaining 7 base3
33
+ let index = 0;
34
+ for (let i = 7; i >= 1; i--) {
35
+ index = index * 3 + orient[i];
36
+ }
37
+ return index;
38
+ }
39
+ export function getOrientationFromIndex(index) {
40
+ const orient = Array(8).fill(0);
41
+ orient[0] = 0;
42
+ let val = index;
43
+ for (let i = 1; i < 8; i++) {
44
+ orient[i] = val % 3;
45
+ val = Math.floor(val / 3);
46
+ }
47
+ return orient;
48
+ }
49
+ export function getCenterIndex(centers) {
50
+ let index = 0;
51
+ const n = centers.length;
52
+ for (let i = 0; i < n; i++) {
53
+ let count = 0;
54
+ for (let j = i + 1; j < n; j++)
55
+ if (centers[j] < centers[i])
56
+ count++;
57
+ index += count * FACT[n - 1 - i];
58
+ }
59
+ return index;
60
+ }
61
+ export function getCenterFromIndex(index) {
62
+ return getPermutationFromIndex(index, 6);
63
+ }
64
+ export function getStateIndex(state) {
65
+ const p = getPermutationIndex(state.corners.permutation);
66
+ const c = getCenterIndex(state.centers);
67
+ const o = getOrientationIndex(state.corners.orientation);
68
+ // p in 0..40319, c in 0..719, o in 0..2186
69
+ // Combine: ((p * 720 + c) * 2187 + o) -> up to 63B fits in JS number (53-bit)
70
+ return ((p * 720 + c) * 2187 + o);
71
+ }
72
+ export function getStateFromIndex(index) {
73
+ const o = index % 2187;
74
+ let val = Math.floor(index / 2187);
75
+ const c = val % 720;
76
+ const p = Math.floor(val / 720);
77
+ return {
78
+ corners: {
79
+ permutation: getPermutationFromIndex(p, 8),
80
+ orientation: getOrientationFromIndex(o),
81
+ },
82
+ centers: getCenterFromIndex(c),
83
+ };
84
+ }
85
+ const MOVES = ["R", "R'", "U", "U'", "L", "L'", "B", "B'"];
86
+ export function solveSkewb(state) {
87
+ const startIdx = getStateIndex(state);
88
+ const solvedIdx = getStateIndex(createSolvedSkewbState());
89
+ if (startIdx === solvedIdx)
90
+ return [];
91
+ const forwardMap = new Map();
92
+ const backwardMap = new Map();
93
+ const forwardQueue = [startIdx];
94
+ const backwardQueue = [solvedIdx];
95
+ forwardMap.set(startIdx, { move: "", parent: -1 });
96
+ backwardMap.set(solvedIdx, { move: "", parent: -1 });
97
+ while (forwardQueue.length > 0 && backwardQueue.length > 0) {
98
+ const fSize = forwardQueue.length;
99
+ for (let i = 0; i < Math.min(fSize, 1000); i++) {
100
+ const currIdx = forwardQueue.shift();
101
+ const currState = getStateFromIndex(currIdx);
102
+ for (const move of MOVES) {
103
+ const nextState = applySkewbMove(currState, move);
104
+ const nextIdx = getStateIndex(nextState);
105
+ if (!forwardMap.has(nextIdx)) {
106
+ forwardMap.set(nextIdx, { move, parent: currIdx });
107
+ if (backwardMap.has(nextIdx))
108
+ return reconstructPath(nextIdx, forwardMap, backwardMap);
109
+ forwardQueue.push(nextIdx);
110
+ }
111
+ }
112
+ }
113
+ const bSize = backwardQueue.length;
114
+ for (let i = 0; i < Math.min(bSize, 1000); i++) {
115
+ const currIdx = backwardQueue.shift();
116
+ const currState = getStateFromIndex(currIdx);
117
+ for (const move of MOVES) {
118
+ const nextState = applySkewbMove(currState, move);
119
+ const nextIdx = getStateIndex(nextState);
120
+ if (!backwardMap.has(nextIdx)) {
121
+ const invMove = move.endsWith("'") ? move.slice(0, -1) : `${move}'`;
122
+ backwardMap.set(nextIdx, { move: invMove, parent: currIdx });
123
+ if (forwardMap.has(nextIdx))
124
+ return reconstructPath(nextIdx, forwardMap, backwardMap);
125
+ backwardQueue.push(nextIdx);
126
+ }
127
+ }
128
+ }
129
+ }
130
+ throw new Error("Skewb state is unsolvable");
131
+ }
132
+ function reconstructPath(meetingIdx, forwardMap, backwardMap) {
133
+ const path = [];
134
+ let curr = meetingIdx;
135
+ while (curr !== -1) {
136
+ const edge = forwardMap.get(curr);
137
+ if (edge.parent !== -1)
138
+ path.unshift(edge.move);
139
+ curr = edge.parent;
140
+ }
141
+ curr = meetingIdx;
142
+ while (curr !== -1) {
143
+ const edge = backwardMap.get(curr);
144
+ if (edge.parent !== -1)
145
+ path.push(edge.move);
146
+ curr = edge.parent;
147
+ }
148
+ return path;
149
+ }
@@ -0,0 +1,6 @@
1
+ import type { SkewbState } from "./types.js";
2
+ export declare function createSolvedSkewbState(): SkewbState;
3
+ export declare function cloneSkewbState(state: SkewbState): SkewbState;
4
+ export declare function skewbStatesEqual(left: SkewbState, right: SkewbState): boolean;
5
+ export declare function isSolvedSkewbState(state: SkewbState): boolean;
6
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/puzzles/skewb/state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,wBAAgB,sBAAsB,IAAI,UAAU,CAQnD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAQ7D;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAM7E;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAM7D"}
@@ -0,0 +1,31 @@
1
+ export function createSolvedSkewbState() {
2
+ return {
3
+ corners: {
4
+ permutation: [0, 1, 2, 3, 4, 5, 6, 7],
5
+ orientation: [0, 0, 0, 0, 0, 0, 0, 0],
6
+ },
7
+ centers: [0, 1, 2, 3, 4, 5],
8
+ };
9
+ }
10
+ export function cloneSkewbState(state) {
11
+ return {
12
+ corners: {
13
+ permutation: [...state.corners.permutation],
14
+ orientation: [...state.corners.orientation],
15
+ },
16
+ centers: [...state.centers],
17
+ };
18
+ }
19
+ export function skewbStatesEqual(left, right) {
20
+ return (arraysEqual(left.corners.permutation, right.corners.permutation) &&
21
+ arraysEqual(left.corners.orientation, right.corners.orientation) &&
22
+ arraysEqual(left.centers, right.centers));
23
+ }
24
+ export function isSolvedSkewbState(state) {
25
+ return (state.corners.permutation.every((value, index) => value === index) &&
26
+ state.corners.orientation.every((value) => value === 0) &&
27
+ state.centers.every((value, index) => value === index));
28
+ }
29
+ function arraysEqual(left, right) {
30
+ return left.length === right.length && left.every((value, index) => value === right[index]);
31
+ }
@@ -0,0 +1,25 @@
1
+ export declare const CORNER_COUNT = 8;
2
+ export declare const CENTER_COUNT = 6;
3
+ export type SkewbFace = "U" | "R" | "F" | "D" | "L" | "B";
4
+ export type SkewbFaceMove = "R" | "U" | "L" | "B";
5
+ export type SkewbMoveSuffix = "" | "'";
6
+ export type SkewbMove = `${SkewbFaceMove}${SkewbMoveSuffix}`;
7
+ export interface SkewbState {
8
+ corners: {
9
+ permutation: number[];
10
+ orientation: number[];
11
+ };
12
+ centers: number[];
13
+ }
14
+ export interface SkewbValidationResult {
15
+ valid: boolean;
16
+ errors: string[];
17
+ }
18
+ export interface SkewbRandomStateOptions {
19
+ seed?: string | number;
20
+ rejectTrivial?: boolean;
21
+ }
22
+ export type SkewbFacelets = Record<SkewbFace, string[]>;
23
+ export declare const SKEWB_MOVES: readonly SkewbMove[];
24
+ export declare const SKEWB_FACES: readonly SkewbFace[];
25
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/puzzles/skewb/types.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,IAAI,CAAC;AAC9B,eAAO,MAAM,YAAY,IAAI,CAAC;AAE9B,MAAM,MAAM,SAAS,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAC1D,MAAM,MAAM,aAAa,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAClD,MAAM,MAAM,eAAe,GAAG,EAAE,GAAG,GAAG,CAAE;AACxC,MAAM,MAAM,SAAS,GAAG,GAAG,aAAa,GAAG,eAAe,EAAE,CAAC;AAE7D,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE;QACP,WAAW,EAAE,MAAM,EAAE,CAAC;QACtB,WAAW,EAAE,MAAM,EAAE,CAAC;KACvB,CAAC;IACF,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;AAExD,eAAO,MAAM,WAAW,EAAE,SAAS,SAAS,EAS3C,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,SAAS,SAAS,EAAmC,CAAC"}
@@ -0,0 +1,13 @@
1
+ export const CORNER_COUNT = 8;
2
+ export const CENTER_COUNT = 6;
3
+ export const SKEWB_MOVES = [
4
+ "R",
5
+ "R'",
6
+ "U",
7
+ "U'",
8
+ "L",
9
+ "L'",
10
+ "B",
11
+ "B'",
12
+ ];
13
+ export const SKEWB_FACES = ["U", "R", "F", "D", "L", "B"];
@@ -0,0 +1,6 @@
1
+ import type { SkewbState, SkewbValidationResult } from "./types.js";
2
+ export declare function validateSkewbState(state: SkewbState): SkewbValidationResult;
3
+ export declare function isOneMoveFromSolvedSkewbState(state: SkewbState): boolean;
4
+ export declare function isAllowedRandomSkewbState(state: SkewbState): boolean;
5
+ export declare function permutationParity(permutation: readonly number[]): 0 | 1;
6
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/puzzles/skewb/validation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAIpE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,qBAAqB,CAwB3E;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAGxE;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAEpE;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAQvE"}