Karakuriからくり

@johnmorrisdotca/karakuri 0.1.1 · 6 entry points · 108 exports

@johnmorrisdotca/karakuri

choiceStory Controller GameInfo gridEscape Info KARAKURI_STRINGS KarakuriLanguage nutsAndBolts pinRescue Point ropeCut saveTheCharacter say Say Status stretchGrabber Theme tubeSort VERSION wordKey

namespace choiceStory

import { choiceStory } from "@johnmorrisdotca/karakuri"; // or everything in it from "@johnmorrisdotca/karakuri/choiceStory"

Everything the @johnmorrisdotca/karakuri/choiceStory entry point exports, as one namespace.

type Controller

interface Controller { /** The size of the play area, in the game's units. The player scales it to the box it is given. */ readonly width: number; readonly height: number; /** `drag` games read a finger's whole path, so the player stops the page scrolling under it; `tap` games leave the page its gestures. */ readonly gesture: "drag" | "tap"; readonly status: Status; /** Why a level was won or lost, in words, once it…

One level of one game, played.

type GameInfo

interface GameInfo { id: string; /** How many levels it has. */ levels: number; gesture: "drag" | "tap"; /** Whether it runs a physics simulation, stepped sixty times a second. */ physics: boolean; /** Builds a controller for a level (from 1). */ create(level: number): Controller; }

What a game of the package is, for a chooser.

namespace gridEscape

import { gridEscape } from "@johnmorrisdotca/karakuri"; // or everything in it from "@johnmorrisdotca/karakuri/gridEscape"

Everything the @johnmorrisdotca/karakuri/gridEscape entry point exports, as one namespace.

type Info

type Info = Say;

What the player reads in the bar above the board.

const KARAKURI_STRINGS

KARAKURI_STRINGS: Record<KarakuriLanguage, Record<string, string>>

Every word the package says: its own, and the stories'.

type KarakuriLanguage

type KarakuriLanguage = "en" | "ja";

namespace nutsAndBolts

import { nutsAndBolts } from "@johnmorrisdotca/karakuri"; // or everything in it from "@johnmorrisdotca/karakuri/nutsAndBolts"

Everything the @johnmorrisdotca/karakuri/nutsAndBolts entry point exports, as one namespace.

namespace pinRescue

import { pinRescue } from "@johnmorrisdotca/karakuri"; // or everything in it from "@johnmorrisdotca/karakuri/pinRescue"

Everything the @johnmorrisdotca/karakuri/pinRescue entry point exports, as one namespace.

type Point

interface Point { x: number; y: number; }

A place in a game's own world, in its own units (not pixels).

namespace ropeCut

import { ropeCut } from "@johnmorrisdotca/karakuri"; // or everything in it from "@johnmorrisdotca/karakuri/ropeCut"

Everything the @johnmorrisdotca/karakuri/ropeCut entry point exports, as one namespace.

namespace saveTheCharacter

import { saveTheCharacter } from "@johnmorrisdotca/karakuri"; // or everything in it from "@johnmorrisdotca/karakuri/saveTheCharacter"

Everything the @johnmorrisdotca/karakuri/saveTheCharacter entry point exports, as one namespace.

function say

say(lang: KarakuriLanguage, key: string, values?: Record<string, string | number>): string

The words for key in lang, with each {name} filled from values; English when the language has none; the key itself when nobody has.

type Say

interface Say { key: string; values?: Record<string, string | number>; }

A line of words to be said in the player's language: a key of KARAKURI_STRINGS and the values for its {name}s.

type Status

type Status = "playing" | "won" | "lost";

How a level stands: still being played, won, or lost.

namespace stretchGrabber

import { stretchGrabber } from "@johnmorrisdotca/karakuri"; // or everything in it from "@johnmorrisdotca/karakuri/stretchGrabber"

Everything the @johnmorrisdotca/karakuri/stretchGrabber entry point exports, as one namespace.

type Theme

interface Theme { /** The play area's floor. */ board: string; /** The darker ground, for grooves and shadows. */ deep: string; ink: string; muted: string; /** The line the player draws, and highlights. */ accent: string; good: string; bad: string; gold: string; water: string; lava: string; stone: string; /** A light colour that reads on `accent`, `bad` and the game colours. */ paper: string; dark: boolean; font: st…

The colours and sizes a game draws with, read from the page's custom properties (--kk-*) so that it follows light, dark and the family's cloths.

namespace tubeSort

import { tubeSort } from "@johnmorrisdotca/karakuri"; // or everything in it from "@johnmorrisdotca/karakuri/tubeSort"

Everything the @johnmorrisdotca/karakuri/tubeSort entry point exports, as one namespace.

const VERSION

VERSION: "0.1.1"

The package's version, kept equal to package.json's by a test.

function wordKey

wordKey: (game: string) => string

The key a game's words are kept under: its id with underscores for hyphens (game_tube_sort, rules_tube_sort).

@johnmorrisdotca/karakuri/physics

addBody addLink addNode addRope Body BodySpec BodyType capsulesAlong CapsuleShape CircleShape clamp closestBetweenSegments closestOnSegment Contact contactsBetween countOf createBody createFluid createRig createWorld Disc distance distanceSquared distanceToSegment fillRect Fluid FluidKind fluidNumbers gap hashNumbers isSettled length linkCrossed nearestTo nodeSpeed Particle pointInPolygon refreshShapes Rig RigLink RigNode rigNumbers segmentsIntersect Shape STEP stepFluid stepRig stepWorld unit Vec World worldNumbers WorldShape

function addBody

addBody<T extends Body>(world: World, body: T): T

Adds a body to the world and gives it back.

function addNode

addNode(rig: Rig, x: number, y: number, invMass: number, r?: number): number

Adds a node and gives back its index.

function addRope

addRope(rig: Rig, ax: number, ay: number, bx: number, by: number, pieces: number, end?: number): { nodes: number[]; links: number[]; }

Lays a rope of pieces links from (ax, ay) to (bx, by): the first node is pinned and the last is the node end if one is given (its place is moved to the rope's end), and the nodes between are light. Gives back the indices of the rope's nodes from the pinned end to the other and of its links.

type Body

interface Body { id: string; /** The game's own word for what the body is: "hero", "rock", "stroke". The physics never reads it. */ tag: string; type: BodyType; x: number; y: number; vx: number; vy: number; /** The angle as its cosine and sine, turned by arithmetic alone, never by a trigonometric call. */ c: number; s: number; /** The angular velocity, in radians a second. */ w: number; mass: number; invMass: number…

A rigid body. A game reads and, for a kinematic one, writes x, y, vx and vy; the rest is the solver's.

type BodySpec

interface BodySpec { id: string; tag?: string; type?: BodyType; x?: number; y?: number; shapes: Shape[]; density?: number; restitution?: number; friction?: number; /** A body that does not turn (a character that stays upright). */ fixedRotation?: boolean; sensor?: boolean; }

What createBody is told. A dynamic body's mass and inertia are worked out from its shapes and density unless given.

type BodyType

type BodyType = "dynamic" | "static" | "kinematic";

How a body moves: dynamic by forces and contacts, kinematic only as the game sets it (it pushes, and is not pushed), static not at all.

function capsulesAlong

capsulesAlong(points: readonly { x: number; y: number; }[], radius: number): CapsuleShape[]

A capsule chain for a drawn line: one capsule between each pair of neighbouring points.

type CapsuleShape

interface CapsuleShape { kind: "capsule"; ax: number; ay: number; bx: number; by: number; r: number; }

A thick segment, from (ax, ay) to (bx, by) with a radius, placed in the body's own frame.

type CircleShape

interface CircleShape { kind: "circle"; x: number; y: number; r: number; }

A disc, placed in the body's own frame (its origin is the body's centre of mass for a dynamic body).

function clamp

clamp: (value: number, low: number, high: number) => number

value held between low and high.

function closestBetweenSegments

closestBetweenSegments(a1x: number, a1y: number, a2x: number, a2y: number, b1x: number, b1y: number, b2x: number, b2y: number): { ax: number; ay: number; bx: number; by: number; distance: number; }

The nearest points of two segments: where on each, and how far apart they are. Crossing segments are 0 apart.

function closestOnSegment

closestOnSegment(px: number, py: number, ax: number, ay: number, bx: number, by: number): { x: number; y: number; t: number; }

The point on the segment a to b nearest to (px, py), and how far along the segment it is (0 to 1).

type Contact

interface Contact { a: Body; b: Body; nx: number; ny: number; depth: number; x: number; y: number; }

One contact between two shapes: the normal points from a towards b.

function contactsBetween

contactsBetween(a: Body, b: Body, margin: number): Contact[]

The contacts between two bodies whose shapes are within margin of each other.

function countOf

countOf(fluid: Fluid, kind: FluidKind): number

How many particles of a kind.

function createBody

createBody(spec: BodySpec): Body

Makes a body from a spec. A dynamic body's origin is moved to its centre of mass, and its shapes are re-placed round it.

function createFluid

createFluid(options?: { radius?: number; gravity?: number; damping?: number; iterations?: number; reacts?: boolean; }): Fluid

Makes a liquid with no particles.

function createRig

createRig(options?: { gravity?: number; damping?: number; iterations?: number; grip?: number; bounce?: number; }): Rig

Makes an empty rig.

function createWorld

createWorld(options?: { gravity?: number; substeps?: number; iterations?: number; }): World

Makes a world.

type Disc

interface Disc { x: number; y: number; r: number; }

The round bodies the particles must not enter (a character, a coin): centre and radius.

function distance

distance: (ax: number, ay: number, bx: number, by: number) => number

The distance between two points.

function distanceSquared

distanceSquared: (ax: number, ay: number, bx: number, by: number) => number

The squared distance between two points, which needs no square root.

function distanceToSegment

distanceToSegment(px: number, py: number, ax: number, ay: number, bx: number, by: number): number

How far (px, py) is from the segment a to b.

function fillRect

fillRect(fluid: Fluid, kind: FluidKind, x: number, y: number, w: number, h: number): number

Fills the rectangle from (x, y) to (x + w, y + h) with particles of one kind on a grid a little looser than they settle.

type Fluid

interface Fluid { particles: Particle[]; /** A particle's radius, which is also how far apart two settle (twice this). */ radius: number; gravity: number; /** How much of its speed a particle keeps each step. */ damping: number; iterations: number; /** Whether water and lava turn each other to stone where they touch. */ reacts: boolean; }

The liquid: its particles and how it behaves.

type FluidKind

type FluidKind = "water" | "lava" | "stone";

What a particle is made of. Stone is lava and water that met: it does not move again.

function fluidNumbers

fluidNumbers(fluid: Fluid): number[]

Every number of the liquid's particles, for hashing it (kinds as 0, 1, 2).

function gap

gap(a: Body, b: Body): number

The gap between two bodies: the smallest gap of any pair of their shapes (negative when they overlap).

function hashNumbers

hashNumbers(values: ArrayLike<number>): string

A 32-bit FNV-1a hash of a list of numbers, taken over the bits of each as a 64-bit float.

function isSettled

isSettled(world: World, speed?: number): boolean

Whether every dynamic body in the world is slower than speed (units a second) and is hardly turning.

function length

length: (x: number, y: number) => number

The length of (x, y).

function linkCrossed

linkCrossed(rig: Rig, x1: number, y1: number, x2: number, y2: number, among?: readonly number[]): number

The index of the first live link that the swipe from (x1, y1) to (x2, y2) crosses, or -1. A swipe cuts one link: the nearest to its start.

function nearestTo

nearestTo(fluid: Fluid, kind: FluidKind, disc: Disc): number

The nearest distance from a particle of a kind to a disc's surface (negative when it is inside), or Infinity when there is none.

function nodeSpeed

nodeSpeed(n: RigNode, dt: number): number

The speed of a node in units a second at a step of dt.

type Particle

interface Particle { x: number; y: number; px: number; py: number; kind: FluidKind; }

One particle.

function pointInPolygon

pointInPolygon(px: number, py: number, polygon: readonly Vec[]): boolean

Whether (px, py) is inside the polygon (an even-odd test over its vertices, in order).

function refreshShapes

refreshShapes(body: Body): void

Recomputes a body's shapes in world places from its position and angle.

type Rig

interface Rig { nodes: RigNode[]; links: RigLink[]; statics: WorldShape[]; gravity: number; /** How much of its speed a node keeps each step (1 keeps all). */ damping: number; iterations: number; /** How much of a round node's speed along a surface it keeps when it touches one. */ grip: number; bounce: number; }

A set of nodes and links, with the fixed shapes the round nodes rest on.

type RigNode

interface RigNode { x: number; y: number; px: number; py: number; invMass: number; r: number; }

A point of a chain. invMass 0 pins it. r above 0 makes it a disc that rests on the rig's statics.

function rigNumbers

rigNumbers(rig: Rig): number[]

Every number of the rig's nodes, for hashing it.

function segmentsIntersect

segmentsIntersect(a1x: number, a1y: number, a2x: number, a2y: number, b1x: number, b1y: number, b2x: number, b2y: number): boolean

Whether the segments a1 to a2 and b1 to b2 cross or touch.

type Shape

type Shape = CircleShape | CapsuleShape;

A shape of a body.

const STEP

STEP: number

The length of the fixed step every game runs at, in seconds.

function stepFluid

stepFluid(fluid: Fluid, dt: number, statics: readonly WorldShape[], discs?: readonly Disc[]): void

Advances the liquid by one step of dt seconds.

function stepRig

stepRig(rig: Rig, dt: number): void

Advances the rig by one step of dt seconds.

function stepWorld

stepWorld(world: World, dt?: number): void

Advances the world by one fixed step (STEP seconds) in substeps smaller ones.

function unit

unit(x: number, y: number): Vec

The unit vector of (x, y), or (0, 0) for a zero vector.

type Vec

interface Vec { x: number; y: number; }

A point or a vector in the plane.

type World

interface World { gravity: number; bodies: Body[]; /** Smaller steps within one fixed step. A fast, small thing needs more. */ substeps: number; /** How many passes the contact solver makes. */ iterations: number; }

The world: its gravity (down is +y, in units a second squared), its bodies in the order they were added, and how hard it works.

function worldNumbers

worldNumbers(world: World): number[]

Every number of a world's bodies' state, for hashing it: a determinism test compares these bit for bit.

type WorldShape

interface WorldShape { ax: number; ay: number; bx: number; by: number; r: number; }

A shape as the world sees it now: a circle as (ax, ay, r) with a = b, a capsule with its two ends.

@johnmorrisdotca/karakuri/levels

GRID_ESCAPE_LEVELS GridEscapeLevel gridPuzzleOf NUTS_AND_BOLTS_LEVELS PIN_RESCUE_LEVELS PinRescueEntry ROPE_CUT_LEVELS RopeCutEntry SAVE_THE_CHARACTER_LEVELS SaveEntry STORIES STRETCH_GRABBER_LEVELS StretchGrabberEntry TUBE_SORT_LEVELS

const GRID_ESCAPE_LEVELS

GRID_ESCAPE_LEVELS: readonly GridEscapeLevel[]

The levels, from easy to hard by their fewest moves.

type GridEscapeLevel

interface GridEscapeLevel { rows: readonly string[]; fewest: number; }

One level: its board and its proved fewest moves.

function gridPuzzleOf

gridPuzzleOf(level: GridEscapeLevel): GridPuzzle

The board of a level, read.

const NUTS_AND_BOLTS_LEVELS

NUTS_AND_BOLTS_LEVELS: readonly NutPuzzle[]

The levels, with more plates and more screws each time.

const PIN_RESCUE_LEVELS

PIN_RESCUE_LEVELS: readonly PinRescueEntry[]

The levels, each with more pins to choose between and more ways to lose.

type PinRescueEntry

interface PinRescueEntry extends PinLevel { solution: readonly number[]; loss: readonly number[]; }

A level, the pins that win it in order (by index in pins), and pins that lose it in order.

const ROPE_CUT_LEVELS

ROPE_CUT_LEVELS: readonly RopeCutEntry[]

The levels.

type RopeCutEntry

interface RopeCutEntry extends RopeLevel { solution: readonly { at: number; rope: number; link: number }[]; loss: readonly { at: number; rope: number; link: number }[]; }

A level and the cuts that win it, in order: the step to cut at, which rope, which link of it.

const SAVE_THE_CHARACTER_LEVELS

SAVE_THE_CHARACTER_LEVELS: readonly SaveEntry[]

The levels.

type SaveEntry

interface SaveEntry extends SaveLevel { solution: readonly { x: number; y: number }[]; loss: readonly { x: number; y: number }[]; }

A level and the stroke that wins it (points, drawn in order) and a stroke that loses it.

const STORIES

STORIES: readonly Story[]

The stories, one to a level.

const STRETCH_GRABBER_LEVELS

STRETCH_GRABBER_LEVELS: readonly StretchGrabberEntry[]

The levels.

type StretchGrabberEntry

interface StretchGrabberEntry extends GrabLevel { solution: readonly Pt[]; loss: readonly Pt[]; }

A level, the waypoints of one way to win it, and of one way to lose it.

const TUBE_SORT_LEVELS

TUBE_SORT_LEVELS: readonly TubePuzzle[]

The levels, each with more colours and more tubes than the one before.

@johnmorrisdotca/karakuri/play

Controller createController defaultTheme GameInfo Info KARAKURI_GAME_IDS KARAKURI_GAMES KARAKURI_STYLE KARAKURI_THEMES KarakuriGame KarakuriMount mountKarakuri MountOptions Point readTheme Say Status StatusEvent Theme

type Controller

interface Controller { /** The size of the play area, in the game's units. The player scales it to the box it is given. */ readonly width: number; readonly height: number; /** `drag` games read a finger's whole path, so the player stops the page scrolling under it; `tap` games leave the page its gestures. */ readonly gesture: "drag" | "tap"; readonly status: Status; /** Why a level was won or lost, in words, once it…

One level of one game, played.

function createController

createController(game: KarakuriGame, level: number): Controller

Makes a controller for level level (from 1) of a game; a level out of range is held to the nearest.

function defaultTheme

defaultTheme(dark?: boolean): Theme

The theme without a page: the light one, for a drawing made where there is no style to read (a test, a server).

type GameInfo

interface GameInfo { id: string; /** How many levels it has. */ levels: number; gesture: "drag" | "tap"; /** Whether it runs a physics simulation, stepped sixty times a second. */ physics: boolean; /** Builds a controller for a level (from 1). */ create(level: number): Controller; }

What a game of the package is, for a chooser.

type Info

type Info = Say;

What the player reads in the bar above the board.

const KARAKURI_GAME_IDS

KARAKURI_GAME_IDS: readonly ["save-the-character", "pin-rescue", "nuts-and-bolts", "stretch-grabber", "grid-escape", "rope-cut", "tube-sort", "choice-story"]

The ids of the games, in the order the package lists them (kebab case, the same in every address and attribute).

const KARAKURI_GAMES

KARAKURI_GAMES: Record<"save-the-character" | "pin-rescue" | "nuts-and-bolts" | "stretch-grabber" | "grid-escape" | "rope-cut" | "tube-sort" | "choice-story", GameInfo>

THE GAMES of the package, by id. Each is a GameInfo: how many levels it has, how it is played (drag or tap), whether it runs physics, and how to make a controller for a level.

const KARAKURI_STYLE

KARAKURI_STYLE: string

THE STYLE the player wears: the colours as custom properties on .karakuri (light, and dark when the page asks for it by prefers-color-scheme or by data-theme="dark" on the root), the layout of the bar, the play area and the buttons, and the one rule that matters for games played with fingers: nothing on the board can be selected, dragged or double-tapped, and the play area stops the page scrolling under a finger only for the games that read a finger's whole path.

const KARAKURI_THEMES

KARAKURI_THEMES: { readonly light: { readonly board: "#f6f1e6"; readonly deep: "#e4dcc9"; readonly ink: "#2b2a28"; readonly muted: "#8a8473"; readonly accent: "#2f6f55"; readonly good: "#2f8f5b"; readonly bad: "#c2453a"; readonly gold: "#e0a82e"; readonly water: "#3b8fd9"; readonly lava: "#e8561f"; readonly stone: "#8d8a82"; readonly paper: "#fffdf7"; }; readonly dark: { readonly board: "#263029"; readonly deep: "#1…

The look the games draw with, as custom properties, so a page can change any of them. Light and dark follow the page.

type KarakuriGame

type KarakuriGame = (typeof KARAKURI_GAME_IDS)[number];

The id of a game of the package.

type KarakuriMount

interface KarakuriMount { readonly game: KarakuriGame; readonly level: number; readonly status: Status; /** The controller of the level being played, for reading its snapshot. */ readonly controller: Controller; readonly canvas: HTMLCanvasElement; restart(): void; /** Goes to another level (from 1) of the same game; a number out of range is held to the nearest. */ setLevel(level: number): void; setLang(lang: Karakur…

The handle mountKarakuri gives back.

function mountKarakuri

mountKarakuri(host: HTMLElement, options: MountOptions): KarakuriMount

Plays one game of the package in host: a canvas that scales to the box it is given, the pointer (finger or mouse) wired to the game, a fixed-step loop that runs only while something moves, and (with ui: "full") the bar, the buttons and the card that says how a level ended. Safe to call again on the same host: the old game is taken down first.

type MountOptions

interface MountOptions { game: KarakuriGame; /** The level to start on, from 1. */ level?: number; /** `en` or `ja`; the page's own language when left out. */ lang?: KarakuriLanguage; /** `full` (default) draws the bar above the board, the result card on it and the buttons below; `board` draws the board alone, for a page that has its own buttons. */ ui?: "full" | "board"; /** * The most height, in pixels, the board …

What mountKarakuri is told.

type Point

interface Point { x: number; y: number; }

A place in a game's own world, in its own units (not pixels).

function readTheme

readTheme(element: Element): Theme

Reads the theme a game draws with from an element's computed style: each colour is --kk-<name>, with the light or the dark default beneath.

type Say

interface Say { key: string; values?: Record<string, string | number>; }

A line of words to be said in the player's language: a key of KARAKURI_STRINGS and the values for its {name}s.

type Status

type Status = "playing" | "won" | "lost";

How a level stands: still being played, won, or lost.

type StatusEvent

interface StatusEvent { game: KarakuriGame; level: number; status: Status; result: Say | null; /** The words of `result` in the player's language ("" while playing), for a page that draws its own result. */ text: string; /** The line the bar would show about the level, in the player's language (moves made, ink left, and so on). */ info: string; }

What a status callback is given.

type Theme

interface Theme { /** The play area's floor. */ board: string; /** The darker ground, for grooves and shadows. */ deep: string; ink: string; muted: string; /** The line the player draws, and highlights. */ accent: string; good: string; bad: string; gold: string; water: string; lava: string; stone: string; /** A light colour that reads on `accent`, `bad` and the game colours. */ paper: string; dark: boolean; font: st…

The colours and sizes a game draws with, read from the page's custom properties (--kk-*) so that it follows light, dark and the family's cloths.

@johnmorrisdotca/karakuri/element

KarakuriBoard

const KarakuriBoard

KarakuriBoard: typeof KarakuriBoard

@johnmorrisdotca/karakuri/element/define

KarakuriBoard

const KarakuriBoard

KarakuriBoard: typeof KarakuriBoard