const ALL_LAYOUTS
ALL_LAYOUTS: readonly MahjongLayout[]
Every layout there is: the five above, which itsutsu.com's kept games are made on and which never change, then Jarajara's own more (layouts-more.ts), each of a width none of the others has.
@johnmorrisdotca/jarajara 1.3.0 · 10 entry points · 213 exports
@johnmorrisdotca/jarajara 87@johnmorrisdotca/jarajara/awase 33@johnmorrisdotca/jarajara/table 19@johnmorrisdotca/jarajara/faces 38@johnmorrisdotca/jarajara/element 22@johnmorrisdotca/jarajara/element/define 0@johnmorrisdotca/jarajara/designs/riichi 1@johnmorrisdotca/jarajara/designs/riichi-black 1@johnmorrisdotca/jarajara/tile-sounds 10@johnmorrisdotca/jarajara/sounds 2@johnmorrisdotca/jarajaraALL_LAYOUTS arrangeIndexes arrangeTiles AwaseCheck AwaseDeal AwaseLevel blockedBy bonusRuleOf canTake copiesOf countTiles dealFits decodeMoves describeSlot EMPTY_SLOT encodeMoves faceOf faceWords faceWordsJa findFace findFaces freePairs freeSlots geometryOf groupFaces groupStarts groupTiles groupWords hashText isBonus isCleared isFaceCode isFree isFreeAmong isNotation isTileGroup kindOf Laid layoutExtent layoutFor layPairs MAHJONG_FACES MAHJONG_LAYOUTS MahjongBonusRule MahjongCells MahjongFace MahjongGeometry MahjongLayout MahjongMove MahjongSlot MahjongSuit matchClass mixTiles MORE_LAYOUTS overlapsOnLayer pairGoesAgain pairPoints pairsLeft playSolve Random readNotation readSlotKeys readTiles Replayed seededRandom setInventory setPairs SHUFFLE_MARK shuffled shuffleTiles SlotAxis SlotSortKey sortSlots SUIT_WORDS takePair TILE_GROUPS TILE_ORDERS TileGroup TileGrouping TileLanguage tileName TileOrder tilesLeft tilesMatch VERSION writeNotation writeTiles
ALL_LAYOUTS: readonly MahjongLayout[]
Every layout there is: the five above, which itsutsu.com's kept games are made on and which never change, then Jarajara's own more (layouts-more.ts), each of a width none of the others has.
arrangeIndexes(codes: readonly string[], order?: TileOrder): number[]
The places the tiles take in an order, as the numbers of the tiles in the order they come: [2, 0, 1] has the third tile first. Every sort is stable, so equal tiles stay as they were. suit is the set's own order (characters, circles, bamboo, winds, dragons, flowers, seasons, each by rank); rank puts every one together, then every two, and so on, within each rank by suit, the honours after; kind groups numbers, honours and bonus tiles and puts each in suit order; code is the letters' own order; dealt is the list as it was.
arrangeTiles(codes: readonly string[], order?: TileOrder): string[]
The tiles in an order, as a new list; see arrangeIndexes for what each order is.
type AwaseCheck = { ok: true } | { ok: false; reason: string };
What a check of a finished game says: cleared, or the first reason it is not.
type AwaseDeal = { size: number; level: AwaseLevel; seed: number; givens: string; solution: string };
A deal of Awase: its layout's size, its level and seed, the tiles where they lie, and one way to clear them.
type AwaseLevel = "easy" | "medium" | "hard";
How hard a deal of Awase is: the easiest of a seed's candidate deals to clear, the middle one, or the hardest.
blockedBy(geometry: MahjongGeometry, cells: MahjongCells, slot: number): "covered" | "sides" | null
Why a tile cannot be taken now: covered by a tile on it, or held on both sides. Null for a free tile or an empty slot.
bonusRuleOf(cells: string): MahjongBonusRule
The deal's rule, read from the deal itself: a bonus tile dealt twice can only be the Identical rule, since the usual one deals each flower and season once. A deal holding no bonus tile plays alike under both, and says group.
canTake(geometry: MahjongGeometry, cells: MahjongCells, rule: MahjongBonusRule, a: number, b: number): boolean
Whether these two slots are a pair that may be taken now.
copiesOf(code: string): number
How many of a face the set holds: four of every ordinary face, one of each flower and season.
countTiles(codes: Iterable<string>): Map<string, number>
How many of each face a list of tiles holds, by code; faces it holds none of are left out.
dealFits(size: number, givens: string): boolean
Whether a deal is written right for a layout: one face a slot, nothing empty.
decodeMoves(text: string, slots: number): MahjongMove[] | null
The moves a string holds, each slot below slots; null for anything else.
describeSlot(layout: MahjongLayout, index: number): { x: number; y: number; z: number; } | null
Where a slot is, in tiles: across and down from the layout's corner (halves where a tile sits between two), and which layer, from 1.
EMPTY_SLOT: "."
encodeMoves(moves: readonly MahjongMove[]): string
faceOf(code: string): MahjongFace | null
The face a letter stands for, or null for anything that is not one.
faceWords(face: MahjongFace): string
What a face is called, in English: "3 of characters", "east wind", "red dragon", "plum (flower)".
faceWordsJa(face: MahjongFace): string
What a face is called in Japanese: 三萬, 五筒, 七索, 東, 中, 梅, 春.
findFace(text: string): MahjongFace | null
The tile a text names, or null. It takes a face's code (F, case matters: one letter is a code); its English name in any usual spelling ("3 of circles", "circles 3", "three circles", "east wind", "plum", "red dragon"); its Japanese name (三筒, 東, 中) or the readings players use (ton, haku); and the notation hands are written in, a number and a suit letter: 3m characters, 3p circles, 3s bamboo, 1z–4z the winds and 5z–7z white, green and red (the riichi order), plus 1f and 1t for the flowers and seasons, which are Jarajara's own. 0m, 0p and 0s are the red fives, which are fives here.
findFaces(text: string): MahjongFace[]
Every tile a text names: the one face findFace finds, or, for the name of a group (winds, honours, circles), all of its faces.
freePairs(geometry: MahjongGeometry, cells: MahjongCells, rule: MahjongBonusRule): [number, number][]
Every pair that could be taken now, each once, lower slot first, in slot order.
freeSlots(geometry: MahjongGeometry, cells: MahjongCells): number[]
Every free tile, by slot.
geometryOf(layout: MahjongLayout): MahjongGeometry
What lies on and beside each slot, worked out once for a layout and kept.
groupFaces(group: TileGroup): MahjongFace[]
The faces of a group, in the set's order.
groupStarts(codes: readonly string[], by?: TileGrouping): number[]
The places in a list, already in order, where a new group starts: every place whose tile is of another suit (or kind) than the one before it.
groupTiles(codes: readonly string[], by?: TileGrouping): string[][]
The tiles split into the runs a gap is drawn between: by suit or by kind, each run in the set's order.
groupWords(group: TileGroup, language?: TileLanguage): string
What a group is called, in English and in Japanese.
hashText(text: string): number
A 32-bit FNV-1a hash of a string: the seed a shuffle is drawn from, so the same tiles shuffled the same time come out the same.
isBonus(code: string): boolean
A flower or a season: one of a kind in the set, and matched by its group under the usual rule.
isCleared(cells: MahjongCells): boolean
Whether every tile has been taken.
isFaceCode(code: string): boolean
isFree(geometry: MahjongGeometry, cells: MahjongCells, slot: number): boolean
Whether the tile in this slot is free.
isFreeAmong(geometry: MahjongGeometry, present: (slot: number): boolean, slot: number) => boolean
Whether a slot's tile could be taken now, among the slots present says are still filled.
isNotation(text: string): boolean
Whether a text is written as hand notation, 123m456p: nothing but digit runs each closed by a suit letter.
isTileGroup(text: unknown): text is TileGroup
Whether a text names a group.
kindOf(code: string): number
The kind of a tile: 0 numbers (characters, circles, bamboo), 1 honours (winds, dragons), 2 bonus tiles (flowers, seasons); 3 for a letter that is none.
type Laid = { cells: MahjongCells; order: [number, number][] };
A deal laid out and the order it can be cleared in, pair by pair.
layoutExtent(layout: MahjongLayout): { width: number; height: number; layers: number; }
The layout's extent in half units: how far right and down its tiles reach, and how many layers.
layoutFor(size: number): MahjongLayout | null
The layout a size names, or null for one there is not.
layPairs(geometry: MahjongGeometry, present: readonly boolean[], pairs: readonly (readonly [string, string])[], random: Random): Laid | null
Lay these pairs of tiles into the slots present names, or null when no attempt reached the end. Higher slots are a little likelier to be taken first, which keeps the stacks coming down together and leaves fewer attempts stuck on a tower of two.
MAHJONG_FACES: readonly MahjongFace[]
MAHJONG_LAYOUTS: readonly MahjongLayout[]
type MahjongBonusRule = "group" | "same";
How a flower or a season matches. group, the usual rule: any flower takes any flower and any season any season. same: only an identical tile, and the deal holds bonus tiles in identical pairs.
type MahjongCells = string;
The tiles as they lie: a face code for each slot of the layout, "." where the tile has been taken. The same string the givens are written as.
type MahjongFace = { code: string; suit: MahjongSuit; rank: number };
One face of the set: its suit and its rank within it (1–9 for a suit, 1–4 or 1–3 for the rest).
type MahjongGeometry = { layout: MahjongLayout; above: readonly (readonly number[])[]; left: readonly (readonly number[])[]; right: readonly (readonly number[])[]; };
What stands on and beside each slot, worked out once per layout: the slots over it (any higher layer overlapping it), and the slots touching its left and right sides on its own layer.
type MahjongLayout = { /** The layout's width in tiles: its "size", unique among the layouts. */ size: number; key: string; slots: readonly MahjongSlot[]; };
A stacked layout: its slots in reading order (layer by layer, row by row), which is the order a deal is written in.
type MahjongMove = { pair: readonly [number, number] } | { shuffle: true };
One move of a solve: a pair taken, by its two slots, or a shuffle of what is left.
type MahjongSlot = { x: number; y: number; z: number };
A place in a layout: a tile's top-left corner in half-tile units across and down, and its layer.
type MahjongSuit = "characters" | "circles" | "bamboo" | "winds" | "dragons" | "flowers" | "seasons";
The suits and honours of a mahjong set, and the two kinds of bonus tile.
matchClass(code: string, rule: MahjongBonusRule): string
THE MATCHING RULE. Two identical tiles match; under the usual rule a flower matches any flower and a season any season, since there is only one of each.
mixTiles(codes: readonly string[], seed?: number): string[]
The same tiles in another order, at random, or the same order for the same seed; never the order they were in unless there is only one to be in.
MORE_LAYOUTS: readonly MahjongLayout[]
overlapsOnLayer(layout: MahjongLayout): boolean
Whether two slots of one layer overlap, which no layout may have: a tile cannot share a place.
pairGoesAgain(code: string): boolean
Whether taking this pair gives its taker another turn at the table: a flower or season pair.
pairPoints(code: string): number
WHAT A PAIR IS WORTH AT THE TABLE (table.ts): the plain suit tiles one, the ones and nines two, the winds three and the dragons four — the honours and terminals are the tiles a mahjong hand prizes — and a flower or season pair two, with another turn, as a bonus tile draws again in the full game.
pairsLeft(cells: MahjongCells, rule: MahjongBonusRule): [string, string][]
The tiles left on a layout as pairs that match, each pair's tiles together: what a shuffle lays again.
playSolve(size: number, givens: string, moves: readonly MahjongMove[], rule?: MahjongBonusRule): Replayed | null
PLAY THE MOVES ON THE DEAL, refusing the first that the rules do not allow: a pair that is not two free tiles that match, or a shuffle while a pair could still be taken (Shuffle is for when you are stuck), or one that cannot help. Null for a deal or a move that does not read.
type Random = () => number;
A number in [0, 1), like Math.random, from a stream a seed fixes.
readNotation(text: string): string[] | null
The tiles a hand's notation writes, as codes in the order written; null when the text is not notation or names a tile there is not (8z).
readSlotKeys(text: string | null | undefined): SlotSortKey[]
The keys a text names, "z y x", "x,-z", in order; anything that is no axis is dropped.
readTiles(text: string | null | undefined): string[]
A LIST OF TILES AS TEXT, read the forgiving way: each word is a tile's name (east, red-dragon, 3p) or a run of one-letter codes (abcF), and hand notation (123m456p) is read as it is written. Words that are no tile are dropped, and so is ., the empty slot. A bare word that is a name wins over its letters, so east is the east wind and not four tiles.
type Replayed = { cells: MahjongCells; shuffles: number };
Where a solve stands after its moves: the tiles, and how many shuffles it has had.
seededRandom(seed: number): Random
A stream of numbers in [0, 1) fixed by a seed.
setInventory(): { face: MahjongFace; copies: number; }[]
The whole set as an inventory: each of the 42 faces and how many of it the 144 tiles hold.
setPairs(rule: MahjongBonusRule, random: (): number) => [string, string][]
EVERY PAIR THE SET HOLDS, 72 of them: each ordinary face twice (four copies), and the eight bonus tiles as four pairs — two flowers and two seasons, split at random under the usual rule, or four faces drawn twice each under the Identical rule. random decides the splits; the order is the set's, and the deal shuffles it.
SHUFFLE_MARK: "*"
A SOLVE AS IT IS WRITTEN: every move in order, a pair as its two slots in two base-36 characters each ("0a1c"), a shuffle as "*". The answer handed in and the run kept half way are both this, so a run opened again replays to exactly where it was left, and the server checks a solve by playing it.
shuffled<T>(items: readonly T[], random: Random): T[]
A copy of the list in a random order (Fisher–Yates); the list given is left alone.
shuffleTiles(geometry: MahjongGeometry, cells: MahjongCells, rule: MahjongBonusRule, index: number): MahjongCells | null
SHUFFLE: the tiles left, laid again in the slots they fill, so that play can go on. Drawn from the tiles themselves and how many shuffles came before (index), never from Math.random, so a solve replayed on the server — or a kept game opened again — shuffles to exactly the same tiles.
Laid in reverse, as a deal is, so what is left can be finished whenever the slots allow it. When they do not (two tiles stacked, nothing beside them), the tiles are laid with one matching pair on two free slots, so the next move exists; and where not even two slots are free, no shuffle can help and this answers null.
type SlotAxis = "x" | "y" | "z";
What a layout's slots are put in order by: where a tile is across (x), down (y), and how high (z).
type SlotSortKey = SlotAxis | `-${SlotAxis}`;
One key of a sort: an axis, and - before it for the other way round (-z, the highest first).
sortSlots(layout: MahjongLayout, keys?: readonly SlotSortKey[]): number[]
The slots of a layout, as their numbers, put in order by the keys: ["z", "y", "x"] is layer by layer, row by row, across (the layout's own order), ["x"] lines them up from the left, ["-z"] has the top of the stacks first. Slots equal in every key keep their own order, and a key left out is settled last, in the order x, y, z, so the result is the same whatever the keys.
SUIT_WORDS: Readonly<Record<MahjongSuit, { en: string; ja: string; }>>
What a suit or group is called, in English and in Japanese.
takePair(cells: MahjongCells, a: number, b: number): MahjongCells
The tiles with a pair taken. The caller has asked canTake.
TILE_GROUPS: readonly ["characters", "circles", "bamboo", "winds", "dragons", "flowers", "seasons", "numbers", "honours", "bonus", "terminals", "simples"]
THE GROUPS A SET FALLS INTO, for a viewer: each suit, and the bigger families people name them by: honours (winds and dragons), bonus (flowers and seasons), the numbers (the three suits), and the terminals (the ones and nines) and simples (two to eight) a hand is judged by.
TILE_ORDERS: readonly TileOrder[]
The orders, in the order a list offers them.
type TileGroup = (typeof TILE_GROUPS)[number];
A group's name.
type TileGrouping = "suit" | "kind";
How tiles are grouped, with a gap between groups: by suit (circles with circles), or by kind (numbers, honours, bonus tiles).
type TileLanguage = "en" | "ja";
The two languages a tile is named in.
tileName(code: string, language?: TileLanguage): string
A tile's name in a language, from its code; the code itself for a letter that is no face.
type TileOrder = "dealt" | "suit" | "rank" | "kind" | "code";
How a hand is put in order: as dealt, by suit then rank, by rank across the suits, by kind, or by the letters.
tilesLeft(cells: MahjongCells): number
How many tiles are still on the layout.
tilesMatch(a: string, b: string, rule: MahjongBonusRule): boolean
VERSION: "1.3.0"
The package's version.
writeNotation(codes: Iterable<string>): string
Tiles written the way hands are: digits then a suit letter, 123m456p789s1112z. Runs of one suit share its letter. Anything that is no tile is left out. readNotation is its inverse.
writeTiles(codes: Iterable<string>): string
Tiles as their one-letter codes run together, abcF: what readTiles reads and a layout's cells are written in.
@johnmorrisdotca/jarajara/awaseAWASE_CHALLENGE_NUMBERS AWASE_CHALLENGES AWASE_SAME_BLOCK AwaseChallenge AwaseCheck AwaseDeal AwaseLevel AwaseOptions awaseRules AwaseRules AwaseRun AwaseRunReading bonusRuleOfSeed checkAwase clearRate dailyAwase freshAwaseSeed generateAwase isAwaseChallenge PURGE_GROUPS readRun runHint runMarked runMoves runPairs runRemaining runShuffle runStart runTake runTick runUndo SEED_MOST startRun
AWASE_CHALLENGE_NUMBERS: { readonly sparkMsPerTile: 2500; readonly sparkBonusMs: 20000; readonly rushMs: 180000; readonly rushPairs: 30; readonly fortuneMsPerTile: 1700; readonly fortunePointsPerPair: 150; readonly sandStartMs: 45000; readonly sandPairMs: 5000; readonly sandComboMs: 1000; readonly sandReserveMs: 60000; readonly purgeMsPerTile: 2200; readonly purgeMsPerTarget: 4000; readonly pairPoints: 100; readonly…
The numbers every challenge is made from.
AWASE_CHALLENGES: readonly ["gold", "spark", "rush", "fortune", "sand", "purge", "blackout"]
AWASE WITH RULES YOU CHOOSE: options a game may be given (hints, shuffles and undo given, limited or taken away) and the challenges worth playing a deal for. The ideas come from the challenge modes of good mahjong solitaires (see docs/credits.md); the code and the numbers are Jarajara's own. None of it touches a deal: a challenge is played on exactly the tiles generateAwase made, so every deal here can still be cleared, and checkAwase still judges a solve by the tiles alone.
A game is an AwaseRun: plain data, changed only by returning a new one. It has no clock of its own: every move is given at, milliseconds on any clock the caller keeps (only the differences matter), so a run is as testable as any other rule here and a server may replay one.
The challenges, each the same deal played for something else: gold take a pair of the gold face, four tiles ringed in gold: hunt it down, the stacks permitting spark clear the layout before time runs out; a pair of the spark face adds time and sends the spark on rush make the goal number of pairs before time runs out; the layout need not be cleared fortune reach the goal score before time runs out; fast pairs in a row build a multiplier sand clear the layout from a little time, every pair adding some, to a limit purge take every tile of the marked group before time runs out blackout clear the layout with only the free tiles showing their faces; no undo
AWASE_SAME_BLOCK: { readonly from: 1500000000; readonly size: 100000000; }
THE SEEDS OF THE IDENTICAL RULE, a block no other seed of this puzzle uses: a kept run is found by its seed, so the seed is what can say which rule a deal was made under without a column of its own (as a Futago's does). A fresh seed of the usual rule is drawn outside it; today's seed (20260929) lies far below it.
type AwaseChallenge = (typeof AWASE_CHALLENGES)[number];
A challenge's name.
type AwaseCheck = { ok: true } | { ok: false; reason: string };
What a check of a finished game says: cleared, or the first reason it is not.
type AwaseDeal = { size: number; level: AwaseLevel; seed: number; givens: string; solution: string };
A deal of Awase: its layout's size, its level and seed, the tiles where they lie, and one way to clear them.
type AwaseLevel = "easy" | "medium" | "hard";
How hard a deal of Awase is: the easiest of a seed's candidate deals to clear, the middle one, or the hardest.
type AwaseOptions = { /** A challenge to play the deal as. Unless said, plain Awase. */ challenge?: AwaseChallenge | null; /** How many hints the game may use: a number, or `null` for as many as it likes (unless said). */ hints?: number | null; /** How many shuffles: a number, or `null` for as many as it likes (unless said). */ shuffles?: number | null; /** Whether a move may be taken back. Unless said, it may, unle…
What the options of a game say. Every field may be left out.
awaseRules(tiles: number, options?: AwaseOptions): AwaseRules
The rules a game is played by. A challenge sets its own; the options given are laid over what it allows, and where a challenge takes something away (undo in the spark and blackout) it stays taken away. tiles is how many tiles the layout holds, which the clocks and goals are scaled by.
type AwaseRules = { challenge: AwaseChallenge | null; hints: number | null; shuffles: number | null; undo: boolean; /** The time a game starts with, in milliseconds; null for no clock. */ timeMs: number | null; /** The most time a game may be holding: pairs add to a clock only up to this. Null for no limit. */ reserveMs: number | null; /** Time added by every pair, by a pair of the spark face, and for each step of a…
What a game is played by, worked out from its options and its challenge.
type AwaseRun = { readonly size: number; readonly seed: number; readonly givens: string; readonly rules: AwaseRules; /** Every move so far, in order: pairs by their slots, and shuffles. */ readonly moves: readonly MahjongMove[]; readonly cells: MahjongCells; readonly shuffles: number; readonly hintsUsed: number; /** Pairs taken, points scored, and how many pairs in a row were quick enough to build a combo. */ readon…
A game of Awase, and what the challenge it is played as asks of it.
type AwaseRunReading = { state: "run" | "won" | "lost"; because: AwaseRun["because"]; tilesLeft: number; pairsFree: number; remainingMs: number | null; /** Progress towards what the challenge asks: pairs of the goal, points of the goal, or tiles of the group left. */ goal: { kind: "pairs" | "score" | "purge" | "gold" | "spark" | "clear"; done: number; of: number } | null; multiplier: number; };
The game as it stands, for a page to read: how the clock, the goal and the hunt are going, without working them out again.
bonusRuleOfSeed(seed: number): MahjongBonusRule
checkAwase(size: number, givens: string, answer: string): AwaseCheck
Whether a solve clears a Mahjong deal: the check the browser makes to say "cleared" and the server makes before it pays. It plays the moves on the deal — every pair two free tiles that match, every shuffle made only when no pair was left and drawn exactly as the rules draw it — and asks that the layout ends empty. A few thousand steps at most, and no search.
clearRate(geometry: MahjongGeometry, cells: string, rule: MahjongBonusRule, random: Random, times: number): number
How often a player taking any free pair at random clears this deal, out of times.
dailyAwase(date: string | Date): { date: string; size: number; level: AwaseLevel; seed: number; challenge: AwaseChallenge; }
THE DAY'S GAME, the same for everybody on the same date: a layout, a level, a seed and a challenge worked out from the date alone (2026-10-01, or a Date, in UTC), so no server need hold it. The seed is never in the block that makes a deal play under the Identical rule, and the layout is never the Tiny one, which is for tests.
freshAwaseSeed(rule: MahjongBonusRule, random?: Random, draw?: (): number) => number
A new seed for a deal under this rule: inside AWASE_SAME_BLOCK for the identical rule, anywhere else from 1 to SEED_MOST for the usual one. draw gives a seed for the usual rule (a site keeping blocks of seeds for its own uses passes its own); a whole number from 1 to SEED_MOST unless said.
generateAwase(size: number, level: AwaseLevel, seed: number): AwaseDeal
A deal of this layout at this level, made from this seed.
isAwaseChallenge(text: unknown): text is AwaseChallenge
Whether a text names a challenge.
PURGE_GROUPS: readonly [{ readonly key: "characters"; readonly suits: readonly ["characters"]; }, { readonly key: "circles"; readonly suits: readonly ["circles"]; }, { readonly key: "bamboo"; readonly suits: readonly ["bamboo"]; }, { readonly key: "honours"; readonly suits: readonly ["winds", "dragons"]; }]
The groups a purge may name, each a set of tiles a layout is likely to hold plenty of.
readRun(run: AwaseRun, at: number): AwaseRunReading
Read a game at at.
runHint(run: AwaseRun): { run: AwaseRun; pair: [number, number]; } | null
Ask for a hint: the new game with it counted and the pair named, or null where none is left to use or no pair can be taken.
runMarked(run: AwaseRun): number[]
The tiles of the group the game is hunting for, as slots: the gold face, the purge's group, or nothing.
runMoves(run: AwaseRun): readonly MahjongMove[]
A game's moves written as encodeMoves writes them, with its seed, so it can be replayed on the deal.
runPairs(run: AwaseRun): [number, number][]
Every pair that may be taken now: none once the game is over.
runRemaining(run: AwaseRun, at: number): number | null
How much time the game has left at at, in milliseconds; null for a game with no clock.
runShuffle(run: AwaseRun, at: number): AwaseRun | null
Shuffle what is left, which is for when no pair can be taken: the new game, or null where it may not be (a pair can still be taken, none is left to use, or no shuffle would help).
runStart(run: AwaseRun, at: number): AwaseRun
Start the clock, if the game has one and it has not started.
runTake(run: AwaseRun, a: number, b: number, at: number): AwaseRun | null
Take a pair at at: the new game, or null where the rules do not allow it (the game is over, or the two are not a pair that may be taken). A pair taken after the time ran out loses the game instead. Scores the pair with its combo, adds the time the challenge gives, moves the spark on, and judges whether the game is won or lost.
runTick(run: AwaseRun, at: number): AwaseRun
The game as it stands at at: lost, if its time has run out. Nothing else changes with the clock, so a page may call this as often as it likes.
runUndo(run: AwaseRun, at: number): AwaseRun | null
Take the last move back, where the rules allow undo: the new game, or null. The clock is not given back, and score is.
SEED_MOST: 2147483647
The most a seed can be: it travels in an address as a plain integer.
startRun(deal: { size: number; seed: number; givens: string; }, options?: AwaseOptions, at?: number | null): AwaseRun
Start a game of a deal. The gold face, the spark and the purge's group are chosen from the seed, so the same deal is the same game; at starts the clock now, or leave it out and the clock starts with the first move (startRun's runStart).
@johnmorrisdotca/jarajara/tableAWASE_TABLE AwaseSeat AwaseTable AwaseTableState AwaseTake computerPair decodeTable encodeTable isComputerSeat playAtTable readTable SEAT_WINDS seatName startTable tablePairs tablePlayersAsked takeAtTable tidySeatName undoAtTable
AWASE_TABLE: { readonly least: 2; readonly most: 4; readonly nameMost: 20; }
THE RULES AT THE TABLE. Two to four players share one layout and take turns, each taking one pair of free tiles that match — the solitaire's own move. Every pair scores for whoever took it: the plain suit tiles one, the ones and nines two, a wind three, a dragon four (pairPoints), and a flower or season pair two and another turn. When the player to move has no pair to take, the tiles left are shuffled where they lie (as the solitaire's Shuffle does) and the same player goes on. The game ends when the layout is cleared, or when what is left cannot be freed by any shuffle; most points wins, and a tie shares it.
Why these rules: taking turns alone would be decided by counting — two players taking a pair each get half the pairs whatever they do. Pairs that are worth different amounts make every turn a choice between taking the dragon now and leaving the next player nothing good, which is a game of reading the whole layout. And everything is face up, so the device is passed with no cover screen: nothing is secret at this table.
Pure, like every rule here: each function returns a new table.
type AwaseSeat = { name: string; computer?: true };
Who sits in a seat: a name typed on the device, or a computer.
type AwaseTable = { size: number; level: AwaseLevel; seed: number; /** The deal, as the solo game writes it: a face a slot (`tiles.ts`). */ givens: string; seats: readonly AwaseSeat[]; /** Every pair taken, by its two slots, in the order they were taken. */ takes: readonly (readonly [number, number])[]; };
A GAME AT THE TABLE, as it is kept: the deal, who sits where, and every pair taken, in order. Everything else — whose turn it is, the scores, the shuffles, the end — is read again from these by readTable, so a game read back out of storage is exactly the game its pairs make, or none.
type AwaseTableState = { /** The tiles as they lie now. */ cells: string; /** The seat to move; meaningless once `over`. */ turn: number; /** Points, and pairs taken, seat by seat. */ scores: number[]; pairs: number[]; taken: AwaseTake[]; /** How many times the table ran out of pairs and the tiles were shuffled; and after which take the last was. */ shuffles: number; shuffledAfter: number | null; /** Over: every til…
Where a game at the table stands, read from its pairs.
type AwaseTake = { seat: number; pair: readonly [number, number]; codes: readonly [string, string]; points: number; again: boolean };
One pair taken, as the table remembers it.
computerPair(table: AwaseTable, state?: AwaseTableState | null): [number, number] | null
The pair a computer in the seat to move takes, or null when there is nothing to take.
decodeTable(text: string | null): AwaseTable | null
A kept table read back, its shape checked and every pair played again; null for anything that is not one.
encodeTable(table: AwaseTable): string
As kept in the browser: the table as JSON, its takes as slot pairs.
isComputerSeat(table: AwaseTable, at: number): boolean
Whether a computer plays this seat.
playAtTable(table: AwaseTable, a: number, b: number, state?: AwaseTableState | null): { table: AwaseTable; state: AwaseTableState; } | null
The player to move takes this pair: the table and where it now stands, or null where the rules do not allow it. Played on from state where the caller holds it, so a turn costs one pair rather than the whole game again.
readTable(table: AwaseTable): AwaseTableState | null
WHERE THE GAME STANDS, played again from its pairs: null for a deal that is not one, or a pair the rules would not have allowed.
SEAT_WINDS: readonly { label: string; kanji: string; }[]
The four seats' winds, east first, as a mahjong table seats its players.
seatName(seats: readonly AwaseSeat[], at: number): string
What a seat is called: its name, "Computer 2", or its wind.
startTable(deal: { size: number; level: AwaseLevel; seed: number; givens: string; }, seats: readonly AwaseSeat[]): AwaseTable
A new game at the table: the deal made from the seed, these seats, and nothing taken.
tablePairs(table: AwaseTable, state?: AwaseTableState | null): [number, number][]
The pairs the player to move may take: none once the game is over.
tablePlayersAsked(value: unknown): number
A players count from an address, or 1 (the solitaire) for anything that is not two to four.
takeAtTable(table: AwaseTable, a: number, b: number): AwaseTable | null
The table after the player to move takes this pair, or null where the rules do not allow it.
tidySeatName(name: string): string
A name as kept: trimmed, one line, and no longer than a line holds.
undoAtTable(table: AwaseTable): AwaseTable | null
The table with its last pair given back: the undo a table agrees to. Null with nothing to give back.
@johnmorrisdotca/jarajara/facesCloth CLOTHS clothVars faceWords isBuiltInBack isCloth isTileDesignName JARAJARA_CLOTHS jarajaraDesign layoutBox layoutSvg LayoutSvgOptions loadTileDesign RED_FIVE_CODES redFiveIndexes TILE_BACKS TILE_BODY TILE_DEPTH TILE_DESIGNS TILE_FONT TILE_INK TILE_MARKS TILE_SIZE tileAt tileBackDrawing tileBackFace TileBackName TileBackOptions tileBackSvg tileBodySvg tileColours TileDesign TileDesignName tileFaceSvg tileFaceSymbols tileSvg TileSvgOptions TileSymbolOptions
type Cloth = keyof typeof JARAJARA_CLOTHS;
A cloth's name.
CLOTHS: ("blue" | "red" | "green" | "black" | "wood")[]
The cloths, in the order the family lists them.
clothVars(cloth: string | undefined | null): Record<`--jarajara-${string}`, string>
A cloth as CSS custom properties (--jarajara-felt, --jarajara-felt-deep, --jarajara-felt-ink); nothing for a name that is not a cloth.
faceWords(face: MahjongFace): string
What a face is called, in English: "3 of characters", "east wind", "red dragon", "plum (flower)".
isBuiltInBack(name: string): name is (typeof TILE_BACKS)[number]
Whether a name is one of the five backs drawn here.
isCloth(text: unknown): text is Cloth
Whether a text names a cloth.
isTileDesignName(text: unknown): text is TileDesignName
Whether a text names one of the designs.
JARAJARA_CLOTHS: { readonly green: { readonly felt: "#2f5d4a"; readonly deep: "#1f4135"; readonly ink: "#f3efe4"; }; readonly blue: { readonly felt: "#2865a6"; readonly deep: "#1a4677"; readonly ink: "#f3efe4"; }; readonly red: { readonly felt: "#a3342e"; readonly deep: "#7a231f"; readonly ink: "#f3efe4"; }; readonly black: { readonly felt: "#2f3236"; readonly deep: "#1b1d20"; readonly ink: "#ece8dc"; }; readonly wo…
THE CLOTHS A TABLE MAY BE LAID IN: the same five the whole family offers, and itsutsu.com's boards: green (the family's own), blue, red, black, and wood. Each is the felt's colour, its deep edge and the ink written on it. An element's cloth attribute and a drawing's cloth option take the name, and a page that draws its own takes clothVars for the custom properties.
jarajaraDesign(): TileDesign
Jarajara's own design as a design, made the first time it is asked for: the 42 faces, its jade back and the plain ivory tile.
layoutBox(size: number): { width: number; height: number; } | null
The drawing's width and height for a layout's size, room left for the thickness below and the layers above. Null for a size with no layout.
layoutSvg(size: number, cells: string, options?: LayoutSvgOptions): string | null
The whole layout as one <svg>, the tiles in cells where they lie (a face letter a slot, "." for one taken). Null for a size with no layout or cells of the wrong length.
type LayoutSvgOptions = { /** The slot chosen, ringed and tinted. */ chosen?: number | null; /** Slots ringed as a hint. */ hinted?: readonly number[]; /** Slots ringed softly: the tiles that match the one chosen, when a page lights them. */ matching?: readonly number[]; /** Wash every blocked tile darker, so the free ones stand out. */ showFree?: boolean; /** Draw a blocked tile blank, its face not shown, as in the…
loadTileDesign(name: TileDesignName | string): Promise<TileDesign | null>
A design by name. jarajara at once; riichi and riichi-black fetched now, and kept. Null for a name that is no design. The drawings are @johnmorrisdotca/jarajara/designs/riichi and /designs/riichi-black if a bundler should be told of them by name.
RED_FIVE_CODES: readonly ["e", "n", "w"]
The codes of the fives of characters, circles and bamboo: the tiles a set may make red.
redFiveIndexes(codes: Iterable<string>): Set<number>
WHICH TILES OF A LIST ARE THE RED FIVES. A set holds one red five of each suit, drawn in place of one of its four fives, and a face's letter cannot say which, so the first five of each suit in the list (in the layout's slot order, or the hand's) is the red one. A pure function of the tiles, so the same tiles are red every time.
TILE_BACKS: readonly ["jade", "bamboo", "blue", "red", "ink"]
THE BACKS OF THE TILES, drawn as SVG in the tile's own 30 by 40: what a tile lying face down shows. Five are drawn here, each in the colours a real set's backs are made in: jade, the green of most sets, with a lattice; bamboo, the colour of the bamboo that old sets were backed with, with its grain and nodes; blue and red, flat colours with a ring; and ink, near black with a field of dots. A design brings its own as well (riichi, riichi-black), named like the design. Any of the five may be given a colour of its own and a few letters of writing.
TILE_BODY: { readonly face: "#fffdf6"; readonly rim: "#b9ad96"; readonly side: "#d9c59b"; readonly sideEdge: "#a8926a"; }
The tile itself: its ivory face, the rim round it, and the bone of its side.
TILE_DEPTH: 5
How thick a tile is, and so how far each layer is lifted, in the face's units.
TILE_DESIGNS: readonly ["jarajara", "riichi", "riichi-black"]
THE DESIGNS A TILE MAY BE DRAWN IN. jarajara is the package's own, drawn in plain strokes and always at hand; riichi and riichi-black are FluffyStuff's riichi tiles (CC0, see docs/credits.md), regular and black, with their own backs and red fives, and each is fetched the first time it is asked for, so a page that stays with the default pays nothing for them. They draw no flowers or seasons, so those stay Jarajara's own within them.
TILE_FONT: "'Hiragino Mincho ProN', 'Yu Mincho', 'Noto Serif CJK JP', serif"
The fonts the characters are drawn in, Japanese serif first; a page may load its own and name it here.
TILE_INK: { readonly ink: "#22231f"; readonly soft: "#6f6a62"; readonly red: "#b2302f"; readonly green: "#2f6b3a"; readonly blue: "#1f4e8c"; readonly ochre: "#9d6c1f"; readonly flower: "#b0457a"; readonly season: "#c2711c"; }
The inks a face is drawn in.
TILE_MARKS: { readonly chosen: "#dbe8d3"; readonly chosenRing: "#52664b"; readonly hinted: "#9d6c1f"; readonly blockedWash: "rgba(34, 35, 31, 0.26)"; readonly shadow: "#2a1d0e"; readonly gold: "#d4a017"; }
The marks a page may put on tiles: the one chosen, ones hinted, and a wash over the blocked.
TILE_SIZE: { readonly width: 30; readonly height: 40; }
THE FACES OF THE TILES, as SVG text: a Japanese-style set in the plainest strokes that still read at thirty pixels. Characters are the numeral over a red 萬; circles and bamboo are counted out in dots and sticks; the winds and dragons are their characters, 中 red and 發 green, and the white dragon a blue frame. The flowers and seasons carry a band of their group's colour across the top, since any of a group matches any other. Every suit tile and wind also has its number or letter small in the top corner, for a reader who does not count dots at a glance or read 東 as east.
Each face is drawn in the tile's own 30 by 40 (TILE_SIZE), and returned as a string, so it goes into any page, framework or none. The tiles are light objects on any table, so every colour is fixed, never a theme's. The text on them cannot be selected: a tile is a thing to press, not a word to copy.
tileAt(slot: MahjongSlot, layers: number): { x: number; y: number; }
Where a slot's tile face is drawn, raised up and right by its layer.
tileBackDrawing(design: TileDesign): string
The back of a tile in a design, plate and pattern, fitted to the tile's 30 by 40.
tileBackFace(name?: TileBackName, options?: TileBackOptions): string
A back's plate and pattern in the tile's 30 by 40, without the sliver of side or the <svg>: the inside of a <symbol>, or the back an element puts behind a tile it turns over. For a name that is no back drawn here, the design given (its own back), and otherwise the jade one.
type TileBackName = (typeof TILE_BACKS)[number] | (string & {});
A back's name: one of the five drawn here, or a design's own (riichi, riichi-black).
type TileBackOptions = { /** The back's colour as `#rgb` or `#rrggbb`, in place of its own; its lines are worked out from it. */ colour?: string; /** A few letters written across the middle of it, a site's name: at most 8 are drawn. */ mark?: string; /** What a screen reader says. Unless said, "tile, face down". An empty string makes the picture decoration. */ title?: string; /** A design whose own back is drawn, fo…
How a back is drawn. Every field may be left out.
tileBackSvg(name?: TileBackName, options?: TileBackOptions): string | null
One tile's back on its own, as a whole <svg> in the same box as tileSvg: the back with a sliver of the tile's side. Null for a name that is no back drawn here and no design to bring it.
tileBodySvg(design: TileDesign | undefined): string | null
The tile's own plate in a design, fitted to the tile's 30 by 40; null for a design that draws the plain rounded rectangle.
tileColours(design?: TileDesign): { face: string; rim: string; side: string; sideEdge: string; }
The colours of a tile's rim and thickness: a design's, or Jarajara's own ivory and bone.
type TileDesign = { /** The design's name, as an element's `design` attribute and `loadTileDesign` take it: `jarajara`, `riichi`, `riichi-black`. */ name: string; /** The width and height each tile's drawing is made in. */ box: readonly [number, number]; /** The tile's own plate, the ivory the face is on, with its rounded corners; null for the plain rounded rectangle Jarajara draws. */ body: string | null; /** The b…
A SET OF DRAWN TILES, a design: how the tile itself is drawn, its back, and each face's picture. Every drawing is what goes inside an <svg> of the design's own box, so a design may be drawn at any scale (Jarajara's own is in a box 30 by 40, the riichi sets in one 300 by 400) and is fitted to the tile's 30 by 40 when it is used.
type TileDesignName = (typeof TILE_DESIGNS)[number];
A design's name.
tileFaceSvg(code: string, design?: TileDesign, red?: boolean): string | null
One face's drawing, in the tile's own 30 by 40, with no tile under it: the inside of a <symbol> or a <g>, for a board that draws its own tiles. Null for a letter that is no face. With a design, that design's drawing of the face (fitted to the 30 by 40), or Jarajara's own where it has none.
tileFaceSymbols(prefix: string, options?: TileSymbolOptions): string
Every face as a <symbol> in one <defs>, ids <prefix>-<code>, so a board of 144 tiles draws 42 pictures and places each with a <use>. A design with a plate of its own adds it as <prefix>-body, and its red fives are <prefix>-<code>-red.
tileSvg(code: string, options?: TileSvgOptions): string | null
One tile on its own, as a whole <svg>: the face on its ivory, with a sliver of its side, named for a screen reader. Its width and height are the caller's, through CSS; the picture keeps its 3 by 4. Null for a letter that is no face.
type TileSvgOptions = { /** A design's drawing instead of Jarajara's own. */ design?: TileDesign; /** The red five of a design that has one, for a five. */ red?: boolean; /** What a screen reader says. Unless said, the tile's name in English. An empty string makes the picture decoration. */ title?: string; };
How a tile on its own is drawn.
type TileSymbolOptions = { /** A design's drawings instead of Jarajara's own. */ design?: TileDesign; /** Only these faces' symbols, for a board that holds few of them. Unless said, all 42. */ faces?: Iterable<string>; /** Also a symbol of each red five, `<prefix>-e-red`, for a design that has them. */ red?: boolean; };
How a set of symbols is made.
@johnmorrisdotca/jarajara/elementAllowance allowanceOf ELEMENT_SIZES followLanguage hintPair JarajaraGroup JarajaraLayout JarajaraRack JarajaraSet JarajaraTable JarajaraTile JarajaraViewer languageOf RackDetail RackLayoutOptions RackPlace rackPlaces RackTurnOptions rackWidth SpinOptions STRINGS TileRef
type Allowance = number | null;
How many of a thing a game allows: a number, or no limit at all.
allowanceOf(text: string | null): Allowance
Read an allowance from an attribute: nothing or unlimited is no limit, off or 0 is none, a number is that many.
ELEMENT_SIZES: { readonly small: 32; readonly medium: 48; readonly large: 72; }
The sizes every element takes, as a tile's width in pixels.
followLanguage(redraw: (): void) => () => void
Redraw an element when the page changes its language (<html lang>), as a language chooser does, until the function returned is called. One watcher serves every element on the page.
hintPair(layout: MahjongLayout, cells: string, rule: MahjongBonusRule): [number, number] | null
What a hint is: the free pair that leaves the stacks lowest after it, so a hint never digs a tile deeper than it must.
JarajaraGroup: typeof JarajaraGroup
A GROUP OF TILES, SHOWN: <jarajara-group group="winds">, the four winds with their names and how many of each the set holds. Any group TILE_GROUPS names: a suit, honours, bonus, terminals, simples, numbers; or tiles of your own in tiles.
<jarajara-group group="seasons" cloth="wood"></jarajara-group>
Attributes, all optional: group (unless said, winds); tiles (tiles written as codes, names or hand notation, in place of group); captions="off" takes the names away; copies="off" takes the counts away; heading="off" takes the group's name away; face-down and flip draw the tiles face down, to be turned over by a tap (to learn them); and red-fives draws the first five of each suit red, in a design that has them; and design, back, back-colour, mark, size, width, lang and cloth as on <jarajara-tile>. A tap on a tile is a jarajara-view event, { code }.
JarajaraLayout: typeof JarajaraLayout
A GAME OF AWASE ON ANY PAGE: <jarajara-layout size="15">, a stacked layout of tiles drawn on a cloth, with the rules of the solitaire in it: tap a free tile, then its match, and the pair goes. The free tiles can be lit, the tiles that match the one chosen ringed, a hint asked for, the tiles shuffled when no pair is left, and a move undone. The same layout can be looked at lined up instead, sorted by where each slot is.
<jarajara-layout size="15" level="medium" show-free controls timer cloth="green"></jarajara-layout>
Attributes, all optional: size the layout's width in tiles, which names it: 15 (the Turtle, unless said), 4, 8, 9, 10 and the rest of MAHJONG_LAYOUTS level easy, medium (unless said) or hard: how forgiving the deal is seed the deal's seed: the same seed deals the same tiles (a fresh one unless said) cells tiles of your own to play from, one letter a slot, . for a slot left empty, instead of a deal show-free washes the blocked tiles darker so the free ones stand out show-matching rings the tiles that match the one chosen hints how many hints a game may use: a number, off, or unlimited (unless said) shuffles how many shuffles a game may use, the same way undo off takes undo away challenge gold, spark, rush, fortune, sand, purge or blackout: the deal played for something else (AWASE_CHALLENGES); the clocks and goals are worked out from the layout timer shows the game's clock: the time left in a challenge that has a clock, otherwise the time taken, from the first pair taken until the layout is clear controls draws New deal, Undo, Hint and Shuffle buttons and the line that says how the game stands view stack (unless said) draws the layout; lined draws its tiles lined up in a row, in the order sort gives, each with where it lies sort how lined puts the slots in order: the keys x, y and z, - before one for the other way round: "z y x" (unless said), "x", "-z x" static the tiles may be looked at but not played sound a tile chosen, a pair taken, a shuffle and a cleared layout make their sounds design, red-fives, lang, cloth as on the other elements
Methods: newDeal(seed?), take(a, b), hint(), shuffle(), undo(), restore(moves), sortBy(keys). Properties: seed, cells, moves (as encodeMoves writes them), tilesLeft, score and run, the game as an AwaseRun. Events, all bubbling: jarajara-take ({ pair, codes, tilesLeft }), jarajara-shuffle, jarajara-stuck, jarajara-clear ({ moves, seconds, score }, a game won), jarajara-lost ({ because }: time or stuck), jarajara-hint, jarajara-undo and jarajara-deal ({ size, level, seed }, a deal made, which a page that keeps the game listens for).
JarajaraRack: typeof JarajaraRack
A RACK OF TILES ON ANY PAGE, lined up in front of you: <jarajara-rack tiles="123m456p789s1112z">. They can be turned over all at once or one after another or only the ones chosen, sorted and grouped by suit or kind, mixed up, taken out and put in, marked to follow while face down, picked up (raised) with a tap, and spun.
<jarajara-rack tiles="FFGHBBCDE" back="bamboo" order="suit" group="kind" pick></jarajara-rack>
Attributes, all optional: tiles the tiles, written as codes run together (abcF), as names (east red-dragon), or as hand notation (123m456p789s11z); every one of them as the tiles lie in the rack as dealt face-down every tile shows its back; their faces are not in the page turned the places (from 0, as dealt) of tiles that show the other side from the rest lifted the places of tiles picked up, raised above the row marked the places of tiles that carry a mark, a dot on the corner seen face up or face down order suit, rank, kind or code puts the tiles in that order; left out, they lie as dealt (arrangeTiles) group suit or kind leaves a gap between groups of the sorted tiles pick a tap, or Enter or Space, picks a tile up or puts it down; pick="one" lets only one be up at a time capacity how many tiles' room the rack keeps whatever it holds, so taking tiles out leaves it the size it was red-fives draws the first five of each suit red, in a design that has red fives sound the changes make their sounds: tiles turned, set down and picked up, shuffled design, back, back-colour, mark, size, width, lang as on <jarajara-tile> cloth lays the rack on a cloth: green, blue, red, black or wood
Methods (each returns a promise that settles when the tiles have stopped moving; the motion is skipped on a device that asks for less): hide(options?), show(options?) and toggle(tiles?, options?) turn tiles over; sort(order?), group(by?), ungroup(), unsort() and mixUp(seed?) put them in order; take(tile) removes one and answers its letter, add(tile, at?) puts one in and replace(tile, next) swaps one; lift(tiles?), lower(tiles?) and liftToggle(tiles) pick up and put down; mark(tiles), unmark(tiles?) and spin(tiles?, options?). A change is a jarajara-rack event that bubbles, its detail RackDetail; a tap that picks a tile is jarajara-pick, { index, code, lifted }.
JarajaraSet: typeof JarajaraSet
THE WHOLE SET, SHOWN: <jarajara-set>, all 42 faces by suit with how many of each the 144 tiles hold, or, with tiles, what a hand or a wall holds of it: each face as have/total, faces it has none of dimmed. A suit a row.
<jarajara-set mode="tiles" tiles="123m456p789s11z" size="small"></jarajara-set>
Attributes, all optional: tiles (what to count: codes, names or hand notation); mode (faces, unless said, draws each face once with its count; tiles draws every tile of the set, four of each ordinary face, one flower, one season); captions="off" takes the names away; red-fives makes one five of each suit red (in tiles mode, in a design that has them); and design, back, size, width, lang and cloth as on <jarajara-tile>. A tap on a tile is a jarajara-view event, { code }.
JarajaraTable: typeof JarajaraTable
AWASE AT A TABLE, ON ANY PAGE: <jarajara-table players="3">, two to four players round one layout, each taking a free pair in turn: the seats with their winds and scores, the layout on a cloth, and computers playing the seats that are not people's. Plain suit pairs score one, ones and nines two, a wind three, a dragon four; a flower or season pair scores two and its taker goes again; most points when the layout is clear wins.
<jarajara-table players="3" people="1" names="Ann" cloth="blue"></jarajara-table>
Attributes, all optional: players how many sit at the table, two to four (2 unless said) people how many of the seats are people's, the first ones; the rest are computers (1 unless said) names the people's names, separated by commas; a seat with none is called by its wind size the layout's width in tiles (10, the Castle, unless said), level, seed as on <jarajara-layout> delay how long a computer thinks before it takes its pair, in milliseconds (700 unless said) show-free washes the blocked tiles darker sound each pair taken, and a game won, make their sounds design, lang, cloth as on the other elements
deal(seed?) deals again. The table property holds the game as encodeTable keeps it. Each pair taken is a jarajara-table event that bubbles, its detail { seat, pair, codes, points, scores, over, winners }.
JarajaraTile: typeof JarajaraTile
ONE TILE ON ANY PAGE: <jarajara-tile code="F">, in any design and with any back, face up or face down, turned over by a tap when it has flip.
<jarajara-tile code="east" back="bamboo" flip size="large"></jarajara-tile>
Attributes, all optional but code: code the tile: its letter (F), or any name it goes by (east, red dragon, 3p, 三筒) design jarajara (unless said); the riichi sets are named in TILE_DESIGNS and fetched the first time they are asked for back jade (unless said), bamboo, blue, red or ink, or a design's own back; back-colour and mark as tileBackSvg takes them face-down shows the back; the face is not in the page while it is down flip a tap, Enter or Space turns it over, with a turn that a device asking for less motion skips marked a mark on its corner, seen face up and face down, to follow it as it moves red draws the red five of a design that has one, for a five size small, medium (unless said) or large; or width in pixels; or the page's --jarajara-tile-width sound the turn makes a sound lang ja for Japanese names; the page's language unless said
Each turn is a jarajara-flip event that bubbles, with { code, faceDown } as its detail. spin(options?) spins it where it lies, slowing to a stop as it was.
JarajaraViewer: typeof JarajaraViewer
A TILE LOOKED UP BY ITS CODE, NAME OR KEY: <jarajara-viewer query="east"> shows the tile big, with what it is called in English and in Japanese, its code, suit and rank, how many the set holds, what a pair of them scores at the table, and how a hand writes it. A query that names several tiles (winds, abcF, 123m456p) shows them in a row to choose from.
<jarajara-viewer query="3 of circles" editable></jarajara-viewer>
Attributes, all optional: query (what to look up: a code, any name findFace knows, a group, or a list of tiles: words apart, or hand notation); editable adds a box to type a query into; and design, back, size, width (the big tile, large unless said), lang and cloth as on <jarajara-tile>. lookup(text) looks one up; each tile shown is a jarajara-view event, { code, query }.
languageOf(element: Element): TileLanguage
The language an element speaks: its own lang, or the nearest one above it, or the page's; Japanese for anything that starts ja.
type RackDetail = { tiles: string[]; faceDown: boolean; turned: number[]; lifted: number[]; marked: number[]; order: TileOrder; group: TileGrouping | null };
How the rack lies, for the page to hear of: what is turned, raised and marked (by place in tiles), and how the tiles are put in order.
type RackLayoutOptions = { /** The places, counted from 0 in the order the tiles lie, where a new group starts, after a gap. Unless said, none. */ breaks?: readonly number[]; /** The places of the tiles that are raised. Unless said, none. */ lifted?: ReadonlySet<number> | readonly number[]; /** How far apart neighbours lie, in tile widths: a little over one, so tiles that touch have a hair of table between. Unless s…
How a rack is laid out. Every field may be left out.
type RackPlace = { x: number; y: number };
One tile's place: across and down in tile widths from where the rack starts. A lifted tile is above the row, so its y is negative.
rackPlaces(count: number, options?: RackLayoutOptions): RackPlace[]
Where each of count tiles lies, in order: the first at x 0, each a step on, groups set apart, raised tiles lifted.
type RackTurnOptions = { /** One tile after another, from the first, rather than all at once. Unless said, all at once. */ oneByOne?: boolean; /** Milliseconds between one tile and the next, one by one. Unless said, 90. */ gap?: number; };
How a rack's tiles are turned face down or face up.
rackWidth(places: readonly RackPlace[]): number
How wide a rack is, in tile widths: the last tile's place and the tile itself.
type SpinOptions = { /** Which way it spins. Unless said, clockwise. */ direction?: "clockwise" | "anticlockwise"; /** How many whole turns before it comes to rest where it lay. Unless said, 3; at most 20. */ turns?: number; /** How long the spin takes, in milliseconds. Unless said, 700 and 420 a turn. */ ms?: number; };
How a tile is spun.
STRINGS: Readonly<Record<TileLanguage, Record<string, string>>>
THE WORDS THE ELEMENTS SAY, to a screen reader and in their own captions, in the two languages the family speaks. An element speaks its own lang, or the nearest one above it, or the page's. {name} marks where a value goes (fillIn).
type TileRef = number | string | readonly (number | string)[];
Tiles as a method is given them: a number is the place of one tile in the rack as it was written (tiles), from 0; a letter or a name ("F", "east", "3p") is every tile of that face; a list is each of its parts.
@johnmorrisdotca/jarajara/element/define@johnmorrisdotca/jarajara/designs/riichiRIICHI: TileDesign
The regular riichi tiles, drawn in a box 300 by 400.
@johnmorrisdotca/jarajara/designs/riichi-blackRIICHI_BLACK: TileDesign
The black riichi tiles, drawn in a box 300 by 400.
@johnmorrisdotca/jarajara/tile-soundscreateTileSounds MOST_SOUNDS_AT_ONCE PlayTileSoundOptions soundTimes TILE_SOUND_KINDS TileSoundData TileSoundKind TileSounds TileSoundsOptions TileSoundWindow
createTileSounds(options?: TileSoundsOptions): TileSounds
A table's tile sounds. Nothing is fetched and no audio context is made until the first sound.
MOST_SOUNDS_AT_ONCE: 8
As many sounds as one call plays: thirteen tiles set down are eight clicks, not a wall of noise.
type PlayTileSoundOptions = { /** How many tiles: `place` with 13 is thirteen tiles set down one after another (heard as at most `MOST_SOUNDS_AT_ONCE`). Unless said, one. */ count?: number; /** Milliseconds between one tile's sound and the next. Unless said, 85. */ gap?: number; /** Milliseconds to wait before the first. Unless said, none. */ delay?: number; };
How one sound is played.
soundTimes(count: number, gap?: number): number[]
When each of count sounds starts, in milliseconds from the first: one every gap, and no more than MOST_SOUNDS_AT_ONCE, spread over the same time.
TILE_SOUND_KINDS: readonly ["pick", "place", "flip", "pair", "shuffle", "win"]
Every kind of sound, in the order a game meets them.
type TileSoundData = Readonly<Record<string, string>>;
The recordings, by name (deal-2), as base64 AAC.
type TileSoundKind = (typeof TILE_SOUND_KINDS)[number];
One kind of sound: pick a tile lifted, place one set down, flip one turned over, pair two knocked together, shuffle the tiles washed, win a clatter of tiles.
type TileSounds = { /** Play a sound, or several of one kind in a row; nothing while muted or closed. */ play(kind: TileSoundKind, options?: PlayTileSoundOptions): void; /** Fetch and decode the recordings now, rather than at the first sound. True once they are ready; false where they cannot be had. */ load(): Promise<boolean>; /** Whether it is muted. */ readonly muted: boolean; /** Mute or unmute. Muting stops not…
A table's sounds: play one, mute and unmute, change the volume, close when the table goes.
type TileSoundsOptions = { /** Start muted: nothing plays, and nothing is fetched, until `setMuted(false)`. Unless said, not muted. */ muted?: boolean; /** How loud, from 0 to 1. Unless said, 0.6. */ volume?: number; /** Where the recordings come from: the package's own module unless another is handed in. */ load?: () => Promise<{ TILE_SOUND_DATA: TileSoundData }>; /** The window to make sound in: the page's own unl…
How a table's sounds are made. Every field may be left out.
type TileSoundWindow = { AudioContext?: typeof AudioContext; webkitAudioContext?: typeof AudioContext; atob?: (text: string) => string; };
The parts of a window the sounds use: an audio context and atob. Any of them may be missing.
@johnmorrisdotca/jarajara/soundsTILE_SOUND_DATA: Readonly<Record<TileSoundFile, string>>
The tile sounds, recorded: a tile picked up, set down, turned over, a pair knocked, the tiles shuffled and a clatter for a win, as base64 AAC (.m4a). From Kenney's Casino Audio pack, CC0; see docs/credits.md. Written by scripts/sounds.mjs from the files in ./sounds, never by hand. createTileSounds loads this module only when a sound is first played, so a page that stays silent never downloads it.
type TileSoundFile = "flip-1" | "flip-2" | "pair-1" | "pair-2" | "pair-3" | "pair-4" | "pick-1" | "pick-2" | "place-1" | "place-2" | "place-3" | "shuffle-1" | "shuffle-2" | "shuffle-3" | "win-1" | "win-2";
The name of one recording: its kind of sound and a number, such as deal-2.
この日本語は、まだ日本語を母語とする方の確認を受けていません。訂正を歓迎します。