A Rubik's cube, held in memory.
Feed it moves and it tells you what the cube looks like, which stages are solved, and how a recorded solve was actually put together.
0 runtime dependencies 3x3 and 2x2 CJS + ESM + types MIT
npm install cube-state-engine
Turn the cube
The 3D view, the flat net and the checks below all read from one
CubeEngine instance. Press a key, or scramble it.
Keyboard
Break a solve into stages
Hand analyzeSolution() a timed move list and it replays
the solve, works out which method was used, and reports when each
stage finished. This is a real recorded solve from the test suite.
The API
Everything is a named export. Predicates accept a
CubeEngine, a state() object, or a flat
sticker array.
Engine
- new CubeEngine(scramble?, { size }) Size 2 or 3. The scramble is applied but not recorded.
- applyMoves(seq, { record }) U D L R F B, wide Uw Dw Lw Rw Fw, slices M E S, rotations x y z, with ' and 2.
- state()Six faces as color matrices.
- isSolved()Every face a single color.
- getMoves(asString?)Move history.
- reset()Back to solved, history cleared.
- rotateU / D / L / R / F / B Plus Uw Dw Lw Rw Fw, M E S, and X Y Z. Each takes a clockwise flag.
Predicates
- isCubeSolved(cube)
- isCrossComplete(cube, { color })
- isF2LComplete(cube, { cross })
- isLastLayerOriented(cube, { face })
- areLastLayerCornersSolved(cube, { face })
- matchesGoal(cube, goal) full · cross · f2l · oll · oll+cp, or your own function.
- getCubeGeometry(size) Derived edges, corners, neighbors and opposites.
Analysis
- analyzeSolution(moves, { size }) Detects CFOP and Roux. Reports per-stage timing and TPS.
- simplifyMoves(moves) Joins identical quarter turns into doubles. Never cancels.
- invertSequence(tokens) Reverses and inverts a move list.
- getMovePermutations(size) The cached permutation tables the engine runs on.
// How this page is wired
import { CubeEngine, matchesGoal, simplifyMoves } from "cube-state-engine";
const cube = new CubeEngine();
function onKey(move) {
cube.applyMoves(move, { record: true });
paintNet(cube.state());
player.alg = cube.getMoves();
solvedEl.textContent = cube.isSolved();
crossLamp.dataset.on = matchesGoal(cube, "cross");
countEl.textContent = simplifyMoves(cube.getMoves(false)).length;
}