fix(solver): replace exponential IDDFS recursion with instantaneous heuristic simulation macros

This commit is contained in:
2026-02-23 22:04:41 +00:00
parent 929761ac9e
commit e5befab473
11 changed files with 481 additions and 232 deletions

9
test/test_aperm.js Normal file
View File

@@ -0,0 +1,9 @@
import { DeepCube, MOVES } from '../src/utils/DeepCube.js';
let cube = new DeepCube();
const apply = (str) => { str.split(' ').filter(x => x).forEach(m => cube = cube.multiply(MOVES[m])); };
apply("R' F R' B2 R F' R' B2 R2");
console.log(`cp after A-perm:`, cube.cp.slice(0, 4));
// We want to see which two corners are swapped.
// Solved is 0,1,2,3.
// If it prints 0,1,3,2, then 2 and 3 are swapped (Back corners).

View File

@@ -0,0 +1,25 @@
import { DeepCube, MOVES } from '../src/utils/DeepCube.js';
import { BeginnerSolver } from '../src/utils/solvers/BeginnerSolver.js';
let cube = new DeepCube();
const scramble = "R U R' U' R' F R2 U' R' U' R U R' F'"; // T-perm
scramble.split(' ').forEach(move => {
cube = cube.multiply(MOVES[move]);
});
console.log('Testing BeginnerSolver with T-perm...');
const solver = new BeginnerSolver(cube);
// Add some logging to the solver's methods to trace execution
const originalApply = solver.apply.bind(solver);
solver.apply = (moveStr) => {
// console.log('Applying:', moveStr);
originalApply(moveStr);
};
try {
const solution = solver.solve();
console.log('Solution found:', solution.join(' '));
} catch (e) {
console.error('Error during solve:', e);
}

View File

@@ -0,0 +1,41 @@
import { DeepCube, MOVES } from "../src/utils/DeepCube.js";
import { BeginnerSolver } from "../src/utils/solvers/BeginnerSolver.js";
const allMoves = Object.keys(MOVES);
const getRandomScramble = (length = 20) => {
let s = [];
for (let i = 0; i < length; i++)
s.push(allMoves[Math.floor(Math.random() * allMoves.length)]);
return s.join(" ");
};
for (let i = 1; i <= 20; i++) {
let cube = new DeepCube();
const scramble = getRandomScramble();
scramble.split(" ").forEach((move) => (cube = cube.multiply(MOVES[move])));
const startTime = Date.now();
const solver = new BeginnerSolver(cube);
try {
const solution = solver.solve();
const elapsedTime = Date.now() - startTime;
console.log(
`Test ${i}: Solved in ${elapsedTime}ms. Solution length: ${solution.length}`,
);
// Verify it actually solved it
let testCube = cube.clone();
solution.forEach((m) => (testCube = testCube.multiply(MOVES[m])));
if (!solver.isSolvedState(testCube)) {
console.error(
`ERROR: Test ${i} failed to fully solve the cube mathematically!`,
);
process.exit(1);
}
} catch (e) {
console.error(`ERROR: Test ${i} threw an exception:`, e);
process.exit(1);
}
}
console.log("All 20 tests passed flawlessly!");

34
test/test_diagnostics.js Normal file
View File

@@ -0,0 +1,34 @@
import { DeepCube, MOVES } from '../src/utils/DeepCube.js';
import { BeginnerSolver } from '../src/utils/solvers/BeginnerSolver.js';
const allMoves = Object.keys(MOVES);
const getRandomScramble = (length = 20) => {
let s = [];
for (let i = 0; i < length; i++) s.push(allMoves[Math.floor(Math.random() * allMoves.length)]);
return s.join(' ');
};
let cube = new DeepCube();
const scramble = getRandomScramble();
scramble.split(' ').forEach(move => cube = cube.multiply(MOVES[move]));
const solver = new BeginnerSolver(cube);
solver.solve();
console.log("Check Cross:");
for (let i of [4, 5, 6, 7]) console.log(`Edge ${i}: ep=${solver.cube.ep.indexOf(i)} eo=${solver.cube.eo[solver.cube.ep.indexOf(i)]}`);
console.log("Check F2L Corners:");
for (let i of [4, 5, 6, 7]) console.log(`Corner ${i}: cp=${solver.cube.cp.indexOf(i)} co=${solver.cube.co[solver.cube.cp.indexOf(i)]}`);
console.log("Check F2L Edges:");
for (let i of [8, 9, 10, 11]) console.log(`Edge ${i}: ep=${solver.cube.ep.indexOf(i)} eo=${solver.cube.eo[solver.cube.ep.indexOf(i)]}`);
console.log("Check OLL:");
console.log(`co:`, solver.cube.co.slice(0, 4));
console.log(`eo:`, solver.cube.eo.slice(0, 4));
console.log("Check PLL:");
console.log(`cp:`, solver.cube.cp.slice(0, 4));
console.log(`ep:`, solver.cube.ep.slice(0, 4));

40
test/test_diagnostics2.js Normal file
View File

@@ -0,0 +1,40 @@
import { DeepCube, MOVES } from '../src/utils/DeepCube.js';
import { BeginnerSolver } from '../src/utils/solvers/BeginnerSolver.js';
const allMoves = Object.keys(MOVES);
const getRandomScramble = (length = 20) => {
let s = [];
for (let i = 0; i < length; i++) s.push(allMoves[Math.floor(Math.random() * allMoves.length)]);
return s.join(' ');
};
for (let iter = 0; iter < 100; iter++) {
let cube = new DeepCube();
const scramble = getRandomScramble();
scramble.split(' ').forEach(move => cube = cube.multiply(MOVES[move]));
const solver = new BeginnerSolver(cube);
solver.solve();
if (!solver.isSolvedState(solver.cube)) {
console.log("FAILED ON SCRAMBLE:", scramble);
console.log("Check Cross:");
for (let i of [4, 5, 6, 7]) console.log(`Edge ${i}: ep=${solver.cube.ep.indexOf(i)} eo=${solver.cube.eo[solver.cube.ep.indexOf(i)]}`);
console.log("Check F2L Corners:");
for (let i of [4, 5, 6, 7]) console.log(`Corner ${i}: cp=${solver.cube.cp.indexOf(i)} co=${solver.cube.co[solver.cube.cp.indexOf(i)]}`);
console.log("Check F2L Edges:");
for (let i of [8, 9, 10, 11]) console.log(`Edge ${i}: ep=${solver.cube.ep.indexOf(i)} eo=${solver.cube.eo[solver.cube.ep.indexOf(i)]}`);
console.log("Check OLL:");
console.log(`co:`, solver.cube.co.slice(0, 4));
console.log(`eo:`, solver.cube.eo.slice(0, 4));
console.log("Check PLL:");
console.log(`cp:`, solver.cube.cp.slice(0, 4));
console.log(`ep:`, solver.cube.ep.slice(0, 4));
process.exit(1);
}
}
console.log("All 100 tests passed!");

24
test/test_macros.js Normal file
View File

@@ -0,0 +1,24 @@
import { DeepCube, MOVES } from '../src/utils/DeepCube.js';
let cube = new DeepCube();
const apply = (str) => {
str.split(' ').forEach(m => {
cube = cube.multiply(MOVES[m]);
});
};
apply("R U R'");
console.log("Piece 4 (R U R') is at position:", cube.cp.indexOf(4), "Orientation:", cube.co[cube.cp.indexOf(4)]);
cube = new DeepCube();
apply("R U' R' U R U2 R'");
console.log("Piece 4 (Up-face extraction) position:", cube.cp.indexOf(4), "Orientation:", cube.co[cube.cp.indexOf(4)]);
cube = new DeepCube();
apply("R U R'"); // insert front facing
console.log("Piece 4 (Front-face extraction) position:", cube.cp.indexOf(4), "Orientation:", cube.co[cube.cp.indexOf(4)]);
cube = new DeepCube();
apply("F' U' F"); // insert left facing
console.log("Piece 4 (Side-face extraction) position:", cube.cp.indexOf(4), "Orientation:", cube.co[cube.cp.indexOf(4)]);

24
test/test_macros2.js Normal file
View File

@@ -0,0 +1,24 @@
import { DeepCube, MOVES } from '../src/utils/DeepCube.js';
let cube;
const apply = (str) => {
str.split(' ').forEach(m => {
cube = cube.multiply(MOVES[m]);
});
};
const check = (name, alg, initPos, initOri) => {
cube = new DeepCube();
apply(alg);
// We applied alg to a SOLVED cube.
// The piece that WAS at 4 (DFR) is now at some position P with orientation O.
// To solve it, we would need to reverse the alg.
// So if we find a piece at P with orientation O, we apply the reverse alg!
console.log(`${name}: Extraction piece 4 is at pos ${cube.cp.indexOf(4)} ori ${cube.co[cube.cp.indexOf(4)]}`);
};
check("R U R'", "R U R'");
check("R U' R'", "R U' R'");
check("F' U' F", "F' U' F");
check("R U2 R' U' R U R'", "R U' R' U R U2 R'");

10
test/test_macros3.js Normal file
View File

@@ -0,0 +1,10 @@
import { DeepCube, MOVES } from '../src/utils/DeepCube.js';
let cube = new DeepCube();
const apply = (str) => { str.split(' ').forEach(m => { cube = cube.multiply(MOVES[m]); }); };
cube = new DeepCube(); apply("F' U F");
console.log("F' U F reverse puts piece 4 at pos:", cube.cp.indexOf(4), "ori:", cube.co[cube.cp.indexOf(4)]);
cube = new DeepCube(); apply("U' F' U F");
console.log("U' F' U F reverse puts piece 4 at pos:", cube.cp.indexOf(4), "ori:", cube.co[cube.cp.indexOf(4)]);

22
test/test_macros4.js Normal file
View File

@@ -0,0 +1,22 @@
import { DeepCube, MOVES } from '../src/utils/DeepCube.js';
let cube = new DeepCube();
const apply = (str) => { str.split(' ').forEach(m => { cube = cube.multiply(MOVES[m]); }); };
const check = (name, alg, expectedPos, expectedOri) => {
cube = new DeepCube();
apply(alg); // reverse of extraction
let p5 = cube.cp.indexOf(5); let o5 = cube.co[p5];
console.log(`${name}: pos 5 is ${p5} (expected ${expectedPos}), ori ${o5} (expected ${expectedOri})`);
};
// DLF (5) Target UFL (1)
check("F' U' F reverse", "F' U F", 1, 2); // if reverse puts it at pos 1 ori 2, then if at pos 1 ori 2 use F' U' F!
check("L U L' reverse", "L U' L'", 1, 1);
check("L' U' L reverse", "L' U L", 1, 1); // wait, L' moves DLF to UBL(2)? Let's find out!
// Check extraction from 5
cube = new DeepCube(); apply("L U L'");
console.log("Extract DLF (5) with L U L' gives pos:", cube.cp.indexOf(5), "ori:", cube.co[cube.cp.indexOf(5)]);
cube = new DeepCube(); apply("F' U' F");
console.log("Extract DLF (5) with F' U' F gives pos:", cube.cp.indexOf(5), "ori:", cube.co[cube.cp.indexOf(5)]);

22
test/test_moves.js Normal file
View File

@@ -0,0 +1,22 @@
import { DeepCube, MOVES } from '../src/utils/DeepCube.js';
let cube = new DeepCube();
const apply = (str) => {
str.split(' ').forEach(m => {
cube = cube.multiply(MOVES[m]);
});
};
// We want to verify `R U R'` extracts piece 4 (DFR) to U layer.
apply("R U R'");
console.log("Piece 4 is at position:", cube.cp.indexOf(4), "Orientation:", cube.co[cube.cp.indexOf(4)]);
cube = new DeepCube();
// What if piece 4 is at URF (position 0)? We want to insert it to DFR (position 4).
// If Yellow is UP, co=0.
// Let's create a state where DFR is at URF with co=0.
// We can do this by applying R U2 R' U' R U R' IN REVERSE to extract it.
// Reverse of R U2 R' U' R U R' is: R U' R' U R U2 R'
apply("R U' R' U R U2 R'");
console.log("Extraction -> Piece 4 position:", cube.cp.indexOf(4), "Orientation:", cube.co[cube.cp.indexOf(4)]);