rubiks-cube-solver
Version:
Outputs a solution using the Fridrich Method for a given cube state.
203 lines (163 loc) • 6.7 kB
JavaScript
import { BaseSolver } from '../BaseSolver';
import {
getFaceOfMove, getRotationFromTo, getDirectionFromFaces
} from '../../utils';
const SOLVED_STATE = '0 0 0 0 0 0 0 0';
class PLLSolver extends BaseSolver {
constructor(...args) {
super(...args);
this.phase = 'pll';
// permutations in order based on http://badmephisto.com/pll.php, however
// the actual algorithms may be different.
this.algorithms = {
[SOLVED_STATE]: '', // already solved
'2 -1 1 -1 1 0 0 2': 'R2 F2 RPrime BPrime R F2 RPrime B RPrime', // #1
'-1 1 -1 2 2 0 0 1': 'R BPrime R F2 RPrime B R F2 R2', // #2
'1 -1 2 2 0 0 1 -1': 'R UPrime R U R U R UPrime RPrime UPrime R2', // #3
'-1 1 -1 1 0 0 2 2': 'R2 U R U RPrime UPrime RPrime UPrime RPrime U RPrime', // #4
'2 2 2 2 2 2 2 2': 'M M U M M U2 M M U M M', // #5
'0 1 1 1 1 0 2 2': 'R U RPrime UPrime RPrime F R2 UPrime RPrime UPrime R U RPrime FPrime', // #6
'1 0 2 0 1 0 0 0': 'R U2 RPrime UPrime R U2 LPrime U RPrime UPrime L', // #7
'0 2 2 0 1 1 1 1': 'F R UPrime RPrime UPrime R U RPrime FPrime R U RPrime UPrime RPrime F R FPrime', // #8
'1 -1 -1 2 -1 -1 1 0': 'RPrime U2 R U2 RPrime F R U RPrime UPrime RPrime FPrime R2', // #9
'0 1 -1 -1 2 -1 -1 1': 'R UPrime RPrime UPrime R U R D RPrime UPrime R DPrime RPrime U2 RPrime', // #10
'0 2 -1 -1 -1 -1 2 0': 'RPrime U RPrime UPrime BPrime D BPrime DPrime B2 RPrime BPrime R B R', // #11
'2 -1 -1 -1 -1 2 0 0': 'RPrime UPrime FPrime R U RPrime UPrime RPrime F R2 UPrime RPrime UPrime R U RPrime U R', // #12
'-1 2 2 2 -1 2 0 2': 'L U LPrime B2 uPrime B UPrime BPrime U BPrime u B2', // #13
'2 -1 2 0 2 -1 2 2': 'RPrime UPrime R B2 u BPrime U B UPrime B uPrime B2', // #14
'2 -1 1 1 0 1 1 -1': 'R2 uPrime R UPrime R U RPrime u R2 B UPrime BPrime', // #15
'1 0 1 1 -1 2 -1 1': 'R2 u RPrime U RPrime UPrime R uPrime R2 FPrime U F', // #16
'1 -1 -1 1 1 -1 -1 1': 'U RPrime UPrime R UPrime R U R UPrime RPrime U R U R2 UPrime RPrime', // #17
'0 1 0 0 0 1 0 2': 'LPrime U2 L U LPrime U2 R UPrime L U RPrime', // #18
'1 1 -1 -1 1 1 -1 -1': 'R BPrime RPrime F R B RPrime FPrime R B RPrime F R BPrime RPrime FPrime', // #19
'2 0 2 0 2 0 2 0': 'R U RPrime U R U RPrime FPrime R U RPrime UPrime RPrime F R2 UPrime RPrime U2 R UPrime RPrime', // #20
'0 2 0 2 0 2 0 2': 'RPrime U R UPrime RPrime FPrime UPrime F R U RPrime F RPrime FPrime R UPrime R', // #21
};
}
solve() {
return this._solve();
}
_getCaseNumber() {
return this.getPllString();
}
_solveCase(pllString) {
let pattern = this.findPattern(pllString);
let algorithm = this.getAlgorithm(pattern);
let frontFace = this._getFrontFace(pllString, pattern);
this.move(algorithm, {
orientation: { up: 'down', front: frontFace }
});
// may need an extra rotation of DOWN for a complete solve
let cubie = this.cube.getCubie(['down', 'front']); // any cubie on DOWN
let origin = 'front';
let target = getFaceOfMove(cubie.getColorOfFace(origin));
let lastLayerMove = getRotationFromTo('down', origin, target);
this.move(lastLayerMove);
}
isSolved() {
return this.cube.isSolved();
}
/**
* Permutations are unique in the way that each cubie is permutated relative
* to the one adjacent to it. For each cubie (in order), find the relative
* direction from its color to the next cubie's color, and turn it into a
* number. This will allow each permutation to be held in a unique string.
*/
getPllString() {
let pllString = [];
let pllCubies = this._getPllCubies();
let faces = ['front', 'left', 'back', 'right']; // we're upside down
for (let i = 0; i < pllCubies.length; i++) {
let cubie1 = pllCubies[i];
let cubie2 = pllCubies[i + 1];
let faceToCheck = faces[~~(i / 2)];
// get the colors of the two cubies
let color1 = cubie1.getColorOfFace(faceToCheck);
// wrap around to the first cubie
if (!cubie2) {
cubie2 = pllCubies[0];
}
let color2 = cubie2.getColorOfFace(faceToCheck);
// find the direction between the two
let face1 = getFaceOfMove(color1);
let face2 = getFaceOfMove(color2);
// turn it into a number
let direction = getDirectionFromFaces(face1, face2, { up: 'down' });
if (direction === 'front') direction = 0;
if (direction === 'right') direction = 1;
if (direction === 'left') direction = -1;
if (direction === 'back') direction = 2;
pllString.push(direction);
}
return pllString.join(' ');
}
findPattern(pllString) {
let initialString = pllString;
if (typeof pllString === 'undefined') {
pllString = this.getPllString();
}
for (let i = 0; i < 4; i++) {
let algorithm = this.algorithms[pllString];
if (typeof algorithm === 'string') {
return pllString;
} else {
pllString = this._rotatePllStringLeft(pllString);
}
}
throw new Error(`No pattern found for pll string "${initialString}"`);
}
getAlgorithm(pattern) {
if (typeof pattern === 'undefined') {
pattern = this.findPattern(pattern); // pattern can be a pllString
}
if (typeof this.algorithms[pattern] === 'undefined') {
throw new Error(`No algorithm found for pattern "${pattern}"`);
}
return this.algorithms[pattern];
}
_getPllCubies() {
let positions = [
['front', 'down', 'right'],
['front', 'down'],
['front', 'down', 'left'],
['left', 'down'],
['left', 'down', 'back'],
['back', 'down'],
['back', 'down', 'right'],
['right', 'down']
];
return positions.map(pos => this.cube.getCubie(pos));
}
_getCubiePermutation(cubie) {
// pick a face, any face (expect for the down face)
let face = cubie.faces().find(face => face !== 'down');
// get the cube face this face lies on
let cubeFace = getFaceOfMove(cubie.getColorOfFace(face));
// find the move that will permute the cubie correctly
let moveToSolveCubie = getRotationFromTo('down', face, cubeFace);
moveToSolveCubie = moveToSolveCubie.toLowerCase();
// translate the move to a number
let dir;
if (moveToSolveCubie === '') dir = 0;
else if (moveToSolveCubie.includes('prime')) dir = 1;
else if (moveToSolveCubie.split(' ').length > 1) dir = 2;
else dir = -1;
return dir;
}
_rotatePllStringLeft(pllString) {
let arr = pllString.split(' ').map(num => parseInt(num));
return [...arr.slice(2), ...arr.slice(0, 2)].join(' ');
}
_getFrontFace(pllString, pattern) {
let rotationOrder = ['front', 'left', 'back', 'right'];
for (let i = 0; i < 4; i++) {
if (pllString === pattern) {
return rotationOrder[i];
} else {
pllString = this._rotatePllStringLeft(pllString);
}
}
throw new Error(`OLL string "${pllString}" does not resolve to the pattern "${pattern}"`);
}
}
export { PLLSolver };