@cubingcenter/scrambler 0.2.0 → 0.4.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/clock/scrambler.js +2 -2
- 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/megaminx/scrambler.js +2 -2
- package/dist/puzzles/pyraminx/scrambler.js +2 -2
- 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/clock/scrambler.js +2 -2
- package/dist-cjs/puzzles/index.js +4 -1
- package/dist-cjs/puzzles/megaminx/scrambler.js +2 -2
- package/dist-cjs/puzzles/pyraminx/scrambler.js +2 -2
- 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
|
@@ -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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { SKEWB_MOVES, CENTER_COUNT, CORNER_COUNT } from "./types.js";
|
|
2
|
+
import { applySkewbMove } from "./moves.js";
|
|
3
|
+
import { createSolvedSkewbState, isSolvedSkewbState, skewbStatesEqual } from "./state.js";
|
|
4
|
+
export function validateSkewbState(state) {
|
|
5
|
+
const errors = [];
|
|
6
|
+
// corners permutation
|
|
7
|
+
errors.push(...validatePermutation("corner", state.corners.permutation, CORNER_COUNT));
|
|
8
|
+
// corners orientation
|
|
9
|
+
errors.push(...validateOrientations("corner", state.corners.orientation, CORNER_COUNT, 3));
|
|
10
|
+
// centers
|
|
11
|
+
errors.push(...validatePermutation("center", state.centers, CENTER_COUNT));
|
|
12
|
+
// parity checks - both perms must be even (as observed in BFS)
|
|
13
|
+
if (errors.length === 0) {
|
|
14
|
+
if (permutationParity(state.corners.permutation) !== 0) {
|
|
15
|
+
errors.push("Corner permutation parity must be even.");
|
|
16
|
+
}
|
|
17
|
+
if (permutationParity(state.centers) !== 0) {
|
|
18
|
+
errors.push("Center permutation parity must be even.");
|
|
19
|
+
}
|
|
20
|
+
// Check fixed corner invariant: corner 0 must be at position 0 with orientation 0 for WCA fixed model
|
|
21
|
+
// But we allow all states for validation; this is enforced by solver's reachable set, not validation.
|
|
22
|
+
// So we don't enforce fixed corner here; any even parity state is considered valid.
|
|
23
|
+
}
|
|
24
|
+
return { valid: errors.length === 0, errors };
|
|
25
|
+
}
|
|
26
|
+
export function isOneMoveFromSolvedSkewbState(state) {
|
|
27
|
+
const solved = createSolvedSkewbState();
|
|
28
|
+
return SKEWB_MOVES.some((move) => skewbStatesEqual(applySkewbMove(solved, move), state));
|
|
29
|
+
}
|
|
30
|
+
export function isAllowedRandomSkewbState(state) {
|
|
31
|
+
return validateSkewbState(state).valid && !isSolvedSkewbState(state) && !isOneMoveFromSolvedSkewbState(state);
|
|
32
|
+
}
|
|
33
|
+
export function permutationParity(permutation) {
|
|
34
|
+
let inversions = 0;
|
|
35
|
+
for (let left = 0; left < permutation.length; left++) {
|
|
36
|
+
for (let right = left + 1; right < permutation.length; right++) {
|
|
37
|
+
if (permutation[left] > permutation[right])
|
|
38
|
+
inversions++;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return (inversions % 2);
|
|
42
|
+
}
|
|
43
|
+
function validatePermutation(label, permutation, length) {
|
|
44
|
+
const errors = [];
|
|
45
|
+
const seen = new Set();
|
|
46
|
+
if (permutation.length !== length)
|
|
47
|
+
errors.push(`${label} permutation must contain ${length} values.`);
|
|
48
|
+
for (const value of permutation) {
|
|
49
|
+
if (!Number.isInteger(value) || value < 0 || value >= length) {
|
|
50
|
+
errors.push(`${label} permutation contains out-of-range value: ${value}.`);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (seen.has(value))
|
|
54
|
+
errors.push(`${label} permutation contains duplicate value: ${value}.`);
|
|
55
|
+
seen.add(value);
|
|
56
|
+
}
|
|
57
|
+
return errors;
|
|
58
|
+
}
|
|
59
|
+
function validateOrientations(label, orientations, length, modulo) {
|
|
60
|
+
const errors = [];
|
|
61
|
+
if (orientations.length !== length)
|
|
62
|
+
errors.push(`${label} orientation must contain ${length} values.`);
|
|
63
|
+
for (const value of orientations) {
|
|
64
|
+
if (!Number.isInteger(value) || value < 0 || value >= modulo) {
|
|
65
|
+
errors.push(`${label} orientation contains out-of-range value: ${value}.`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return errors;
|
|
69
|
+
}
|
|
@@ -4,7 +4,7 @@ exports.clockScrambleGenerator = void 0;
|
|
|
4
4
|
exports.generateClockScramble = generateClockScramble;
|
|
5
5
|
exports.clockScrambleGenerator = {
|
|
6
6
|
info: {
|
|
7
|
-
eventId: "
|
|
7
|
+
eventId: "clck",
|
|
8
8
|
name: "Clock",
|
|
9
9
|
puzzle: "clock",
|
|
10
10
|
quality: "random-state",
|
|
@@ -42,7 +42,7 @@ async function generateClockScramble(options = {}) {
|
|
|
42
42
|
const finalPinsStr = finalPins.join(" ");
|
|
43
43
|
const scramble = [...moves, finalPinsStr].filter(Boolean).join(" ");
|
|
44
44
|
return {
|
|
45
|
-
eventId: "
|
|
45
|
+
eventId: "clck",
|
|
46
46
|
scramble,
|
|
47
47
|
moves, // For moves we return the steps without final pins as parsed moves, or just string array
|
|
48
48
|
quality: "random-state",
|
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.clock = exports.pyraminx = exports.cube7x7 = exports.cube6x6 = exports.cube5x5 = exports.megaminx = exports.cube4 = exports.cube3 = exports.cube2 = void 0;
|
|
36
|
+
exports.skewb = exports.clock = exports.pyraminx = exports.cube7x7 = exports.cube6x6 = exports.cube5x5 = exports.megaminx = exports.cube4 = exports.cube3 = exports.cube2 = void 0;
|
|
37
37
|
const index_js_1 = require("../core/index.js");
|
|
38
38
|
const index_js_2 = require("./cube2/index.js");
|
|
39
39
|
const index_js_3 = require("./cube3/index.js");
|
|
@@ -44,6 +44,7 @@ const index_js_7 = require("./cube6x6/index.js");
|
|
|
44
44
|
const index_js_8 = require("./cube7x7/index.js");
|
|
45
45
|
const index_js_9 = require("./pyraminx/index.js");
|
|
46
46
|
const index_js_10 = require("./clock/index.js");
|
|
47
|
+
const index_js_11 = require("./skewb/index.js");
|
|
47
48
|
(0, index_js_1.registerScrambleGenerator)(index_js_2.cube2ScrambleGenerator);
|
|
48
49
|
(0, index_js_1.registerScrambleGenerator)(index_js_3.cube3ScrambleGenerator);
|
|
49
50
|
(0, index_js_1.registerScrambleGenerator)(index_js_4.cube4ScrambleGenerator);
|
|
@@ -53,6 +54,7 @@ const index_js_10 = require("./clock/index.js");
|
|
|
53
54
|
(0, index_js_1.registerScrambleGenerator)(index_js_8.cube7ScrambleGenerator);
|
|
54
55
|
(0, index_js_1.registerScrambleGenerator)(index_js_9.pyraminxScrambleGenerator);
|
|
55
56
|
(0, index_js_1.registerScrambleGenerator)(index_js_10.clockScrambleGenerator);
|
|
57
|
+
(0, index_js_1.registerScrambleGenerator)(index_js_11.skewbScrambleGenerator);
|
|
56
58
|
exports.cube2 = __importStar(require("./cube2/index.js"));
|
|
57
59
|
exports.cube3 = __importStar(require("./cube3/index.js"));
|
|
58
60
|
exports.cube4 = __importStar(require("./cube4/index.js"));
|
|
@@ -62,3 +64,4 @@ exports.cube6x6 = __importStar(require("./cube6x6/index.js"));
|
|
|
62
64
|
exports.cube7x7 = __importStar(require("./cube7x7/index.js"));
|
|
63
65
|
exports.pyraminx = __importStar(require("./pyraminx/index.js"));
|
|
64
66
|
exports.clock = __importStar(require("./clock/index.js"));
|
|
67
|
+
exports.skewb = __importStar(require("./skewb/index.js"));
|
|
@@ -6,7 +6,7 @@ const index_js_1 = require("../../random/index.js");
|
|
|
6
6
|
const types_js_1 = require("./types.js");
|
|
7
7
|
exports.megaminxScrambleGenerator = {
|
|
8
8
|
info: {
|
|
9
|
-
eventId: "
|
|
9
|
+
eventId: "mgmx",
|
|
10
10
|
name: "Megaminx",
|
|
11
11
|
puzzle: "megaminx",
|
|
12
12
|
quality: "random-move",
|
|
@@ -33,7 +33,7 @@ function generateMegaminxScramble(options = {}) {
|
|
|
33
33
|
}
|
|
34
34
|
const scramble = formatMegaminxScramble(moves);
|
|
35
35
|
const result = {
|
|
36
|
-
eventId: "
|
|
36
|
+
eventId: "mgmx",
|
|
37
37
|
scramble,
|
|
38
38
|
moves,
|
|
39
39
|
quality: "random-move",
|
|
@@ -9,7 +9,7 @@ const solver_js_1 = require("./solver.js");
|
|
|
9
9
|
const facelets_js_1 = require("./facelets.js");
|
|
10
10
|
exports.pyraminxScrambleGenerator = {
|
|
11
11
|
info: {
|
|
12
|
-
eventId: "
|
|
12
|
+
eventId: "pymx",
|
|
13
13
|
name: "Pyraminx",
|
|
14
14
|
puzzle: "pyraminx",
|
|
15
15
|
quality: "random-state",
|
|
@@ -50,7 +50,7 @@ async function generatePyraminxScramble(options = {}) {
|
|
|
50
50
|
}
|
|
51
51
|
const facelets = (0, facelets_js_1.toPyraminxFacelets)(state);
|
|
52
52
|
return {
|
|
53
|
-
eventId: "
|
|
53
|
+
eventId: "pymx",
|
|
54
54
|
scramble: finalMoves.join(" "),
|
|
55
55
|
moves: finalMoves,
|
|
56
56
|
quality: "random-state",
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSolvedSkewbFacelets = createSolvedSkewbFacelets;
|
|
4
|
+
exports.toSkewbFacelets = toSkewbFacelets;
|
|
5
|
+
exports.formatSkewbFacelets = formatSkewbFacelets;
|
|
6
|
+
exports.getSkewbFaceOrder = getSkewbFaceOrder;
|
|
7
|
+
// Facelets model matching cubingcenter-viz/src/puzzles/skewb.ts
|
|
8
|
+
// Each face has 5 stickers: 0=center, 1=TL,2=TR,3=BL,4=BR (net layout)
|
|
9
|
+
const FACE_ORDER = ["U", "R", "F", "D", "L", "B"];
|
|
10
|
+
// Canonical sticker faces per corner position: [UD sticker, RL sticker, FB sticker]
|
|
11
|
+
const CORNER_STICKERS = [
|
|
12
|
+
["U", "R", "F"],
|
|
13
|
+
["U", "L", "F"],
|
|
14
|
+
["U", "R", "B"],
|
|
15
|
+
["U", "L", "B"],
|
|
16
|
+
["D", "R", "F"],
|
|
17
|
+
["D", "L", "F"],
|
|
18
|
+
["D", "R", "B"],
|
|
19
|
+
["D", "L", "B"],
|
|
20
|
+
];
|
|
21
|
+
// Facelet index of each corner position on each of its 3 faces, aligned with CORNER_STICKERS order.
|
|
22
|
+
const CORNER_SLOTS = [
|
|
23
|
+
[["U", 4], ["R", 1], ["F", 2]],
|
|
24
|
+
[["U", 3], ["L", 2], ["F", 1]],
|
|
25
|
+
[["U", 2], ["R", 2], ["B", 1]],
|
|
26
|
+
[["U", 1], ["L", 1], ["B", 2]],
|
|
27
|
+
[["D", 2], ["R", 3], ["F", 4]],
|
|
28
|
+
[["D", 1], ["L", 4], ["F", 3]],
|
|
29
|
+
[["D", 4], ["R", 4], ["B", 3]],
|
|
30
|
+
[["D", 3], ["L", 3], ["B", 4]],
|
|
31
|
+
];
|
|
32
|
+
function createSolvedSkewbFacelets() {
|
|
33
|
+
return {
|
|
34
|
+
U: Array.from({ length: 5 }, () => "U"),
|
|
35
|
+
R: Array.from({ length: 5 }, () => "R"),
|
|
36
|
+
F: Array.from({ length: 5 }, () => "F"),
|
|
37
|
+
D: Array.from({ length: 5 }, () => "D"),
|
|
38
|
+
L: Array.from({ length: 5 }, () => "L"),
|
|
39
|
+
B: Array.from({ length: 5 }, () => "B"),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function createEmptySkewbFacelets() {
|
|
43
|
+
return {
|
|
44
|
+
U: Array(5).fill("?"),
|
|
45
|
+
R: Array(5).fill("?"),
|
|
46
|
+
F: Array(5).fill("?"),
|
|
47
|
+
D: Array(5).fill("?"),
|
|
48
|
+
L: Array(5).fill("?"),
|
|
49
|
+
B: Array(5).fill("?"),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function toSkewbFacelets(state) {
|
|
53
|
+
const facelets = createEmptySkewbFacelets();
|
|
54
|
+
// centers
|
|
55
|
+
for (let face = 0; face < 6; face++) {
|
|
56
|
+
const targetFace = FACE_ORDER[face];
|
|
57
|
+
facelets[targetFace][0] = FACE_ORDER[state.centers[face]];
|
|
58
|
+
}
|
|
59
|
+
// corners
|
|
60
|
+
for (let pos = 0; pos < 8; pos++) {
|
|
61
|
+
const piece = state.corners.permutation[pos];
|
|
62
|
+
const ori = state.corners.orientation[pos];
|
|
63
|
+
const slots = CORNER_SLOTS[pos];
|
|
64
|
+
const stickers = CORNER_STICKERS[piece];
|
|
65
|
+
for (let i = 0; i < 3; i++) {
|
|
66
|
+
const [face, idx] = slots[i];
|
|
67
|
+
facelets[face][idx] = stickers[(i + 3 - ori) % 3];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return facelets;
|
|
71
|
+
}
|
|
72
|
+
function formatSkewbFacelets(facelets) {
|
|
73
|
+
return FACE_ORDER.map((face) => facelets[face].join("")).join("");
|
|
74
|
+
}
|
|
75
|
+
function getSkewbFaceOrder() {
|
|
76
|
+
return FACE_ORDER;
|
|
77
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.skewbScrambleGenerator = exports.generateSkewbScramble = exports.getStateFromIndex = exports.getStateIndex = exports.solveSkewb = exports.toSkewbFacelets = exports.getSkewbFaceOrder = exports.formatSkewbFacelets = exports.createSolvedSkewbFacelets = exports.generateRandomSkewbState = exports.isOneMoveFromSolvedSkewbState = exports.isAllowedRandomSkewbState = exports.validateSkewbState = exports.permutationParity = exports.skewbStatesEqual = exports.isSolvedSkewbState = exports.createSolvedSkewbState = exports.cloneSkewbState = exports.parseSkewbMove = exports.parseSkewbAlgorithm = exports.invertSkewbMove = exports.invertSkewbAlgorithm = exports.formatSkewbAlgorithm = exports.applySkewbMove = exports.applySkewbAlgorithm = exports.SKEWB_FACES = exports.SKEWB_MOVES = void 0;
|
|
4
|
+
var types_js_1 = require("./types.js");
|
|
5
|
+
Object.defineProperty(exports, "SKEWB_MOVES", { enumerable: true, get: function () { return types_js_1.SKEWB_MOVES; } });
|
|
6
|
+
Object.defineProperty(exports, "SKEWB_FACES", { enumerable: true, get: function () { return types_js_1.SKEWB_FACES; } });
|
|
7
|
+
var moves_js_1 = require("./moves.js");
|
|
8
|
+
Object.defineProperty(exports, "applySkewbAlgorithm", { enumerable: true, get: function () { return moves_js_1.applySkewbAlgorithm; } });
|
|
9
|
+
Object.defineProperty(exports, "applySkewbMove", { enumerable: true, get: function () { return moves_js_1.applySkewbMove; } });
|
|
10
|
+
Object.defineProperty(exports, "formatSkewbAlgorithm", { enumerable: true, get: function () { return moves_js_1.formatSkewbAlgorithm; } });
|
|
11
|
+
Object.defineProperty(exports, "invertSkewbAlgorithm", { enumerable: true, get: function () { return moves_js_1.invertSkewbAlgorithm; } });
|
|
12
|
+
Object.defineProperty(exports, "invertSkewbMove", { enumerable: true, get: function () { return moves_js_1.invertSkewbMove; } });
|
|
13
|
+
Object.defineProperty(exports, "parseSkewbAlgorithm", { enumerable: true, get: function () { return moves_js_1.parseSkewbAlgorithm; } });
|
|
14
|
+
Object.defineProperty(exports, "parseSkewbMove", { enumerable: true, get: function () { return moves_js_1.parseSkewbMove; } });
|
|
15
|
+
var state_js_1 = require("./state.js");
|
|
16
|
+
Object.defineProperty(exports, "cloneSkewbState", { enumerable: true, get: function () { return state_js_1.cloneSkewbState; } });
|
|
17
|
+
Object.defineProperty(exports, "createSolvedSkewbState", { enumerable: true, get: function () { return state_js_1.createSolvedSkewbState; } });
|
|
18
|
+
Object.defineProperty(exports, "isSolvedSkewbState", { enumerable: true, get: function () { return state_js_1.isSolvedSkewbState; } });
|
|
19
|
+
Object.defineProperty(exports, "skewbStatesEqual", { enumerable: true, get: function () { return state_js_1.skewbStatesEqual; } });
|
|
20
|
+
var validation_js_1 = require("./validation.js");
|
|
21
|
+
Object.defineProperty(exports, "permutationParity", { enumerable: true, get: function () { return validation_js_1.permutationParity; } });
|
|
22
|
+
Object.defineProperty(exports, "validateSkewbState", { enumerable: true, get: function () { return validation_js_1.validateSkewbState; } });
|
|
23
|
+
Object.defineProperty(exports, "isAllowedRandomSkewbState", { enumerable: true, get: function () { return validation_js_1.isAllowedRandomSkewbState; } });
|
|
24
|
+
Object.defineProperty(exports, "isOneMoveFromSolvedSkewbState", { enumerable: true, get: function () { return validation_js_1.isOneMoveFromSolvedSkewbState; } });
|
|
25
|
+
var random_state_js_1 = require("./random-state.js");
|
|
26
|
+
Object.defineProperty(exports, "generateRandomSkewbState", { enumerable: true, get: function () { return random_state_js_1.generateRandomSkewbState; } });
|
|
27
|
+
var facelets_js_1 = require("./facelets.js");
|
|
28
|
+
Object.defineProperty(exports, "createSolvedSkewbFacelets", { enumerable: true, get: function () { return facelets_js_1.createSolvedSkewbFacelets; } });
|
|
29
|
+
Object.defineProperty(exports, "formatSkewbFacelets", { enumerable: true, get: function () { return facelets_js_1.formatSkewbFacelets; } });
|
|
30
|
+
Object.defineProperty(exports, "getSkewbFaceOrder", { enumerable: true, get: function () { return facelets_js_1.getSkewbFaceOrder; } });
|
|
31
|
+
Object.defineProperty(exports, "toSkewbFacelets", { enumerable: true, get: function () { return facelets_js_1.toSkewbFacelets; } });
|
|
32
|
+
var solver_js_1 = require("./solver.js");
|
|
33
|
+
Object.defineProperty(exports, "solveSkewb", { enumerable: true, get: function () { return solver_js_1.solveSkewb; } });
|
|
34
|
+
Object.defineProperty(exports, "getStateIndex", { enumerable: true, get: function () { return solver_js_1.getStateIndex; } });
|
|
35
|
+
Object.defineProperty(exports, "getStateFromIndex", { enumerable: true, get: function () { return solver_js_1.getStateFromIndex; } });
|
|
36
|
+
var scrambler_js_1 = require("./scrambler.js");
|
|
37
|
+
Object.defineProperty(exports, "generateSkewbScramble", { enumerable: true, get: function () { return scrambler_js_1.generateSkewbScramble; } });
|
|
38
|
+
Object.defineProperty(exports, "skewbScrambleGenerator", { enumerable: true, get: function () { return scrambler_js_1.skewbScrambleGenerator; } });
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MOVE_TABLES = void 0;
|
|
4
|
+
exports.applySkewbMove = applySkewbMove;
|
|
5
|
+
exports.applySkewbAlgorithm = applySkewbAlgorithm;
|
|
6
|
+
exports.invertSkewbMove = invertSkewbMove;
|
|
7
|
+
exports.invertSkewbAlgorithm = invertSkewbAlgorithm;
|
|
8
|
+
exports.parseSkewbMove = parseSkewbMove;
|
|
9
|
+
exports.parseSkewbAlgorithm = parseSkewbAlgorithm;
|
|
10
|
+
exports.formatSkewbAlgorithm = formatSkewbAlgorithm;
|
|
11
|
+
exports.getAllSkewbMovesForTables = getAllSkewbMovesForTables;
|
|
12
|
+
const types_js_1 = require("./types.js");
|
|
13
|
+
const state_js_1 = require("./state.js");
|
|
14
|
+
const MOVE_TABLES = {
|
|
15
|
+
F: {
|
|
16
|
+
corners: { permutation: [0, 4, 1, 3, 2, 5, 6, 7], orientation: [1, 1, 1, 0, 1, 0, 0, 0] },
|
|
17
|
+
centers: [2, 0, 1, 3, 4, 5],
|
|
18
|
+
},
|
|
19
|
+
D: {
|
|
20
|
+
corners: { permutation: [5, 1, 2, 3, 4, 6, 0, 7], orientation: [2, 0, 0, 0, 2, 2, 2, 0] },
|
|
21
|
+
centers: [0, 2, 3, 1, 4, 5],
|
|
22
|
+
},
|
|
23
|
+
U: {
|
|
24
|
+
corners: { permutation: [0, 2, 7, 3, 4, 5, 6, 1], orientation: [0, 1, 1, 1, 0, 0, 0, 1] },
|
|
25
|
+
centers: [5, 1, 2, 3, 0, 4],
|
|
26
|
+
},
|
|
27
|
+
R: {
|
|
28
|
+
corners: { permutation: [0, 1, 4, 3, 7, 5, 6, 2], orientation: [0, 0, 1, 0, 1, 0, 1, 1] },
|
|
29
|
+
centers: [0, 3, 2, 5, 4, 1],
|
|
30
|
+
},
|
|
31
|
+
L: {
|
|
32
|
+
corners: { permutation: [0, 7, 2, 3, 1, 5, 6, 4], orientation: [0, 1, 0, 0, 1, 1, 0, 1] },
|
|
33
|
+
centers: [0, 1, 4, 2, 3, 5],
|
|
34
|
+
},
|
|
35
|
+
B: {
|
|
36
|
+
corners: { permutation: [0, 1, 2, 6, 4, 3, 5, 7], orientation: [0, 0, 0, 2, 0, 2, 2, 2] },
|
|
37
|
+
centers: [0, 1, 2, 4, 5, 3],
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
exports.MOVE_TABLES = MOVE_TABLES;
|
|
41
|
+
function applyFaceMove(state, table) {
|
|
42
|
+
const perm = state.corners.permutation;
|
|
43
|
+
const orient = state.corners.orientation;
|
|
44
|
+
const centers = state.centers;
|
|
45
|
+
const newPerm = table.corners.permutation.map((src) => perm[src]);
|
|
46
|
+
const newOrient = table.corners.permutation.map((src, tgt) => (orient[src] + table.corners.orientation[tgt]) % 3);
|
|
47
|
+
const newCenters = table.centers.map((src) => centers[src]);
|
|
48
|
+
for (let i = 0; i < perm.length; i++)
|
|
49
|
+
perm[i] = newPerm[i];
|
|
50
|
+
for (let i = 0; i < orient.length; i++)
|
|
51
|
+
orient[i] = newOrient[i];
|
|
52
|
+
for (let i = 0; i < centers.length; i++)
|
|
53
|
+
centers[i] = newCenters[i];
|
|
54
|
+
}
|
|
55
|
+
function applySkewbMove(state, move) {
|
|
56
|
+
const face = move[0].toUpperCase();
|
|
57
|
+
const suffix = move.slice(1);
|
|
58
|
+
const table = MOVE_TABLES[face];
|
|
59
|
+
if (!table)
|
|
60
|
+
throw new Error(`Unknown Skewb move: ${move}`);
|
|
61
|
+
const repetitions = suffix === "'" || suffix === "2" ? 2 : 1;
|
|
62
|
+
const next = (0, state_js_1.cloneSkewbState)(state);
|
|
63
|
+
for (let i = 0; i < repetitions; i++)
|
|
64
|
+
applyFaceMove(next, table);
|
|
65
|
+
return next;
|
|
66
|
+
}
|
|
67
|
+
function applySkewbAlgorithm(state, moves) {
|
|
68
|
+
return moves.reduce((current, move) => applySkewbMove(current, move), state);
|
|
69
|
+
}
|
|
70
|
+
function invertSkewbMove(move) {
|
|
71
|
+
if (move.endsWith("'"))
|
|
72
|
+
return move.slice(0, -1);
|
|
73
|
+
return `${move}'`;
|
|
74
|
+
}
|
|
75
|
+
function invertSkewbAlgorithm(moves) {
|
|
76
|
+
return [...moves].reverse().map(invertSkewbMove);
|
|
77
|
+
}
|
|
78
|
+
function parseSkewbMove(value) {
|
|
79
|
+
if (types_js_1.SKEWB_MOVES.includes(value))
|
|
80
|
+
return value;
|
|
81
|
+
throw new Error(`Invalid Skewb move: ${value}`);
|
|
82
|
+
}
|
|
83
|
+
function parseSkewbAlgorithm(algorithm) {
|
|
84
|
+
const trimmed = algorithm.trim();
|
|
85
|
+
if (trimmed.length === 0)
|
|
86
|
+
return [];
|
|
87
|
+
return trimmed.split(/\s+/).map(parseSkewbMove);
|
|
88
|
+
}
|
|
89
|
+
function formatSkewbAlgorithm(moves) {
|
|
90
|
+
return moves.join(" ");
|
|
91
|
+
}
|
|
92
|
+
// For internal use by solver (needs all 6 faces for table generation)
|
|
93
|
+
function getAllSkewbMovesForTables() {
|
|
94
|
+
return ["R", "U", "L", "B", "F", "D"].flatMap((f) => [f, `${f}'`]);
|
|
95
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateRandomSkewbState = generateRandomSkewbState;
|
|
4
|
+
const index_js_1 = require("../../random/index.js");
|
|
5
|
+
const validation_js_1 = require("./validation.js");
|
|
6
|
+
const moves_js_1 = require("./moves.js");
|
|
7
|
+
const state_js_1 = require("./state.js");
|
|
8
|
+
const types_js_1 = require("./types.js");
|
|
9
|
+
function generateRandomSkewbState(options = {}) {
|
|
10
|
+
const random = (0, index_js_1.createRandomSource)(options.seed);
|
|
11
|
+
const rejectTrivial = options.rejectTrivial ?? true;
|
|
12
|
+
for (let attempt = 0; attempt < 100; attempt++) {
|
|
13
|
+
const state = createLegalRandomState(random);
|
|
14
|
+
if (!rejectTrivial || (0, validation_js_1.isAllowedRandomSkewbState)(state))
|
|
15
|
+
return state;
|
|
16
|
+
}
|
|
17
|
+
throw new Error("Unable to generate a non-trivial random Skewb state.");
|
|
18
|
+
}
|
|
19
|
+
function createLegalRandomState(random) {
|
|
20
|
+
// Generate a random reachable Skewb state via random walk from solved.
|
|
21
|
+
// Skewb has 3,149,280 reachable states with WCA moves (fixed corner).
|
|
22
|
+
// A walk of 30+ moves is sufficient to approach uniform distribution.
|
|
23
|
+
let state = (0, state_js_1.createSolvedSkewbState)();
|
|
24
|
+
const walkLength = 30 + random.nextInt(5); // 30-34 moves
|
|
25
|
+
for (let i = 0; i < walkLength; i++) {
|
|
26
|
+
const move = random.choice([...types_js_1.SKEWB_MOVES]);
|
|
27
|
+
state = (0, moves_js_1.applySkewbMove)(state, move);
|
|
28
|
+
}
|
|
29
|
+
return state;
|
|
30
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.skewbScrambleGenerator = void 0;
|
|
4
|
+
exports.generateSkewbScramble = generateSkewbScramble;
|
|
5
|
+
const random_state_js_1 = require("./random-state.js");
|
|
6
|
+
const state_js_1 = require("./state.js");
|
|
7
|
+
const moves_js_1 = require("./moves.js");
|
|
8
|
+
const solver_js_1 = require("./solver.js");
|
|
9
|
+
const facelets_js_1 = require("./facelets.js");
|
|
10
|
+
exports.skewbScrambleGenerator = {
|
|
11
|
+
info: {
|
|
12
|
+
eventId: "skwb",
|
|
13
|
+
name: "Skewb",
|
|
14
|
+
puzzle: "skewb",
|
|
15
|
+
quality: "random-state",
|
|
16
|
+
generator: "skewb-bfs",
|
|
17
|
+
description: "Random-state Skewb scramble generated by an optimal bidirectional BFS solver.",
|
|
18
|
+
},
|
|
19
|
+
generate(options = {}) {
|
|
20
|
+
return generateSkewbScramble(options);
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
async function generateSkewbScramble(options = {}) {
|
|
24
|
+
const state = (0, random_state_js_1.generateRandomSkewbState)(options);
|
|
25
|
+
const solution = (0, solver_js_1.solveSkewb)(state);
|
|
26
|
+
const scrambleMoves = (0, moves_js_1.invertSkewbAlgorithm)(solution);
|
|
27
|
+
if (!options.skipValidation) {
|
|
28
|
+
const scrambledState = (0, moves_js_1.applySkewbAlgorithm)((0, state_js_1.createSolvedSkewbState)(), scrambleMoves);
|
|
29
|
+
if (!(0, state_js_1.skewbStatesEqual)(scrambledState, state)) {
|
|
30
|
+
throw new Error("Generated Skewb scramble does not reproduce the target random state.");
|
|
31
|
+
}
|
|
32
|
+
if (!(0, state_js_1.isSolvedSkewbState)((0, moves_js_1.applySkewbAlgorithm)(state, solution))) {
|
|
33
|
+
throw new Error("Generated Skewb solution does not solve the target random state.");
|
|
34
|
+
}
|
|
35
|
+
if (scrambleMoves.length < 2)
|
|
36
|
+
throw new Error("Generated Skewb scramble is too short.");
|
|
37
|
+
}
|
|
38
|
+
const facelets = (0, facelets_js_1.toSkewbFacelets)(state);
|
|
39
|
+
const result = {
|
|
40
|
+
eventId: "skwb",
|
|
41
|
+
scramble: (0, moves_js_1.formatSkewbAlgorithm)(scrambleMoves),
|
|
42
|
+
moves: scrambleMoves,
|
|
43
|
+
quality: "random-state",
|
|
44
|
+
generator: "skewb-bfs",
|
|
45
|
+
metadata: {
|
|
46
|
+
state,
|
|
47
|
+
facelets,
|
|
48
|
+
faceletString: (0, facelets_js_1.formatSkewbFacelets)(facelets),
|
|
49
|
+
solutionMoves: solution,
|
|
50
|
+
solutionDepth: solution.length,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
if (options.seed !== undefined)
|
|
54
|
+
result.seed = options.seed;
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getPermutationIndex = getPermutationIndex;
|
|
4
|
+
exports.getPermutationFromIndex = getPermutationFromIndex;
|
|
5
|
+
exports.getOrientationIndex = getOrientationIndex;
|
|
6
|
+
exports.getOrientationFromIndex = getOrientationFromIndex;
|
|
7
|
+
exports.getCenterIndex = getCenterIndex;
|
|
8
|
+
exports.getCenterFromIndex = getCenterFromIndex;
|
|
9
|
+
exports.getStateIndex = getStateIndex;
|
|
10
|
+
exports.getStateFromIndex = getStateFromIndex;
|
|
11
|
+
exports.solveSkewb = solveSkewb;
|
|
12
|
+
const moves_js_1 = require("./moves.js");
|
|
13
|
+
const state_js_1 = require("./state.js");
|
|
14
|
+
const FACT = [1, 1, 2, 6, 24, 120, 720, 5040, 40320];
|
|
15
|
+
function getPermutationIndex(perm) {
|
|
16
|
+
// perm length 8, but pos0 is always 0 for WCA reachable states.
|
|
17
|
+
// We encode full 8-perm via Lehmer code (0..40319), but for fixed corner it will be in subset.
|
|
18
|
+
let index = 0;
|
|
19
|
+
const n = perm.length;
|
|
20
|
+
for (let i = 0; i < n; i++) {
|
|
21
|
+
let count = 0;
|
|
22
|
+
for (let j = i + 1; j < n; j++)
|
|
23
|
+
if (perm[j] < perm[i])
|
|
24
|
+
count++;
|
|
25
|
+
index += count * FACT[n - 1 - i];
|
|
26
|
+
}
|
|
27
|
+
return index;
|
|
28
|
+
}
|
|
29
|
+
function getPermutationFromIndex(index, n) {
|
|
30
|
+
const perm = Array(n).fill(0);
|
|
31
|
+
const temp = Array.from({ length: n }, (_, i) => i);
|
|
32
|
+
let val = index;
|
|
33
|
+
for (let i = 0; i < n; i++) {
|
|
34
|
+
const f = FACT[n - 1 - i];
|
|
35
|
+
const choice = Math.floor(val / f);
|
|
36
|
+
val %= f;
|
|
37
|
+
perm[i] = temp[choice];
|
|
38
|
+
temp.splice(choice, 1);
|
|
39
|
+
}
|
|
40
|
+
return perm;
|
|
41
|
+
}
|
|
42
|
+
function getOrientationIndex(orient) {
|
|
43
|
+
// orient length 8, index 0 is fixed 0, encode remaining 7 base3
|
|
44
|
+
let index = 0;
|
|
45
|
+
for (let i = 7; i >= 1; i--) {
|
|
46
|
+
index = index * 3 + orient[i];
|
|
47
|
+
}
|
|
48
|
+
return index;
|
|
49
|
+
}
|
|
50
|
+
function getOrientationFromIndex(index) {
|
|
51
|
+
const orient = Array(8).fill(0);
|
|
52
|
+
orient[0] = 0;
|
|
53
|
+
let val = index;
|
|
54
|
+
for (let i = 1; i < 8; i++) {
|
|
55
|
+
orient[i] = val % 3;
|
|
56
|
+
val = Math.floor(val / 3);
|
|
57
|
+
}
|
|
58
|
+
return orient;
|
|
59
|
+
}
|
|
60
|
+
function getCenterIndex(centers) {
|
|
61
|
+
let index = 0;
|
|
62
|
+
const n = centers.length;
|
|
63
|
+
for (let i = 0; i < n; i++) {
|
|
64
|
+
let count = 0;
|
|
65
|
+
for (let j = i + 1; j < n; j++)
|
|
66
|
+
if (centers[j] < centers[i])
|
|
67
|
+
count++;
|
|
68
|
+
index += count * FACT[n - 1 - i];
|
|
69
|
+
}
|
|
70
|
+
return index;
|
|
71
|
+
}
|
|
72
|
+
function getCenterFromIndex(index) {
|
|
73
|
+
return getPermutationFromIndex(index, 6);
|
|
74
|
+
}
|
|
75
|
+
function getStateIndex(state) {
|
|
76
|
+
const p = getPermutationIndex(state.corners.permutation);
|
|
77
|
+
const c = getCenterIndex(state.centers);
|
|
78
|
+
const o = getOrientationIndex(state.corners.orientation);
|
|
79
|
+
// p in 0..40319, c in 0..719, o in 0..2186
|
|
80
|
+
// Combine: ((p * 720 + c) * 2187 + o) -> up to 63B fits in JS number (53-bit)
|
|
81
|
+
return ((p * 720 + c) * 2187 + o);
|
|
82
|
+
}
|
|
83
|
+
function getStateFromIndex(index) {
|
|
84
|
+
const o = index % 2187;
|
|
85
|
+
let val = Math.floor(index / 2187);
|
|
86
|
+
const c = val % 720;
|
|
87
|
+
const p = Math.floor(val / 720);
|
|
88
|
+
return {
|
|
89
|
+
corners: {
|
|
90
|
+
permutation: getPermutationFromIndex(p, 8),
|
|
91
|
+
orientation: getOrientationFromIndex(o),
|
|
92
|
+
},
|
|
93
|
+
centers: getCenterFromIndex(c),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const MOVES = ["R", "R'", "U", "U'", "L", "L'", "B", "B'"];
|
|
97
|
+
function solveSkewb(state) {
|
|
98
|
+
const startIdx = getStateIndex(state);
|
|
99
|
+
const solvedIdx = getStateIndex((0, state_js_1.createSolvedSkewbState)());
|
|
100
|
+
if (startIdx === solvedIdx)
|
|
101
|
+
return [];
|
|
102
|
+
const forwardMap = new Map();
|
|
103
|
+
const backwardMap = new Map();
|
|
104
|
+
const forwardQueue = [startIdx];
|
|
105
|
+
const backwardQueue = [solvedIdx];
|
|
106
|
+
forwardMap.set(startIdx, { move: "", parent: -1 });
|
|
107
|
+
backwardMap.set(solvedIdx, { move: "", parent: -1 });
|
|
108
|
+
while (forwardQueue.length > 0 && backwardQueue.length > 0) {
|
|
109
|
+
const fSize = forwardQueue.length;
|
|
110
|
+
for (let i = 0; i < Math.min(fSize, 1000); i++) {
|
|
111
|
+
const currIdx = forwardQueue.shift();
|
|
112
|
+
const currState = getStateFromIndex(currIdx);
|
|
113
|
+
for (const move of MOVES) {
|
|
114
|
+
const nextState = (0, moves_js_1.applySkewbMove)(currState, move);
|
|
115
|
+
const nextIdx = getStateIndex(nextState);
|
|
116
|
+
if (!forwardMap.has(nextIdx)) {
|
|
117
|
+
forwardMap.set(nextIdx, { move, parent: currIdx });
|
|
118
|
+
if (backwardMap.has(nextIdx))
|
|
119
|
+
return reconstructPath(nextIdx, forwardMap, backwardMap);
|
|
120
|
+
forwardQueue.push(nextIdx);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const bSize = backwardQueue.length;
|
|
125
|
+
for (let i = 0; i < Math.min(bSize, 1000); i++) {
|
|
126
|
+
const currIdx = backwardQueue.shift();
|
|
127
|
+
const currState = getStateFromIndex(currIdx);
|
|
128
|
+
for (const move of MOVES) {
|
|
129
|
+
const nextState = (0, moves_js_1.applySkewbMove)(currState, move);
|
|
130
|
+
const nextIdx = getStateIndex(nextState);
|
|
131
|
+
if (!backwardMap.has(nextIdx)) {
|
|
132
|
+
const invMove = move.endsWith("'") ? move.slice(0, -1) : `${move}'`;
|
|
133
|
+
backwardMap.set(nextIdx, { move: invMove, parent: currIdx });
|
|
134
|
+
if (forwardMap.has(nextIdx))
|
|
135
|
+
return reconstructPath(nextIdx, forwardMap, backwardMap);
|
|
136
|
+
backwardQueue.push(nextIdx);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
throw new Error("Skewb state is unsolvable");
|
|
142
|
+
}
|
|
143
|
+
function reconstructPath(meetingIdx, forwardMap, backwardMap) {
|
|
144
|
+
const path = [];
|
|
145
|
+
let curr = meetingIdx;
|
|
146
|
+
while (curr !== -1) {
|
|
147
|
+
const edge = forwardMap.get(curr);
|
|
148
|
+
if (edge.parent !== -1)
|
|
149
|
+
path.unshift(edge.move);
|
|
150
|
+
curr = edge.parent;
|
|
151
|
+
}
|
|
152
|
+
curr = meetingIdx;
|
|
153
|
+
while (curr !== -1) {
|
|
154
|
+
const edge = backwardMap.get(curr);
|
|
155
|
+
if (edge.parent !== -1)
|
|
156
|
+
path.push(edge.move);
|
|
157
|
+
curr = edge.parent;
|
|
158
|
+
}
|
|
159
|
+
return path;
|
|
160
|
+
}
|