@cubingcenter/scrambler 0.1.0 → 0.2.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/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 +10 -4
- package/dist-cjs/puzzles/cube3/scrambler.js +1 -0
- package/dist-cjs/puzzles/cube4/scrambler.js +3 -3
- 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 +10 -4
- 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);
|
|
@@ -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,6 +2,7 @@ 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
8
|
eventId: "pyrso",
|
|
@@ -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,6 +44,7 @@ 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
49
|
eventId: "pyrso",
|
|
46
50
|
scramble: finalMoves.join(" "),
|
|
@@ -49,6 +53,8 @@ export async function generatePyraminxScramble(options = {}) {
|
|
|
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
|
}
|
|
@@ -46,6 +46,7 @@ async function generateCube3Scramble(options = {}) {
|
|
|
46
46
|
metadata: {
|
|
47
47
|
generator: "cube3-two-phase",
|
|
48
48
|
generatorVersion: "1.0.0",
|
|
49
|
+
facelets,
|
|
49
50
|
faceletString: (0, facelets_js_1.formatCube3Facelets)(facelets),
|
|
50
51
|
state: JSON.parse(JSON.stringify(state)),
|
|
51
52
|
solutionMoves: solution.moves,
|
|
@@ -44,9 +44,9 @@ exports.cube4ScrambleGenerator = {
|
|
|
44
44
|
eventId: "444",
|
|
45
45
|
name: "4x4x4 Cube",
|
|
46
46
|
puzzle: "cube-4x4",
|
|
47
|
-
quality: "random-
|
|
48
|
-
generator: "
|
|
49
|
-
description: "Random-
|
|
47
|
+
quality: "random-state",
|
|
48
|
+
generator: "cubing-scramble",
|
|
49
|
+
description: "Random-state 4x4 scramble generated by cubing.js."
|
|
50
50
|
},
|
|
51
51
|
generate(options = {}) {
|
|
52
52
|
return generateCube4Scramble(options);
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSolvedPyraminxFacelets = createSolvedPyraminxFacelets;
|
|
4
|
+
exports.toPyraminxFacelets = toPyraminxFacelets;
|
|
5
|
+
exports.formatPyraminxFacelets = formatPyraminxFacelets;
|
|
6
|
+
exports.getPyraminxFaceOrder = getPyraminxFaceOrder;
|
|
7
|
+
const FACE_ORDER = ["F", "L", "R", "D"];
|
|
8
|
+
// positionId -> sticker slots (each slot = one triangle cell of the grid).
|
|
9
|
+
// Positions follow the scrambler piece model (moves.ts):
|
|
10
|
+
// edges 0..5 = UL, UR, UB, LR, LB, RB
|
|
11
|
+
// centers 0..3 = U, L, R, B
|
|
12
|
+
// tips 0..3 = U, L, R, B
|
|
13
|
+
//
|
|
14
|
+
// The slot layout is validated against the piece model by
|
|
15
|
+
// test/pyraminx-facelets.test.ts (single moves + randomized sequences).
|
|
16
|
+
const EDGE_POSITION_SLOTS = [
|
|
17
|
+
[["F", 1], ["L", 7]], // 0 UL
|
|
18
|
+
[["F", 3], ["R", 5]], // 1 UR
|
|
19
|
+
[["L", 2], ["R", 2]], // 2 UB
|
|
20
|
+
[["F", 6], ["D", 2]], // 3 LR
|
|
21
|
+
[["L", 5], ["D", 5]], // 4 LB
|
|
22
|
+
[["R", 7], ["D", 7]], // 5 RB
|
|
23
|
+
];
|
|
24
|
+
const CENTER_POSITION_SLOTS = [
|
|
25
|
+
[["F", 2], ["L", 3], ["R", 1]], // 0 U
|
|
26
|
+
[["F", 5], ["L", 6], ["D", 1]], // 1 L
|
|
27
|
+
[["F", 7], ["R", 6], ["D", 3]], // 2 R
|
|
28
|
+
[["L", 1], ["R", 3], ["D", 6]], // 3 B
|
|
29
|
+
];
|
|
30
|
+
const TIP_POSITION_SLOTS = [
|
|
31
|
+
[["F", 0], ["L", 4], ["R", 0]], // 0 U
|
|
32
|
+
[["F", 4], ["D", 0], ["L", 8]], // 1 L
|
|
33
|
+
[["F", 8], ["R", 8], ["D", 4]], // 2 R
|
|
34
|
+
[["L", 0], ["D", 8], ["R", 4]], // 3 B
|
|
35
|
+
];
|
|
36
|
+
// pieceId -> sticker colors (canonical order aligned with the role order above)
|
|
37
|
+
const EDGE_PIECE_STICKERS = [
|
|
38
|
+
["F", "L"], // 0 UL
|
|
39
|
+
["F", "R"], // 1 UR
|
|
40
|
+
["L", "R"], // 2 UB
|
|
41
|
+
["F", "D"], // 3 LR
|
|
42
|
+
["L", "D"], // 4 LB
|
|
43
|
+
["R", "D"], // 5 RB
|
|
44
|
+
];
|
|
45
|
+
const CENTER_PIECE_STICKERS = [
|
|
46
|
+
["F", "L", "R"], // 0 U
|
|
47
|
+
["F", "L", "D"], // 1 L
|
|
48
|
+
["F", "R", "D"], // 2 R
|
|
49
|
+
["L", "R", "D"], // 3 B
|
|
50
|
+
];
|
|
51
|
+
const TIP_PIECE_STICKERS = [
|
|
52
|
+
["F", "L", "R"], // 0 U
|
|
53
|
+
["F", "D", "L"], // 1 L
|
|
54
|
+
["F", "R", "D"], // 2 R
|
|
55
|
+
["L", "D", "R"], // 3 B
|
|
56
|
+
];
|
|
57
|
+
function createSolvedPyraminxFacelets() {
|
|
58
|
+
return {
|
|
59
|
+
F: Array.from({ length: 9 }, () => "F"),
|
|
60
|
+
L: Array.from({ length: 9 }, () => "L"),
|
|
61
|
+
R: Array.from({ length: 9 }, () => "R"),
|
|
62
|
+
D: Array.from({ length: 9 }, () => "D"),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function createEmptyPyraminxFacelets() {
|
|
66
|
+
return {
|
|
67
|
+
F: Array(9).fill("?"),
|
|
68
|
+
L: Array(9).fill("?"),
|
|
69
|
+
R: Array(9).fill("?"),
|
|
70
|
+
D: Array(9).fill("?"),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function placeOrientedStickers(facelets, slots, stickers, orientation) {
|
|
74
|
+
for (let index = 0; index < slots.length; index += 1) {
|
|
75
|
+
const [face, stickerIndex] = slots[index];
|
|
76
|
+
const sticker = stickers[(index - orientation + stickers.length) % stickers.length];
|
|
77
|
+
const arr = facelets[face];
|
|
78
|
+
arr[stickerIndex] = sticker;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function toPyraminxFacelets(state) {
|
|
82
|
+
const facelets = createEmptyPyraminxFacelets();
|
|
83
|
+
for (let position = 0; position < 6; position += 1) {
|
|
84
|
+
const piece = state.edgesPerm[position];
|
|
85
|
+
const orientation = state.edgesOrient[position];
|
|
86
|
+
placeOrientedStickers(facelets, EDGE_POSITION_SLOTS[position], EDGE_PIECE_STICKERS[piece], orientation);
|
|
87
|
+
}
|
|
88
|
+
for (let position = 0; position < 4; position += 1) {
|
|
89
|
+
const orientation = state.centers[position];
|
|
90
|
+
placeOrientedStickers(facelets, CENTER_POSITION_SLOTS[position], CENTER_PIECE_STICKERS[position], orientation);
|
|
91
|
+
}
|
|
92
|
+
for (let position = 0; position < 4; position += 1) {
|
|
93
|
+
const orientation = state.tips[position];
|
|
94
|
+
placeOrientedStickers(facelets, TIP_POSITION_SLOTS[position], TIP_PIECE_STICKERS[position], orientation);
|
|
95
|
+
}
|
|
96
|
+
return facelets;
|
|
97
|
+
}
|
|
98
|
+
function formatPyraminxFacelets(facelets) {
|
|
99
|
+
return FACE_ORDER.map((face) => facelets[face].join("")).join("");
|
|
100
|
+
}
|
|
101
|
+
function getPyraminxFaceOrder() {
|
|
102
|
+
return FACE_ORDER;
|
|
103
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.solvePyraminx = exports.generatePyraminxScramble = exports.pyraminxScrambleGenerator = exports.generateRandomPyraminxState = exports.isAllowedRandomPyraminxState = exports.validatePyraminxState = exports.permutationParity = exports.isSolvedPyraminxState = exports.pyraminxStatesEqual = exports.createSolvedPyraminxState = exports.clonePyraminxState = exports.parsePyraminxMove = exports.parsePyraminxAlgorithm = exports.invertPyraminxMove = exports.invertPyraminxAlgorithm = exports.formatPyraminxAlgorithm = exports.applyPyraminxMove = exports.applyPyraminxAlgorithm = exports.PYRAMINX_TIPS = exports.PYRAMINX_CORE_MOVES = void 0;
|
|
3
|
+
exports.solvePyraminx = exports.toPyraminxFacelets = exports.getPyraminxFaceOrder = exports.formatPyraminxFacelets = exports.createSolvedPyraminxFacelets = exports.generatePyraminxScramble = exports.pyraminxScrambleGenerator = exports.generateRandomPyraminxState = exports.isAllowedRandomPyraminxState = exports.validatePyraminxState = exports.permutationParity = exports.isSolvedPyraminxState = exports.pyraminxStatesEqual = exports.createSolvedPyraminxState = exports.clonePyraminxState = exports.parsePyraminxMove = exports.parsePyraminxAlgorithm = exports.invertPyraminxMove = exports.invertPyraminxAlgorithm = exports.formatPyraminxAlgorithm = exports.applyPyraminxMove = exports.applyPyraminxAlgorithm = exports.PYRAMINX_TIPS = exports.PYRAMINX_CORE_MOVES = void 0;
|
|
4
4
|
var types_js_1 = require("./types.js");
|
|
5
5
|
Object.defineProperty(exports, "PYRAMINX_CORE_MOVES", { enumerable: true, get: function () { return types_js_1.PYRAMINX_CORE_MOVES; } });
|
|
6
6
|
Object.defineProperty(exports, "PYRAMINX_TIPS", { enumerable: true, get: function () { return types_js_1.PYRAMINX_TIPS; } });
|
|
@@ -26,5 +26,10 @@ Object.defineProperty(exports, "generateRandomPyraminxState", { enumerable: true
|
|
|
26
26
|
var scrambler_js_1 = require("./scrambler.js");
|
|
27
27
|
Object.defineProperty(exports, "pyraminxScrambleGenerator", { enumerable: true, get: function () { return scrambler_js_1.pyraminxScrambleGenerator; } });
|
|
28
28
|
Object.defineProperty(exports, "generatePyraminxScramble", { enumerable: true, get: function () { return scrambler_js_1.generatePyraminxScramble; } });
|
|
29
|
+
var facelets_js_1 = require("./facelets.js");
|
|
30
|
+
Object.defineProperty(exports, "createSolvedPyraminxFacelets", { enumerable: true, get: function () { return facelets_js_1.createSolvedPyraminxFacelets; } });
|
|
31
|
+
Object.defineProperty(exports, "formatPyraminxFacelets", { enumerable: true, get: function () { return facelets_js_1.formatPyraminxFacelets; } });
|
|
32
|
+
Object.defineProperty(exports, "getPyraminxFaceOrder", { enumerable: true, get: function () { return facelets_js_1.getPyraminxFaceOrder; } });
|
|
33
|
+
Object.defineProperty(exports, "toPyraminxFacelets", { enumerable: true, get: function () { return facelets_js_1.toPyraminxFacelets; } });
|
|
29
34
|
var solver_js_1 = require("./solver.js");
|
|
30
35
|
Object.defineProperty(exports, "solvePyraminx", { enumerable: true, get: function () { return solver_js_1.solvePyraminx; } });
|
|
@@ -39,56 +39,53 @@ function applySingleCoreMoveCW(state, face) {
|
|
|
39
39
|
const centers = state.centers;
|
|
40
40
|
const perm = state.edgesPerm;
|
|
41
41
|
const orient = state.edgesOrient;
|
|
42
|
+
const tips = state.tips;
|
|
42
43
|
// Clone arrays for temporary storage
|
|
43
44
|
const prevPerm = [...perm];
|
|
44
45
|
const prevOrient = [...orient];
|
|
45
46
|
if (face === "U") {
|
|
46
|
-
//
|
|
47
|
+
// U: UL -> UB -> UR -> UL with the physical orientation (matches viz)
|
|
47
48
|
centers[0] = (centers[0] + 1) % 3;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
perm[1] = prevPerm[
|
|
51
|
-
perm[2] = prevPerm[
|
|
52
|
-
|
|
53
|
-
orient[1] = prevOrient[
|
|
54
|
-
orient[2] = prevOrient[
|
|
55
|
-
orient[0] = prevOrient[2];
|
|
49
|
+
tips[0] = (tips[0] + 1) % 3;
|
|
50
|
+
perm[0] = prevPerm[1];
|
|
51
|
+
perm[1] = prevPerm[2];
|
|
52
|
+
perm[2] = prevPerm[0];
|
|
53
|
+
orient[0] = (prevOrient[1] + 1) % 2;
|
|
54
|
+
orient[1] = (prevOrient[2] + 1) % 2;
|
|
55
|
+
orient[2] = prevOrient[0];
|
|
56
56
|
}
|
|
57
57
|
else if (face === "L") {
|
|
58
|
-
//
|
|
59
|
-
centers[1] = (centers[1] +
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
perm[
|
|
63
|
-
perm[
|
|
64
|
-
|
|
65
|
-
orient[
|
|
66
|
-
orient[
|
|
67
|
-
orient[0] = prevOrient[3];
|
|
58
|
+
// L: UL -> LB -> LR -> UL (physical, matches viz)
|
|
59
|
+
centers[1] = (centers[1] + 2) % 3;
|
|
60
|
+
tips[1] = (tips[1] + 1) % 3;
|
|
61
|
+
perm[0] = prevPerm[4];
|
|
62
|
+
perm[3] = prevPerm[0];
|
|
63
|
+
perm[4] = prevPerm[3];
|
|
64
|
+
orient[0] = prevOrient[4];
|
|
65
|
+
orient[3] = (prevOrient[0] + 1) % 2;
|
|
66
|
+
orient[4] = (prevOrient[3] + 1) % 2;
|
|
68
67
|
}
|
|
69
68
|
else if (face === "R") {
|
|
70
|
-
//
|
|
69
|
+
// R: UR -> LR -> RB -> UR (physical, matches viz)
|
|
71
70
|
centers[2] = (centers[2] + 1) % 3;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
perm[3] = prevPerm[
|
|
75
|
-
perm[5] = prevPerm[
|
|
76
|
-
|
|
77
|
-
orient[3] = (prevOrient[
|
|
78
|
-
orient[5] =
|
|
79
|
-
orient[1] = prevOrient[5];
|
|
71
|
+
tips[2] = (tips[2] + 1) % 3;
|
|
72
|
+
perm[1] = prevPerm[3];
|
|
73
|
+
perm[3] = prevPerm[5];
|
|
74
|
+
perm[5] = prevPerm[1];
|
|
75
|
+
orient[1] = (prevOrient[3] + 1) % 2;
|
|
76
|
+
orient[3] = (prevOrient[5] + 1) % 2;
|
|
77
|
+
orient[5] = prevOrient[1];
|
|
80
78
|
}
|
|
81
79
|
else if (face === "B") {
|
|
82
|
-
//
|
|
83
|
-
centers[3] = (centers[3] +
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
perm[
|
|
87
|
-
perm[
|
|
88
|
-
|
|
89
|
-
orient[
|
|
90
|
-
orient[
|
|
91
|
-
orient[2] = prevOrient[4];
|
|
80
|
+
// B: UB -> LB -> RB -> UB (physical, matches viz)
|
|
81
|
+
centers[3] = (centers[3] + 2) % 3;
|
|
82
|
+
tips[3] = (tips[3] + 1) % 3;
|
|
83
|
+
perm[2] = prevPerm[5];
|
|
84
|
+
perm[4] = prevPerm[2];
|
|
85
|
+
perm[5] = prevPerm[4];
|
|
86
|
+
orient[2] = prevOrient[5];
|
|
87
|
+
orient[4] = (prevOrient[2] + 1) % 2;
|
|
88
|
+
orient[5] = (prevOrient[4] + 1) % 2;
|
|
92
89
|
}
|
|
93
90
|
}
|
|
94
91
|
function applyPyraminxAlgorithm(state, moves) {
|
|
@@ -6,6 +6,7 @@ const random_state_js_1 = require("./random-state.js");
|
|
|
6
6
|
const state_js_1 = require("./state.js");
|
|
7
7
|
const moves_js_1 = require("./moves.js");
|
|
8
8
|
const solver_js_1 = require("./solver.js");
|
|
9
|
+
const facelets_js_1 = require("./facelets.js");
|
|
9
10
|
exports.pyraminxScrambleGenerator = {
|
|
10
11
|
info: {
|
|
11
12
|
eventId: "pyrso",
|
|
@@ -24,15 +25,17 @@ async function generatePyraminxScramble(options = {}) {
|
|
|
24
25
|
// Solve the core edges and centers
|
|
25
26
|
const solution = (0, solver_js_1.solvePyraminx)(state);
|
|
26
27
|
const scrambleMoves = (0, moves_js_1.invertPyraminxAlgorithm)(solution);
|
|
27
|
-
// Append tips
|
|
28
|
+
// Append tips: layer moves already rotate tips, so we need the delta
|
|
29
|
+
// between the target tips and the tips produced by the core scramble.
|
|
30
|
+
const coreState = (0, moves_js_1.applyPyraminxAlgorithm)((0, state_js_1.createSolvedPyraminxState)(), scrambleMoves);
|
|
28
31
|
const finalMoves = [...scrambleMoves];
|
|
29
32
|
const tipsNotation = ["u", "l", "r", "b"];
|
|
30
33
|
for (let i = 0; i < 4; i++) {
|
|
31
|
-
const
|
|
32
|
-
if (
|
|
34
|
+
const delta = (state.tips[i] - coreState.tips[i] + 3) % 3;
|
|
35
|
+
if (delta === 1) {
|
|
33
36
|
finalMoves.push(tipsNotation[i]);
|
|
34
37
|
}
|
|
35
|
-
else if (
|
|
38
|
+
else if (delta === 2) {
|
|
36
39
|
finalMoves.push(`${tipsNotation[i]}'`);
|
|
37
40
|
}
|
|
38
41
|
}
|
|
@@ -45,6 +48,7 @@ async function generatePyraminxScramble(options = {}) {
|
|
|
45
48
|
throw new Error("Generated Pyraminx scramble does not reproduce the target random state.");
|
|
46
49
|
}
|
|
47
50
|
}
|
|
51
|
+
const facelets = (0, facelets_js_1.toPyraminxFacelets)(state);
|
|
48
52
|
return {
|
|
49
53
|
eventId: "pyrso",
|
|
50
54
|
scramble: finalMoves.join(" "),
|
|
@@ -53,6 +57,8 @@ async function generatePyraminxScramble(options = {}) {
|
|
|
53
57
|
generator: "pyraminx-ida-star",
|
|
54
58
|
metadata: {
|
|
55
59
|
state,
|
|
60
|
+
facelets,
|
|
61
|
+
faceletString: (0, facelets_js_1.formatPyraminxFacelets)(facelets),
|
|
56
62
|
solutionMoves: solution,
|
|
57
63
|
solutionDepth: solution.length
|
|
58
64
|
}
|