feat: reposition solver controls to a dropdown

Moved the Kociemba/Beginner solve options into a sleek dropdown menu positioned above the Scramble button on the left side of the screen. This ensures the solver controls no longer obstruct the programmatic move queue at the bottom.
This commit is contained in:
2026-02-23 21:46:15 +00:00
parent f6b34449df
commit 929761ac9e
31 changed files with 6843 additions and 987 deletions

View File

@@ -0,0 +1,139 @@
import Cube from "cubejs";
// Initialize the core pruning tables on module load
Cube.initSolver();
import { DeepCube, CORNERS, EDGES } from "../DeepCube.js";
export class KociembaSolver {
constructor(cube) {
this.cube = cube.clone();
}
// Convert DeepCube permutation/orientation to Kociemba facelet string
// Kociemba format: U1..U9 R1..R9 F1..F9 D1..D9 L1..L9 B1..B9
toFaceletString() {
// Array of 54 characters representing the 6 faces.
// 0..8 = U
// 9..17 = R
// 18..26 = F
// 27..35 = D
// 36..44 = L
// 45..53 = B
const f = new Array(54).fill(" ");
// Centers
f[4] = "U";
f[13] = "R";
f[22] = "F";
f[31] = "D";
f[40] = "L";
f[49] = "B";
// DeepCube to Kociemba mapping:
// Corners:
// 0: URF, 1: UFL, 2: ULB, 3: UBR, 4: DFR, 5: DLF, 6: DBL, 7: DRB
// Edges:
// 0: UR, 1: UF, 2: UL, 3: UB, 4: DR, 5: DF, 6: DL, 7: DB, 8: FR, 9: FL, 10: BL, 11: BR
const cornerColors = [
["U", "R", "F"], // 0: URF
["U", "F", "L"], // 1: UFL
["U", "L", "B"], // 2: ULB
["U", "B", "R"], // 3: UBR
["D", "F", "R"], // 4: DFR
["D", "L", "F"], // 5: DLF
["D", "B", "L"], // 6: DBL
["D", "R", "B"], // 7: DRB
];
const cornerFacelets = [
[8, 9, 20], // URF (U9, R1, F3)
[6, 18, 38], // UFL (U7, F1, L3)
[0, 36, 47], // ULB (U1, L1, B3)
[2, 45, 11], // UBR (U3, B1, R3)
[29, 26, 15], // DFR (D3, F9, R7)
[27, 44, 24], // DLF (D1, L9, F7)
[33, 53, 42], // DBL (D7, B9, L7)
[35, 17, 51], // DRB (D9, R9, B7)
];
for (let i = 0; i < 8; i++) {
const perm = this.cube.cp[i];
const ori = this.cube.co[i];
// The physical piece at position `i` is `perm`.
// Its colors are cornerColors[perm].
// Because of orientation, the colors are shifted.
// If ori=0, U/D color is on U/D face.
// If ori=1, U/D color is twisted clockwise.
// If ori=2, U/D color is twisted counter-clockwise.
const c0 = cornerColors[perm][(0 - ori + 3) % 3];
const c1 = cornerColors[perm][(1 - ori + 3) % 3];
const c2 = cornerColors[perm][(2 - ori + 3) % 3];
f[cornerFacelets[i][0]] = c0;
f[cornerFacelets[i][1]] = c1;
f[cornerFacelets[i][2]] = c2;
}
const edgeColors = [
["U", "R"], // 0: UR
["U", "F"], // 1: UF
["U", "L"], // 2: UL
["U", "B"], // 3: UB
["D", "R"], // 4: DR
["D", "F"], // 5: DF
["D", "L"], // 6: DL
["D", "B"], // 7: DB
["F", "R"], // 8: FR
["F", "L"], // 9: FL
["B", "L"], // 10: BL
["B", "R"], // 11: BR
];
const edgeFacelets = [
[5, 10], // UR (U6, R2)
[7, 19], // UF (U8, F2)
[3, 37], // UL (U4, L2)
[1, 46], // UB (U2, B2)
[32, 16], // DR (D6, R8)
[28, 25], // DF (D2, F8)
[30, 43], // DL (D4, L8)
[34, 52], // DB (D8, B8)
[23, 12], // FR (F6, R4)
[21, 41], // FL (F4, L6)
[50, 39], // BL (B6, L4)
[48, 14], // BR (B4, R6)
];
for (let i = 0; i < 12; i++) {
const perm = this.cube.ep[i];
const ori = this.cube.eo[i];
const e0 = edgeColors[perm][(0 + ori) % 2];
const e1 = edgeColors[perm][(1 + ori) % 2];
f[edgeFacelets[i][0]] = e0;
f[edgeFacelets[i][1]] = e1;
}
return f.join("");
}
solve() {
const faceletStr = this.toFaceletString();
try {
const cube = Cube.fromString(faceletStr);
if (cube.isSolved()) return [];
const solution = cube.solve();
if (!solution) return [];
return solution.split(" ").filter((m) => m);
} catch (e) {
throw new Error(
`Kociemba Solve Failed: ${e.message} \nFacelet: ${faceletStr}`,
);
}
}
}