Kyuubuキューブ

@johnmorrisdotca/kyuubu 1.6.0 · 6 entry points · 182 exports

@johnmorrisdotca/kyuubu

algorithmName applySolve CliResult CliSurroundings CliWords COMMIT_ANGLE countsAsMove countSolveMoves CUBE_FACE_ORDER CUBE_SCALE_INTERACTIVE CUBE_SCALE_PX CUBE_SCALES CUBE_THEMES CubeAxis CubeFace CubeFaceLetter CubeMove cubeNet CubeScale cubeSlots cubeSolved CubeTheme CubeTurns CubeView CubeViewEvents CubeViewOptions cubeWidthPx CubeWords decodeCubeMoves DEFAULT_COLOURS DEFAULT_PLASTIC DRAG_CLEAR_RATIO DRAG_DECIDE_PX DRAG_START_PX dragAngle dragHint DragHint DragPick encodeCubeMoves FACE_FRAMES FACE_PROPERTIES faceMove faceOfNormal faceOfSlot fill FLICK_ANGLE FLICK_SPEED fromJSON fromText FULL_SCRAMBLE_LENGTHS Guide GUIDE_CSS GuideHandle GuideHeard GuidePanelOptions GuideSource GuideStep HINT_MIN_FACING HINT_MIN_FOLLOW HintArrow isCubeScale isCubeState joinTurns keepScrambling KeepScramblingHandle KeepScramblingOptions KeyReading KyuubuLanguage KyuubuStrings languageOf layerOf Mat3 MAX_CLI_COUNT MAX_CLI_LENGTH MAX_RECORD_MOVES MAX_RECORD_SEED MAX_RECORD_SIZE MAX_RECORDS MAX_REPLAY_STEPS middleMove MIN_RECORD_SIZE mountGuide moveFits moveForDrag moveForRelease moveForWheel movementSays movementText moveNotation movesNotation nextTurn NotationFault paceSeconds parseMove parseMoves parseSolve parseSolveMove pastCommit permutationOf pickDrag planReplay prefersReducedMotion quartersForRelease quarterTurn randomScramble readKey readSolveLink RECORD_FORMAT Replay REPLAY_LOOP_REST_MS REPLAY_SPEEDS REPLAY_STEP_MS ReplayClock ReplayCube ReplayFault ReplayOptions ReplayPlan ReplaySource ReplayStatus rotationKeys runCli SCRAMBLE_DEFAULT_PACE SCRAMBLE_PACES SCRAMBLE_SHORTEST Scrambled ScrambleOptions ScramblePace ScramblePage seededRandom SOLVABLE_SIZES SOLVE_ALGORITHMS SolveAlgorithm solvedCube SolveLink SolveMove solveMoves SolvePart SolveReading SolveRecord SolveStage SolveStep solveSteps SolveSummary solveText stageName stageSays StickerSlot STRINGS summarize toCSV toJSON toText turnAll turnCube undoAll undoOf Vec3 VERSION viewMatrix wholeMove WORDS

function algorithmName

algorithmName(algorithm: SolveAlgorithm, language?: KyuubuLanguage): string

The name of one of the method's algorithms, as a person would say it: "Sune", 「コーナーの三点交換」.

function applySolve

applySolve(state: string, n: number, steps: readonly SolveMove[]): string

A cube after these steps.

type CliResult

type CliResult = { code: 0 | 1 | 2; out: string; err: string };

What the command line came to: the exit code, and what goes to standard output and standard error.

type CliSurroundings

type CliSurroundings = { /** The environment: `LC_ALL`, `LC_MESSAGES` and `LANG` choose the language, and `NO_COLOR` turns colour off. */ env?: Readonly<Record<string, string | undefined>>; /** What was piped in, for `--stdin`. */ stdin?: string; /** Whether standard output is a terminal that shows colour. Piped output is never coloured. */ colour?: boolean; /** The system's language, used where the environment name…

What the command line is told about where it runs. All of it may be left out.

type CliWords

type CliWords = { cliUsage: string; cliBadOption: string; cliNeedsValue: string; cliOneMode: string; cliBadSize: string; cliBadLength: string; cliBadCount: string; cliBadLang: string; cliBadMoves: string; cliBadState: string; cliNoMethod: string; cliImpossible: string; cliNeedsStart: string; cliSolvedIn: string; cliNotSolvedAfter: string; cliAlreadySolved: string; cliSteps: string; cliState: string; };

The command line's words: its help, its refusals and what it reports.

const COMMIT_ANGLE

COMMIT_ANGLE: 30

The point of no return, in degrees, where none is given: let go short of it and the layer goes back.

function countsAsMove

countsAsMove(move: CubeMove): boolean

Whether a move counts towards a solve's moves: a turn of the whole cube in the hand does not.

function countSolveMoves

countSolveMoves(steps: readonly SolveMove[]): number

How many of the steps count as moves: every turn, and no turn of the whole cube.

const CUBE_FACE_ORDER

CUBE_FACE_ORDER: readonly ["U", "R", "F", "D", "L", "B"]

The faces in the order a state is written: up, right, front, down, left, back. Each letter is also that face's colour when solved.

const CUBE_SCALE_INTERACTIVE

CUBE_SCALE_INTERACTIVE: Readonly<Record<"small" | "medium" | "large", boolean>>

What a cube drawn at each scale is, apart from its width: a small one is for looking at, so it is not turned by a hand or a key unless asked.

const CUBE_SCALE_PX

CUBE_SCALE_PX: Readonly<Record<"small" | "medium" | "large", number>>

How wide each is, in pixels: a cube is as tall as it is wide.

const CUBE_SCALES

CUBE_SCALES: readonly ["small", "medium", "large"]

HOW BIG A CUBE IS DRAWN, as a setting: small for a list or a picker, medium, and large. The same names, in the same order, as Toranpu's cards, so a page that uses both says one thing. (A cube's size is its side, 2 to 7, so the setting here is scale.)

const CUBE_THEMES

CUBE_THEMES: { readonly standard: { readonly colours: { readonly U: string; readonly R: string; readonly F: string; readonly D: string; readonly L: string; readonly B: string; }; readonly plastic: "#111"; readonly stickerInset: "6%"; readonly stickerRadius: "14%"; }; readonly paper: { readonly colours: { readonly U: "#fffdf7"; readonly R: "#b5452c"; readonly F: "#2f7a4f"; readonly D: "#e0b43b"; readonly L: "#d97a2b"…

Looks that come with the package. standard is the cube as it is sold; paper is the quieter one the demo site wears, made to sit on warm paper and green felt; stickerless has colour to the edge of every piece.

type CubeAxis

type CubeAxis = 0 | 1 | 2;

An axis of the cube: 0 is x (to the right), 1 is y (up) and 2 is z (towards the viewer).

type CubeFace

type CubeFace = (typeof CUBE_FACE_ORDER)[number];

A face's letter: U, R, F, D, L or B.

type CubeFaceLetter

type CubeFaceLetter = "R" | "L" | "U" | "D" | "F" | "B";

A face as the notation names it.

type CubeMove

type CubeMove = { /** The axis turned about. */ axis: CubeAxis; /** The layer, 0 to n − 1 from the axis's negative side; "all" for the whole cube. */ layer: number | "all"; /** How far: one, two or three quarter turns by the right-hand rule. */ turns: CubeTurns; };

One turn: a layer, or the whole cube, turned about an axis by one, two or three quarter turns.

function cubeNet

cubeNet(state: string, n: number, colour?: boolean): string

The cube unfolded flat, as lines of text: the top above the front, the left, front, right and back in a row, and the bottom below.

U U U U U U U U U L L L F F F R R R B B B …

With colour, each sticker is a block of its colour in place of its letter.

type CubeScale

type CubeScale = (typeof CUBE_SCALES)[number];

One of the three.

function cubeSlots

cubeSlots(n: number): { slots: readonly StickerSlot[]; index: ReadonlyMap<string, number>; }

Every sticker slot of a cube of this size, in the order its state is written, and the way back from a slot to its place.

function cubeSolved

cubeSolved(state: string, n: number): boolean

SOLVED: every face one colour. Which colour does not matter, so a cube solved and then turned whole in the hand is still solved, and a 2×2 or 4×4, which has no fixed centres, is solved in whichever way round it ends.

type CubeTheme

type CubeTheme = { /** Colours for the six faces, by the letter a solved face carries. Any CSS colour. */ colours?: Partial<Record<CubeFace, string>>; /** The colour of the plastic between stickers and inside the cube. */ plastic?: string; /** How far a sticker sits in from the edge of its square, as a CSS length or percentage of the square. `"6%"` when left out. */ stickerInset?: string; /** How round a sticker's c…

How a cube looks: its six colours, its plastic, and the shape of a sticker. Everything may be left out, and what is left out stays as it was.

type CubeTurns

type CubeTurns = 1 | 2 | 3;

One, two or three quarter turns, counter-clockwise seen from the axis's positive end.

const CubeView

CubeView: typeof CubeView

A cube on the screen. new CubeView(element, { size: 3 }) draws it in the element, which it fills, so the element needs a size; destroy() takes it away again with every listener.

type CubeViewEvents

type CubeViewEvents = { turn: (move: CubeMove, state: string) => void; look: (yaw: number, pitch: number) => void; };

What a cube tells the listeners on adds: a turn a person made, with the stickers after it; and the view turned, in degrees.

type CubeViewOptions

type CubeViewOptions = { /** The cube's side: 2 for a 2×2, 3 for the classic. */ size: number; /** The stickers, one letter each (`solvedCube`); solved when left out. */ state?: string; /** Colours for the six faces, by the letter a solved face carries. A face left out takes its CSS custom property (`--kyuubu-up` and the rest), and the standard colour where that is not set. */ colours?: Partial<Record<CubeFace, stri…

Everything a cube on the screen can be told when it is made. Only size is needed.

function cubeWidthPx

cubeWidthPx(scale?: CubeScale, width?: number): number | null

The width in pixels a cube is drawn at: width when it is a number above zero, and otherwise the scale's. Null when neither is given, which leaves the cube filling whatever box it is in.

type CubeWords

type CubeWords = { cubeLabel: string; solved: string; notSolved: string; stageHold: string; stageWhiteCross: string; stageWhiteCorners: string; stageWhiteLayer: string; stageMiddleLayer: string; stageYellowCross: string; stageYellowFace: string; stageYellowCorners: string; stageYellowEdges: string; saysHold: string; saysWhiteCross: string; saysWhiteCorners: string; saysWhiteLayer: string; saysMiddleLayer: string; sa…

THE WORDS THE CUBE ITSELF SAYS, in English and Japanese: what a screen reader calls it, whether it is solved, the names of the steps of a solve and what each is for, and the names of the method's algorithms. They are kept apart from the command line's words (strings.ts), so that a page that only draws a cube carries only these.

{n} and the other braces are filled in when a line is shown; a table in another language must keep them.

function decodeCubeMoves

decodeCubeMoves(code: string): CubeMove[] | null

The moves written in a code, or null where any of it is not a move.

const DEFAULT_COLOURS

DEFAULT_COLOURS: Readonly<Record<"U" | "R" | "F" | "D" | "L" | "B", string>>

The standard colours, by the letter a solved face carries: white up, red right, green front, yellow down, orange left, blue back.

const DEFAULT_PLASTIC

DEFAULT_PLASTIC: "#111"

The colour of the plastic between the stickers, where none is given.

const DRAG_CLEAR_RATIO

DRAG_CLEAR_RATIO: 1.4

How much more one layer must go the pointer's way than the other to be picked before that.

const DRAG_DECIDE_PX

DRAG_DECIDE_PX: 27

How far it goes before a drag that could be either of two layers picks the likelier anyway.

const DRAG_START_PX

DRAG_START_PX: 9

How far the pointer goes, in pixels, before a drag picks a layer at all.

function dragAngle

dragAngle(pick: DragPick, dx: number, dy: number, quarterPx: number): number

The angle, in degrees, a picked layer has been dragged to: forwards is positive, and it stops at a half turn either way. quarterPx is the pixels that make a quarter turn.

function dragHint

dragHint(moves: CubeMove | readonly CubeMove[], n: number, view: Mat3): DragHint | null

The drag that makes this turn, from the way the cube is looked at now (view, as viewMatrix makes it). Several layers about one axis, turned as far, are one slab and get one arrow across it; each layer is still turned by a drag of its own. Null for a turn of the whole cube, which no drag on a sticker makes, and for turns that are not one movement.

type DragHint

type DragHint = { /** The turn's axis. */ axis: CubeAxis; /** The layers that turn, counted from the axis's negative side. */ layers: number[]; /** How far, by the right-hand rule. */ turns: CubeTurns; /** Every sticker those layers carry, as indexes into the state: the stickers to light. */ slots: number[]; /** The face the arrow lies on, or null where no side of the layer can be seen well enough from here: look ro…

What to drag to make a turn of one layer, or of several about one axis as one (a wide turn).

type DragPick

type DragPick = { axis: CubeAxis; layer: number; along: [number, number] };

The layer a drag has picked: its axis and layer, and the way across the screen (a unit vector, x right and y down) that turns it forwards by the right-hand rule.

function encodeCubeMoves

encodeCubeMoves(moves: readonly CubeMove[]): string

Moves written three characters each, such as x23y02z*1: compact, and readable without knowing the cube's size.

const FACE_FRAMES

FACE_FRAMES: Readonly<Record<"U" | "R" | "F" | "D" | "L" | "B", { normal: Vec3; right: Vec3; down: Vec3; }>>

Which way each face looks, and its rows and columns as the face is seen from outside, in the order above.

const FACE_PROPERTIES

FACE_PROPERTIES: Readonly<Record<"U" | "R" | "F" | "D" | "L" | "B", string>>

The CSS custom property that colours each face, where no colour is given in code: set it on the cube's element or any ancestor.

function faceMove

faceMove(face: CubeFaceLetter, depth: number, amount: "cw" | "half" | "ccw", n: number): CubeMove | null

A face turned, depth layers in from it (1 is the face itself), on a cube of this size; null where the cube has no such layer.

function faceOfNormal

faceOfNormal(normal: Vec3): CubeFace

The face whose outward normal this is.

function faceOfSlot

faceOfSlot(at: number, n: number): CubeFace

The face letter a solved cube shows at this slot.

function fill

fill(text: string, values?: Readonly<Record<string, string | number>>): string

A line of a table with its braces filled in: fill("A {n}×{n} cube", { n: 3 }) is "A 3×3 cube". A brace with nothing to fill it is left as it is.

const FLICK_ANGLE

FLICK_ANGLE: 4

The least a flick must have turned the layer, in degrees.

const FLICK_SPEED

FLICK_SPEED: 0.25

A flick: the layer still turning this fast the drag's own way, in degrees a millisecond, when the pointer lets go.

function fromJSON

fromJSON(text: string): SolveRecord[] | null

The solves in a JSON export, or null where the text is not one: not JSON, not this format, or a later one. Each solve is put together again from its size and its notation; one whose notation the cube cannot turn is left out, and what the file says of state, solved and count is ignored.

function fromText

fromText(text: string): SolveRecord | null

A solve read back from plain lines, or null where they are not one. The size is a line such as 3x3 or 3×3 (a 3×3 where there is none), and scramble:, solve:, time: and seed: name the rest, in any order and either case. Lines with no name are turns of the solve, so a bare line of notation is read too.

const FULL_SCRAMBLE_LENGTHS

FULL_SCRAMBLE_LENGTHS: Readonly<Record<number, number>>

The lengths official competitions scramble each size to, near enough: what a "full" scramble means here.

const Guide

Guide: typeof Guide

A guide through a list of moves or the method's steps, for a cube of side n starting from state.

const GUIDE_CSS

GUIDE_CSS: "\n.kyuubu-guide{display:grid;gap:.3rem;font:inherit;color:inherit;max-width:100%}\n.kyuubu-guide p{margin:0}\n.kyuubu-guide-move{display:flex;align-items:baseline;gap:.75rem;flex-wrap:wrap}\n.kyuubu-guide-move b{font:700 1.6em/1.1 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:var(--kyuubu-guide-ink,inherit)}\n.kyuubu-guide-at{font-size:.85em;opacity:.75;margin-inline-start:auto;font-variant-…

The guide's stylesheet, put in the page once.

type GuideHandle

type GuideHandle = { /** The guide it shows: what is next, what has been done, any detour. */ readonly guide: Guide; /** Every detour taken back, on the cube. */ takeBack(): void; /** The next movement made for the person, on the cube, after taking back any detour. */ makeNext(): void; /** Another list or the method, from the cube as it is now. */ load(source: GuideSource): void; setLocale(locale: KyuubuLanguage): v…

A guide beside a cube, on the page.

type GuideHeard

type GuideHeard = "done" | "part" | "off" | "back";

What a guide made of a turn it was told of: the movement finished, part of it made, a detour, or a detour taken back by hand.

type GuidePanelOptions

type GuidePanelOptions = GuideSource & { /** English or Japanese; the page's `lang` when left out. */ locale?: KyuubuLanguage; /** Called whenever what it shows changes: after every turn it hears (with what it made of it), and when it takes a turn back or makes one. */ onChange?: (guide: Guide, heard: GuideHeard | null) => void; /** Called once, when the last movement has been made. */ onEnd?: () => void; };

How a guide beside a cube is set up: what it walks, and how it speaks.

type GuideSource

type GuideSource = { moves: string | readonly (CubeMove | readonly CubeMove[] | SolveMove)[] } | { method: true };

What a guide walks: moves as written ("R U R' U'", wide turns and rotations read as parseSolve reads them), or as moves, each a movement; or the layer-by-layer method, step by step from the cube as it is (2×2 and 3×3).

type GuideStep

type GuideStep = { /** What is still to turn of it: the whole movement at first, less any layer or quarter already made. */ moves: CubeMove[]; /** The movement as asked for. */ whole: CubeMove[]; /** The movement in notation: `R'`, `Rw`, `x`. */ text: string; /** What is still to turn, in notation: `2R` once the `R` of an `Rw` is made. */ left: string; /** Whether it turns the whole cube, which no drag on a sticker …

The movement a guide asks for now.

const HINT_MIN_FACING

HINT_MIN_FACING: 0.2

How squarely a face must face the viewer for an arrow to be drawn on it: the cosine of its angle to the eye.

const HINT_MIN_FOLLOW

HINT_MIN_FOLLOW: 0.5

How closely a drag along the arrow must go the way the layer turns: the cosine between them. The layer turns at least this fast for the pointer's pace.

type HintArrow

type HintArrow = { /** Where it begins: the middle of the sticker to take hold of, on the face (on the slab's middle line, for several layers). */ from: Vec3; /** The way it points, a unit vector in the face: seen from where the viewer is, the way to drag. */ along: Vec3; /** How long it is, in doubled units, kept inside the face. */ length: number; /** How wide the slab it lies across is, in doubled units: 2 a laye…

The arrow a hint draws, in the cube's doubled units (a sticker is 2 across), lying on a face.

function isCubeScale

isCubeScale(value: unknown): value is CubeScale

Whether a value is one of the three scales.

function isCubeState

isCubeState(state: string, n: number): boolean

Whether a string is a state of a cube this size: the right length, and the right number of every colour.

function joinTurns

joinTurns(moves: readonly CubeMove[]): CubeMove[]

Turns of one layer in a row written as one, and any that come to nothing left out: "U' U2" is "U", "U' U" is nothing.

function keepScrambling

keepScrambling(cube: Scrambled, options?: KeepScramblingOptions): KeepScramblingHandle

Keep a cube turning. Starts at once unless autoplay is false.

type KeepScramblingHandle

type KeepScramblingHandle = { /** Carry on, or begin. Does nothing while the page is hidden or motion is reduced; it starts when they allow it. */ start(): void; /** Stop turning. The cube stays as it is. */ stop(): void; /** Change how often it turns, at once. */ setPace(pace: number | ScramblePace): void; /** Whether it has been asked to run and nothing is holding it back. */ readonly running: boolean; /** Stop fo…

What a running loop can be told.

type KeepScramblingOptions

type KeepScramblingOptions = { /** Seconds between turns, or a pace's name: one when left out, never under 0.2. */ pace?: number | ScramblePace; /** Turn only the six outer faces, never an inner layer. */ faces?: boolean; /** A number in [0, 1), like `Math.random`; seeded, the same cube turns the same way. */ random?: () => number; /** Start by itself: true when left out. */ autoplay?: boolean; /** The page, to ask …

How it is run.

type KeyReading

type KeyReading = { move: CubeMove } | { depth: number } | null;

What a key means: a turn, a depth for the next face letter, or nothing.

type KyuubuLanguage

type KyuubuLanguage = "en" | "ja";

The two languages the package speaks.

type KyuubuStrings

type KyuubuStrings = CubeWords & CliWords;

Every word the package says: the cube's and the command line's.

function languageOf

languageOf(tag: string | null | undefined): KyuubuLanguage

The language a tag names, as far as the package speaks it: anything beginning "ja" is Japanese, and the rest is English.

function layerOf

layerOf(centre: Vec3, axis: CubeAxis, n: number): number

The layer, 0 to n − 1 from the axis's negative side, that a cubie centre sits in.

type Mat3

type Mat3 = readonly [Vec3, Vec3, Vec3];

A 3×3 matrix, rows of three.

const MAX_CLI_COUNT

MAX_CLI_COUNT: 100

The most scrambles the command line makes at once.

const MAX_CLI_LENGTH

MAX_CLI_LENGTH: 1000

The longest scramble the command line will make.

const MAX_RECORD_MOVES

MAX_RECORD_MOVES: 10000

The most turns one record may hold, scramble and solve each.

const MAX_RECORD_SEED

MAX_RECORD_SEED: 200

The longest seed kept with a solve.

const MAX_RECORD_SIZE

MAX_RECORD_SIZE: 7

The largest cube a record may be of: the package is made and tested for 2×2 to 7×7.

const MAX_RECORDS

MAX_RECORDS: 1000

The most solves read from one file.

const MAX_REPLAY_STEPS

MAX_REPLAY_STEPS: 2000

The longest solve a replay takes, in steps.

function middleMove

middleMove(letter: "M" | "E" | "S", amount: "cw" | "half" | "ccw", n: number): CubeMove | null

The middle layer of an odd cube, turned as M, E or S; null on an even cube, which has none.

const MIN_RECORD_SIZE

MIN_RECORD_SIZE: 2

The smallest cube a record may be of.

function mountGuide

mountGuide(host: HTMLElement, view: CubeView, options: GuidePanelOptions): GuideHandle

A guide drawn in an element, for the cube in view: the view shows the hint, and a person makes each movement on it. The view must let a person turn it (interactive).

function moveFits

moveFits(n: number, move: CubeMove): boolean

Whether a move can be made on a cube of this size.

function moveForDrag

moveForDrag(slot: StickerSlot, n: number, view: Mat3, dx: number, dy: number): CubeMove

The turn a drag across a sticker means: the layer that carries it most nearly the way the pointer went, that way.

function moveForRelease

moveForRelease(pick: DragPick, quarters: number): CubeMove | null

The move a release makes: the picked layer turned by those quarters, or null for none.

function moveForWheel

moveForWheel(slot: StickerSlot, n: number, view: Mat3, way: "across" | "upDown" | "face", down: boolean): CubeMove

The turn the wheel means over a sticker. across turns the layer that carries it most nearly sideways, rolling the wheel down carrying it right; upDown the layer that carries it most nearly up and down, down carrying it down; face turns the face it is on, down turning it clockwise.

function movementSays

movementSays(moves: readonly CubeMove[], n: number, language?: KyuubuLanguage): string | null

A movement in plain words, for a cube held with its front towards you: "Turn the right face away from you.", 「右の面を奥に回します。」. For a turn of the whole cube, where its faces go. Null for turns that are not one movement (different axes, or different amounts).

function movementText

movementText(moves: readonly CubeMove[], n: number): string

A movement in notation: one layer as moveNotation writes it; several layers from a face inwards, about one axis and as far, as a wide turn (Rw, 3Rw'); anything else as its moves one after another.

function moveNotation

moveNotation(move: CubeMove, n: number): string

A move as a person writes it, for a cube of this size.

function movesNotation

movesNotation(moves: readonly CubeMove[], n: number): string

Moves as a person writes them, a space between each.

function nextTurn

nextTurn(n: number, last: CubeMove | null, random: (): number, faces: boolean) => CubeMove

One random layer, a quarter or a half turn either way, never about the axis the last one used, so it never undoes or joins it.

type NotationFault

type NotationFault = { /** The piece of text that is not a move on this cube. */ token: string; /** Where it begins, counted in characters from 0. */ at: number; /** Its line, from 1. */ line: number; /** Its place in that line, from 1. */ column: number; /** What is wrong with it: not notation at all, or a layer this cube does not have. */ reason: "unknown" | "no-such-layer"; };

Why a line of notation could not be read, and where.

function paceSeconds

paceSeconds(pace: number | ScramblePace | undefined): number

Seconds for a pace given as a number or a name; the default for anything else.

function parseMove

parseMove(text: string, n: number): CubeMove | null

One move read back from the notation, for a cube of this size, or null where it is not one: R, R', R2, 2R', M2, x'.

function parseMoves

parseMoves(text: string, n: number): CubeMove[] | null

A line of notation read back, or null where any of it is not a move on this cube.

function parseSolve

parseSolve(text: string, n: number): SolveReading

A solve, or a scramble, read from the way it is written, for a cube of this size. Everything it could read comes back even when it stops at a fault, so a page can show how far it got.

function parseSolveMove

parseSolveMove(token: string, n: number): Omit<SolveMove, "at"> | "unknown" | "no-such-layer"

One written step read for a cube of this size, or why it is not one.

function pastCommit

pastCommit(angle: number, commitAngle?: number): boolean

Whether a dragged layer is past the point of no return: letting go now, at rest, makes a move.

function permutationOf

permutationOf(n: number, move: CubeMove): Int32Array

Where every sticker goes under one turn: to[i] is the slot the sticker in slot i lands on. Worked out once and kept.

function pickDrag

pickDrag(slot: StickerSlot, n: number, view: Mat3, dx: number, dy: number): DragPick | null

The layer a drag across a sticker means, once it can be told: null while the pointer has not gone far enough, or while the two layers that carry the sticker are too nearly as likely as each other. Past DRAG_DECIDE_PX the likelier is picked whatever the odds.

function planReplay

planReplay(source: ReplaySource): { ok: true; plan: ReplayPlan; } | { ok: false; fault: ReplayFault; }

A scramble and a solve read, checked and timed. A solve that does not end on a solved cube is still planned, with solved: false, so that it can be shown and said to be unfinished; only text that cannot be read is refused.

function prefersReducedMotion

prefersReducedMotion(): boolean

Whether this device asks for less motion.

function quartersForRelease

quartersForRelease(angle: number, speed?: number, commitAngle?: number): -2 | -1 | 0 | 1 | 2

What letting go makes of a dragged layer: the quarter turns it snaps to, from −2 to 2, where 0 is back where it was and no move at all.

Short of the point of no return (commitAngle) it goes back; from there up to the same distance past a quarter turn it is one quarter turn; further is a half turn. A flick, still moving fast the way it was dragged, makes the quarter turn from short of the point. speed is in degrees a millisecond, signed like the angle.

function quarterTurn

quarterTurn(v: Vec3, axis: CubeAxis): Vec3

A quarter turn counter-clockwise, seen from the axis's positive end.

function randomScramble

randomScramble(n: number, length: number, random?: (): number, options?: ScrambleOptions) => CubeMove[]

A scramble of length turns, each of one layer chosen at random, never two in a row about the same axis, so no turn undoes or joins the one before it and the count is the count. Never leaves the cube solved: a turn is added until it is not.

random is a number in [0, 1), like Math.random; pass a seeded one (seededRandom) to get the same scramble from the same seed everywhere. With faces, only the six outer faces are turned.

function readKey

readKey(key: string, shift: boolean, n: number, depth: number): KeyReading

What a key means on a cube of side n, given the depth a digit before it set (1 where none did).

const RECORD_FORMAT

RECORD_FORMAT: 1

The version of the JSON's shape. It goes up only if a reader of the old shape would be wrong about the new one.

const Replay

Replay: typeof Replay

A planned solve played on a cube: play, pause, a step either way, anywhere by seek, faster or slower, once or over and over.

const REPLAY_LOOP_REST_MS

REPLAY_LOOP_REST_MS: 1200

How long a looped replay rests on the solved cube before it begins again, in milliseconds.

const REPLAY_SPEEDS

REPLAY_SPEEDS: readonly [1, 0.5, 0.25, 0.1]

The speeds a replay offers: the solve's own pace, half, a quarter and a tenth of it.

const REPLAY_STEP_MS

REPLAY_STEP_MS: 400

How long a step takes when the solve gives no time of its own, in milliseconds.

type ReplayClock

type ReplayClock = { set(run: () => void, ms: number): unknown; clear(handle: unknown): void };

The clock a replay runs by; the page's own unless a test gives another.

type ReplayCube

type ReplayCube = { setState(state: string, size?: number): void; turnTogether(moves: readonly CubeMove[], options?: { animate?: boolean; ms?: number }): void; };

The part of a cube's view a replay needs: CubeView is one.

type ReplayFault

type ReplayFault = { part: "size" | "scramble" | "solution" | "length"; fault?: NotationFault };

Why a replay could not be planned: which text would not read, and where.

type ReplayOptions

type ReplayOptions = { /** The speed it starts at: 1, the solve's own pace, when left out. */ speed?: number; /** Whether it begins again when it ends. */ loop?: boolean; /** Called whenever anything in `status` changes. */ onChange?: (status: ReplayStatus) => void; /** Called once each time the end is reached. */ onEnd?: () => void; /** The clock to run by. */ clock?: ReplayClock; };

How a replay is set up.

type ReplayPlan

type ReplayPlan = { /** The cube's side. */ size: number; /** The scramble's steps. */ scramble: SolveMove[]; /** The solve's steps. */ steps: SolveMove[]; /** The cube after the scramble: where a replay starts. */ start: string; /** The cube after every step, `states[k]` being the cube after `k` steps; `states[0]` is `start`. */ states: string[]; /** Whether the last of them is a solved cube, whichever way up. */ s…

A solve ready to be played: read, checked and timed.

type ReplaySource

type ReplaySource = { /** The cube's side: 3 when left out. */ size?: number; /** The scramble, as written. */ scramble: string; /** The solve, as written; comments and brackets are skipped. */ solution: string; /** How long the solve took, in milliseconds, when that is known. */ timeMs?: number; /** How long each step of the solution took, in milliseconds, when that is known; one for every step. */ stepMs?: readonl…

What a replay is made from.

type ReplayStatus

type ReplayStatus = { /** How many steps have been made: 0 on the scrambled cube, `total` at the end. */ position: number; /** How many steps the solve has. */ total: number; /** Whether it is running. */ playing: boolean; /** Whether it has reached the end. */ ended: boolean; /** The speed it runs at: 1 is the solve's own pace. */ speed: number; /** Whether it begins again when it ends. */ loop: boolean; /** The ti…

What a replay tells whoever is showing it.

function rotationKeys

rotationKeys(move: CubeMove): string

The key or keys that make a turn of the whole cube: X, Shift+X, X X.

function runCli

runCli(args: readonly string[], surroundings?: CliSurroundings): CliResult

The whole command line. args are the arguments after the command's name; the result is the exit code and the text for each stream. Exit code 0 is done, 1 is something asked for that could not be done (turns the cube cannot make, a cube that is not solved under --verify), and 2 is a command that was itself wrong.

const SCRAMBLE_DEFAULT_PACE

SCRAMBLE_DEFAULT_PACE: 1

The pace when none is given: one turn a second.

const SCRAMBLE_PACES

SCRAMBLE_PACES: { readonly fast: 0.5; readonly normal: 1; readonly slow: 4; }

How often it turns, in seconds, by name.

const SCRAMBLE_SHORTEST

SCRAMBLE_SHORTEST: 0.2

The shortest wait it allows, in seconds: faster is a blur, and a page that asks for it gets this.

type Scrambled

type Scrambled = { readonly size: number; readonly busy: boolean; turn(move: CubeMove, options?: { animate?: boolean }): void; };

What the loop turns: the cube on the screen has all of it.

type ScrambleOptions

type ScrambleOptions = { /** Turn only the six outer faces, never an inner layer: on a 3×3, no M, E or S. Every layer may turn when left out. */ faces?: boolean; };

How a scramble is made.

type ScramblePace

type ScramblePace = keyof typeof SCRAMBLE_PACES;

One of those names.

type ScramblePage

type ScramblePage = { readonly hidden: boolean; addEventListener(type: "visibilitychange", listener: () => void): void; removeEventListener(type: "visibilitychange", listener: () => void): void; };

The part of a page the loop listens to: a Document is one.

function seededRandom

seededRandom(seed: string): () => number

A SEEDED RANDOM SOURCE: the same seed gives the same numbers, in the same order, on every device and in every version from 1.1.0 on. Hand it to randomScramble and two people a world apart turn the same scramble.

The seed is any text. It is hashed to 32 bits (xmur3) and the numbers come from mulberry32: small, quick and even enough for a scramble. It is not for secrets.

const SOLVABLE_SIZES

SOLVABLE_SIZES: readonly number[]

The sizes the method is written for.

const SOLVE_ALGORITHMS

SOLVE_ALGORITHMS: { readonly cornerIn: "R U R' U'"; readonly edgeRight: "U R U' R' U' F' U F"; readonly edgeLeft: "U' L' U L U F U' F'"; readonly yellowCross: "F R U R' U' F'"; readonly sune: "R U R' U R U2 R'"; readonly cornerCycle: "R' F R' B2 R F' R' B2 R2"; readonly edgeCycle: "R U' R U R U R U' R' U' R2"; }

The algorithms the method uses, by name. The notation is for white on the bottom.

type SolveAlgorithm

type SolveAlgorithm = keyof typeof SOLVE_ALGORITHMS;

The name of one of the method's algorithms: a key of SOLVE_ALGORITHMS.

function solvedCube

solvedCube(n: number): string

A solved cube of this size.

type SolveMove

type SolveMove = { /** The step as it is written in standard form: `R`, `Rw'`, `3Uw2`, `x`. */ text: string; /** The layers it turns, each as a move of its own; more than one for a wide turn. */ moves: CubeMove[]; /** Whether it counts as a move: a turn of the whole cube does not. */ counts: boolean; /** Where it began in the text it was read from, counted in characters from 0. */ at: number; };

One thing a solver did: a turn, a wide turn or a turn of the whole cube.

function solveMoves

solveMoves(steps: readonly SolveMove[]): CubeMove[]

Every layer a list of steps turns, in order: what turnAll takes.

type SolvePart

type SolvePart = { moves: CubeMove[]; algorithm?: SolveAlgorithm };

A part of a step: turns to line something up, or one of the method's algorithms.

type SolveReading

type SolveReading = { ok: true; steps: SolveMove[] } | { ok: false; steps: SolveMove[]; fault: NotationFault };

What reading a solve gives: its steps, or the first thing that could not be read.

type SolveRecord

type SolveRecord = { /** The cube's side, 2 to 7. */ size: number; /** The turns that scrambled a solved cube. */ scramble: CubeMove[]; /** The turns made after the scramble, in order, whole-cube turns among them. */ moves: CubeMove[]; /** How long the solve took, in milliseconds, where it was timed. */ ms?: number; /** When it was done, in milliseconds since 1970, where that is known. */ at?: number; /** The seed t…

One solve: the cube's size, the scramble that mixed it, and the turns made after.

type SolveStage

type SolveStage = | "hold" | "whiteCross" | "whiteCorners" | "whiteLayer" | "middleLayer" | "yellowCross" | "yellowFace" | "yellowCorners" | "yellowEdges";

The stages of the method, in the order they are done.

type SolveStep

type SolveStep = { stage: SolveStage; /** The turns, in order, for a cube held as the step before left it, with a turn of a face followed by another of the same face written as one. */ moves: CubeMove[]; /** The same turns as they are taught: lining-up turns, and each algorithm whole. */ parts: SolvePart[]; /** The algorithms the step turns, in order. */ algorithms: SolveAlgorithm[]; };

One step of a solve: its stage, the turns that do it, and the parts they come in.

function solveSteps

solveSteps(state: string, n: number): SolveStep[] | null

The layer-by-layer solve of a 2×2 or 3×3 in this state, as steps a person can follow, or null for a size the method is not written for. Turning every step's moves in order, from state, leaves the cube solved.

type SolveSummary

type SolveSummary = { size: number; /** The scramble in notation. */ scramble: string; /** The turns after it in notation. */ moves: string; /** The cube once scrambled. */ start: string; /** The cube after every turn. */ state: string; /** Whether it ended solved. */ solved: boolean; /** The moves that count: every turn but those of the whole cube. */ count: number; };

What a record comes to, worked out from its turns.

function solveText

solveText(steps: readonly SolveMove[]): string

The steps written out in standard form, a space between each.

function stageName

stageName(stage: SolveStage, language?: KyuubuLanguage): string

The name of a step of the solve, as a person would say it: "White cross", 「白のクロス」.

function stageSays

stageSays(stage: SolveStage, language?: KyuubuLanguage): string

What a step of the solve is for, in a sentence: "Make a yellow cross on the top."

type StickerSlot

type StickerSlot = { centre: Vec3; normal: Vec3 };

Where one sticker slot sits: its cubie's centre and the direction it faces.

const STRINGS

STRINGS: Readonly<Record<KyuubuLanguage, KyuubuStrings>>

The package's words in both languages. A page in a third language passes its own table where one is taken: { ...STRINGS.en, solved: "Resuelto" }.

function summarize

summarize(record: SolveRecord): SolveSummary

A record's scramble and turns in notation, the cube before and after, whether it ended solved, and its count of moves.

function toCSV

toCSV(records: readonly SolveRecord[]): string

A list of solves as CSV, for a spreadsheet: a header, then a row a solve, with lines ended CRLF as RFC 4180 has them. The columns are time (when, ISO 8601), size, scramble, moves, count, solved, seconds and seed.

function toJSON

toJSON(records: SolveRecord | readonly SolveRecord[]): string

One solve or many, as JSON: { "format": 1, "generator": "kyuubu 1.1.0", "solves": [ … ] }. Each solve carries its size, its scramble and its turns in notation, and what they come to (state, solved, count), for a reader that does not have the package.

function toText

toText(record: SolveRecord): string

A solve as a few plain lines, to paste in a chat or a note:

3x3 scramble: R U2 F' solve: F U2 R' time: 12.34

A line is left out where there is nothing to say. fromText reads it back.

function turnAll

turnAll(state: string, n: number, moves: readonly CubeMove[]): string

The state after every turn in order.

function turnCube

turnCube(state: string, n: number, move: CubeMove): string

The state after one turn.

function undoAll

undoAll(moves: readonly CubeMove[]): CubeMove[]

A list of moves undone, last first.

function undoOf

undoOf(move: CubeMove): CubeMove

The move that takes this one back.

type Vec3

type Vec3 = readonly [number, number, number];

A point in the cube's doubled coordinates: a cubie's centre is a whole even or odd step from the middle, by the cube's size.

const VERSION

VERSION: "1.6.0"

The package's version, as in package.json. Written into every export, so a file says what made it.

function viewMatrix

viewMatrix(yaw: number, pitch: number): Mat3

The way the viewer looks at the cube: turned about the model's y (yaw), then tipped about the screen's x (pitch), in degrees.

function wholeMove

wholeMove(letter: "x" | "y" | "z", amount: "cw" | "half" | "ccw"): CubeMove

The whole cube turned in the hand, as x, y or z.

const WORDS

WORDS: Readonly<Record<KyuubuLanguage, CubeWords>>

The cube's words in both languages. STRINGS in strings.ts is these and the command line's together.

@johnmorrisdotca/kyuubu/react

Kyuubu KyuubuHandle KyuubuProps

function Kyuubu

Kyuubu({ ref, className, style, size, state, interactive: interactiveGiven, hint, onTurn, onLook, ...rest }: KyuubuProps): import("/home/runner/work/kyuubu/kyuubu/node_modules/.pnpm/@types+react@19.3.0/node_modules/@types/react/index").JSX.Element

THE CUBE AS A REACT COMPONENT: a thin wrapper round CubeView, which does the work. It fills the box it is given: with no className that box is a square as wide as its container, and a className must position it (relative or absolute) and give it a size.

type KyuubuHandle

type KyuubuHandle = { /** Turn a layer, animated; told to `onTurn` only when `report` is set. */ turn: (move: CubeMove, options?: { report?: boolean; animate?: boolean }) => void; /** Put the stickers as given, at once. */ setState: (state: string) => void; /** Back to the way it was first seen. */ resetLook: () => void; /** Change its colours, its plastic or the shape of its stickers, without redrawing it. */ setTh…

What a parent can ask of the cube on the screen.

type KyuubuProps

type KyuubuProps = Omit<CubeViewOptions, "state"> & { /** * The stickers the cube starts with. Read when the cube is made, and again * whenever it changes to something the cube is not already showing, so a * parent that keeps the state and hands it back never makes it jump. */ state?: string; className?: string; style?: CSSProperties; ref?: Ref<KyuubuHandle>; colours?: Partial<Record<CubeFace, string>>; /** * Moves …

The component's props: every option of CubeView, a state the parent may keep, and the box's className, style and data-*.

@johnmorrisdotca/kyuubu/famous

FAMOUS_SOLVES famousSolve FamousSolve

const FAMOUS_SOLVES

FAMOUS_SOLVES: readonly FamousSolve[]

The famous solves, the newest record first.

function famousSolve

famousSolve(id: string): FamousSolve | undefined

One famous solve by its id, or undefined.

type FamousSolve

type FamousSolve = { /** A short name for it, fixed for good: the solver and the time. */ id: string; /** The cube's side. */ size: number; /** The official time, in milliseconds. */ timeMs: number; /** Who solved it, as the WCA's results name them. */ solver: string; /** The country they represent. */ country: string; /** The competition. */ competition: string; /** The competition's id at worldcubeassociation.org.…

One famous solve.

@johnmorrisdotca/kyuubu/player

mountPlayer PLAYER_CSS PlayerHandle PlayerOptions

function mountPlayer

mountPlayer(host: HTMLElement, options: PlayerOptions): PlayerHandle

A solve drawn in an element, with its controls.

const PLAYER_CSS

PLAYER_CSS: "\n.kyuubu-player{display:grid;gap:.5rem;font:inherit;color:inherit;max-width:100%}\n.kyuubu-player-cube{aspect-ratio:1;width:100%;position:relative;background:var(--kyuubu-player-felt,transparent);border-radius:var(--kyuubu-player-radius,12px);touch-action:none}\n.kyuubu-player-row{display:flex;flex-wrap:wrap;gap:.375rem;align-items:center}\n.kyuubu-player button{touch-action:manipulation;font:inherit;c…

The player's stylesheet, put in the page once.

type PlayerHandle

type PlayerHandle = { /** The solve as it was read, or why it could not be. */ readonly plan: ReplayPlan | null; readonly fault: ReplayFault | null; /** Another solve on the same cube, in place of the one showing. The speed and the repeat are kept unless given. */ load(source: ReplaySource, how?: { speed?: number; loop?: boolean; controls?: boolean; autoplay?: boolean; guide?: boolean }): void; play(): void; pause()…

A player on the page.

type PlayerOptions

type PlayerOptions = ReplaySource & { /** Start playing as soon as it is drawn. */ autoplay?: boolean; /** Begin again when it ends. */ loop?: boolean; /** Show the controls: true unless said otherwise. A cube with none still plays when `autoplay` is set. */ controls?: boolean; /** The speed it starts at: 1 is the solve's own pace. */ speed?: number; /** The cube's colours. */ theme?: CubeTheme; /** Start with the c…

How a player is set up: the solve, and how it is shown.

@johnmorrisdotca/kyuubu/element

CUBE_ELEMENT_ATTRIBUTES CUBE_ELEMENT_NAME defineCube defineScramble KyuubuCubeElement KyuubuScrambleElement paceFromAttribute SCRAMBLE_ELEMENT_ATTRIBUTES SCRAMBLE_ELEMENT_NAME

const CUBE_ELEMENT_ATTRIBUTES

CUBE_ELEMENT_ATTRIBUTES: readonly ["size", "scramble", "moves", "time", "autoplay", "controls", "loop", "speed", "theme", "lang", "guide"]

The attributes the element reads. Changing any draws the player afresh.

const CUBE_ELEMENT_NAME

CUBE_ELEMENT_NAME: "kyuubu-cube"

The name the element is registered under.

function defineCube

defineCube(name?: string): void

Register <kyuubu-cube>, once. Where there is no browser, or the name is already taken, it does nothing. A page that wants another name passes it.

function defineScramble

defineScramble(name?: string): void

Register <kyuubu-scramble>, once. Where there is no browser, or the name is already taken, it does nothing.

type KyuubuCubeElement

type KyuubuCubeElement = HTMLElement & { play(): void; pause(): void; step(by: 1 | -1): void; seek(position: number): void; restart(): void; readonly status: ReplayStatus | null; };

What the element adds to an ordinary one.

type KyuubuScrambleElement

type KyuubuScrambleElement = HTMLElement & { play(): void; pause(): void; readonly running: boolean; readonly cube: CubeView | null; };

What the element adds to an ordinary one.

function paceFromAttribute

paceFromAttribute(value: string | null): number | ScramblePace | undefined

A pace from an attribute: a name, or seconds. Anything else is left to the default.

const SCRAMBLE_ELEMENT_ATTRIBUTES

SCRAMBLE_ELEMENT_ATTRIBUTES: readonly ["size", "pace", "paused", "scale", "width", "theme", "faces", "interactive", "seed", "lang"]

The attributes the element reads. Changing any draws the cube afresh, except pace and paused, which change the loop alone.

const SCRAMBLE_ELEMENT_NAME

SCRAMBLE_ELEMENT_NAME: "kyuubu-scramble"

The name the element is registered under.

@johnmorrisdotca/kyuubu/element/define

defineCube defineScramble

function defineCube

defineCube(name?: string): void

Register <kyuubu-cube>, once. Where there is no browser, or the name is already taken, it does nothing. A page that wants another name passes it.

function defineScramble

defineScramble(name?: string): void

Register <kyuubu-scramble>, once. Where there is no browser, or the name is already taken, it does nothing.