rubiks-cube-solver
Version:
Outputs a solution using the Fridrich Method for a given cube state.
1,753 lines (1,441 loc) • 131 kB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["rubiksCubeSolver"] = factory();
else
root["rubiksCubeSolver"] = factory();
})(this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // identity function for calling harmony imports with the correct context
/******/ __webpack_require__.i = function(value) { return value; };
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 33);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_gl_vec3_cross__ = __webpack_require__(12);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_gl_vec3_cross___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_gl_vec3_cross__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__models_Face__ = __webpack_require__(14);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__models_Vector__ = __webpack_require__(4);
// maps each face with the notation for their middle moves
const _middlesMatchingFace = {
f: 's',
r: 'mprime',
u: 'eprime',
d: 'e',
l: 'm',
b: 'sprime'
};
/**
* @param {string} move - The notation of a move, e.g. rPrime.
* @return {string}
*/
const getFaceOfMove = (move) => {
if (typeof move !== 'string') {
throw new TypeError('move must be a string');
}
let faceLetter = move[0].toLowerCase();
if (faceLetter === 'f') return 'front';
if (faceLetter === 'r') return 'right';
if (faceLetter === 'u') return 'up';
if (faceLetter === 'd') return 'down';
if (faceLetter === 'l') return 'left';
if (faceLetter === 'b') return 'back';
};
/* harmony export (immutable) */ __webpack_exports__["a"] = getFaceOfMove;
/**
* Almost useless. Almost.
* @param {string} face - The string identifying a face.
* @return {string}
*/
const getMoveOfFace = (face) => {
if (typeof face !== 'string') {
throw new TypeError('face must be a string');
}
face = face.toLowerCase();
if (!['front', 'right', 'up', 'down', 'left', 'back'].includes(face)) {
throw new Error(`${face} is not valid face`);
}
return face[0];
};
/* harmony export (immutable) */ __webpack_exports__["f"] = getMoveOfFace;
const getMiddleMatchingFace = (face) => {
face = face.toLowerCase()[0];
return _middlesMatchingFace[face];
};
/* harmony export (immutable) */ __webpack_exports__["g"] = getMiddleMatchingFace;
const getFaceMatchingMiddle = (middle) => {
middle = middle.toLowerCase();
for (let face of Object.keys(_middlesMatchingFace)) {
let testMiddle = _middlesMatchingFace[face];
if (middle === testMiddle) {
return face;
}
}
};
/* unused harmony export getFaceMatchingMiddle */
/**
* @param {string|array} notations - The move notation.
* @param {object} options - Move options.
* @prop {boolean} options.upperCase - Turn all moves to upper case (i.e. no "double" moves).
*
* @return {string|array} -- whichever was initially given.
*/
const transformNotations = (notations, options = {}) => {
let normalized = normalizeNotations(notations);
if (options.upperCase) {
normalized = normalized.map(n => n[0].toUpperCase() + n.slice(1));
}
if (options.orientation) {
normalized = orientMoves(normalized, options.orientation);
}
if (options.reverse) {
normalized = _reverseNotations(normalized);
}
return typeof notations === 'string' ? normalized.join(' ') : normalized;
};
/* harmony export (immutable) */ __webpack_exports__["d"] = transformNotations;
/**
* @param {array|string} notations - The notations to noramlize.
* @return {array}
*/
const normalizeNotations = (notations) => {
if (typeof notations === 'string') {
notations = notations.split(' ');
}
notations = notations.filter(notation => notation !== '');
return notations.map(notation => {
let isPrime = notation.toLowerCase().includes('prime');
let isDouble = notation.includes('2');
notation = notation[0];
if (isDouble) notation = notation[0] + '2';
else if (isPrime) notation = notation + 'prime';
return notation;
});
};
/* harmony export (immutable) */ __webpack_exports__["h"] = normalizeNotations;
/**
* Finds the direction from an origin face to a target face. The origin face
* will be oriented so that it becomes FRONT. An orientation object must be
* provided that specifies any of these faces (exclusively): TOP, RIGHT, DOWN,
* LEFT.
* If FRONT or BACK is provided along with one of those faces, it will be
* ignored. If FRONT or BACK is the only face provided, the orientation is
* ambiguous and an error will be thrown.
*
* Example:
* getDirectionFromFaces('back', 'up', { down: 'right' })
* Step 1) orient the BACK face so that it becomes FRONT.
* Step 2) orient the DOWN face so that it becomes RIGHT.
* Step 3) Find the direction from BACK (now FRONT) to UP (now LEFT).
* Step 4) Returns 'left'.
*
* @param {string} origin - The origin face.
* @param {string} target - The target face.
* @param {object} orientation - The object that specifies the cube orientation.
* @return {string|number}
*/
const getDirectionFromFaces = (origin, target, orientation) => {
orientation = _toLowerCase(orientation);
orientation = _prepOrientationForDirection(orientation, origin);
let fromFace = new __WEBPACK_IMPORTED_MODULE_1__models_Face__["a" /* Face */](origin);
let toFace = new __WEBPACK_IMPORTED_MODULE_1__models_Face__["a" /* Face */](target);
let rotations = _getRotationsForOrientation(orientation);
_rotateFacesByRotations([fromFace, toFace], rotations);
let axis = new __WEBPACK_IMPORTED_MODULE_2__models_Vector__["a" /* Vector */](__WEBPACK_IMPORTED_MODULE_0_gl_vec3_cross___default()([], fromFace.normal(), toFace.normal())).getAxis();
let direction = __WEBPACK_IMPORTED_MODULE_2__models_Vector__["a" /* Vector */].getAngle(fromFace.normal(), toFace.normal());
if (axis === 'x' && direction > 0) return 'down';
if (axis === 'x' && direction < 0) return 'up';
if (axis === 'y' && direction > 0) return 'right';
if (axis === 'y' && direction < 0) return 'left';
if (direction === 0) {
return 'front';
} else if (direction === Math.PI) {
return 'back';
}
};
/* harmony export (immutable) */ __webpack_exports__["c"] = getDirectionFromFaces;
/**
* See `getDirectionFromFaces`. Almost identical, but instead of finding a
* direction from an origin face and target face, this finds a target face from
* an origin face and direction.
* @param {string} origin - The origin face.
* @param {string} direction - The direction.
* @param {object} orientation - The orientation object.
* @return {string}
*/
const getFaceFromDirection = (origin, direction, orientation) => {
orientation = _toLowerCase(orientation);
orientation = _prepOrientationForDirection(orientation, origin);
let fromFace = new __WEBPACK_IMPORTED_MODULE_1__models_Face__["a" /* Face */](origin);
let rotations = _getRotationsForOrientation(orientation);
_rotateFacesByRotations([fromFace], rotations);
let directionFace = new __WEBPACK_IMPORTED_MODULE_1__models_Face__["a" /* Face */](direction);
let { axis, angle } = __WEBPACK_IMPORTED_MODULE_2__models_Vector__["a" /* Vector */].getRotationFromNormals(fromFace.normal(), directionFace.normal());
fromFace.rotate(axis, angle);
// at this point fromFace is now the target face, but we still need to revert
// the orientation to return the correct string
let reversedRotations = rotations.map(rotation => __WEBPACK_IMPORTED_MODULE_2__models_Vector__["a" /* Vector */].reverseRotation(rotation)).reverse();
_rotateFacesByRotations([fromFace], reversedRotations);
return fromFace.toString();
};
/* harmony export (immutable) */ __webpack_exports__["e"] = getFaceFromDirection;
/**
* Finds a move that rotates the given face around its normal, by the angle
* described by normal1 -> normal2.
* @param {string} face - The face to rotate.
* @param {string} from - The origin face.
* @param {string} to - The target face.
* @return {string}
*/
const getRotationFromTo = (face, from, to) => {
const rotationFace = new __WEBPACK_IMPORTED_MODULE_1__models_Face__["a" /* Face */](face);
const fromFace = new __WEBPACK_IMPORTED_MODULE_1__models_Face__["a" /* Face */](from);
const toFace = new __WEBPACK_IMPORTED_MODULE_1__models_Face__["a" /* Face */](to);
let rotationAxis = rotationFace.vector.getAxis();
let [fromAxis, toAxis] = [fromFace.vector.getAxis(), toFace.vector.getAxis()];
if ([fromAxis.toLowerCase(), toAxis.toLowerCase()].includes(rotationAxis.toLowerCase())) {
throw new Error(`moving ${rotationFace} from ${fromFace} to ${toFace} is not possible.`);
}
let move = getMoveOfFace(face).toUpperCase();
let angle = __WEBPACK_IMPORTED_MODULE_2__models_Vector__["a" /* Vector */].getAngle(fromFace.normal(), toFace.normal());
if (rotationFace.vector.getMagnitude() < 0) {
angle *= -1;
}
if (angle === 0) {
return '';
} else if (Math.abs(angle) === Math.PI) {
return `${move} ${move}`;
} else if (angle < 0) {
return `${move}`;
} else if (angle > 0) {
return `${move}Prime`;
}
};
/* harmony export (immutable) */ __webpack_exports__["b"] = getRotationFromTo;
/**
* Returns an array of transformed notations so that if done when the cube's
* orientation is default (FRONT face is FRONT, RIGHT face is RIGHT, etc.), the
* moves will have the same effect as performing the given notations on a cube
* oriented by the specified orientation.
*
* Examples:
* orientMoves(['R', 'U'], { front: 'front', up: 'up' }) === ['R', 'U']
* orientMoves(['R', 'U'], { front: 'front', down: 'right' }) === ['U', 'L']
* orientMoves(['R', 'U', 'LPrime', 'D'], { up: 'back', right: 'down' }) === ['D', 'B', 'UPrime', 'F']
*
* @param {array} notations - An array of notation strings.
* @param {object} orientation - The orientation object.
*/
const orientMoves = (notations, orientation) => {
orientation = _toLowerCase(orientation);
let rotations = _getRotationsForOrientation(orientation);
rotations.reverse().map(rotation => __WEBPACK_IMPORTED_MODULE_2__models_Vector__["a" /* Vector */].reverseRotation(rotation));
return notations.map(notation => {
let isPrime = notation.toLowerCase().includes('prime');
let isDouble = notation.includes('2');
let isWithMiddle = notation[0] === notation[0].toLowerCase();
let isMiddle = ['m', 'e', 's'].includes(notation[0].toLowerCase());
if (isDouble) {
notation = notation.replace('2', '');
}
let face;
if (isMiddle) {
let faceStr = getFaceOfMove(getFaceMatchingMiddle(notation));
face = new __WEBPACK_IMPORTED_MODULE_1__models_Face__["a" /* Face */](faceStr);
} else {
let faceStr = getFaceOfMove(notation[0]);
face = new __WEBPACK_IMPORTED_MODULE_1__models_Face__["a" /* Face */](faceStr);
}
_rotateFacesByRotations([face], rotations);
let newNotation; // this will always be lower case
if (isMiddle) {
newNotation = getMiddleMatchingFace(face.toString());
} else {
newNotation = face.toString()[0];
}
if (!isWithMiddle) newNotation = newNotation.toUpperCase();
if (isDouble) newNotation = newNotation + '2';
if (isPrime && !isMiddle) newNotation += 'prime';
return newNotation;
});
};
/* unused harmony export orientMoves */
//-----------------
// Helper functions
//-----------------
/**
* Returns an object with all keys and values lowercased. Assumes all keys and
* values are strings.
* @param {object} object - The object to map.
*/
function _toLowerCase(object) {
let ret = {};
Object.keys(object).forEach(key => {
ret[key.toLowerCase()] = object[key].toLowerCase();
});
return ret;
}
/**
* This function is specificly for `getDirectionFromFaces` and
* `getFaceFromDirection`. It removes all keys that are either 'front' or 'back'
* and sets the given front face to orientation.front.
* @param {object} orientation - The orientation object.
* @param {string} front - The face to set as front.
*/
function _prepOrientationForDirection(orientation, front) {
let keys = Object.keys(orientation);
if (keys.length <= 1 && ['front', 'back'].includes(keys[0])) {
throw new Error(`Orientation object "${orientation}" is ambiguous. Please specify one of these faces: "up", "right", "down", "left"`);
}
// remove "front" and "back" from provided orientation object
let temp = orientation;
orientation = {};
keys.forEach(key => {
if (['front', 'back'].includes(key)) {
return;
}
orientation[key] = temp[key];
});
orientation.front = front.toLowerCase();
return orientation;
}
/**
* @param {object} orientation - The orientation object.
* @return {array}
*/
function _getRotationsForOrientation(orientation) {
if (Object.keys(orientation) <= 1) {
throw new Error(`Orientation object "${orientation}" is ambiguous. Please specify 2 faces.`);
}
let keys = Object.keys(orientation);
let origins = keys.map(key => new __WEBPACK_IMPORTED_MODULE_1__models_Face__["a" /* Face */](orientation[key]));
let targets = keys.map(key => new __WEBPACK_IMPORTED_MODULE_1__models_Face__["a" /* Face */](key));
// perform the first rotation, and save it
let rotation1 = __WEBPACK_IMPORTED_MODULE_2__models_Vector__["a" /* Vector */].getRotationFromNormals(
origins[0].normal(),
origins[0].orientTo(targets[0]).normal()
);
// perform the first rotation on the second origin face
origins[1].rotate(rotation1.axis, rotation1.angle);
// peform the second rotation, and save it
let rotation2 = __WEBPACK_IMPORTED_MODULE_2__models_Vector__["a" /* Vector */].getRotationFromNormals(
origins[1].normal(),
origins[1].orientTo(targets[1]).normal()
);
// if the rotation angle is PI, there are 3 possible axes that can perform the
// rotation. however only one axis will perform the rotation while keeping
// the first origin face on the target. this axis is the same as the origin
// face's normal.
if (Math.abs(rotation2.angle) === Math.PI) {
let rotation2Axis = new __WEBPACK_IMPORTED_MODULE_1__models_Face__["a" /* Face */](keys[0]).vector.getAxis();
rotation2.axis = rotation2Axis;
}
return [rotation1, rotation2];
}
/**
* @param {array} - Array of Face objects to rotate.
* @param {array} - Array of rotations to apply to faces.
* @return {null}
*/
function _rotateFacesByRotations(faces, rotations) {
for (let face of faces) {
for (let rotation of rotations) {
face.rotate(rotation.axis, rotation.angle);
}
}
}
/**
* @param {array} notations
* @return {array}
*/
function _reverseNotations(notations) {
const reversed = [];
for (let notation of notations) {
let isPrime = notation.includes('prime');
notation = isPrime ? notation[0] : notation[0] + 'prime';
reversed.push(notation);
}
return typeof moves === 'string' ? reversed.join(' ') : reversed;
}
/* unused harmony default export */ var _unused_webpack_default_export = ({
getFaceOfMove,
getMoveOfFace,
getMiddleMatchingFace,
getFaceMatchingMiddle,
transformNotations,
normalizeNotations,
getDirectionFromFaces,
getRotationFromTo,
getFaceFromDirection,
orientMoves
});
/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return RubiksCube; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__Cubie__ = __webpack_require__(6);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__algorithm_shortener__ = __webpack_require__(2);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__utils__ = __webpack_require__(0);
const SOLVED_STATE = 'fffffffffrrrrrrrrruuuuuuuuudddddddddlllllllllbbbbbbbbb';
class RubiksCube {
/**
* Factory method. Returns an instance of a solved Rubiks Cube.
*/
static Solved() {
return new RubiksCube(SOLVED_STATE);
}
/**
* Factory method.
* @param {string|array} moves
*/
static FromMoves(moves) {
const cube = RubiksCube.Solved();
cube.move(moves);
return cube;
}
/**
* Factory method. Returns an instance of a scrambled Rubiks Cube.
*/
static Scrambled() {
let cube = RubiksCube.Solved();
let randomMoves = RubiksCube.getRandomMoves(25);
cube.move(randomMoves);
return cube;
}
/**
* @param {string|array} notations - The list of moves to reverse.
* @return {string|array} -- whichever was initially given.
*/
static reverseMoves(moves) {
return RubiksCube.transformMoves(moves, { reverse: true });
}
/**
* @param {string|array} moves - The moves to transform;
* @param {object} options
* @prop {boolean} options.upperCase - Turn lowercase moves into uppercase.
* @prop {object} options.orientation - An object describing the orientation
* from which to makes the moves. See src/js/utils#orientMoves.
*
* @return {string|array} -- whichever was initially given.
*/
static transformMoves(moves, options = {}) {
return __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_2__utils__["d" /* transformNotations */])(moves, options);
}
static getRandomMoves(length = 25) {
let randomMoves = [];
let totalMoves = [
'F',
'Fprime',
'R',
'Rprime',
'U',
'Uprime',
'D',
'Dprime',
'L',
'Lprime',
'B',
'Bprime'
];
while (randomMoves.length < length) {
for (let i = 0; i < length - randomMoves.length; i++) {
let idx = ~~(Math.random() * totalMoves.length);
randomMoves.push(totalMoves[idx]);
}
randomMoves = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_1__algorithm_shortener__["a" /* algorithmShortener */])(randomMoves).split(' ');
}
return randomMoves.join(' ');
}
/**
* @param {string} cubeState - The string representing the Rubik's Cube.
*
* The cube state are represented as:
* 'FFFFFFFFFRRRRRRRRRUUUUUUUUUDDDDDDDDDLLLLLLLLLBBBBBBBBB'
*
* where:
* F stands for the FRONT COLOR
* R stands for the RIGHT COLOR
* U stands for the UP COLOR
* D stands for the DOWN COLOR
* L stands for the LEFT COLOR
* B stands for the BACK COLOR
*
* and the faces are given in the order of:
* FRONT, RIGHT, UP, DOWN, LEFT, BACK
*
* The order of each color per face is ordered by starting from the top left
* corner and moving to the bottom right, as if reading lines of text.
*
* See this example: http://2.bp.blogspot.com/_XQ7FznWBAYE/S9Sbric1KNI/AAAAAAAAAFs/wGAb_LcSOwo/s1600/rubik.png
*/
constructor(cubeState) {
if (cubeState.length !== 9 * 6) {
throw new Error('Wrong number of colors provided');
}
this._notationToRotation = {
f: { axis: 'z', mag: -1 },
r: { axis: 'x', mag: -1 },
u: { axis: 'y', mag: -1 },
d: { axis: 'y', mag: 1 },
l: { axis: 'x', mag: 1 },
b: { axis: 'z', mag: 1 },
m: { axis: 'x', mag: 1 },
e: { axis: 'y', mag: 1 },
s: { axis: 'z', mag: -1 }
};
this._build(cubeState);
}
/**
* Grab all the cubes on a given face, and return them in order from top left
* to bottom right.
* @param {string} face - The face to grab.
* @return {array}
*/
getFace(face) {
if (typeof face !== 'string') {
throw new Error(`"face" must be a string (received: ${face})`);
}
face = face.toLowerCase()[0];
// The 3D position of cubies and the way they're ordered on each face
// do not play nicely. Below is a shitty way to reconcile the two.
// The way the cubies are sorted depends on the row and column they
// occupy on their face. Cubies on a higher row will have a lower sorting
// index, but rows are not always denoted by cubies' y position, and
// "higher rows" do not always mean "higher axis values".
let row, col, rowOrder, colOrder;
let cubies;
// grab correct cubies
if (face === 'f') {
[row, col, rowOrder, colOrder] = ['Y', 'X', -1, 1];
cubies = this._cubies.filter(cubie => cubie.getZ() === 1);
} else if (face === 'r') {
[row, col, rowOrder, colOrder] = ['Y', 'Z', -1, -1];
cubies = this._cubies.filter(cubie => cubie.getX() === 1);
} else if (face === 'u') {
[row, col, rowOrder, colOrder] = ['Z', 'X', 1, 1];
cubies = this._cubies.filter(cubie => cubie.getY() === 1);
} else if (face === 'd') {
[row, col, rowOrder, colOrder] = ['Z', 'X', -1, 1];
cubies = this._cubies.filter(cubie => cubie.getY() === -1);
} else if (face === 'l') {
[row, col, rowOrder, colOrder] = ['Y', 'Z', -1, 1];
cubies = this._cubies.filter(cubie => cubie.getX() === -1);
} else if (face === 'b') {
[row, col, rowOrder, colOrder] = ['Y', 'X', -1, -1];
cubies = this._cubies.filter(cubie => cubie.getZ() === -1);
} else if (['m', 'e', 's'].includes(face)) {
return this._getMiddleCubiesForMove(face);
}
// order cubies from top left to bottom right
return cubies.sort((first, second) => {
let firstCubieRow = first[`get${row}`]() * rowOrder;
let firstCubieCol = first[`get${col}`]() * colOrder;
let secondCubieRow = second[`get${row}`]() * rowOrder;
let secondCubieCol = second[`get${col}`]() * colOrder;
if (firstCubieRow < secondCubieRow) {
return -1;
} else if (firstCubieRow > secondCubieRow) {
return 1;
} else {
return firstCubieCol < secondCubieCol ? -1 : 1;
}
});
}
/**
* @param {array} faces - The list of faces the cubie belongs on.
*/
getCubie(faces) {
return this._cubies.find(cubie => {
if (faces.length != cubie.faces().length) {
return false;
}
for (let face of faces) {
if (!cubie.faces().includes(face)) {
return false;
}
}
return true;
});
}
/**
* Finds and returns all cubies with three colors.
* @return {array}
*/
corners() {
return this._cubies.filter(cubie => cubie.isCorner());
}
/**
* Finds and returns all cubies with two colors.
* @return {array}
*/
edges() {
return this._cubies.filter(cubie => cubie.isEdge());
}
/**
* Finds and returns all cubies with one color.
* @return {array}
*/
middles() {
return this._cubies.filter(cubie => cubie.isMiddle());
}
/**
* Gets the rotation axis and magnitude of rotation based on notation.
* Then finds all cubes on the correct face, and rotates them around the
* rotation axis.
* @param {string|array} notations - The move notation.
* @param {object} options - Move options.
* @prop {boolean} options.upperCase - Turn all moves to upper case (i.e. no "double" moves).
*/
move(notations, options = {}) {
if (typeof notations === 'string') {
notations = notations.split(' ');
}
notations = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_2__utils__["d" /* transformNotations */])(notations, options);
for (let notation of notations) {
let move = notation[0];
if (!move) {
continue;
}
let isPrime = notation.toLowerCase().includes('prime');
let isWithMiddle = move === move.toLowerCase();
let isDoubleMove = notation.includes('2');
let { axis, mag } = this._getRotationForFace(move);
let cubesToRotate = this.getFace(move);
if (isPrime) mag *= -1;
if (isDoubleMove) mag *= 2;
if (isWithMiddle) {
let middleMove = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_2__utils__["g" /* getMiddleMatchingFace */])(move);
let middleCubies = this._getMiddleCubiesForMove(middleMove);
cubesToRotate = [...cubesToRotate, ...middleCubies];
}
for (let cubie of cubesToRotate) {
cubie.rotate(axis, mag);
}
}
}
isSolved() {
return this.toString() === SOLVED_STATE;
}
toString() {
let cubeState = '';
let faces = ['front', 'right', 'up', 'down', 'left', 'back'];
for (let face of faces) {
let cubies = this.getFace(face);
for (let cubie of cubies) {
cubeState += cubie.getColorOfFace(face);
}
}
return cubeState;
}
clone() {
return new RubiksCube(this.toString());
}
/**
* Create a "virtual" cube, with individual "cubies" having a 3D coordinate
* position and 1 or more colors attached to them.
*/
_build(cubeState) {
this._cubies = [];
this._populateCube();
let parsedColors = this._parseColors(cubeState);
for (let face of Object.keys(parsedColors)) {
let colors = parsedColors[face];
this._colorFace(face, colors);
}
}
/**
* Populates the "virtual" cube with 26 "empty" cubies by their position.
* @return {null}
*/
_populateCube() {
for (let x = -1; x <= 1; x++) {
for (let y = -1; y <= 1; y++) {
for (let z = -1; z <= 1; z++) {
// no cubie in the center of the rubik's cube
if (x === 0 && y === 0 && z === 0) {
continue;
}
let cubie = new __WEBPACK_IMPORTED_MODULE_0__Cubie__["a" /* Cubie */]({ position: [x, y, z] });
this._cubies.push(cubie);
}
}
}
}
/**
* @return {object} - A map with faces for keys and colors for values
*/
_parseColors(cubeState) {
let faceColors = {
front: [],
right: [],
up: [],
down: [],
left: [],
back: []
};
let currentFace;
for (let i = 0; i < cubeState.length; i++) {
let color = cubeState[i];
if (i < 9) {
currentFace = 'front';
} else if (i < 9 * 2) {
currentFace = 'right';
} else if (i < 9 * 3) {
currentFace = 'up';
} else if (i < 9 * 4) {
currentFace = 'down';
} else if (i < 9 * 5) {
currentFace = 'left';
} else {
currentFace = 'back';
}
faceColors[currentFace].push(color);
}
return faceColors;
}
/**
* @param {array} face - An array of the cubies on the given face.
* @param {array} colors - An array of the colors on the given face.
*/
_colorFace(face, colors) {
let cubiesToColor = this.getFace(face);
for (let i = 0; i < colors.length; i++) {
cubiesToColor[i].colorFace(face, colors[i]);
}
}
/**
* @return {object} - The the rotation axis and magnitude for the given face.
*/
_getRotationForFace(face) {
if (typeof face !== 'string') {
throw new Error(`"face" must be a string (received: ${face})`);
}
face = face.toLowerCase();
return {
axis: this._notationToRotation[face].axis,
mag: this._notationToRotation[face].mag * Math.PI / 2
};
}
_getMiddleCubiesForMove(move) {
move = move[0].toLowerCase();
let nonMiddles;
if (move === 'm') {
nonMiddles = ['left', 'right'];
} else if (move === 'e') {
nonMiddles = ['up', 'down'];
} else if (move === 's') {
nonMiddles = ['front', 'back'];
}
return this._cubies.filter(cubie => {
return !cubie.hasFace(nonMiddles[0]) && !cubie.hasFace(nonMiddles[1]);
});
}
}
/***/ }),
/* 2 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return algorithmShortener; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_array_element_combiner__ = __webpack_require__(19);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_array_element_combiner___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_array_element_combiner__);
const parallelMoves = {
F: 'B',
R: 'L',
U: 'D'
};
/**
* @param {array|string} notations - The array of move notations.
* @return {string}
*/
const algorithmShortener = (notations) => {
if (typeof notations === 'string') {
notations = notations.split(' ');
}
const options = {
compare(a, b) {
return a[0] === b[0];
},
combine(a, b) {
const aDir = a.includes('2') ? 2 : (a.includes('prime') ? -1 : 1);
const bDir = b.includes('2') ? 2 : (b.includes('prime') ? -1 : 1);
let totalDir = aDir + bDir;
if (totalDir === 4) totalDir = 0;
if (totalDir === -2) totalDir = 2;
if (totalDir === 3) totalDir = -1;
if (totalDir === 0) {
return '';
}
let dirString = totalDir === 2 ? '2' : (totalDir === -1 ? 'prime' : '');
return `${a[0]}${dirString}`;
},
cancel(value) {
return value === '';
},
ignore(a, b) {
return (parallelMoves[a[0]] === b[0] || parallelMoves[b[0]] === a[0]);
}
};
return __WEBPACK_IMPORTED_MODULE_0_array_element_combiner___default()(notations, options).join(' ');
};
/***/ }),
/* 3 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return BaseSolver; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__models_RubiksCube__ = __webpack_require__(1);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__utils___ = __webpack_require__(0);
class BaseSolver {
/**
* Solves the first step following the Fridrich Method: the cross. Solves the
* cross on the UP face by default.
*
* @param {string|RubiksCube} rubiksCube - This can either be a 54-character
* long string representing the cube state (in this case it will have to
* "build" another rubik's Cube), or an already built RubiksCube object.
*/
constructor(rubiksCube, options = {}) {
this.cube = typeof rubiksCube === 'string' ? new __WEBPACK_IMPORTED_MODULE_0__models_RubiksCube__["a" /* RubiksCube */](rubiksCube) : rubiksCube;
this.options = options;
this.partition = {};
this.partitions = [];
this.totalMoves = [];
this._afterEachCallbacks = [];
}
/**
* @param {string|array} notation - A string of move(s) to execute and store.
* @param {object} options - The options to pass to RubiksCube#move.
*/
move(notations, options) {
if (typeof notations === 'string') {
notations = notations.split(' ');
}
this.cube.move(notations, options);
// this step is also in RubiksCube#move, but it is important we do it here
// as well. The notations need to be saved to the partition correctly.
notations = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_1__utils___["d" /* transformNotations */])(notations, options);
for (let notation of notations) {
this.totalMoves.push(notation);
}
}
afterEach(callback) {
this._afterEachCallbacks.push(callback);
}
/**
* @param {...*} callbackArgs - The arguments to call the function with.
*/
_triggerAfterEach(...callbackArgs) {
this._afterEachCallbacks.forEach(fn => fn(...callbackArgs));
}
/**
* Solves the edge and/or corner and returns information about the state
* about them right before they are solved. It's important to construct the
* object in steps for debugging, so that we can still have access to e.g.
* the case number if the solve method fails.
*/
_solve(cubies = {}) {
this.partition = {};
this.partition.cubies = cubies;
let { corner, edge } = cubies;
this.partition.caseNumber = this._getCaseNumber({ corner, edge });
this._solveCase(this.partition.caseNumber, { corner, edge });
this.partition.moves = this.totalMoves;
this.totalMoves = [];
if (!this._overrideAfterEach) {
this._triggerAfterEach(this.partition, this.phase);
}
return this.partition;
}
_solveCase(caseNumber, cubies = {}) {
let { corner, edge } = cubies;
this[`_solveCase${caseNumber}`]({ corner, edge });
}
}
/***/ }),
/* 4 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return Vector; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_gl_vec3_angle__ = __webpack_require__(22);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_gl_vec3_angle___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_gl_vec3_angle__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_gl_vec3_cross__ = __webpack_require__(12);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_gl_vec3_cross___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_gl_vec3_cross__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_gl_vec3_rotateX__ = __webpack_require__(26);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_gl_vec3_rotateX___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2_gl_vec3_rotateX__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3_gl_vec3_rotateY__ = __webpack_require__(27);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3_gl_vec3_rotateY___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_3_gl_vec3_rotateY__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4_gl_vec3_rotateZ__ = __webpack_require__(28);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4_gl_vec3_rotateZ___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_4_gl_vec3_rotateZ__);
const rotate = {
x: __WEBPACK_IMPORTED_MODULE_2_gl_vec3_rotateX___default.a,
y: __WEBPACK_IMPORTED_MODULE_3_gl_vec3_rotateY___default.a,
z: __WEBPACK_IMPORTED_MODULE_4_gl_vec3_rotateZ___default.a
};
class Vector {
/**
* Factory method.
* @param {string} vector - Space-deliminated x, y, and z values.
* @return {Vector}
*/
static FromString(vector) {
return new Vector(vector.split(' ').map(value => parseInt(value)));
}
/**
* @param {array} vector1 - Vector 1.
* @param {array} vector2 - Vector 2.
* @return {boolean}
*/
static areEqual(vector1, vector2) {
return vector1[0] === vector2[0] && vector1[1] === vector2[1] && vector1[2] === vector2[2];
}
/**
* Helper method. gl-vec3's angle function always returns positive but in many
* cases we want the angle in the direction from one vector to another. To get
* the sign of the angle, cross the two vectors and determine the direction the
* crossed vector, um, directs in. For example, the vector [0, -1, 0] would
* shoot negatively along the y-axis.
*
* @param {array} v1 - Vector 1.
* @param {array} v2 - Vector 2.
* @return {number}
*/
static getAngle(v1, v2) {
let _angle = __WEBPACK_IMPORTED_MODULE_0_gl_vec3_angle___default()(v1, v2);
let crossVector = __WEBPACK_IMPORTED_MODULE_1_gl_vec3_cross___default()([], v1, v2);
let sign = new Vector(crossVector).getMagnitude();
return sign ? _angle * sign : _angle;
}
/**
* Finds the rotation axis and angle to get from one normal to another.
* @param {array} normal1 - The from normal.
* @param {array} normal2 - The to normal.
* @return {object} - Stores the rotation axis and angle
*/
static getRotationFromNormals(normal1, normal2) {
let axis = new Vector(__WEBPACK_IMPORTED_MODULE_1_gl_vec3_cross___default()([], normal1, normal2)).getAxis();
let angle = Vector.getAngle(normal1, normal2);
// when normal1 is equal to or opposite from normal2, it means 2 things: 1)
// the cross axis is undefined and 2) the angle is either 0 or PI. This
// means that rotating around the axis parallel to normal1 will not result
// in any change, while rotating around either of the other two will work
// properly.
if (!axis) {
let axes = ['x', 'y', 'z'];
axes.splice(axes.indexOf(new Vector(normal1).getAxis()), 1);
axis = axes[0];
}
return { axis, angle };
}
/**
* @param {object} rotation - The rotation to reverse.
* @return {object}
*/
static reverseRotation(rotation) {
rotation.angle *= -1;
return rotation;
}
/**
* @param {array} [vector] - Contains x, y, and z values.
*/
constructor(vector) {
this.set(vector);
}
/**
* @return {array}
*/
toArray() {
return this.vector;
}
/**
* @param {array} vector - The new vector to store.
*/
set(vector) {
if (typeof vector === 'undefined') {
return;
}
this.vector = vector.map(value => Math.round(value));
}
/**
* @param {number} value - The value to store.
*/
setX(value) {
this.vector[0] = value;
}
/**
* @param {number} value - The value to store.
*/
setY(value) {
this.vector[1] = value;
}
/**
* @param {number} value - The value to store.
*/
setZ(value) {
this.vector[2] = value;
}
/**
* @return {number}
*/
getX() {
return this.toArray()[0];
}
/**
* @return {number}
*/
getY() {
return this.toArray()[1];
}
/**
* @return {number}
*/
getZ() {
return this.toArray()[2];
}
/**
* Kind of a flimsy method. If this vector points parallel to an axis, this
* returns true. A hacky way to find this is to count the number of 0's and
* return true if and only if the count is 2.
* @return {boolean}
*/
isAxis() {
let count = 0;
for (let value of this.vector) {
if (value === 0) {
count += 1;
}
}
return count === 2;
}
/**
* Kind of a flimsy method. If this vector points parallel to an axis, return
* that axis.
* @return {string}
*/
getAxis() {
if (!this.isAxis()) {
return;
}
if (this.vector[0] !== 0) return 'x';
if (this.vector[1] !== 0) return 'y';
if (this.vector[2] !== 0) return 'z';
}
/**
* Kind of a flimsy method. If this vector points parallel to an axis, return
* the magnitude of the value along that axis. (Basically, return whether it
* is positive or negative.)
* @return {number}
*/
getMagnitude() {
if (!this.isAxis()) {
return;
}
return this[`get${this.getAxis().toUpperCase()}`]();
}
/**
* @param {string} axis - The axis to rotate around.
* @param {number} angle - The angle of rotation.
* @return {Vector}
*/
rotate(axis, angle) {
axis = axis.toLowerCase();
this.set(rotate[axis]([], this.vector, [0, 0, 0], angle));
return this;
}
}
/***/ }),
/* 5 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return F2LCaseBaseSolver; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__F2LBaseSolver__ = __webpack_require__(15);
class F2LCaseBaseSolver extends __WEBPACK_IMPORTED_MODULE_0__F2LBaseSolver__["a" /* F2LBaseSolver */] {
solve({ corner, edge }) {
return this._solve({ corner, edge });
}
}
/***/ }),
/* 6 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return Cubie; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__Vector__ = __webpack_require__(4);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__Face__ = __webpack_require__(14);
class Cubie {
/**
* Factory method. Returns an instance of a cubie identified by the faces it
* sits on.
* @param {array} faces - A list of all the faces this cubie sits on.
*/
static FromFaces(faces) {
let position = new __WEBPACK_IMPORTED_MODULE_0__Vector__["a" /* Vector */]([0, 0, 0]);
let colorMap = {};
for (let face of faces) {
if (!face) {
continue;
}
let temp = new __WEBPACK_IMPORTED_MODULE_1__Face__["a" /* Face */](face);
let axis = temp.vector.getAxis().toUpperCase();
position[`set${axis}`](temp.vector.getMagnitude());
colorMap[face.toLowerCase()] = temp.toString()[0].toLowerCase();
}
return new Cubie({ position: position.toArray(), colorMap });
}
/**
* @param {object} [options]
* @param {object} options.position - The cubie's position.
* @param {object} options.colorMap - A map with faces as keys and colors
* as values. For example: { 'front' : 'f' }.
*/
constructor({ position, colorMap = {} }) {
this.position(position);
this.colorMap = {};
Object.keys(colorMap).forEach(face => {
let color = colorMap[face];
this.colorFace(face, color);
});
}
/**
* @return {Cubie}
*/
clone() {
return new Cubie({
position: this.position(),
colorMap: this.colorMap
});
}
/**
* Getter/setter for the vector position.
* @param {array} [position] - The new position to store.
* @return {array}
*/
position(position) {
if (typeof position === 'undefined') {
return this.vector ? this.vector.toArray() : this.vector;
}
this.vector = new __WEBPACK_IMPORTED_MODULE_0__Vector__["a" /* Vector */](position);
}
/**
* @return {number}
*/
getX() {
return this.vector.getX();
}
/**
* @return {number}
*/
getY() {
return this.vector.getY();
}
/**
* @return {number}
*/
getZ() {
return this.vector.getZ();
}
/**
* @return {boolean}
*/
isCorner() {
return Object.keys(this.colorMap).length === 3;
}
/**
* @return {boolean}
*/
isEdge() {
return Object.keys(this.colorMap).length === 2;
}
/**
* @return {boolean}
*/
isMiddle() {
return Object.keys(this.colorMap).length === 1;
}
/**
* @return {array}
*/
colors() {
return Object.keys(this.colorMap).map(face => this.colorMap[face]);
}
/**
* @param {string} color - Check if the cubie has this color.
* @return {boolean}
*/
hasColor(color) {
color = color.toLowerCase();
for (let face of Object.keys(this.colorMap)) {
if (this.colorMap[face] === color) {
return true;
}
}
return false;
}
/**
* @param {string} face - Check if the cubie has this face.
* @return {boolean}
*/
hasFace(face) {
face = face.toLowerCase();
return Object.keys(this.colorMap).includes(face);
}
/**
* Sets a color on a given face or normal of a cubie.
* @param {string} face - The face of the cubie we want to set the color on.
* @param {string} color - The color we want to set.
* @return {Cubie}
*/
colorFace(face, color) {
face = face.toLowerCase();
color = color.toLowerCase();
this.colorMap[face] = color;
return this;
}
/**
* @param {string} face - The color on the face this cubie sits on.
* @return {string}
*/
getColorOfFace(face) {
face = face.toLowerCase();
return this.colorMap[face];
}
/**
* @param {string} color - Find the face that this color sits on.
* @return {string}
*/
getFaceOfColor(color) {
color = color.toLowerCase();
return Object.keys(this.colorMap).find(cubieColor => {
return this.colorMap[cubieColor] === color;
});
}
/**
* Return all the faces this cubie sits on.
* @return {array}
*/
faces() {
return Object.keys(this.colorMap);
}
/**
* Rotates the position vector around `axis` by `angle`. Updates the internal
* position vector and the normal-color map.
* @param {string} axis - The axis of rotation.
* @param {number} angle - The magnitude of rotation.
* @return {null}
*/
rotate(axis, angle) {
// update position vector after rotation
this.vector.rotate(axis, angle);
// update normal-color map
let newMap = {}; // need to completely overwrite the old one
// go through each normal, rotate it, and assign the new normal the old color
for (let face of Object.keys(this.colorMap)) {
let color = this.colorMap[face];
let faceModel = new __WEBPACK_IMPORTED_MODULE_1__Face__["a" /* Face */](face);
let newNormal = faceModel.rotate(axis, angle).normal().join(' ');
let newFace = __WEBPACK_IMPORTED_MODULE_1__Face__["a" /* Face */].FromNormal(newNormal).toString().toLowerCase();
newMap[newFace] = color;
}
this.colorMap = {};
Object.keys(newMap).forEach(face => this.colorFace(face, newMap[face]));
}
}
/***/ }),
/* 7 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return CrossSolver; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__BaseSolver__ = __webpack_require__(3);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__models_RubiksCube__ = __webpack_require__(1);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__utils__ = __webpack_require__(0);
const CROSS_COLOR = 'u';
const R = (moves) => __WEBPACK_IMPORTED_MODULE_1__models_RubiksCube__["a" /* RubiksCube */].reverseMoves(moves);
class CrossSolver extends __WEBPACK_IMPORTED_MODULE_0__BaseSolver__["a" /* BaseSolver */] {
constructor(...args) {
super(...args);
this.phase = 'cross';
}
solve() {
let crossEdges = this._getCrossEdges();
for (let edge of crossEdges) {
let partition = this._solve({ edge });
this.partitions.push(partition);
}
return this.partitions;
}
isSolved() {
let edges = this._getCrossEdges();
for (let edge of edges) {
if (!this.isEdgeSolved(edge)) {
return false;
}
}
return true;
}
isEdgeSolved(edge) {
let otherColor = edge.colors().find(color => color !== 'u');
let otherFace = edge.faces().find(face => face !== 'up');
const matchesMiddle = otherFace[0] === otherColor;
const isOnCrossFace = edge.getColorOfFace('up') === 'u';
return isOnCrossFace && matchesMiddle;
}
/**
* Finds all edges that have 'F' as a color.
* @return {array}
*/
_getCrossEdges() {
return this.cube.edges().filter(edge => edge.hasColor(CROSS_COLOR));
}
/**
* 6 Cases!
* 1) The edge's UP color is on the UP face.
* 2) the edge's UP color is on the DOWN face.
* 3) The edge's UP color is not on the UP or DOWN face and the other color is on the UP face.
* 4) The edge's UP color is not on the UP or DOWN face and the other color is on the DOWN face.
* 5) The edge's UP color is not on the UP or DOWN face and the other color is on the RELATIVE RIGHT face.
* 6) The edge's UP color is not on the UP or DOWN face and the other color is on the RELATIVE LEFT face.
*
* @param {cubie} edge
*/
_getCaseNumber({ edge }) {
if (edge.getColorOfFace('up') === CROSS_COLOR) {
return 1;
} else if (edge.getColorOfFace('down') === CROSS_COLOR) {
return 2;
}
if (edge.faces().includes('up')) {
return 3;
} else if (edge.faces().includes('down')) {
return 4;
}
let crossFace = edge.getFaceOfColor(CROSS_COLOR);
let otherFace = edge.getFaceOfColor(edge.colors().find(color => color !== CROSS_COLOR));
let direction = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_2__utils__["c" /* getDirectionFromFaces */])(crossFace, otherFace, { up: 'up' });
if (direction === 'right') {
return 5;
} else if (direction === 'left') {
return 6;
}
}
_solveCase1({ edge }) {
if (this.isEdgeSolved(edge)) {
return;
}
let face = edge.faces().find(face => face !== 'up');
this.move(`${face} ${face}`, { upperCase: true });
this._solveCase2({ edge });
}
_solveCase2({ edge }) {
let solveMoves = this._case1And2Helper({ edge }, 2);
this.move(solveMoves, { upperCase: true });
}
_solveCase3({ edge }) {
let prepMove = this._case3And4Helper({ edge }, 3);
this.move(prepMove, { upperCase: true });
this._solveCase5({ edge });
}
_solveCase4({ edge }) {
let prepMove = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_2__utils__["b" /* getRotationFromTo */])(
'down',
edge.getFaceOfColor('u'),
__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_2__utils__["a" /* getFaceOfMove */])(edge.getColorOfFace('down'))
);
this.move(prepMove, { upperCase: true });
let edgeToMiddle = R(edge.getFaceOfColor('u'));
this.move(edgeToMiddle, { upperCase: true });
this._solveCase5({ edge });
}
_solveCase5({ edge }) {
let solveMoves = this._case5And6Helper({ edge }, 5);
this.move(solveMoves, { upperCase: true });
}
_solveCase6({ edge }) {
let solveMoves = this._case5And6Helper({ edge }, 6);
this.move(solveMoves, { upperCase: true });
}
_case1And2Helper({ edge }, caseNum) {
let crossColorFace = caseNum === 1 ? 'up' : 'down';
let currentFace = edge.faces().find(face => face !== crossColorFace);
let targetFace = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_2__utils__["a" /* getFaceOfMove */])(edge.getColorOfFace(currentFace));
let solveMoves = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_2__utils__["b" /* getRotationFromTo */])(crossColorFace, currentFace, targetFace);
if (caseNum === 2) {
let edgeToCrossFace = __webpack_require__.i(__WEBPACK_IMPORTED_MODULE_2__utils__["f" /* getMoveOfFace */])(targetFace);
solveMoves += ` ${edgeToCrossFace} ${edgeToCrossFace}`;
}
return solveMoves;
}
_case3And4Helper({ edge }, caseNum) {
let prepMove = edge.faces().find(face => !['up', 'down'].includes(face));
if (caseNum === 4) {
prepMove = R(prepMove);
}
return prepMove;
}
_case5And6Helper({ edge }, caseNum) {
let otherCo