@cubingcenter/scrambler 0.5.1 → 0.5.3
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/cube2/facelets.d.ts.map +1 -1
- package/dist/puzzles/cube2/facelets.js +43 -22
- package/dist/puzzles/cube2/random-state.d.ts.map +1 -1
- package/dist/puzzles/cube2/random-state.js +38 -34
- package/dist/puzzles/cube2/scrambler.d.ts.map +1 -1
- package/dist/puzzles/cube2/scrambler.js +24 -10
- package/dist/puzzles/cube2/solver/browser-loader.d.ts +2 -2
- package/dist/puzzles/cube2/solver/browser-loader.d.ts.map +1 -1
- package/dist/puzzles/cube2/solver/browser-loader.js +2 -2
- package/dist/puzzles/cube2/solver/generated/manifest.json +3 -3
- package/dist/puzzles/cube2/solver/generated/policy.bin +0 -0
- package/dist/puzzles/cube2/solver/generated-assets.d.ts +1 -1
- package/dist/puzzles/cube2/solver/generated-assets.d.ts.map +1 -1
- package/dist/puzzles/cube2/solver/index.d.ts +2 -1
- package/dist/puzzles/cube2/solver/index.d.ts.map +1 -1
- package/dist/puzzles/cube2/solver/index.js +2 -1
- package/dist/puzzles/cube2/solver/node-loader.d.ts +1 -1
- package/dist/puzzles/cube2/solver/node-loader.d.ts.map +1 -1
- package/dist/puzzles/cube2/solver/node-loader.js +2 -2
- package/dist/puzzles/cube2/solver/search.d.ts +3 -0
- package/dist/puzzles/cube2/solver/search.d.ts.map +1 -1
- package/dist/puzzles/cube2/solver/search.js +51 -36
- package/dist/puzzles/cube2/solver/table-metadata.d.ts +2 -2
- package/dist/puzzles/cube2/solver/table-metadata.js +2 -2
- package/dist/puzzles/cube2/solver/tables.d.ts +2 -1
- package/dist/puzzles/cube2/solver/tables.d.ts.map +1 -1
- package/dist/puzzles/cube2/solver/tables.js +39 -13
- package/dist/puzzles/cube2/state.d.ts.map +1 -1
- package/dist/puzzles/cube2/state.js +3 -1
- package/dist/puzzles/cube2/validation.d.ts +2 -0
- package/dist/puzzles/cube2/validation.d.ts.map +1 -1
- package/dist/puzzles/cube2/validation.js +45 -8
- package/dist/puzzles/cube3/facelets.d.ts.map +1 -1
- package/dist/puzzles/cube3/facelets.js +53 -28
- package/dist/puzzles/cube3/random-state.d.ts.map +1 -1
- package/dist/puzzles/cube3/random-state.js +9 -2
- package/dist/puzzles/cube3/scrambler.js +1 -1
- package/dist/puzzles/cube3/state.d.ts.map +1 -1
- package/dist/puzzles/cube3/state.js +4 -1
- package/dist/puzzles/cube3/validation.d.ts.map +1 -1
- package/dist/puzzles/cube3/validation.js +5 -2
- package/dist/puzzles/skewb/random-state.d.ts +1 -0
- package/dist/puzzles/skewb/random-state.d.ts.map +1 -1
- package/dist/puzzles/skewb/random-state.js +9 -1
- package/dist/puzzles/skewb/scrambler.d.ts.map +1 -1
- package/dist/puzzles/skewb/scrambler.js +6 -2
- package/dist/puzzles/skewb/solver/tables.d.ts +1 -0
- package/dist/puzzles/skewb/solver/tables.d.ts.map +1 -1
- package/dist/puzzles/skewb/solver/tables.js +11 -5
- package/dist-cjs/puzzles/cube2/facelets.js +43 -22
- package/dist-cjs/puzzles/cube2/random-state.js +37 -33
- package/dist-cjs/puzzles/cube2/scrambler.js +22 -8
- package/dist-cjs/puzzles/cube2/solver/browser-loader.js +2 -2
- package/dist-cjs/puzzles/cube2/solver/generated/manifest.json +3 -3
- package/dist-cjs/puzzles/cube2/solver/generated/policy.bin +0 -0
- package/dist-cjs/puzzles/cube2/solver/index.js +18 -1
- package/dist-cjs/puzzles/cube2/solver/node-loader.js +2 -2
- package/dist-cjs/puzzles/cube2/solver/search.js +53 -35
- package/dist-cjs/puzzles/cube2/solver/table-metadata.js +2 -2
- package/dist-cjs/puzzles/cube2/solver/tables.js +41 -14
- package/dist-cjs/puzzles/cube2/state.js +3 -1
- package/dist-cjs/puzzles/cube2/validation.js +47 -8
- package/dist-cjs/puzzles/cube3/facelets.js +53 -28
- package/dist-cjs/puzzles/cube3/random-state.js +8 -1
- package/dist-cjs/puzzles/cube3/scrambler.js +1 -1
- package/dist-cjs/puzzles/cube3/state.js +4 -1
- package/dist-cjs/puzzles/cube3/validation.js +5 -2
- package/dist-cjs/puzzles/skewb/random-state.js +9 -0
- package/dist-cjs/puzzles/skewb/scrambler.js +5 -1
- package/dist-cjs/puzzles/skewb/solver/tables.js +12 -6
- package/package.json +1 -1
- package/dist/puzzles/cube2/solver/generated/distances.bin +0 -1
- package/dist/puzzles/cube3/solver/browser-worker-pool.d.ts +0 -31
- package/dist/puzzles/cube3/solver/browser-worker-pool.d.ts.map +0 -1
- package/dist/puzzles/cube3/solver/browser-worker-pool.js +0 -118
- package/dist/puzzles/cube3/solver/browser-worker.d.ts +0 -2
- package/dist/puzzles/cube3/solver/browser-worker.d.ts.map +0 -1
- package/dist/puzzles/cube3/solver/browser-worker.js +0 -71
- package/dist/puzzles/cube3/solver/worker-pool.d.ts +0 -18
- package/dist/puzzles/cube3/solver/worker-pool.d.ts.map +0 -1
- package/dist/puzzles/cube3/solver/worker-pool.js +0 -116
- package/dist/puzzles/cube3/solver/worker.d.ts +0 -2
- package/dist/puzzles/cube3/solver/worker.d.ts.map +0 -1
- package/dist/puzzles/cube3/solver/worker.js +0 -73
- package/dist-cjs/puzzles/cube2/solver/generated/distances.bin +0 -1
|
@@ -19,8 +19,10 @@ export function cube2StatesEqual(left, right) {
|
|
|
19
19
|
return (arraysEqual(left.corners.permutation, right.corners.permutation) &&
|
|
20
20
|
arraysEqual(left.corners.orientation, right.corners.orientation));
|
|
21
21
|
}
|
|
22
|
+
// Read-only canonical solved state for comparisons (never mutated).
|
|
23
|
+
const SOLVED_STATE = createSolvedCube2State();
|
|
22
24
|
export function isSolvedCube2State(state) {
|
|
23
|
-
return cube2StatesEqual(state,
|
|
25
|
+
return cube2StatesEqual(state, SOLVED_STATE);
|
|
24
26
|
}
|
|
25
27
|
function range(length) {
|
|
26
28
|
return Array.from({ length }, (_, index) => index);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { Cube2State, Cube2ValidationResult } from "./types.js";
|
|
2
|
+
export declare function isTrivialCube2State(state: Cube2State): boolean;
|
|
2
3
|
export declare function validateCube2State(state: Cube2State): Cube2ValidationResult;
|
|
3
4
|
export declare function isOneMoveFromSolvedCube2State(state: Cube2State): boolean;
|
|
5
|
+
export declare function isTrivialSamplingCube2State(p: number, o: number): boolean;
|
|
4
6
|
export declare function isAllowedRandomCube2State(state: Cube2State): boolean;
|
|
5
7
|
export declare function permutationParity(permutation: readonly number[]): 0 | 1;
|
|
6
8
|
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube2/validation.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube2/validation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAwBpE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAuB9D;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,qBAAqB,CAkB3E;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAIxE;AAKD,wBAAgB,2BAA2B,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAEzE;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAIpE;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAYvE"}
|
|
@@ -2,16 +2,47 @@ import { CUBE2_MOVES, CORNER_COUNT } from "./types.js";
|
|
|
2
2
|
import { applyCube2Move } from "./moves.js";
|
|
3
3
|
import { createSolvedCube2State, isSolvedCube2State } from "./state.js";
|
|
4
4
|
import { encodeOrientation, encodePermutation } from "./solver/tables.js";
|
|
5
|
+
// The 18 single-move states, precomputed once so the hot random-state path
|
|
6
|
+
// checks "solved or one move from solved" with pure array comparisons
|
|
7
|
+
// (no coordinate encodings per generated state).
|
|
8
|
+
const ONE_MOVE_FROM_SOLVED_STATES = CUBE2_MOVES.map((move) => applyCube2Move(createSolvedCube2State(), move));
|
|
5
9
|
// Precomputed coordinate encodings of every single-move state, so the
|
|
6
10
|
// "one move from solved" check is a couple of O(n) encodings + 18 comparisons
|
|
7
11
|
// instead of 18 full state applications.
|
|
8
|
-
const ONE_MOVE_FROM_SOLVED_ENCODED =
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
const ONE_MOVE_FROM_SOLVED_ENCODED = ONE_MOVE_FROM_SOLVED_STATES.map((state) => ({
|
|
13
|
+
p: encodePermutation(state.corners.permutation),
|
|
14
|
+
o: encodeOrientation(state.corners.orientation)
|
|
15
|
+
}));
|
|
16
|
+
// Cheap trivial check for random-state sampling: solved + the 18 single-move
|
|
17
|
+
// states, compared by arrays with early exit (a random state differs from
|
|
18
|
+
// each candidate within the first couple of permutation entries, so the
|
|
19
|
+
// typical cost is tens of comparisons — no coordinate encodings).
|
|
20
|
+
export function isTrivialCube2State(state) {
|
|
21
|
+
const cp = state.corners.permutation;
|
|
22
|
+
const co = state.corners.orientation;
|
|
23
|
+
for (const candidate of ONE_MOVE_FROM_SOLVED_STATES) {
|
|
24
|
+
const p = candidate.corners.permutation;
|
|
25
|
+
const o = candidate.corners.orientation;
|
|
26
|
+
let match = true;
|
|
27
|
+
for (let i = 0; i < 8; i += 1) {
|
|
28
|
+
if (cp[i] !== p[i]) {
|
|
29
|
+
match = false;
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (!match)
|
|
34
|
+
continue;
|
|
35
|
+
for (let i = 0; i < 8; i += 1) {
|
|
36
|
+
if (co[i] !== o[i]) {
|
|
37
|
+
match = false;
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (match)
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
return isSolvedCube2State(state);
|
|
45
|
+
}
|
|
15
46
|
export function validateCube2State(state) {
|
|
16
47
|
const errors = [
|
|
17
48
|
...validatePermutation("corner", state.corners.permutation, CORNER_COUNT),
|
|
@@ -31,7 +62,13 @@ export function validateCube2State(state) {
|
|
|
31
62
|
export function isOneMoveFromSolvedCube2State(state) {
|
|
32
63
|
const p = encodePermutation(state.corners.permutation);
|
|
33
64
|
const o = encodeOrientation(state.corners.orientation);
|
|
34
|
-
return
|
|
65
|
+
return isTrivialSamplingCube2State(p, o);
|
|
66
|
+
}
|
|
67
|
+
// Cheap trivial check for random-state sampling: the caller has already
|
|
68
|
+
// encoded the coordinates once. True for the solved state and the 18
|
|
69
|
+
// single-move states (which would produce scrambles shorter than 2 moves).
|
|
70
|
+
export function isTrivialSamplingCube2State(p, o) {
|
|
71
|
+
return (p === 0 && o === 0) || ONE_MOVE_FROM_SOLVED_ENCODED.some((entry) => entry.p === p && entry.o === o);
|
|
35
72
|
}
|
|
36
73
|
export function isAllowedRandomCube2State(state) {
|
|
37
74
|
return validateCube2State(state).valid &&
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"facelets.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/facelets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAgB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"facelets.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/facelets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAgB,MAAM,YAAY,CAAC;AAyGrF,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,aAAa,CA6BhE;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,CAEnE;AAED,wBAAgB,iBAAiB,IAAI,SAAS,SAAS,EAAE,CAExD"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
const FACE_ORDER = ["U", "R", "F", "D", "L", "B"];
|
|
2
|
+
const STICKERS = ["U", "R", "F", "D", "L", "B"];
|
|
3
|
+
const STICKER_INDEX = { U: 0, R: 1, F: 2, D: 3, L: 4, B: 5 };
|
|
2
4
|
const CORNER_PIECE_STICKERS = [
|
|
3
5
|
["U", "R", "F"],
|
|
4
6
|
["U", "F", "L"],
|
|
@@ -47,20 +49,60 @@ const EDGE_POSITION_SLOTS = [
|
|
|
47
49
|
[["B", 5], ["L", 3]],
|
|
48
50
|
[["B", 3], ["R", 5]]
|
|
49
51
|
];
|
|
52
|
+
const CORNER_CELLS = buildCells(CORNER_POSITION_SLOTS, CORNER_PIECE_STICKERS, 3);
|
|
53
|
+
const EDGE_CELLS = buildCells(EDGE_POSITION_SLOTS, EDGE_PIECE_STICKERS, 2);
|
|
54
|
+
function buildCells(slots, stickersTable, orientationCount) {
|
|
55
|
+
const cells = [];
|
|
56
|
+
for (let position = 0; position < slots.length; position += 1) {
|
|
57
|
+
const byPiece = [];
|
|
58
|
+
for (let piece = 0; piece < stickersTable.length; piece += 1) {
|
|
59
|
+
const byOrientation = [];
|
|
60
|
+
for (let orientation = 0; orientation < orientationCount; orientation += 1) {
|
|
61
|
+
const stickers = stickersTable[piece];
|
|
62
|
+
const slotList = slots[position];
|
|
63
|
+
const cell = [];
|
|
64
|
+
for (let index = 0; index < slotList.length; index += 1) {
|
|
65
|
+
const [face, stickerIndex] = slotList[index];
|
|
66
|
+
cell.push(FACE_ORDER.indexOf(face));
|
|
67
|
+
cell.push(stickerIndex);
|
|
68
|
+
cell.push(STICKER_INDEX[stickers[(index - orientation + stickers.length) % stickers.length]]);
|
|
69
|
+
}
|
|
70
|
+
byOrientation.push(cell);
|
|
71
|
+
}
|
|
72
|
+
byPiece.push(byOrientation);
|
|
73
|
+
}
|
|
74
|
+
cells.push(byPiece);
|
|
75
|
+
}
|
|
76
|
+
return cells;
|
|
77
|
+
}
|
|
78
|
+
function placeCells(faces, cells) {
|
|
79
|
+
for (let cellIndex = 0; cellIndex < cells.length; cellIndex += 3) {
|
|
80
|
+
faces[cells[cellIndex]][cells[cellIndex + 1]] = STICKERS[cells[cellIndex + 2]];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
50
83
|
export function toCube3Facelets(state) {
|
|
51
|
-
const facelets =
|
|
52
|
-
|
|
53
|
-
|
|
84
|
+
const facelets = {
|
|
85
|
+
U: Array(9),
|
|
86
|
+
R: Array(9),
|
|
87
|
+
F: Array(9),
|
|
88
|
+
D: Array(9),
|
|
89
|
+
L: Array(9),
|
|
90
|
+
B: Array(9)
|
|
91
|
+
};
|
|
92
|
+
const faces = [facelets.U, facelets.R, facelets.F, facelets.D, facelets.L, facelets.B];
|
|
93
|
+
// Centers (fixed, since no center permutation on the 3x3)
|
|
94
|
+
for (let face = 0; face < 6; face += 1) {
|
|
95
|
+
faces[face][4] = STICKERS[face];
|
|
54
96
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
97
|
+
const cornerPermutation = state.corners.permutation;
|
|
98
|
+
const cornerOrientation = state.corners.orientation;
|
|
99
|
+
for (let position = 0; position < CORNER_CELLS.length; position += 1) {
|
|
100
|
+
placeCells(faces, CORNER_CELLS[position][cornerPermutation[position]][cornerOrientation[position]]);
|
|
59
101
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
102
|
+
const edgePermutation = state.edges.permutation;
|
|
103
|
+
const edgeOrientation = state.edges.orientation;
|
|
104
|
+
for (let position = 0; position < EDGE_CELLS.length; position += 1) {
|
|
105
|
+
placeCells(faces, EDGE_CELLS[position][edgePermutation[position]][edgeOrientation[position]]);
|
|
64
106
|
}
|
|
65
107
|
return facelets;
|
|
66
108
|
}
|
|
@@ -70,20 +112,3 @@ export function formatCube3Facelets(facelets) {
|
|
|
70
112
|
export function getCube3FaceOrder() {
|
|
71
113
|
return FACE_ORDER;
|
|
72
114
|
}
|
|
73
|
-
function createEmptyFacelets() {
|
|
74
|
-
return {
|
|
75
|
-
U: Array(9).fill("U"),
|
|
76
|
-
R: Array(9).fill("R"),
|
|
77
|
-
F: Array(9).fill("F"),
|
|
78
|
-
D: Array(9).fill("D"),
|
|
79
|
-
L: Array(9).fill("L"),
|
|
80
|
-
B: Array(9).fill("B")
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
function placeOrientedStickers(facelets, slots, stickers, orientation) {
|
|
84
|
-
for (let index = 0; index < slots.length; index += 1) {
|
|
85
|
-
const [face, stickerIndex] = slots[index];
|
|
86
|
-
const sticker = stickers[(index - orientation + stickers.length) % stickers.length];
|
|
87
|
-
facelets[face][stickerIndex] = sticker;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"random-state.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/random-state.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"random-state.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/random-state.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAKtE,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,uBAA4B,GAAG,UAAU,CAsB1F"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createRandomSource } from "../../random/index.js";
|
|
2
2
|
import { CORNER_COUNT, EDGE_COUNT } from "./types.js";
|
|
3
|
-
import {
|
|
3
|
+
import { isOneMoveFromSolvedCube3State, permutationParity } from "./validation.js";
|
|
4
|
+
import { isSolvedCube3State } from "./state.js";
|
|
4
5
|
import { generateSubsetCube3State } from "./subset-state.js";
|
|
5
6
|
export function generateRandomCube3State(options = {}) {
|
|
6
7
|
const random = createRandomSource(options.seed);
|
|
@@ -9,8 +10,14 @@ export function generateRandomCube3State(options = {}) {
|
|
|
9
10
|
return generateSubsetCube3State(options.subset, random);
|
|
10
11
|
}
|
|
11
12
|
for (let attempt = 0; attempt < 100; attempt += 1) {
|
|
13
|
+
// The state is legal by construction (permutations shuffled, orientation
|
|
14
|
+
// sums fixed, parity matched), so the only meaningful rejection is the
|
|
15
|
+
// trivial set (solved + 18 single-move states) — no full re-validation.
|
|
12
16
|
const state = createLegalRandomState(random);
|
|
13
|
-
if (!rejectTrivial
|
|
17
|
+
if (!rejectTrivial) {
|
|
18
|
+
return state;
|
|
19
|
+
}
|
|
20
|
+
if (!isSolvedCube3State(state) && !isOneMoveFromSolvedCube3State(state)) {
|
|
14
21
|
return state;
|
|
15
22
|
}
|
|
16
23
|
}
|
|
@@ -44,7 +44,7 @@ export async function generateCube3Scramble(options = {}) {
|
|
|
44
44
|
generatorVersion: "1.0.0",
|
|
45
45
|
facelets,
|
|
46
46
|
faceletString: formatCube3Facelets(facelets),
|
|
47
|
-
state
|
|
47
|
+
state,
|
|
48
48
|
solutionMoves: solution.moves,
|
|
49
49
|
solutionDepth: solution.depth,
|
|
50
50
|
phase1Depth: solution.phase1.depth,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,wBAAgB,sBAAsB,IAAI,UAAU,CAWnD;
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,wBAAgB,sBAAsB,IAAI,UAAU,CAWnD;AAMD,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAW7D;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAO7E;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAE7D"}
|
|
@@ -11,6 +11,9 @@ export function createSolvedCube3State() {
|
|
|
11
11
|
}
|
|
12
12
|
};
|
|
13
13
|
}
|
|
14
|
+
// Read-only canonical solved state for comparisons (never mutated, so a
|
|
15
|
+
// single shared instance is safe and avoids allocating per call).
|
|
16
|
+
const SOLVED_STATE = createSolvedCube3State();
|
|
14
17
|
export function cloneCube3State(state) {
|
|
15
18
|
return {
|
|
16
19
|
corners: {
|
|
@@ -30,7 +33,7 @@ export function cube3StatesEqual(left, right) {
|
|
|
30
33
|
arraysEqual(left.edges.orientation, right.edges.orientation));
|
|
31
34
|
}
|
|
32
35
|
export function isSolvedCube3State(state) {
|
|
33
|
-
return cube3StatesEqual(state,
|
|
36
|
+
return cube3StatesEqual(state, SOLVED_STATE);
|
|
34
37
|
}
|
|
35
38
|
function range(length) {
|
|
36
39
|
return Array.from({ length }, (_, index) => index);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/validation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAIpE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,qBAAqB,CA+B3E;
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/puzzles/cube3/validation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAIpE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,qBAAqB,CA+B3E;AASD,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAExE;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAIpE;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAYvE"}
|
|
@@ -28,9 +28,12 @@ export function validateCube3State(state) {
|
|
|
28
28
|
errors
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
|
+
// The 18 single-move states, precomputed once so the hot random-state path
|
|
32
|
+
// checks "one move from solved" with pure comparisons instead of 18 full
|
|
33
|
+
// move applications per generated state.
|
|
34
|
+
const ONE_MOVE_FROM_SOLVED_STATES = CUBE3_MOVES.map((move) => applyCube3Move(createSolvedCube3State(), move));
|
|
31
35
|
export function isOneMoveFromSolvedCube3State(state) {
|
|
32
|
-
|
|
33
|
-
return CUBE3_MOVES.some((move) => cube3StatesEqual(applyCube3Move(solved, move), state));
|
|
36
|
+
return ONE_MOVE_FROM_SOLVED_STATES.some((candidate) => cube3StatesEqual(candidate, state));
|
|
34
37
|
}
|
|
35
38
|
export function isAllowedRandomCube3State(state) {
|
|
36
39
|
return validateCube3State(state).valid &&
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { SkewbRandomStateOptions, SkewbState } from "./types.js";
|
|
2
2
|
export declare function generateRandomSkewbState(options?: SkewbRandomStateOptions): Promise<SkewbState>;
|
|
3
|
+
export declare function generateRandomSkewbStateSync(options?: SkewbRandomStateOptions): SkewbState;
|
|
3
4
|
//# sourceMappingURL=random-state.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"random-state.d.ts","sourceRoot":"","sources":["../../../src/puzzles/skewb/random-state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"random-state.d.ts","sourceRoot":"","sources":["../../../src/puzzles/skewb/random-state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AActE,wBAAsB,wBAAwB,CAC5C,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,UAAU,CAAC,CAGrB;AAID,wBAAgB,4BAA4B,CAAC,OAAO,GAAE,uBAA4B,GAAG,UAAU,CAmB9F"}
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import { createRandomSource } from "../../random/index.js";
|
|
2
|
-
import { ensureTablesLoaded, getCentersTransition, getOrientationTransition, getPermutationTransition, getStateFromRank, MOVE_COUNT, rankFromComponents, SOLVED_RANK, STATE_COUNT, } from "./solver/index.js";
|
|
2
|
+
import { ensureTablesLoaded, areSkewbTablesLoaded, getCentersTransition, getOrientationTransition, getPermutationTransition, getStateFromRank, MOVE_COUNT, rankFromComponents, SOLVED_RANK, STATE_COUNT, } from "./solver/index.js";
|
|
3
3
|
export async function generateRandomSkewbState(options = {}) {
|
|
4
4
|
await ensureTablesLoaded();
|
|
5
|
+
return generateRandomSkewbStateSync(options);
|
|
6
|
+
}
|
|
7
|
+
// Synchronous variant for callers that have already ensured the tables are
|
|
8
|
+
// loaded (e.g. the scrambler after the first call): no per-call await hop.
|
|
9
|
+
export function generateRandomSkewbStateSync(options = {}) {
|
|
10
|
+
if (!areSkewbTablesLoaded()) {
|
|
11
|
+
throw new Error("Skewb solver tables not loaded. Call ensureTablesLoaded() first.");
|
|
12
|
+
}
|
|
5
13
|
const random = createRandomSource(options.seed);
|
|
6
14
|
const rejectTrivial = options.rejectTrivial ?? true;
|
|
7
15
|
const trivialRanks = rejectTrivial ? getTrivialRanks() : null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scrambler.d.ts","sourceRoot":"","sources":["../../../src/puzzles/skewb/scrambler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAa9F,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/skewb/scrambler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAa9F,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,CAoDzB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { generateRandomSkewbState } from "./random-state.js";
|
|
1
|
+
import { generateRandomSkewbState, generateRandomSkewbStateSync } from "./random-state.js";
|
|
2
2
|
import { formatSkewbAlgorithm } from "./moves.js";
|
|
3
3
|
import { areSkewbTablesLoaded, solveSkewbIndices, solveSkewbIndicesSync, verifySkewbScrambleIndices, SKEWB_MOVES } from "./solver/index.js";
|
|
4
4
|
import { formatSkewbFacelets, toSkewbFacelets } from "./facelets.js";
|
|
@@ -16,7 +16,11 @@ export const skewbScrambleGenerator = {
|
|
|
16
16
|
},
|
|
17
17
|
};
|
|
18
18
|
export async function generateSkewbScramble(options = {}) {
|
|
19
|
-
|
|
19
|
+
// generateRandomSkewbStateSync guarantees the tables are loaded, so use
|
|
20
|
+
// the sync state generation on every call after the first (no await hop).
|
|
21
|
+
const state = areSkewbTablesLoaded()
|
|
22
|
+
? generateRandomSkewbStateSync(options)
|
|
23
|
+
: await generateRandomSkewbState(options);
|
|
20
24
|
// generateRandomSkewbState guarantees the tables are loaded, so use the
|
|
21
25
|
// sync solve on every call after the first (no extra microtask).
|
|
22
26
|
const solution = areSkewbTablesLoaded()
|
|
@@ -29,6 +29,7 @@ export declare let ORIENT_CLASS: Uint8Array | null;
|
|
|
29
29
|
export declare let R7_SETS: Uint16Array | null;
|
|
30
30
|
export declare let PERM7_UNRANK: Uint8Array | null;
|
|
31
31
|
export declare let CENTER_UNRANK: Uint8Array | null;
|
|
32
|
+
export declare let ORIENT_UNRANK: Uint8Array | null;
|
|
32
33
|
export declare let SOLVED_RANK: number;
|
|
33
34
|
export declare function ensureTablesLoaded(): Promise<void>;
|
|
34
35
|
export declare function areSkewbTablesLoaded(): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/skewb/solver/tables.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAazD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAanE;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAY1E;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAOrE;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAS/D;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAWjE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAE1D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAOvD;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAY3D;AAYD,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,YAAY,MAAM,CAAC;AAChC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,kBAAkB,IAAI,CAAC;AACpC,eAAO,MAAM,YAAY,IAAI,CAAC;AAC9B,eAAO,MAAM,UAAU,IAAI,CAAC;AAC5B,eAAO,MAAM,WAAW,QAAuD,CAAC;AAChF,eAAO,MAAM,qBAAqB,QAAmC,CAAC;AAEtE,eAAO,MAAM,WAAW,EAAE,SAAS,SAAS,EAAiD,CAAC;AAE9F,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAa7D;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAYzD;AAOD,eAAO,IAAI,iBAAiB,EAAE,WAAW,GAAG,IAAW,CAAC;AACxD,eAAO,IAAI,iBAAiB,EAAE,WAAW,GAAG,IAAW,CAAC;AACxD,eAAO,IAAI,aAAa,EAAE,WAAW,GAAG,IAAW,CAAC;AACpD,eAAO,IAAI,YAAY,EAAE,UAAU,GAAG,IAAW,CAAC;AAClD,eAAO,IAAI,gBAAgB,EAAE,WAAW,GAAG,IAAW,CAAC;AACvD,eAAO,IAAI,gBAAgB,EAAE,WAAW,GAAG,IAAW,CAAC;AACvD,eAAO,IAAI,YAAY,EAAE,UAAU,GAAG,IAAW,CAAC;AAClD,eAAO,IAAI,OAAO,EAAE,WAAW,GAAG,IAAW,CAAC;AAC9C,eAAO,IAAI,YAAY,EAAE,UAAU,GAAG,IAAW,CAAC;AAClD,eAAO,IAAI,aAAa,EAAE,UAAU,GAAG,IAAW,CAAC;AACnD,eAAO,IAAI,WAAW,QAAI,CAAC;AAE3B,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAIxD;AAED,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE;IACnC,gBAAgB,EAAE,WAAW,CAAC;IAC9B,gBAAgB,EAAE,WAAW,CAAC;IAC9B,YAAY,EAAE,WAAW,CAAC;IAC1B,WAAW,EAAE,UAAU,CAAC;IACxB,cAAc,EAAE,WAAW,CAAC;IAC5B,WAAW,EAAE,UAAU,CAAC;IACxB,MAAM,EAAE,WAAW,CAAC;CACrB,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../../../src/puzzles/skewb/solver/tables.ts"],"names":[],"mappings":"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"}
|
|
@@ -149,6 +149,7 @@ export let ORIENT_CLASS = null; // rO -> class 0..8
|
|
|
149
149
|
export let R7_SETS = null; // (rC_even * 9 + class) * 4 + i
|
|
150
150
|
export let PERM7_UNRANK = null; // r7 * 7 + i -> piece at corner i+1 (derived at load)
|
|
151
151
|
export let CENTER_UNRANK = null; // rC * 6 + i -> center piece (derived at load)
|
|
152
|
+
export let ORIENT_UNRANK = null; // rO * 7 + i -> orientation of corner i+1 (derived at load)
|
|
152
153
|
export let SOLVED_RANK = 0;
|
|
153
154
|
export async function ensureTablesLoaded() {
|
|
154
155
|
if (tablesLoaded)
|
|
@@ -185,6 +186,12 @@ export function assignTables(tables) {
|
|
|
185
186
|
for (let i = 0; i < 6; i++)
|
|
186
187
|
CENTER_UNRANK[rC * 6 + i] = centers[i];
|
|
187
188
|
}
|
|
189
|
+
ORIENT_UNRANK = new Uint8Array(ORIENTATION_COUNT * 7);
|
|
190
|
+
for (let rO = 0; rO < ORIENTATION_COUNT; rO++) {
|
|
191
|
+
const orient = getOrientationFromIndex(rO);
|
|
192
|
+
for (let i = 0; i < 7; i++)
|
|
193
|
+
ORIENT_UNRANK[rO * 7 + i] = orient[i + 1];
|
|
194
|
+
}
|
|
188
195
|
SOLVED_RANK = rankFromComponents(0, 0, 0);
|
|
189
196
|
tablesLoaded = true;
|
|
190
197
|
}
|
|
@@ -200,6 +207,7 @@ export function resetTablesForTests() {
|
|
|
200
207
|
R7_SETS = null;
|
|
201
208
|
PERM7_UNRANK = null;
|
|
202
209
|
CENTER_UNRANK = null;
|
|
210
|
+
ORIENT_UNRANK = null;
|
|
203
211
|
}
|
|
204
212
|
// ---------------------------------------------------------------------------
|
|
205
213
|
// Compact rank helpers
|
|
@@ -245,11 +253,9 @@ export function getStateFromRank(rank) {
|
|
|
245
253
|
perm[i + 1] = PERM7_UNRANK[permBase + i];
|
|
246
254
|
const orient = new Array(8);
|
|
247
255
|
orient[0] = 0;
|
|
248
|
-
|
|
249
|
-
for (let i = 1; i < 8; i++)
|
|
250
|
-
orient[i] =
|
|
251
|
-
val = Math.floor(val / 3);
|
|
252
|
-
}
|
|
256
|
+
const orientBase = rO * 7;
|
|
257
|
+
for (let i = 1; i < 8; i++)
|
|
258
|
+
orient[i] = ORIENT_UNRANK[orientBase + i - 1];
|
|
253
259
|
const centers = new Array(6);
|
|
254
260
|
const centerBase = EVEN_CENTER_FULL[even] * 6;
|
|
255
261
|
for (let i = 0; i < 6; i++)
|
|
@@ -4,6 +4,8 @@ exports.toCube2Facelets = toCube2Facelets;
|
|
|
4
4
|
exports.formatCube2Facelets = formatCube2Facelets;
|
|
5
5
|
exports.getCube2FaceOrder = getCube2FaceOrder;
|
|
6
6
|
const FACE_ORDER = ["U", "R", "F", "D", "L", "B"];
|
|
7
|
+
const STICKERS = ["U", "R", "F", "D", "L", "B"];
|
|
8
|
+
const STICKER_INDEX = { U: 0, R: 1, F: 2, D: 3, L: 4, B: 5 };
|
|
7
9
|
const CORNER_PIECE_STICKERS = [
|
|
8
10
|
["U", "R", "F"],
|
|
9
11
|
["U", "F", "L"],
|
|
@@ -24,12 +26,48 @@ const CORNER_POSITION_SLOTS = [
|
|
|
24
26
|
[["D", 2], ["B", 3], ["L", 2]],
|
|
25
27
|
[["D", 3], ["R", 3], ["B", 2]]
|
|
26
28
|
];
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
const CORNER_CELLS = buildCells();
|
|
30
|
+
function buildCells() {
|
|
31
|
+
const cells = [];
|
|
29
32
|
for (let position = 0; position < CORNER_POSITION_SLOTS.length; position += 1) {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
const byPiece = [];
|
|
34
|
+
for (let piece = 0; piece < CORNER_PIECE_STICKERS.length; piece += 1) {
|
|
35
|
+
const byOrientation = [];
|
|
36
|
+
for (let orientation = 0; orientation < 3; orientation += 1) {
|
|
37
|
+
const stickers = CORNER_PIECE_STICKERS[piece];
|
|
38
|
+
const slotList = CORNER_POSITION_SLOTS[position];
|
|
39
|
+
const cell = [];
|
|
40
|
+
for (let index = 0; index < slotList.length; index += 1) {
|
|
41
|
+
const [face, stickerIndex] = slotList[index];
|
|
42
|
+
cell.push(FACE_ORDER.indexOf(face));
|
|
43
|
+
cell.push(stickerIndex);
|
|
44
|
+
cell.push(STICKER_INDEX[stickers[(index - orientation + stickers.length) % stickers.length]]);
|
|
45
|
+
}
|
|
46
|
+
byOrientation.push(cell);
|
|
47
|
+
}
|
|
48
|
+
byPiece.push(byOrientation);
|
|
49
|
+
}
|
|
50
|
+
cells.push(byPiece);
|
|
51
|
+
}
|
|
52
|
+
return cells;
|
|
53
|
+
}
|
|
54
|
+
function toCube2Facelets(state) {
|
|
55
|
+
const facelets = {
|
|
56
|
+
U: Array(4),
|
|
57
|
+
R: Array(4),
|
|
58
|
+
F: Array(4),
|
|
59
|
+
D: Array(4),
|
|
60
|
+
L: Array(4),
|
|
61
|
+
B: Array(4)
|
|
62
|
+
};
|
|
63
|
+
const faces = [facelets.U, facelets.R, facelets.F, facelets.D, facelets.L, facelets.B];
|
|
64
|
+
const permutation = state.corners.permutation;
|
|
65
|
+
const orientation = state.corners.orientation;
|
|
66
|
+
for (let position = 0; position < CORNER_CELLS.length; position += 1) {
|
|
67
|
+
const cells = CORNER_CELLS[position][permutation[position]][orientation[position]];
|
|
68
|
+
for (let cellIndex = 0; cellIndex < cells.length; cellIndex += 3) {
|
|
69
|
+
faces[cells[cellIndex]][cells[cellIndex + 1]] = STICKERS[cells[cellIndex + 2]];
|
|
70
|
+
}
|
|
33
71
|
}
|
|
34
72
|
return facelets;
|
|
35
73
|
}
|
|
@@ -39,20 +77,3 @@ function formatCube2Facelets(facelets) {
|
|
|
39
77
|
function getCube2FaceOrder() {
|
|
40
78
|
return FACE_ORDER;
|
|
41
79
|
}
|
|
42
|
-
function createEmptyFacelets() {
|
|
43
|
-
return {
|
|
44
|
-
U: Array(4).fill("U"),
|
|
45
|
-
R: Array(4).fill("R"),
|
|
46
|
-
F: Array(4).fill("F"),
|
|
47
|
-
D: Array(4).fill("D"),
|
|
48
|
-
L: Array(4).fill("L"),
|
|
49
|
-
B: Array(4).fill("B")
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
function placeOrientedStickers(facelets, slots, stickers, orientation) {
|
|
53
|
-
for (let index = 0; index < slots.length; index += 1) {
|
|
54
|
-
const [face, stickerIndex] = slots[index];
|
|
55
|
-
const sticker = stickers[(index - orientation + stickers.length) % stickers.length];
|
|
56
|
-
facelets[face][stickerIndex] = sticker;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
@@ -2,53 +2,57 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.generateRandomCube2State = generateRandomCube2State;
|
|
4
4
|
const index_js_1 = require("../../random/index.js");
|
|
5
|
-
const types_js_1 = require("./types.js");
|
|
6
5
|
const validation_js_1 = require("./validation.js");
|
|
7
6
|
function generateRandomCube2State(options = {}) {
|
|
8
7
|
const random = (0, index_js_1.createRandomSource)(options.seed);
|
|
9
8
|
const rejectTrivial = options.rejectTrivial ?? true;
|
|
10
9
|
for (let attempt = 0; attempt < 100; attempt += 1) {
|
|
10
|
+
// The state is legal by construction (DBL corner fixed, even
|
|
11
|
+
// permutation, orientation sum divisible by 3, values in range), so the
|
|
12
|
+
// only meaningful rejection is the trivial set: solved + the 18
|
|
13
|
+
// single-move states (scrambles shorter than 2 moves), compared by
|
|
14
|
+
// arrays — no coordinate encodings in this hot path.
|
|
11
15
|
const state = createLegalRandomState(random);
|
|
12
|
-
if (!rejectTrivial || (0, validation_js_1.
|
|
16
|
+
if (!rejectTrivial || !(0, validation_js_1.isTrivialCube2State)(state)) {
|
|
13
17
|
return state;
|
|
14
18
|
}
|
|
15
19
|
}
|
|
16
20
|
throw new Error("Unable to generate a non-trivial random 2x2 state.");
|
|
17
21
|
}
|
|
18
22
|
function createLegalRandomState(random) {
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
const orient7 = randomOrientations(types_js_1.CORNER_COUNT - 1, 3, random);
|
|
23
|
-
orient7.splice(6, 0, 0);
|
|
24
|
-
return {
|
|
25
|
-
corners: {
|
|
26
|
-
permutation: mappedPerm,
|
|
27
|
-
orientation: orient7
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
function shuffledRange(length, random) {
|
|
32
|
-
const values = Array.from({ length }, (_, index) => index);
|
|
33
|
-
for (let index = values.length - 1; index > 0; index -= 1) {
|
|
23
|
+
// 7 free corners (piece 6 = DBL stays fixed at position 6)
|
|
24
|
+
const perm7 = [0, 1, 2, 3, 4, 5, 6];
|
|
25
|
+
for (let index = 6; index > 0; index -= 1) {
|
|
34
26
|
const swapIndex = random.nextInt(index + 1);
|
|
35
|
-
|
|
27
|
+
if (swapIndex < index) {
|
|
28
|
+
const temporary = perm7[index];
|
|
29
|
+
perm7[index] = perm7[swapIndex];
|
|
30
|
+
perm7[swapIndex] = temporary;
|
|
31
|
+
}
|
|
36
32
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
33
|
+
// Map the shuffle domain {0..6} to {0..5,7} and insert 6 at position 6
|
|
34
|
+
// (DBL fixed). Single allocation, no intermediate arrays.
|
|
35
|
+
const last = perm7[6] >= 6 ? perm7[6] + 1 : perm7[6];
|
|
36
|
+
const permutation = new Array(8);
|
|
37
|
+
for (let index = 0; index < 6; index += 1) {
|
|
38
|
+
const value = perm7[index];
|
|
39
|
+
permutation[index] = value >= 6 ? value + 1 : value;
|
|
40
|
+
}
|
|
41
|
+
permutation[6] = 6;
|
|
42
|
+
permutation[7] = last;
|
|
43
|
+
// 7 free orientations with sum divisible by 3; DBL stays oriented
|
|
44
|
+
const orientation = [0, 0, 0, 0, 0, 0, 0, 0];
|
|
41
45
|
let total = 0;
|
|
42
|
-
for (let index = 0; index <
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
total +=
|
|
46
|
+
for (let index = 0; index < 6; index += 1) {
|
|
47
|
+
const value = random.nextInt(3);
|
|
48
|
+
orientation[index] = value;
|
|
49
|
+
total += value;
|
|
46
50
|
}
|
|
47
|
-
|
|
48
|
-
return
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
orientation[7] = (3 - (total % 3)) % 3;
|
|
52
|
+
return {
|
|
53
|
+
corners: {
|
|
54
|
+
permutation,
|
|
55
|
+
orientation
|
|
56
|
+
}
|
|
57
|
+
};
|
|
54
58
|
}
|