@cubingcenter/scrambler 0.1.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.
- package/dist/puzzles/cube3/scrambler.d.ts.map +1 -1
- package/dist/puzzles/cube3/scrambler.js +1 -0
- package/dist/puzzles/cube4/scrambler.js +3 -3
- package/dist/puzzles/index.d.ts +1 -0
- package/dist/puzzles/index.d.ts.map +1 -1
- package/dist/puzzles/index.js +3 -0
- package/dist/puzzles/pyraminx/facelets.d.ts +7 -0
- package/dist/puzzles/pyraminx/facelets.d.ts.map +1 -0
- package/dist/puzzles/pyraminx/facelets.js +97 -0
- package/dist/puzzles/pyraminx/index.d.ts +2 -0
- package/dist/puzzles/pyraminx/index.d.ts.map +1 -1
- package/dist/puzzles/pyraminx/index.js +1 -0
- package/dist/puzzles/pyraminx/moves.d.ts.map +1 -1
- package/dist/puzzles/pyraminx/moves.js +35 -38
- package/dist/puzzles/pyraminx/scrambler.d.ts.map +1 -1
- package/dist/puzzles/pyraminx/scrambler.js +12 -6
- package/dist/puzzles/skewb/facelets.d.ts +6 -0
- package/dist/puzzles/skewb/facelets.d.ts.map +1 -0
- package/dist/puzzles/skewb/facelets.js +71 -0
- package/dist/puzzles/skewb/index.d.ts +10 -0
- package/dist/puzzles/skewb/index.d.ts.map +1 -0
- package/dist/puzzles/skewb/index.js +8 -0
- package/dist/puzzles/skewb/moves.d.ts +19 -0
- package/dist/puzzles/skewb/moves.d.ts.map +1 -0
- package/dist/puzzles/skewb/moves.js +84 -0
- package/dist/puzzles/skewb/random-state.d.ts +3 -0
- package/dist/puzzles/skewb/random-state.d.ts.map +1 -0
- package/dist/puzzles/skewb/random-state.js +27 -0
- package/dist/puzzles/skewb/scrambler.d.ts +6 -0
- package/dist/puzzles/skewb/scrambler.d.ts.map +1 -0
- package/dist/puzzles/skewb/scrambler.js +52 -0
- package/dist/puzzles/skewb/solver.d.ts +11 -0
- package/dist/puzzles/skewb/solver.d.ts.map +1 -0
- package/dist/puzzles/skewb/solver.js +149 -0
- package/dist/puzzles/skewb/state.d.ts +6 -0
- package/dist/puzzles/skewb/state.d.ts.map +1 -0
- package/dist/puzzles/skewb/state.js +31 -0
- package/dist/puzzles/skewb/types.d.ts +25 -0
- package/dist/puzzles/skewb/types.d.ts.map +1 -0
- package/dist/puzzles/skewb/types.js +13 -0
- package/dist/puzzles/skewb/validation.d.ts +6 -0
- package/dist/puzzles/skewb/validation.d.ts.map +1 -0
- package/dist/puzzles/skewb/validation.js +69 -0
- package/dist-cjs/puzzles/cube3/scrambler.js +1 -0
- package/dist-cjs/puzzles/cube4/scrambler.js +3 -3
- package/dist-cjs/puzzles/index.js +4 -1
- package/dist-cjs/puzzles/pyraminx/facelets.js +103 -0
- package/dist-cjs/puzzles/pyraminx/index.js +6 -1
- package/dist-cjs/puzzles/pyraminx/moves.js +35 -38
- package/dist-cjs/puzzles/pyraminx/scrambler.js +12 -6
- package/dist-cjs/puzzles/skewb/facelets.js +77 -0
- package/dist-cjs/puzzles/skewb/index.js +38 -0
- package/dist-cjs/puzzles/skewb/moves.js +95 -0
- package/dist-cjs/puzzles/skewb/random-state.js +30 -0
- package/dist-cjs/puzzles/skewb/scrambler.js +56 -0
- package/dist-cjs/puzzles/skewb/solver.js +160 -0
- package/dist-cjs/puzzles/skewb/state.js +37 -0
- package/dist-cjs/puzzles/skewb/types.js +16 -0
- package/dist-cjs/puzzles/skewb/validation.js +75 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scrambler.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/scrambler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAW9F,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,
|
|
1
|
+
{"version":3,"file":"scrambler.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/scrambler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAW9F,eAAO,MAAM,sBAAsB,EAAE,iBAYpC,CAAC;AAEF,wBAAsB,qBAAqB,CACzC,OAAO,GAAE,eAAe,GAAG;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAO,GAC3D,OAAO,CAAC,cAAc,CAAC,CAgDzB"}
|
|
@@ -42,6 +42,7 @@ export async function generateCube3Scramble(options = {}) {
|
|
|
42
42
|
metadata: {
|
|
43
43
|
generator: "cube3-two-phase",
|
|
44
44
|
generatorVersion: "1.0.0",
|
|
45
|
+
facelets,
|
|
45
46
|
faceletString: formatCube3Facelets(facelets),
|
|
46
47
|
state: JSON.parse(JSON.stringify(state)),
|
|
47
48
|
solutionMoves: solution.moves,
|
|
@@ -7,9 +7,9 @@ export const cube4ScrambleGenerator = {
|
|
|
7
7
|
eventId: "444",
|
|
8
8
|
name: "4x4x4 Cube",
|
|
9
9
|
puzzle: "cube-4x4",
|
|
10
|
-
quality: "random-
|
|
11
|
-
generator: "
|
|
12
|
-
description: "Random-
|
|
10
|
+
quality: "random-state",
|
|
11
|
+
generator: "cubing-scramble",
|
|
12
|
+
description: "Random-state 4x4 scramble generated by cubing.js."
|
|
13
13
|
},
|
|
14
14
|
generate(options = {}) {
|
|
15
15
|
return generateCube4Scramble(options);
|
package/dist/puzzles/index.d.ts
CHANGED
|
@@ -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":"
|
|
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"}
|
package/dist/puzzles/index.js
CHANGED
|
@@ -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";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { PyraminxState } from "./types.js";
|
|
2
|
+
export type PyraminxFacelets = Record<"F" | "L" | "R" | "D", readonly string[]>;
|
|
3
|
+
export declare function createSolvedPyraminxFacelets(): PyraminxFacelets;
|
|
4
|
+
export declare function toPyraminxFacelets(state: PyraminxState): PyraminxFacelets;
|
|
5
|
+
export declare function formatPyraminxFacelets(facelets: PyraminxFacelets): string;
|
|
6
|
+
export declare function getPyraminxFaceOrder(): readonly ("F" | "L" | "R" | "D")[];
|
|
7
|
+
//# sourceMappingURL=facelets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"facelets.d.ts","sourceRoot":"","sources":["../../../src/puzzles/pyraminx/facelets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAUhD,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;AA6DhF,wBAAgB,4BAA4B,IAAI,gBAAgB,CAO/D;AAyBD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,GAAG,gBAAgB,CAiCzE;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,CAEzE;AAED,wBAAgB,oBAAoB,IAAI,SAAS,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,EAAE,CAEzE"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
const FACE_ORDER = ["F", "L", "R", "D"];
|
|
2
|
+
// positionId -> sticker slots (each slot = one triangle cell of the grid).
|
|
3
|
+
// Positions follow the scrambler piece model (moves.ts):
|
|
4
|
+
// edges 0..5 = UL, UR, UB, LR, LB, RB
|
|
5
|
+
// centers 0..3 = U, L, R, B
|
|
6
|
+
// tips 0..3 = U, L, R, B
|
|
7
|
+
//
|
|
8
|
+
// The slot layout is validated against the piece model by
|
|
9
|
+
// test/pyraminx-facelets.test.ts (single moves + randomized sequences).
|
|
10
|
+
const EDGE_POSITION_SLOTS = [
|
|
11
|
+
[["F", 1], ["L", 7]], // 0 UL
|
|
12
|
+
[["F", 3], ["R", 5]], // 1 UR
|
|
13
|
+
[["L", 2], ["R", 2]], // 2 UB
|
|
14
|
+
[["F", 6], ["D", 2]], // 3 LR
|
|
15
|
+
[["L", 5], ["D", 5]], // 4 LB
|
|
16
|
+
[["R", 7], ["D", 7]], // 5 RB
|
|
17
|
+
];
|
|
18
|
+
const CENTER_POSITION_SLOTS = [
|
|
19
|
+
[["F", 2], ["L", 3], ["R", 1]], // 0 U
|
|
20
|
+
[["F", 5], ["L", 6], ["D", 1]], // 1 L
|
|
21
|
+
[["F", 7], ["R", 6], ["D", 3]], // 2 R
|
|
22
|
+
[["L", 1], ["R", 3], ["D", 6]], // 3 B
|
|
23
|
+
];
|
|
24
|
+
const TIP_POSITION_SLOTS = [
|
|
25
|
+
[["F", 0], ["L", 4], ["R", 0]], // 0 U
|
|
26
|
+
[["F", 4], ["D", 0], ["L", 8]], // 1 L
|
|
27
|
+
[["F", 8], ["R", 8], ["D", 4]], // 2 R
|
|
28
|
+
[["L", 0], ["D", 8], ["R", 4]], // 3 B
|
|
29
|
+
];
|
|
30
|
+
// pieceId -> sticker colors (canonical order aligned with the role order above)
|
|
31
|
+
const EDGE_PIECE_STICKERS = [
|
|
32
|
+
["F", "L"], // 0 UL
|
|
33
|
+
["F", "R"], // 1 UR
|
|
34
|
+
["L", "R"], // 2 UB
|
|
35
|
+
["F", "D"], // 3 LR
|
|
36
|
+
["L", "D"], // 4 LB
|
|
37
|
+
["R", "D"], // 5 RB
|
|
38
|
+
];
|
|
39
|
+
const CENTER_PIECE_STICKERS = [
|
|
40
|
+
["F", "L", "R"], // 0 U
|
|
41
|
+
["F", "L", "D"], // 1 L
|
|
42
|
+
["F", "R", "D"], // 2 R
|
|
43
|
+
["L", "R", "D"], // 3 B
|
|
44
|
+
];
|
|
45
|
+
const TIP_PIECE_STICKERS = [
|
|
46
|
+
["F", "L", "R"], // 0 U
|
|
47
|
+
["F", "D", "L"], // 1 L
|
|
48
|
+
["F", "R", "D"], // 2 R
|
|
49
|
+
["L", "D", "R"], // 3 B
|
|
50
|
+
];
|
|
51
|
+
export function createSolvedPyraminxFacelets() {
|
|
52
|
+
return {
|
|
53
|
+
F: Array.from({ length: 9 }, () => "F"),
|
|
54
|
+
L: Array.from({ length: 9 }, () => "L"),
|
|
55
|
+
R: Array.from({ length: 9 }, () => "R"),
|
|
56
|
+
D: Array.from({ length: 9 }, () => "D"),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function createEmptyPyraminxFacelets() {
|
|
60
|
+
return {
|
|
61
|
+
F: Array(9).fill("?"),
|
|
62
|
+
L: Array(9).fill("?"),
|
|
63
|
+
R: Array(9).fill("?"),
|
|
64
|
+
D: Array(9).fill("?"),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function placeOrientedStickers(facelets, slots, stickers, orientation) {
|
|
68
|
+
for (let index = 0; index < slots.length; index += 1) {
|
|
69
|
+
const [face, stickerIndex] = slots[index];
|
|
70
|
+
const sticker = stickers[(index - orientation + stickers.length) % stickers.length];
|
|
71
|
+
const arr = facelets[face];
|
|
72
|
+
arr[stickerIndex] = sticker;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export function toPyraminxFacelets(state) {
|
|
76
|
+
const facelets = createEmptyPyraminxFacelets();
|
|
77
|
+
for (let position = 0; position < 6; position += 1) {
|
|
78
|
+
const piece = state.edgesPerm[position];
|
|
79
|
+
const orientation = state.edgesOrient[position];
|
|
80
|
+
placeOrientedStickers(facelets, EDGE_POSITION_SLOTS[position], EDGE_PIECE_STICKERS[piece], orientation);
|
|
81
|
+
}
|
|
82
|
+
for (let position = 0; position < 4; position += 1) {
|
|
83
|
+
const orientation = state.centers[position];
|
|
84
|
+
placeOrientedStickers(facelets, CENTER_POSITION_SLOTS[position], CENTER_PIECE_STICKERS[position], orientation);
|
|
85
|
+
}
|
|
86
|
+
for (let position = 0; position < 4; position += 1) {
|
|
87
|
+
const orientation = state.tips[position];
|
|
88
|
+
placeOrientedStickers(facelets, TIP_POSITION_SLOTS[position], TIP_PIECE_STICKERS[position], orientation);
|
|
89
|
+
}
|
|
90
|
+
return facelets;
|
|
91
|
+
}
|
|
92
|
+
export function formatPyraminxFacelets(facelets) {
|
|
93
|
+
return FACE_ORDER.map((face) => facelets[face].join("")).join("");
|
|
94
|
+
}
|
|
95
|
+
export function getPyraminxFaceOrder() {
|
|
96
|
+
return FACE_ORDER;
|
|
97
|
+
}
|
|
@@ -5,5 +5,7 @@ export { clonePyraminxState, createSolvedPyraminxState, pyraminxStatesEqual, isS
|
|
|
5
5
|
export { permutationParity, validatePyraminxState, isAllowedRandomPyraminxState } from "./validation.js";
|
|
6
6
|
export { generateRandomPyraminxState } from "./random-state.js";
|
|
7
7
|
export { pyraminxScrambleGenerator, generatePyraminxScramble } from "./scrambler.js";
|
|
8
|
+
export { createSolvedPyraminxFacelets, formatPyraminxFacelets, getPyraminxFaceOrder, toPyraminxFacelets } from "./facelets.js";
|
|
9
|
+
export type { PyraminxFacelets } from "./facelets.js";
|
|
8
10
|
export { solvePyraminx } from "./solver.js";
|
|
9
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/puzzles/pyraminx/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,aAAa,EACd,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,0BAA0B,EAC3B,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,iBAAiB,EAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,kBAAkB,EAClB,yBAAyB,EACzB,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,4BAA4B,EAC7B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,2BAA2B,EAC5B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACzB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,aAAa,EACd,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/puzzles/pyraminx/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,aAAa,EACd,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,0BAA0B,EAC3B,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,iBAAiB,EAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,kBAAkB,EAClB,yBAAyB,EACzB,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,4BAA4B,EAC7B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,2BAA2B,EAC5B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACzB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,4BAA4B,EAC5B,sBAAsB,EACtB,oBAAoB,EACpB,kBAAkB,EACnB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,gBAAgB,EACjB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,aAAa,EACd,MAAM,aAAa,CAAC"}
|
|
@@ -4,4 +4,5 @@ export { clonePyraminxState, createSolvedPyraminxState, pyraminxStatesEqual, isS
|
|
|
4
4
|
export { permutationParity, validatePyraminxState, isAllowedRandomPyraminxState } from "./validation.js";
|
|
5
5
|
export { generateRandomPyraminxState } from "./random-state.js";
|
|
6
6
|
export { pyraminxScrambleGenerator, generatePyraminxScramble } from "./scrambler.js";
|
|
7
|
+
export { createSolvedPyraminxFacelets, formatPyraminxFacelets, getPyraminxFaceOrder, toPyraminxFacelets } from "./facelets.js";
|
|
7
8
|
export { solvePyraminx } from "./solver.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"moves.d.ts","sourceRoot":"","sources":["../../../src/puzzles/pyraminx/moves.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAqC,MAAM,YAAY,CAAC;AAKjG,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,YAAY,GAAG,aAAa,CA4BzF;
|
|
1
|
+
{"version":3,"file":"moves.d.ts","sourceRoot":"","sources":["../../../src/puzzles/pyraminx/moves.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAqC,MAAM,YAAY,CAAC;AAKjG,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,YAAY,GAAG,aAAa,CA4BzF;AA2DD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,SAAS,YAAY,EAAE,GAAG,aAAa,CAE1G;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,GAAG,YAAY,EAAE,CAEtF;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,EAAE,CAMxE;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CAE9E;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAU7D;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,YAAY,CAKnE"}
|
|
@@ -30,56 +30,53 @@ function applySingleCoreMoveCW(state, face) {
|
|
|
30
30
|
const centers = state.centers;
|
|
31
31
|
const perm = state.edgesPerm;
|
|
32
32
|
const orient = state.edgesOrient;
|
|
33
|
+
const tips = state.tips;
|
|
33
34
|
// Clone arrays for temporary storage
|
|
34
35
|
const prevPerm = [...perm];
|
|
35
36
|
const prevOrient = [...orient];
|
|
36
37
|
if (face === "U") {
|
|
37
|
-
//
|
|
38
|
+
// U: UL -> UB -> UR -> UL with the physical orientation (matches viz)
|
|
38
39
|
centers[0] = (centers[0] + 1) % 3;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
perm[1] = prevPerm[
|
|
42
|
-
perm[2] = prevPerm[
|
|
43
|
-
|
|
44
|
-
orient[1] = prevOrient[
|
|
45
|
-
orient[2] = prevOrient[
|
|
46
|
-
orient[0] = prevOrient[2];
|
|
40
|
+
tips[0] = (tips[0] + 1) % 3;
|
|
41
|
+
perm[0] = prevPerm[1];
|
|
42
|
+
perm[1] = prevPerm[2];
|
|
43
|
+
perm[2] = prevPerm[0];
|
|
44
|
+
orient[0] = (prevOrient[1] + 1) % 2;
|
|
45
|
+
orient[1] = (prevOrient[2] + 1) % 2;
|
|
46
|
+
orient[2] = prevOrient[0];
|
|
47
47
|
}
|
|
48
48
|
else if (face === "L") {
|
|
49
|
-
//
|
|
50
|
-
centers[1] = (centers[1] +
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
perm[
|
|
54
|
-
perm[
|
|
55
|
-
|
|
56
|
-
orient[
|
|
57
|
-
orient[
|
|
58
|
-
orient[0] = prevOrient[3];
|
|
49
|
+
// L: UL -> LB -> LR -> UL (physical, matches viz)
|
|
50
|
+
centers[1] = (centers[1] + 2) % 3;
|
|
51
|
+
tips[1] = (tips[1] + 1) % 3;
|
|
52
|
+
perm[0] = prevPerm[4];
|
|
53
|
+
perm[3] = prevPerm[0];
|
|
54
|
+
perm[4] = prevPerm[3];
|
|
55
|
+
orient[0] = prevOrient[4];
|
|
56
|
+
orient[3] = (prevOrient[0] + 1) % 2;
|
|
57
|
+
orient[4] = (prevOrient[3] + 1) % 2;
|
|
59
58
|
}
|
|
60
59
|
else if (face === "R") {
|
|
61
|
-
//
|
|
60
|
+
// R: UR -> LR -> RB -> UR (physical, matches viz)
|
|
62
61
|
centers[2] = (centers[2] + 1) % 3;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
perm[3] = prevPerm[
|
|
66
|
-
perm[5] = prevPerm[
|
|
67
|
-
|
|
68
|
-
orient[3] = (prevOrient[
|
|
69
|
-
orient[5] =
|
|
70
|
-
orient[1] = prevOrient[5];
|
|
62
|
+
tips[2] = (tips[2] + 1) % 3;
|
|
63
|
+
perm[1] = prevPerm[3];
|
|
64
|
+
perm[3] = prevPerm[5];
|
|
65
|
+
perm[5] = prevPerm[1];
|
|
66
|
+
orient[1] = (prevOrient[3] + 1) % 2;
|
|
67
|
+
orient[3] = (prevOrient[5] + 1) % 2;
|
|
68
|
+
orient[5] = prevOrient[1];
|
|
71
69
|
}
|
|
72
70
|
else if (face === "B") {
|
|
73
|
-
//
|
|
74
|
-
centers[3] = (centers[3] +
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
perm[
|
|
78
|
-
perm[
|
|
79
|
-
|
|
80
|
-
orient[
|
|
81
|
-
orient[
|
|
82
|
-
orient[2] = prevOrient[4];
|
|
71
|
+
// B: UB -> LB -> RB -> UB (physical, matches viz)
|
|
72
|
+
centers[3] = (centers[3] + 2) % 3;
|
|
73
|
+
tips[3] = (tips[3] + 1) % 3;
|
|
74
|
+
perm[2] = prevPerm[5];
|
|
75
|
+
perm[4] = prevPerm[2];
|
|
76
|
+
perm[5] = prevPerm[4];
|
|
77
|
+
orient[2] = prevOrient[5];
|
|
78
|
+
orient[4] = (prevOrient[2] + 1) % 2;
|
|
79
|
+
orient[5] = (prevOrient[4] + 1) % 2;
|
|
83
80
|
}
|
|
84
81
|
}
|
|
85
82
|
export function applyPyraminxAlgorithm(state, moves) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scrambler.d.ts","sourceRoot":"","sources":["../../../src/puzzles/pyraminx/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/pyraminx/scrambler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAQ9F,eAAO,MAAM,yBAAyB,EAAE,iBAYvC,CAAC;AAEF,wBAAsB,wBAAwB,CAC5C,OAAO,GAAE,eAAe,GAAG;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAO,GAC3D,OAAO,CAAC,cAAc,CAAC,CA+CzB"}
|
|
@@ -2,9 +2,10 @@ import { generateRandomPyraminxState } from "./random-state.js";
|
|
|
2
2
|
import { createSolvedPyraminxState } from "./state.js";
|
|
3
3
|
import { applyPyraminxAlgorithm, invertPyraminxAlgorithm } from "./moves.js";
|
|
4
4
|
import { solvePyraminx } from "./solver.js";
|
|
5
|
+
import { formatPyraminxFacelets, toPyraminxFacelets } from "./facelets.js";
|
|
5
6
|
export const pyraminxScrambleGenerator = {
|
|
6
7
|
info: {
|
|
7
|
-
eventId: "
|
|
8
|
+
eventId: "pyra",
|
|
8
9
|
name: "Pyraminx",
|
|
9
10
|
puzzle: "pyraminx",
|
|
10
11
|
quality: "random-state",
|
|
@@ -20,15 +21,17 @@ export async function generatePyraminxScramble(options = {}) {
|
|
|
20
21
|
// Solve the core edges and centers
|
|
21
22
|
const solution = solvePyraminx(state);
|
|
22
23
|
const scrambleMoves = invertPyraminxAlgorithm(solution);
|
|
23
|
-
// Append tips
|
|
24
|
+
// Append tips: layer moves already rotate tips, so we need the delta
|
|
25
|
+
// between the target tips and the tips produced by the core scramble.
|
|
26
|
+
const coreState = applyPyraminxAlgorithm(createSolvedPyraminxState(), scrambleMoves);
|
|
24
27
|
const finalMoves = [...scrambleMoves];
|
|
25
28
|
const tipsNotation = ["u", "l", "r", "b"];
|
|
26
29
|
for (let i = 0; i < 4; i++) {
|
|
27
|
-
const
|
|
28
|
-
if (
|
|
30
|
+
const delta = (state.tips[i] - coreState.tips[i] + 3) % 3;
|
|
31
|
+
if (delta === 1) {
|
|
29
32
|
finalMoves.push(tipsNotation[i]);
|
|
30
33
|
}
|
|
31
|
-
else if (
|
|
34
|
+
else if (delta === 2) {
|
|
32
35
|
finalMoves.push(`${tipsNotation[i]}'`);
|
|
33
36
|
}
|
|
34
37
|
}
|
|
@@ -41,14 +44,17 @@ export async function generatePyraminxScramble(options = {}) {
|
|
|
41
44
|
throw new Error("Generated Pyraminx scramble does not reproduce the target random state.");
|
|
42
45
|
}
|
|
43
46
|
}
|
|
47
|
+
const facelets = toPyraminxFacelets(state);
|
|
44
48
|
return {
|
|
45
|
-
eventId: "
|
|
49
|
+
eventId: "pyra",
|
|
46
50
|
scramble: finalMoves.join(" "),
|
|
47
51
|
moves: finalMoves,
|
|
48
52
|
quality: "random-state",
|
|
49
53
|
generator: "pyraminx-ida-star",
|
|
50
54
|
metadata: {
|
|
51
55
|
state,
|
|
56
|
+
facelets,
|
|
57
|
+
faceletString: formatPyraminxFacelets(facelets),
|
|
52
58
|
solutionMoves: solution,
|
|
53
59
|
solutionDepth: solution.length
|
|
54
60
|
}
|
|
@@ -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 @@
|
|
|
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"}
|