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.
167 lines
4.9 KiB
JavaScript
167 lines
4.9 KiB
JavaScript
import { Cube, FACES, COLORS } from "../src/utils/Cube.js";
|
|
import assert from "assert";
|
|
|
|
console.log("Running Cube Integrity Tests...");
|
|
|
|
const cube = new Cube();
|
|
|
|
// Helper: Count colors on all faces
|
|
const countColors = () => {
|
|
const counts = {
|
|
[COLORS.WHITE]: 0,
|
|
[COLORS.YELLOW]: 0,
|
|
[COLORS.ORANGE]: 0,
|
|
[COLORS.RED]: 0,
|
|
[COLORS.GREEN]: 0,
|
|
[COLORS.BLUE]: 0,
|
|
[COLORS.BLACK]: 0, // Should be ignored or internal
|
|
};
|
|
|
|
cube.cubies.forEach((cubie) => {
|
|
Object.values(cubie.faces).forEach((color) => {
|
|
if (counts[color] !== undefined) {
|
|
counts[color]++;
|
|
}
|
|
});
|
|
});
|
|
|
|
return counts;
|
|
};
|
|
|
|
// Helper: Verify solved state counts
|
|
const verifyCounts = (counts) => {
|
|
// Each face has 9 stickers. 6 faces.
|
|
// 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");
|
|
};
|
|
|
|
// Helper: Verify piece integrity
|
|
// Corners: 8 corners, each has 3 colors.
|
|
// Edges: 12 edges, each has 2 colors.
|
|
// Centers: 6 centers, each has 1 color.
|
|
// Core: 1 core, 0 colors.
|
|
const verifyPieceTypes = () => {
|
|
let corners = 0;
|
|
let edges = 0;
|
|
let centers = 0;
|
|
let cores = 0;
|
|
|
|
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})`,
|
|
);
|
|
});
|
|
|
|
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)
|
|
// Up (White) opposite Down (Yellow)
|
|
// 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,
|
|
);
|
|
|
|
// Find center by 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);
|
|
const blue = findCenter(COLORS.BLUE);
|
|
const orange = findCenter(COLORS.ORANGE);
|
|
const red = findCenter(COLORS.RED);
|
|
|
|
// 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");
|
|
};
|
|
|
|
// --- Test Execution ---
|
|
|
|
// 1. Initial State
|
|
console.log("Test 1: Initial State Integrity");
|
|
verifyCounts(countColors());
|
|
verifyPieceTypes();
|
|
verifyCenters();
|
|
console.log("PASS Initial State");
|
|
|
|
// 2. Single Rotation (R)
|
|
console.log("Test 2: Single Rotation (R)");
|
|
cube.rotateLayer("x", 1, -1); // R
|
|
verifyCounts(countColors());
|
|
verifyPieceTypes();
|
|
verifyCenters();
|
|
console.log("PASS Single Rotation");
|
|
|
|
// 3. Multiple Rotations (R U R' U')
|
|
console.log("Test 3: Sexy Move (R U R' U')");
|
|
cube.reset();
|
|
cube.move("R");
|
|
cube.move("U");
|
|
cube.move("R'");
|
|
cube.move("U'");
|
|
verifyCounts(countColors());
|
|
verifyPieceTypes();
|
|
verifyCenters();
|
|
console.log("PASS Sexy Move");
|
|
|
|
// 4. Random Rotations (Fuzzing)
|
|
console.log("Test 4: 100 Random Moves");
|
|
cube.reset();
|
|
const axes = ["x", "y", "z"];
|
|
const indices = [-1, 0, 1];
|
|
const dirs = [1, -1];
|
|
|
|
for (let i = 0; i < 100; i++) {
|
|
const axis = axes[Math.floor(Math.random() * axes.length)];
|
|
const index = indices[Math.floor(Math.random() * indices.length)];
|
|
const dir = dirs[Math.floor(Math.random() * dirs.length)];
|
|
cube.rotateLayer(axis, index, dir);
|
|
}
|
|
verifyCounts(countColors());
|
|
verifyPieceTypes();
|
|
verifyCenters();
|
|
console.log("PASS 100 Random Moves");
|
|
|
|
console.log("ALL INTEGRITY TESTS PASSED");
|