Kazu数

@johnmorrisdotca/kazu 1.0.0 · 5 entry points · 122 exports

@johnmorrisdotca/kazu

answerOf boxedLayout Boxes boxOf Cage CAGE_LETTERS cageOutline checkKazu clearCell cluesOf conflictsOf countKazuSolutions decodeCells decodeJigsaw decodeKiller decodeMoreOrLess decodeNotes decodeRegions decodeRun decodeSteps decodeTowers EMPTY_CELL encodeCells encodeJigsaw encodeKiller encodeMoreOrLess encodeNotes encodeRegions encodeRun encodeSteps encodeTowers enterNumber freshKazuSeed gameConflicts generateDiagonal generateJigsaw generateKazu generateMoreOrLess generateNumberPlace generateSumCages generateTowers hintKazu isKazuGivens isKazuKind isKazuLevel isKazuSeed isKazuSize isPrinted isSolved KAZU_BOXES KAZU_KIND_OF_SITE_KIND KAZU_KINDS KAZU_LEVELS KAZU_SEED_MOST KAZU_SPECS KAZU_STEPS_KEPT KazuCheck kazuClockText KazuGame KazuGameOptions KazuGivens kazuGuessDepth kazuHash KazuHint KazuKind KazuLevel kazuProgress KazuPuzzle KazuSpec layoutOfGivens lineFrom Mark neighbours newKazuGame NO_MARK noClues notesOf numberCounts Random readGivens regionsAreSound restartKazu seededRandom Segment shuffled solveKazu stepEntry symbolOf toggleNote TOWER_SIDES TowerClues TowerSide towersSeen undoKazu valueOfSymbol valuesOf VERSION

function answerOf

answerOf(game: KazuGame): string

The whole grid as a cells code: what checkKazu takes as an answer.

function boxedLayout

boxedLayout(size: number, diagonal?: boolean): Layout

Rows, columns and boxes; with diagonal, the two long diagonals as well.

type Boxes

type Boxes = { rows: number; cols: number };

Where the boxes are on a Sudoku grid: how many rows and columns of cells each box holds.

function boxOf

boxOf(size: number, index: number): number

The box a cell is in, numbered row-major from 0.

type Cage

type Cage = { cells: number[]; sum: number };

A cage: the cells in it and the sum of the numbers inside.

const CAGE_LETTERS

CAGE_LETTERS: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

The letters that name a cage in a code: up to sixty-two cages.

function cageOutline

cageOutline(size: number, cageOf: (index: number): number | undefined, inset?: number) => Segment[]

THE DASHED OUTLINE OF EVERY CAGE, as line segments in cell units: each cage drawn a little inside its own edge, the way a printed Killer Sudoku draws it, so a cage reads as a shape within the box rules rather than as one of them.

Each cell draws the sides where its neighbour is in another cage, inset inside the cell. Where the cage goes on past a side's end, the line runs on to meet the next line of the outline: to the cell's edge when the cage's edge carries on straight, and inset past it at an inside corner, where the outline turns back into the cage. That is what makes the lines of one cage meet, with no gap and no overshoot, whatever its shape.

function checkKazu

checkKazu(kind: KazuKind, size: number, givens: string, answer: string): KazuCheck

Whether an answer solves a puzzle: O(cells), no search, nothing remembered between calls. A browser runs it to say "done"; a server runs it before it believes a solve, so a grid that is right is accepted and a grid that was merely posted is not. It refuses rather than repairs: a grid of the wrong size, a value out of range or a given moved is a "no" with its reason, never a best guess at what was meant.

The rules of each puzzle are restated here rather than shared with the solver on purpose: the solver is what MADE the puzzle, and a check that reads the solver's mind proves only that the solver agrees with itself.

function clearCell

clearCell(game: KazuGame, cell: number): KazuGame

Empty a cell of its number and its pencil marks.

function cluesOf

cluesOf(solution: readonly number[], size: number): TowerClues

Every clue a finished square makes true: what each side of it sees.

function conflictsOf

conflictsOf(givens: KazuGivens, values: readonly number[]): number[]

THE CELLS THAT BREAK A RULE RIGHT NOW, read from the rules alone: no answer is needed, so it works on any puzzle, and it names a mistake the moment it is made rather than when it is checked.

values is the whole grid as it stands: the printed numbers and what has been written, row-major, 0 for empty. A cell is in conflict when it shares a row, a column, a box, a region, a diagonal or a cage with a cell holding the same number; when a cage is full and does not add to its sum, or already adds to more; when a more-than mark between two filled cells is not true; or when the cells from a Towers clue show more towers than the clue says (or, once the line is full, any other number). An empty cell is never in conflict, and a grid with no conflict is not thereby right: it may simply not be finished.

function countKazuSolutions

countKazuSolutions(kind: KazuKind, size: number, givens: string, limit?: number, budget?: number): number | null

How many answers a puzzle has, up to limit (two by default, so "many" costs no more than "two"): 1 is a puzzle, 0 is a grid that cannot be finished, 2 is a guessing game. Null, never a number, for givens that are not a puzzle of this kind and side, and for a search that ran past budget steps (Sudoku, Jigsaw, Diagonal and Sum Cages count steps; the other two have no budget): "I could not say" is not "there are none".

function decodeCells

decodeCells(code: string, size: number): number[] | null

".3.1" at a side of 2 → [0, 3, 0, 1], or null for a string that is not a grid of that size: the wrong length, a value past the side, a stray character. Null rather than a grid with holes, because a grid with holes is a grid.

function decodeJigsaw

decodeJigsaw(code: string, size: number): { cells: number[]; regions: number[]; } | null

The cells and regions a Jigsaw's givens say, or null for a string that is not a Jigsaw of this side.

function decodeKiller

decodeKiller(code: string, size: number): { cells: number[]; cages: Cage[]; } | null

The cells and cages a code says, or null for one that is not a whole, well-formed puzzle: every cell named to a cage, every cage used, and a sum for each.

function decodeMoreOrLess

decodeMoreOrLess(code: string, size: number): { cells: number[]; marks: Mark[]; } | null

function decodeNotes

decodeNotes(code: string, size: number): number[] | null

The pencil marks a code says (each cell a bit mask, bit v for the number v), or null for a code that is not one for a grid of this side.

function decodeRegions

decodeRegions(code: string, size: number): number[] | null

Regions row-major, one letter each, or null for a string that is not that.

function decodeRun

decodeRun(code: string, size: number): number[] | null

The entries a run's code says, or null for a code that is not a run of this side.

function decodeSteps

decodeSteps(log: string, cells: number): string[] | null

The grids a step log holds, each cells characters long, or null for a log that does not read as one.

function decodeTowers

decodeTowers(code: string, size: number): { cells: number[]; clues: TowerClues; } | null

The cells and clues a code says, or null for a string that is not a Towers puzzle of this size.

const EMPTY_CELL

EMPTY_CELL: "."

A GRID OF NUMBERS AS A STRING: for an address, a POST body, and a kept run.

Row-major, one character per cell: a digit for a value, . for an empty cell. Past nine the values are letters, A for 10 up to G for 16, as a 16×16 Sudoku is printed (symbolOf), so one character is still one cell. Upper case only in a code, so one grid has one spelling. These are the spellings itsutsu.com has always stored, and they decode here unchanged.

function encodeCells

encodeCells(cells: readonly number[]): string

[0, 3, 0, 1] → ".3.1".

function encodeJigsaw

encodeJigsaw(cells: readonly number[], regions: readonly number[]): string

A Jigsaw's givens: the cells, then the regions, one letter a cell. The regions ride in the givens because they are the puzzle: a check reads a finished grid against the regions it was handed, in one pass, and never has to make them again.

function encodeKiller

encodeKiller(cells: readonly number[], cages: readonly Cage[]): string

A Sum Cages puzzle's givens: the cells, then which cage each cell is in, then each cage's sum.

cells is size² characters as every number puzzle writes them (all empty, usually; a cage of one cell is its own given). cages is size² characters, one per cell, naming its cage from CAGE_LETTERS. sums is two base-36 characters a cage, in cage order. The cages ride in the givens because they are the puzzle.

function encodeMoreOrLess

encodeMoreOrLess(cells: readonly number[], marks: readonly Mark[], size: number): string

function encodeNotes

encodeNotes(notes: readonly number[]): string

Pencil marks as a code: for each cell that has any, its index in base 36 (two characters) and its marks as one number in base 36 (four characters); nothing at all for a grid with none.

function encodeRegions

encodeRegions(regions: readonly number[]): string

The region of every cell as a letter, a for the first region and so on: one character a cell.

function encodeRun

encodeRun(entries: readonly number[]): string

The entries of a run as its code.

function encodeSteps

encodeSteps(codes: readonly string[]): string

THE STEPS OF A PUZZLE, written down so a scrubber has them when the puzzle is picked up again. Every step is a grid in a run's code. The first is written whole; each after it as the cells that changed (a cell's place in base 36, two characters, and its new character), the steps parted by "~", which no run uses. A long puzzle's log is a few hundred characters, not a grid per step.

function encodeTowers

encodeTowers(cells: readonly number[], clues: TowerClues): string

function enterNumber

enterNumber(game: KazuGame, cell: number, value: number, tidy?: boolean): KazuGame

Write a number into a cell (0 empties it). Its own pencil marks go, and with tidy (the default) the number comes out of the pencil marks of every cell that shares a group with it, as a person with a pencil would rub it out. Nothing happens to a printed cell, or when the cell already holds that number.

function freshKazuSeed

freshKazuSeed(random?: Random): number

A new seed, from Math.random or the stream given: anywhere in the range.

function gameConflicts

gameConflicts(game: KazuGame): number[]

The cells that break a rule now (conflictsOf).

function generateDiagonal

generateDiagonal(size: number, level: KazuLevel, seed: number): KazuPuzzle

A Diagonal Sudoku (Sudoku X): Sudoku with the two long diagonals as groups too. 6 or 9.

function generateJigsaw

generateJigsaw(size: number, level: KazuLevel, seed: number): KazuPuzzle

A Jigsaw Sudoku of this side, level and seed: 5, 6, 7 or 9.

function generateKazu

generateKazu(kind: KazuKind, size: number, level: KazuLevel, seed: number): KazuPuzzle

A puzzle of any of the six, from a seed: the one door. The same kind, size, level and seed make the same puzzle in every browser and every Node, for ever, which is what lets a solve be kept as those four and a race be handed one number. Every puzzle has exactly one answer, and an easy one yields to singles alone.

Throws a RangeError for a kind, size, level or seed it does not make, rather than a puzzle made from something else.

function generateMoreOrLess

generateMoreOrLess(size: number, level: KazuLevel, seed: number): KazuPuzzle

A Futoshiki (More or Less) of this side, level and seed: 4, 5, 6 or 7.

function generateNumberPlace

generateNumberPlace(size: number, level: KazuLevel, seed: number): KazuPuzzle

A Sudoku (Number Place) of this side, level and seed: 4, 6, 9 or 16.

function generateSumCages

generateSumCages(size: number, level: KazuLevel, seed: number): KazuPuzzle

A Killer Sudoku (Sum Cages) of this side, level and seed: 6 or 9.

function generateTowers

generateTowers(size: number, level: KazuLevel, seed: number): KazuPuzzle

A Skyscrapers (Towers) puzzle of this side, level and seed: 4, 5, 6 or 7.

function hintKazu

hintKazu(kind: KazuKind, size: number, givens: string, entries: readonly number[], answer?: string): KazuHint | null

The next cell a person could fill in, and why (see KazuHint). entries is what the player has written, row-major (0 for empty; printed cells are ignored). answer is the puzzle's solution as a cells code; left out, it is worked out from the givens. Null when every cell is right, or when the givens are not a puzzle with exactly one answer: there is nothing true to say.

function isKazuGivens

isKazuGivens(kind: KazuKind, size: number, givens: string): boolean

Whether a puzzle's givens are a well-formed puzzle of that kind and side at all (a Jigsaw's regions sound, every code readable).

function isKazuKind

isKazuKind(value: unknown): value is KazuKind

Whether a value is one of the six keys.

function isKazuLevel

isKazuLevel(value: unknown): value is KazuLevel

Whether a value is one of the three levels.

function isKazuSeed

isKazuSeed(value: unknown): value is number

Whether a value is a seed this package takes: a whole number from 1 to KAZU_SEED_MOST.

function isKazuSize

isKazuSize(kind: KazuKind, size: number): boolean

Whether this puzzle can be made at this side.

function isPrinted

isPrinted(game: KazuGame, cell: number): boolean

Whether a cell was printed with its number: it never changes.

function isSolved

isSolved(game: KazuGame): boolean

Whether the grid is full and right, by the rules alone (checkKazu): every puzzle has exactly one answer, so a grid that keeps every rule IS that answer.

const KAZU_BOXES

KAZU_BOXES: Record<number, Boxes>

A 9×9 has 3×3 boxes and a 4×4 has 2×2; a 6×6 has boxes two rows tall and three columns wide, which is the one people get wrong and the reason this is a table rather than a square root.

const KAZU_KIND_OF_SITE_KIND

KAZU_KIND_OF_SITE_KIND: Record<string, "number-place" | "jigsaw" | "diagonal" | "sum-cages" | "more-or-less" | "towers">

The names itsutsu.com gave the six in its code, to the keys here: what a site's stored kind becomes.

const KAZU_KINDS

KAZU_KINDS: readonly ["number-place", "jigsaw", "diagonal", "sum-cages", "more-or-less", "towers"]

THE SIX PUZZLES, and what each offers: the keys, the sizes and the levels.

A key is kebab case and is the name of the puzzle everywhere in this package: the address of one, the kind of a puzzle, the value of the element's kind attribute. itsutsu.com spelled the same six numberPlace, jigsaw, diagonal, sumCages, moreOrLess and towers; KAZU_KIND_OF_SITE_KIND maps one to the other.

const KAZU_LEVELS

KAZU_LEVELS: readonly ["easy", "medium", "hard"]

How hard a puzzle is made: what the solver needed. Easy yields to singles alone, medium to one guess, hard to whatever it takes.

const KAZU_SEED_MOST

KAZU_SEED_MOST: number

The most a seed can be: a whole number from 1 to 2³¹ − 1, so it travels in an address as a plain integer.

const KAZU_SPECS

KAZU_SPECS: Record<"number-place" | "jigsaw" | "diagonal" | "sum-cages" | "more-or-less" | "towers", KazuSpec>

const KAZU_STEPS_KEPT

KAZU_STEPS_KEPT: 400

How many grids a step log keeps: the newest, so a long puzzle's log has a ceiling.

type KazuCheck

type KazuCheck = { ok: true } | { ok: false; reason: string };

The verdict on a submitted answer: { ok: true }, or { ok: false, reason } in the words the site has always used.

function kazuClockText

kazuClockText(ms: number): string

A time taken, as a clock shows it: m:ss, and h:mm:ss past an hour.

type KazuGame

type KazuGame = { kind: KazuKind; size: number; /** The puzzle's givens code, as made. */ givens: string; /** What the puzzle was printed with. */ printed: KazuGivens; /** What the player has written, row-major, 0 where nothing; always 0 on a printed cell. */ entries: readonly number[]; /** Pencil marks, a bit mask a cell: bit `v` is the note `v`. Always 0 on a printed cell or a cell with a number in it. */ notes: r…

A PUZZLE IN PLAY, as pure functions: what has been written, the pencil marks, and how to take a step back. Every function returns a new game and leaves the one it was given as it was, so a page can keep them as its history, and a server can replay a solve with no page at all. mountKazu is these functions with a drawing and a finger on them.

type KazuGameOptions

type KazuGameOptions = { /** Entries to start from: a run kept half done (`decodeRun`). Printed cells are ignored. */ entries?: readonly number[]; /** Pencil marks to start from (`decodeNotes`). */ notes?: readonly number[]; };

type KazuGivens

type KazuGivens = { kind: KazuKind; size: number; /** The printed numbers, row-major; 0 where nothing is printed. */ cells: number[]; /** The region each cell is drawn in (a box, or a Jigsaw's own region), which the heavy rules go round; null for More or Less and Towers. */ regions: number[] | null; /** Whether the two long diagonals are groups too (Diagonal). */ diagonals: boolean; /** Sum Cages: the cages and thei…

WHAT A PUZZLE WAS PRINTED WITH, read from its givens code: the printed cells, and for a Jigsaw its regions, for Sum Cages its cages, for More or Less its marks, for Towers its clues. Each kind writes them after the cells. The drawing, the play and the hint all read a puzzle through this.

function kazuGuessDepth

kazuGuessDepth(kind: KazuKind, size: number, givens: string): number | null

How many guesses, each followed by everything reasoning then finds, a person needs to finish the puzzle: 0 when reasoning alone finishes it (easy), 1 when one guess does (medium), more when more (hard); Infinity when there is no answer. Null for givens that are not a puzzle. Meant for a puzzle already known to have exactly one answer.

function kazuHash

kazuHash(givens: string): string

A short fingerprint of a puzzle's givens (FNV-1a, eight hex characters): the same puzzle, however it was kept, has the same one. Not a credential.

type KazuHint

type KazuHint = { cell: number; value: number; why: "only-number" | "only-place" | "answer"; /** `only-place`: the group the number has one place in. */ group?: { type: "row" | "column" | "box" | "region" | "diagonal"; index: number }; /** `only-number`: what ruled the others out beyond the groups, if anything did. */ by?: "cage" | "marks" | "clues"; /** Whether the cell holds a wrong number now, which this one repl…

A HINT: the next cell a person could fill in by looking, and why.

It reasons from what is right on the grid so far (the printed numbers and every entry that agrees with the answer; a wrong entry is treated as empty, so a hint never builds on a mistake) the way a person does, one step at a time:

1. a cell that only one number fits (only-number): every other number is already in its row, column, box, region, diagonal or cage, or a cage's sum, a Futoshiki mark or a Skyscrapers clue rules it out (by says which, when one did); 2. a number that fits only one cell of a group (only-place): the row, column, box, region, diagonal or cage it must go in, and where.

When neither is left (a hard puzzle asks for a guess here) it says so (answer): the tightest cell, whose number is the answer's, with no reason a single step gives. Null when every cell is already right.

type KazuKind

type KazuKind = (typeof KAZU_KINDS)[number];

type KazuLevel

type KazuLevel = (typeof KAZU_LEVELS)[number];

function kazuProgress

kazuProgress(game: KazuGame): { filled: number; total: number; }

How far along it is: cells with a number (printed or written) out of all of them.

type KazuPuzzle

type KazuPuzzle = { kind: KazuKind; size: number; level: KazuLevel; seed: number; givens: string; solution: string; };

One puzzle, made from a seed or read back from its codes. givens and solution are the strings cells.ts and the kinds' own codes write: row-major cells, one character each, and for a Jigsaw, Sum Cages, More or Less or Towers the rest of what the puzzle is printed with, after the cells.

type KazuSpec

type KazuSpec = { /** Every side it can be made at, smallest first. */ sizes: readonly number[]; /** The side a first visit opens on. */ defaultSize: number; /** The most characters one of its codes can hold, for a route to refuse anything larger. */ mostCells: number; };

What one puzzle offers.

function layoutOfGivens

layoutOfGivens(givens: KazuGivens): Layout | null

The groups that must each hold every number once, for the four kinds made of groups; null for More or Less and Towers, which only have rows and columns.

function lineFrom

lineFrom(side: TowerSide, at: number, size: number): number[]

The cells a clue looks along, nearest first: the clue at at on the top looks down column at, on the bottom up it, on the left along row at to the right, on the right along it to the left.

type Mark

type Mark = { less: number; more: number };

A mark: the cell at less holds a smaller number than the cell at more. Both are indexes, always adjacent.

function neighbours

neighbours(size: number, index: number): number[]

The cells sharing an edge with index.

function newKazuGame

newKazuGame(kind: KazuKind, size: number, givens: string, options?: KazuGameOptions): KazuGame | null

A game of a puzzle, or null for givens that are not a puzzle of that kind and side.

const NO_MARK

NO_MARK: "."

function noClues

noClues(size: number): TowerClues

function notesOf

notesOf(mask: number): number[]

Every value of a pencil-mark mask, smallest first.

function numberCounts

numberCounts(game: KazuGame): number[]

The numbers 1 up to the side, each with how many of it are on the grid: a pad greys a number that is all placed.

type Random

type Random = () => number;

A number in [0, 1), like Math.random, from a stream a seed fixes.

function readGivens

readGivens(kind: KazuKind, size: number, code: string): KazuGivens | null

The givens of a puzzle, or null for a code that is not one of that kind and side. Null rather than a puzzle with holes.

function regionsAreSound

regionsAreSound(size: number, region: readonly number[]): boolean

Whether region divides a size×size grid into size regions of size cells each, every one of them joined edge to edge. O(cells): a check of a Jigsaw asks it of whatever regions it was sent.

function restartKazu

restartKazu(game: KazuGame): KazuGame

Start again: every entry and pencil mark gone, and nothing to undo.

function seededRandom

seededRandom(seed: number): Random

A stream of numbers in [0, 1) fixed by a seed. The seed is read as an unsigned 32-bit integer.

type Segment

type Segment = { x1: number; y1: number; x2: number; y2: number };

A line segment in cell units: where one part of a cage's dashed outline runs.

function shuffled

shuffled<T>(items: readonly T[], random: Random): T[]

A copy of the list in a random order (Fisher–Yates, from the end); the list given is left alone.

function solveKazu

solveKazu(kind: KazuKind, size: number, givens: string, budget?: number): string | null

The one answer a puzzle's givens allow, as a cells code, or null when they allow none, more than one, or the search ran past budget steps (Sudoku, Jigsaw, Diagonal and Sum Cages). A grid this cannot vouch for is never handed back as though it were the answer.

function stepEntry

stepEntry(value: number, size: number): number

The number a tap on the chosen cell puts in it: one more, and after the largest the cell empties, and then it starts again at 1. A tap on a cell that is not chosen still only chooses it.

function symbolOf

symbolOf(value: number): string

How a value is written, in a code and on a cell: 1–9, then A–G.

function toggleNote

toggleNote(game: KazuGame, cell: number, value: number): KazuGame

Turn the pencil mark value on or off in a cell. Nothing happens to a printed cell or a cell that holds a number.

const TOWER_SIDES

TOWER_SIDES: readonly ["top", "bottom", "left", "right"]

Towers (Skyscrapers) as a string: the cells, then the clues around the edge.

The cells come first, row-major, as every number grid is written (. for empty; most puzzles print none). Then one character for every place a clue can stand outside the square, four sides of size: along the top left to right, along the bottom left to right, down the left top to bottom, down the right top to bottom. A digit is how many towers can be seen from there; . is no clue. A 7×7 is 49 + 28 characters.

type TowerClues

type TowerClues = Record<TowerSide, number[]>;

Each side's clues, in reading order along it; 0 where there is none.

type TowerSide

type TowerSide = (typeof TOWER_SIDES)[number];

function towersSeen

towersSeen(heights: readonly number[]): number

How many towers show looking along heights from its first end: each one taller than every one before it.

function undoKazu

undoKazu(game: KazuGame): KazuGame

Take back the last change. The same game when there is nothing to take back.

function valueOfSymbol

valueOfSymbol(symbol: string): number

The value a symbol names, or 0 for one that is not 1–9 or A–G (either case).

function valuesOf

valuesOf(game: KazuGame): number[]

The whole grid as it stands: the printed numbers, and the player's where nothing is printed.

const VERSION

VERSION: "1.0.0"

The package's version.

@johnmorrisdotca/kazu/draw

drawKazu KAZU_CELL KAZU_NAMES KAZU_SIZE_NAMES KAZU_STRINGS KAZU_STYLE kazuCellName KazuDrawOptions kazuGeometry KazuGeometry KazuLanguage kazuLanguageOf KazuName kazuNameOf kazuSay kazuWhere

function drawKazu

drawKazu(kind: KazuKind, size: number, givensCode: string, options?: KazuDrawOptions): string | null

A PUZZLE AS SVG TEXT: the grid with its printed numbers, whatever has been written in it, pencil marks, and everything the kind of puzzle prints: the heavier rules round the boxes or a Jigsaw's regions, the shaded diagonals, a cage's dashed outline with its sum, the more-than marks between cells, and the tower clues round the edge. The chosen cell, the cells that break a rule, the cells Check flagged and the cell a hint pointed at are washed in colour.

Returns the <svg> as a string: put it in a page, a file or an image, with nothing to load. It is one steady square whatever is drawn, so nothing moves as numbers are written. Nothing in it can be selected or dragged. Null for givens that are not a puzzle of that kind and side.

const KAZU_CELL

KAZU_CELL: 100

The units of a cell, and the margin round the drawing.

const KAZU_NAMES

KAZU_NAMES: Record<"number-place" | "jigsaw" | "diagonal" | "sum-cages" | "more-or-less" | "towers", KazuName>

const KAZU_SIZE_NAMES

KAZU_SIZE_NAMES: Record<"number-place" | "jigsaw" | "diagonal" | "sum-cages" | "more-or-less" | "towers", Record<number, { en: string; ja: string; }>>

What each side is for, under its size on a chooser: the quick one, the usual one, the long one.

const KAZU_STRINGS

KAZU_STRINGS: Record<KazuLanguage, Record<string, string>>

const KAZU_STYLE

KAZU_STYLE: "\n.kazu {\n --kz-paper: #fbf8f1; --kz-ink: #1f2320; --kz-given: #1f2320; --kz-entry: #1d5fa8; --kz-note: #5b6b7d;\n --kz-grid: #cfc6b2; --kz-box: #3a3d38; --kz-frame: #a98954; --kz-cage: #6a5a8e; --kz-clue: #7a4b14;\n --kz-diagonal: #e9dfc6; --kz-peer: #efe8d8; --kz-same: #dcd0f2; --kz-select: #ffe08a; --kz-hint: #b9e3c4;\n --kz-conflict: #f4b8ad; --kz-wrong: #f4b8ad; --kz-bad: #b5452c; --kz-good: #2f7a…

THE STYLE a Kazu drawing wears: the colours of its board as custom properties, and the one rule that matters for a puzzle played with fingers: nothing in the drawing can be selected, dragged or double-tapped.

drawKazu only writes classes, data attributes and a few custom properties; this is what gives them a look. Every colour is a custom property on .kazu (--kz-paper, --kz-ink, --kz-given, --kz-entry, --kz-note, --kz-grid, --kz-box, --kz-frame, --kz-cage, --kz-clue, --kz-diagonal, --kz-peer, --kz-same, --kz-select, --kz-hint, --kz-conflict, --kz-wrong, --kz-good), so a page's own style needs to set only the ones it wants different. The paper follows the page's light or dark. Nothing moves, so there is nothing for reduced motion to still.

function kazuCellName

kazuCellName(size: number, index: number, language: KazuLanguage): string

A cell as a screen reader names it: "row 3, column 5".

type KazuDrawOptions

type KazuDrawOptions = { /** What the player has written, row-major, 0 for empty. */ entries?: readonly number[]; /** Pencil marks, a bit mask a cell: bit `v` is the note `v` (`notesOf`). */ notes?: readonly number[]; /** The chosen cell. */ selected?: number | null; /** Wash the chosen cell's row, column and group, and every cell holding its number. */ peers?: boolean; /** Cells that break a rule, drawn in red with…

What a drawing shows beyond the puzzle itself. Every part is optional: a puzzle alone is its printed grid.

function kazuGeometry

kazuGeometry(kind: KazuKind, size: number): KazuGeometry

Where everything is in the drawing of a puzzle of this kind and side.

type KazuGeometry

type KazuGeometry = { /** The side of the drawing, in its own units (the viewBox is `0 0 side side`). */ side: number; /** The side of one cell. */ cell: number; /** Where the grid's top left corner is. */ origin: number; /** How many cells deep the ring round the grid is: 1 for Towers, which keeps its clues there, 0 for the rest. */ ring: number; size: number; /** The top left corner of a cell. */ corner: (index: n…

WHERE EVERYTHING IS IN A DRAWING, so a page of your own can play it: the drawing is a square of side units, every cell cell units across, and a Towers square sits inside a ring one cell deep that holds its clues. Pure arithmetic, no page needed.

type KazuLanguage

type KazuLanguage = "en" | "ja";

THE WORDS A KAZU BOARD SAYS, in English and Japanese: what a screen reader hears of the drawing, the buttons and lines under a playable board, and what a hint says. Plain data, so a page can read them, replace a few or add a language of its own beside these two.

{name} in a line is a value filled in; a line foo that has a fooOne beside it is said as fooOne when its {n} is 1.

function kazuLanguageOf

kazuLanguageOf(tag: string | null | undefined): KazuLanguage

The language a piece of text is in: Japanese for anything starting ja, English for everything else.

type KazuName

type KazuName = { /** The name players search for. */ en: string; /** The Japanese name. */ ja: string; /** Other names the same puzzle goes by. */ alsoKnownAs: readonly string[]; /** How it is played, in a line. */ tagline: { en: string; ja: string }; /** The rules, a few lines each. */ rules: { en: readonly string[]; ja: readonly string[] }; /** Where it comes from, in a sentence. */ origin: { en: string; ja: stri…

WHAT EACH PUZZLE IS CALLED, and what it is, in English and Japanese: the name a player searches for, its Japanese name, a line saying how it is played, and a few lines of rules. Plain data, for a page that lists the puzzles (a chooser, a rules page); strings.ts has the words a playable board says.

The English names are the ones players search for: Sudoku, Killer Sudoku, Futoshiki, Skyscrapers. 数独 is Nikoli's mark in Japan, so Sudoku's Japanese name here is ナンプレ (Number Place, the puzzle's own original name, and the word Japanese publishers use); the others keep names of their own. The keys the package uses are number-place, sum-cages, more-or-less, towers: what each one is, not somebody's trademark.

function kazuNameOf

kazuNameOf(kind: KazuKind, language: KazuLanguage): string

What a puzzle is called in a language, as a screen reader says it.

function kazuSay

kazuSay(language: KazuLanguage, key: string, values?: Record<string, string | number>): string

A line in a language, with its values filled in; the line itself if there is none by that name.

function kazuWhere

kazuWhere(kind: KazuKind, language: KazuLanguage): string

The words for the groups a number is ruled out by, for a hint's line: "its row, column and box".

@johnmorrisdotca/kazu/play

ensureKazuPlayStyle KAZU_PLAY_STYLE kazuClockText KazuEventDetail KazuMount KazuMountOptions KazuMountSettings mountKazu

function ensureKazuPlayStyle

ensureKazuPlayStyle(host: Element): void

Put the style in the page once: in the document's head, or in the shadow root the host is in.

const KAZU_PLAY_STYLE

KAZU_PLAY_STYLE: "\n.kazu {\n --kz-paper: #fbf8f1; --kz-ink: #1f2320; --kz-given: #1f2320; --kz-entry: #1d5fa8; --kz-note: #5b6b7d;\n --kz-grid: #cfc6b2; --kz-box: #3a3d38; --kz-frame: #a98954; --kz-cage: #6a5a8e; --kz-clue: #7a4b14;\n --kz-diagonal: #e9dfc6; --kz-peer: #efe8d8; --kz-same: #dcd0f2; --kz-select: #ffe08a; --kz-hint: #b9e3c4;\n --kz-conflict: #f4b8ad; --kz-wrong: #f4b8ad; --kz-bad: #b5452c; --kz-good: …

THE STYLE a playable Kazu board wears (mountKazu, <kazu-board>): the drawing's own (KAZU_STYLE) and the board's box, its number pad, its buttons and its lines of words. Colours are custom properties on .kazu-play (--kzp-ink, --kzp-muted, --kzp-rule, --kzp-surface, --kzp-accent, --kzp-good) so a page sets only what it wants different.

Nothing moves when something is chosen: the board is one square box, the lines of words keep the room their longest wording takes, and the buttons are one size. Nothing the player touches can be selected, and no tap on it zooms the page.

function kazuClockText

kazuClockText(ms: number): string

A time taken, as a clock shows it: m:ss, and h:mm:ss past an hour.

type KazuEventDetail

type KazuEventDetail = { kind: KazuKind; size: number; level?: KazuLevel; seed?: number; /** What the player has written, as a run's code (`decodeRun` brings it back): to keep a puzzle half done. */ run: string; /** The pencil marks, as a code (`decodeNotes`). Empty for none. */ notes: string; /** The whole grid, printed numbers and the player's: what `checkKazu` takes as an answer. */ answer: string; progress: { fi…

What every event tells of the board.

type KazuMount

type KazuMount = { readonly host: HTMLElement; /** The game as it stands. */ game: () => KazuGame; /** What the events would tell now. */ detail: () => KazuEventDetail; /** Play another puzzle (or the same one again, fresh). `run`, `notes` and `elapsed` carry on a kept one. */ load: (puzzle: Pick<KazuMountOptions, "kind" | "size" | "givens" | "solution" | "level" | "seed" | "run" | "notes" | "elapsed">) => boolean; …

type KazuMountOptions

type KazuMountOptions = { kind: KazuKind; size: number; /** The puzzle's givens code. */ givens: string; /** The puzzle's one answer, as a cells code. Left out, it is worked out from the givens when Hint or Check needs it. */ solution?: string; /** Which level and seed made it, to carry in the events. */ level?: KazuLevel; seed?: number; /** A run to start from: the entries of a puzzle half done (`decodeRun`'s code)…

type KazuMountSettings

type KazuMountSettings = Pick<KazuMountOptions, "hints" | "check" | "conflicts" | "peers" | "tidy" | "tapToStep" | "language" | "clock" | "controls">;

Everything about how a mounted board plays that can change while it is on the page.

function mountKazu

mountKazu(host: HTMLElement, options: KazuMountOptions): KazuMount | null

Draw a puzzle into host and play it. Returns the handle that drives it, or null for givens that are not a puzzle of that kind and side.

@johnmorrisdotca/kazu/element

KazuBoard

const KazuBoard

KazuBoard: typeof KazuBoard

@johnmorrisdotca/kazu/element/define