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:
@@ -1,8 +1,7 @@
|
||||
import { Cube, FACES, COLORS } from "../src/utils/Cube.js";
|
||||
import assert from "assert";
|
||||
|
||||
import { Cube, FACES, COLORS } from '../src/utils/Cube.js';
|
||||
import assert from 'assert';
|
||||
|
||||
console.log('Running Cube Integrity Tests...');
|
||||
console.log("Running Cube Integrity Tests...");
|
||||
|
||||
const cube = new Cube();
|
||||
|
||||
@@ -15,11 +14,11 @@ const countColors = () => {
|
||||
[COLORS.RED]: 0,
|
||||
[COLORS.GREEN]: 0,
|
||||
[COLORS.BLUE]: 0,
|
||||
[COLORS.BLACK]: 0 // Should be ignored or internal
|
||||
[COLORS.BLACK]: 0, // Should be ignored or internal
|
||||
};
|
||||
|
||||
cube.cubies.forEach(cubie => {
|
||||
Object.values(cubie.faces).forEach(color => {
|
||||
cube.cubies.forEach((cubie) => {
|
||||
Object.values(cubie.faces).forEach((color) => {
|
||||
if (counts[color] !== undefined) {
|
||||
counts[color]++;
|
||||
}
|
||||
@@ -35,13 +34,13 @@ const verifyCounts = (counts) => {
|
||||
// 9 * 6 = 54 colored stickers.
|
||||
// 27 cubies * 6 faces = 162 total faces.
|
||||
// 162 - 54 = 108 black faces (internal).
|
||||
|
||||
assert.strictEqual(counts[COLORS.WHITE], 9, 'White count should be 9');
|
||||
assert.strictEqual(counts[COLORS.YELLOW], 9, 'Yellow count should be 9');
|
||||
assert.strictEqual(counts[COLORS.ORANGE], 9, 'Orange count should be 9');
|
||||
assert.strictEqual(counts[COLORS.RED], 9, 'Red count should be 9');
|
||||
assert.strictEqual(counts[COLORS.GREEN], 9, 'Green count should be 9');
|
||||
assert.strictEqual(counts[COLORS.BLUE], 9, 'Blue count should be 9');
|
||||
|
||||
assert.strictEqual(counts[COLORS.WHITE], 9, "White count should be 9");
|
||||
assert.strictEqual(counts[COLORS.YELLOW], 9, "Yellow count should be 9");
|
||||
assert.strictEqual(counts[COLORS.ORANGE], 9, "Orange count should be 9");
|
||||
assert.strictEqual(counts[COLORS.RED], 9, "Red count should be 9");
|
||||
assert.strictEqual(counts[COLORS.GREEN], 9, "Green count should be 9");
|
||||
assert.strictEqual(counts[COLORS.BLUE], 9, "Blue count should be 9");
|
||||
};
|
||||
|
||||
// Helper: Verify piece integrity
|
||||
@@ -55,19 +54,24 @@ const verifyPieceTypes = () => {
|
||||
let centers = 0;
|
||||
let cores = 0;
|
||||
|
||||
cube.cubies.forEach(cubie => {
|
||||
const coloredFaces = Object.values(cubie.faces).filter(c => c !== COLORS.BLACK).length;
|
||||
cube.cubies.forEach((cubie) => {
|
||||
const coloredFaces = Object.values(cubie.faces).filter(
|
||||
(c) => c !== COLORS.BLACK,
|
||||
).length;
|
||||
if (coloredFaces === 3) corners++;
|
||||
else if (coloredFaces === 2) edges++;
|
||||
else if (coloredFaces === 1) centers++;
|
||||
else if (coloredFaces === 0) cores++;
|
||||
else assert.fail(`Invalid cubie with ${coloredFaces} colors at (${cubie.x},${cubie.y},${cubie.z})`);
|
||||
else
|
||||
assert.fail(
|
||||
`Invalid cubie with ${coloredFaces} colors at (${cubie.x},${cubie.y},${cubie.z})`,
|
||||
);
|
||||
});
|
||||
|
||||
assert.strictEqual(corners, 8, 'Should have 8 corners');
|
||||
assert.strictEqual(edges, 12, 'Should have 12 edges');
|
||||
assert.strictEqual(centers, 6, 'Should have 6 centers');
|
||||
assert.strictEqual(cores, 1, 'Should have 1 core');
|
||||
assert.strictEqual(corners, 8, "Should have 8 corners");
|
||||
assert.strictEqual(edges, 12, "Should have 12 edges");
|
||||
assert.strictEqual(centers, 6, "Should have 6 centers");
|
||||
assert.strictEqual(cores, 1, "Should have 1 core");
|
||||
};
|
||||
|
||||
// Helper: Verify specific relative positions of centers (they never change relative to each other)
|
||||
@@ -75,13 +79,15 @@ const verifyPieceTypes = () => {
|
||||
// Front (Green) opposite Back (Blue)
|
||||
// Left (Orange) opposite Right (Red)
|
||||
const verifyCenters = () => {
|
||||
const centers = cube.cubies.filter(c =>
|
||||
Object.values(c.faces).filter(f => f !== COLORS.BLACK).length === 1
|
||||
const centers = cube.cubies.filter(
|
||||
(c) =>
|
||||
Object.values(c.faces).filter((f) => f !== COLORS.BLACK).length === 1,
|
||||
);
|
||||
|
||||
|
||||
// Find center by color
|
||||
const findCenter = (color) => centers.find(c => Object.values(c.faces).includes(color));
|
||||
|
||||
const findCenter = (color) =>
|
||||
centers.find((c) => Object.values(c.faces).includes(color));
|
||||
|
||||
const white = findCenter(COLORS.WHITE);
|
||||
const yellow = findCenter(COLORS.YELLOW);
|
||||
const green = findCenter(COLORS.GREEN);
|
||||
@@ -92,44 +98,43 @@ const verifyCenters = () => {
|
||||
// Check opposites
|
||||
// Distance between opposites should be 2 (e.g. y=1 and y=-1)
|
||||
// And they should be on same axis
|
||||
|
||||
|
||||
// Note: After rotations, x/y/z coordinates change.
|
||||
// But relative vectors should hold?
|
||||
// Actually, centers DO rotate around the core.
|
||||
// But White is always opposite Yellow.
|
||||
// So vector(White) + vector(Yellow) == (0,0,0).
|
||||
|
||||
|
||||
const checkOpposite = (c1, c2, name) => {
|
||||
assert.strictEqual(c1.x + c2.x, 0, `${name} X mismatch`);
|
||||
assert.strictEqual(c1.y + c2.y, 0, `${name} Y mismatch`);
|
||||
assert.strictEqual(c1.z + c2.z, 0, `${name} Z mismatch`);
|
||||
};
|
||||
|
||||
checkOpposite(white, yellow, 'White-Yellow');
|
||||
checkOpposite(green, blue, 'Green-Blue');
|
||||
checkOpposite(orange, red, 'Orange-Red');
|
||||
checkOpposite(white, yellow, "White-Yellow");
|
||||
checkOpposite(green, blue, "Green-Blue");
|
||||
checkOpposite(orange, red, "Orange-Red");
|
||||
};
|
||||
|
||||
|
||||
// --- Test Execution ---
|
||||
|
||||
// 1. Initial State
|
||||
console.log('Test 1: Initial State Integrity');
|
||||
console.log("Test 1: Initial State Integrity");
|
||||
verifyCounts(countColors());
|
||||
verifyPieceTypes();
|
||||
verifyCenters();
|
||||
console.log('PASS Initial State');
|
||||
console.log("PASS Initial State");
|
||||
|
||||
// 2. Single Rotation (R)
|
||||
console.log('Test 2: Single Rotation (R)');
|
||||
cube.rotateLayer('x', 1, -1); // R
|
||||
console.log("Test 2: Single Rotation (R)");
|
||||
cube.rotateLayer("x", 1, -1); // R
|
||||
verifyCounts(countColors());
|
||||
verifyPieceTypes();
|
||||
verifyCenters();
|
||||
console.log('PASS Single Rotation');
|
||||
console.log("PASS Single Rotation");
|
||||
|
||||
// 3. Multiple Rotations (R U R' U')
|
||||
console.log('Test 3: Sexy Move (R U R\' U\')');
|
||||
console.log("Test 3: Sexy Move (R U R' U')");
|
||||
cube.reset();
|
||||
cube.move("R");
|
||||
cube.move("U");
|
||||
@@ -138,12 +143,12 @@ cube.move("U'");
|
||||
verifyCounts(countColors());
|
||||
verifyPieceTypes();
|
||||
verifyCenters();
|
||||
console.log('PASS Sexy Move');
|
||||
console.log("PASS Sexy Move");
|
||||
|
||||
// 4. Random Rotations (Fuzzing)
|
||||
console.log('Test 4: 100 Random Moves');
|
||||
console.log("Test 4: 100 Random Moves");
|
||||
cube.reset();
|
||||
const axes = ['x', 'y', 'z'];
|
||||
const axes = ["x", "y", "z"];
|
||||
const indices = [-1, 0, 1];
|
||||
const dirs = [1, -1];
|
||||
|
||||
@@ -156,6 +161,6 @@ for (let i = 0; i < 100; i++) {
|
||||
verifyCounts(countColors());
|
||||
verifyPieceTypes();
|
||||
verifyCenters();
|
||||
console.log('PASS 100 Random Moves');
|
||||
console.log("PASS 100 Random Moves");
|
||||
|
||||
console.log('ALL INTEGRITY TESTS PASSED');
|
||||
console.log("ALL INTEGRITY TESTS PASSED");
|
||||
|
||||
Reference in New Issue
Block a user