# Cube geometry

An N×N×N twisty cube as exact integer state: cubies on a lattice, each with an orientation matrix, plus moves, notation, scrambles, sticker lookup, the drag geometry and a layer-by-layer solver.

> For the complete index, see [llms.txt](https://robocn.dev/llms.txt). A Markdown version of any page is available by appending `.md` to its URL or by sending an `Accept: text/markdown` header.

## Install

```bash
bunx --bun shadcn@latest add https://robocn.dev/r/cube-geometry.json
```

Registry item: `cube-geometry` · [`https://robocn.dev/r/cube-geometry.json`](https://robocn.dev/r/cube-geometry.json)

## Notes

- State is exact integer arithmetic: lattice indices and 3×3 matrices of -1, 0 and 1. A cube turned ten thousand times is bit-identical to one turned none, so `isSolved` is a comparison rather than a tolerance.
- Clockwise means clockwise **looking at that face from outside**, the way notation means it. The sign flip that costs — negative right-handed about a positive normal — lives in `moveToTurn` and nowhere else.
- It does solve a cube, and it says how: the beginner's layer-by-layer method, written as a staged iterative-deepening search over an alphabet of *macros* — the U turns plus one standard algorithm rotated into each of the four side slots — rather than as a hundred hand-cased positions. A small search over big steps. The line is then cancelled down and **replayed**; if the replay is not solved, `solveCube` returns `null` rather than a line it has not checked.
- The search runs on a packed encoding — where each piece sits, and which of the 24 rotations it carries — so a turn is 27 table lookups and no allocation, and a scrambled 3×3 is solved in single-digit milliseconds. It is not optimal: a beginner method spends about 120 moves where God's number is 20, and this one is honest about being the method it is.
- 3×3 only. A 4×4 has parities the method knows nothing about, so every other order is `null`.
- Pure functions over plain objects: no React, no three.js, no dependencies.

## Usage

```tsx
import { applyMoves, createCube, isSolved, scrambleMoves, solveCube } from "@/lib/robocn/cube"

const scrambled = applyMoves(createCube(3), scrambleMoves(3, 25, 7))

const line = solveCube(scrambled)          // the moves that take it home, or null
isSolved(applyMoves(scrambled, line!))     // true — and it was checked before you got it
```

## API

| name | type | default | description |
| --- | --- | --- | --- |
| `createCube(order?)` | `(order?: number) => CubeState` | — | A solved cube, `order` cubies to a side. 2 … 7, clamped. |
| `applyMove(state, move)` | `(state: CubeState, move: CubeMove) => CubeState` | — | The state after one turn. Immutable — the previous state is untouched. |
| `applyMoves(state, moves)` | `(state: CubeState, moves: CubeMove[]) => CubeState` | — | A whole algorithm, in order. |
| `isSolved(state)` | `(state: CubeState) => boolean` | — | Every sticker showing its own face — which is what a person means by solved, and what a spun centre does not break. Exact integer comparisons, never a tolerance. |
| `parseMove / parseAlgorithm` | `(text: string) => CubeMove | null` | — | The notation people type: `R`, `U'`, `F2`, `2R'`. Anything else is dropped rather than guessed at. |
| `formatMove / formatAlgorithm` | `(move: CubeMove) => string` | — | The same notation back out. |
| `invertMove / invertMoves` | `(moves: CubeMove[]) => CubeMove[]` | — | The undo of a sequence: reversed, each turn the other way. |
| `scrambleMoves(order?, count?, seed?)` | `(order?: number, count?: number, seed?: number) => CubeMove[]` | — | A deterministic scramble — same seed, same shuffle — never repeating a face back to back. |
| `moveToTurn / turnToMove` | `(move: CubeMove, order: number) => CubeTurn` | — | A person's move (clockwise from outside) as the renderer's turn (right-handed about the positive axis), and back. |
| `moveFromDrag(drag)` | `(drag: CubeDrag) => CubeMove | null` | — | The turn a drag across a face asks for: the grabbed face, the grabbed cubie and a direction in world units. Null when the drag has no direction in the plane of the face. |
| `grabFromDrag(drag)` | `(drag: CubeDrag) => CubeGrab | null` | — | The same drag with the tangent as well — the direction the hand keeps pulling to keep winding that turn. What a rig follows the pointer with. |
| `solveCube(state)` | `(state: CubeState) => CubeMove[] | null` | — | A line that takes this cube home, by a layer-by-layer method searched over a macro alphabet. Replayed and checked before it is returned; 3×3 only, and `null` on anything else rather than a guess. |
| `solveStep(state)` | `(state: CubeState) => CubeMove | null` | — | The next move of that solve, which is what a hint is. |
| `simplifyMoves(moves)` | `(moves: CubeMove[]) => CubeMove[]` | — | `R R` → `R2`, `R R'` → nothing. Same face and same layer only — a line as short as it was going to be without searching again. |
| `stickerFace(cubie, face)` | `(cubie: Cubie, face: CubeFace) => CubeFace` | — | Which face's colour the sticker on that side of the cubie now shows. |
| `exposedFaces / shellCubies` | `(cubie: Cubie, order: number) => CubeFace[]` | — | The sides of a cubie that are on the outside, and the cubies worth drawing at all. |

## Source

- `src/lib/robocn/cube.ts`
