Dominoドミノ

@johnmorrisdotca/domino 1.0.0 · 1 entry points · 60 exports

@johnmorrisdotca/domino

cleanTrainName computerMove decodeTrain Domino DoublesRule encodeTrain endsOf everyTile fits handPips handSizeFor isDouble laidAgainst LaidDomino laidEnds legalPlays longestRun mayLay mexicanOf MexicanStart moveCount movesOf openEnd peopleAt pipsOf playTrain Random replayTrain RoundEnding RoundResult roundsFor seededRandom shuffled startTrain tileOf tileOfLaid tileWords Train TRAIN_DEFAULT_OPTIONS TRAIN_DOUBLES TRAIN_LENGTHS TRAIN_MEXICAN TRAIN_NAME_MOST TRAIN_PHASES TRAIN_PIP_BASE TRAIN_SET_NAMES TRAIN_SETS trainAgain TrainGame TrainHistory TrainLength TrainMove trainMoves TrainOptions TrainPhase trainPlayerName TrainSeat trainSetName trainTotals VERSION

function cleanTrainName

cleanTrainName(name: string): string

A seat's name as given, its spaces tidied and cut to TRAIN_NAME_MOST characters.

function computerMove

computerMove(game: TrainGame): TrainMove

The move the computer in the seat to move makes now: the best-scored lay, or the draw, pass or next round it must take.

function decodeTrain

decodeTrain(text: string | null): TrainGame | null

A kept game read back, or null for nothing kept or anything these rules cannot play out again.

type Domino

type Domino = number;

A domino in a hand or the boneyard: low * 16 + high.

type DoublesRule

type DoublesRule = "one" | "chain";

How a double is dealt with, the table's one house rule about them.

- one (the default, as most published rules play it): a double must be covered before anything else is played anywhere, and whoever laid it lays again to cover it. - chain: after laying a double you may lay another double in the same turn, anywhere one fits, before covering; then every double left open is covered, the last laid first, before anything else is played.

function encodeTrain

encodeTrain(game: TrainGame): string

function endsOf

endsOf(tile: Domino): [number, number]

A tile's two ends, the smaller first.

function everyTile

everyTile(set: number): Domino[]

Every tile of a set, double-blank to its highest double, in order.

function fits

fits(tile: Domino, end: number): boolean

Whether a tile has this number at either end, so it can be laid against it.

function handPips

handPips(hand: readonly Domino[]): number

The pips in a hand, which count against its holder when the round ends.

function handSizeFor

handSizeFor(set: number, players: number): number

HOW MANY TILES EACH PLAYER IS DEALT, by the set and the number at the table. Double-twelve's are the figures most published rules give (fifteen each for two to four, twelve for five or six, ten for seven or eight); the other sets are scaled so that at every table some tiles are left to draw.

function isDouble

isDouble(tile: Domino): boolean

Both ends the same: a double, which is laid across a train and must be covered.

function laidAgainst

laidAgainst(tile: Domino, end: number): LaidDomino

The tile laid against end: turned so that end touches, the other one left open.

type LaidDomino

type LaidDomino = number;

A domino laid in a train, turned so its from end touches the train: from * 16 + to.

function laidEnds

laidEnds(laid: LaidDomino): [number, number]

A laid tile's two ends in the order it lies: the one touching the train, then the open one.

function legalPlays

legalPlays(game: TrainGame): { tile: Domino; train: number; }[]

Every tile the player to move may lay, and where: what moves offers, and what the table lights up. While a double is uncovered anywhere the only lay is to cover the last of them, on whoever's train it is — or, under the chained-doubles rule, another double laid by the player still laying them.

function longestRun

longestRun(hand: readonly Domino[], end: number): Domino[]

The longest run of tiles from hand that can be laid one after another against end, in the order they would be laid. A depth-first search over the hand, bounded by PLAN_STEPS, preferring the heavier run of two the same length: pips laid are pips that do not count.

function mayLay

mayLay(game: TrainGame, tile: Domino, train: number): boolean

Whether the player to move may lay this tile on this train now: the same answer legalPlays gives, for one tile, without listing every other.

function mexicanOf

mexicanOf(game: Pick<TrainGame, "players">): number

The Mexican Train's number among a game's trains: after every seat's own.

type MexicanStart

type MexicanStart = "any" | "ownFirst";

When the Mexican Train may be started.

- any (the default): on any turn, by anybody, as most published rules say. - ownFirst: a player may lay on the Mexican Train only once their own train has been started, the common house rule that keeps a first turn about your own train.

function moveCount

moveCount(game: Pick<TrainGame, "history">): number

How many moves a game has made.

function movesOf

movesOf(game: Pick<TrainGame, "history">): TrainMove[]

Every move a game has made, first to last.

function openEnd

openEnd(game: TrainGame, train: number): number

The number a train's next tile must match: its last tile's open end, or the engine double's when nothing is laid on it yet.

function peopleAt

peopleAt(game: Pick<TrainGame, "computers">): TrainSeat[]

The seats a person plays: a table with two or more of them passes the device, and covers each hand between turns.

function pipsOf

pipsOf(tile: Domino): number

Every pip on a tile: what it counts against its holder when a round ends.

function playTrain

playTrain(game: TrainGame, move: TrainMove): TrainGame | null

The game after that move, or null for a move that may not be made now. The game given is left untouched, and the move is added to its record.

type Random

type Random = () => number;

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

function replayTrain

replayTrain(set: number, players: readonly string[], seed: number, options: TrainOptions, computers: readonly boolean[], moves: readonly TrainMove[]): TrainGame | null

A game played again from its table and its moves: what reading a kept game back does. Null if any move is one the rules would not have taken.

type RoundEnding

type RoundEnding = "domino" | "blocked";

Why a round ended: somebody played their last tile, or nobody could play and nothing was left to draw.

type RoundResult

type RoundResult = { /** The round's engine double, by its number: 12 for double-twelve. */ engine: number; /** Pips left in each seat's hand when it ended: that round's score. */ pips: readonly number[]; ending: RoundEnding; /** The seat that played out, on a round that ended that way. */ out: TrainSeat | null; };

A round's result, kept for the table of scores.

function roundsFor

roundsFor(set: number, length: TrainLength): number

How many rounds a game of this length plays with this set: one per double, top to blank, or the first half of them.

function seededRandom

seededRandom(seed: number): Random

A stream of numbers in [0, 1) fixed by a seed.

function shuffled

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.

function startTrain

startTrain(set: number, players: readonly string[], seed?: number, options?: TrainOptions, computers?: readonly boolean[]): TrainGame | null

A new game: the set (its highest double), the names at the table (one a seat), which seats a computer plays, the options, and the seed every shuffle is drawn from. Null for a table the game is not offered for — a set not in PARTY_SPECS, or too few or too many players — rather than a game nobody chose.

function tileOf

tileOf(a: number, b: number): Domino

The tile with these two ends, whichever order they are given in.

function tileOfLaid

tileOfLaid(laid: LaidDomino): Domino

The tile a laid tile is, whichever way round it lies.

function tileWords

tileWords(tile: Domino): string

A tile as a person reads it: "6–4", the larger end first, as a tile is named at the table.

type Train

type Train = { laid: readonly LaidDomino[]; /** Open to everybody: the Mexican Train always, a player's own once they could not play. */ open: boolean; };

A train on the table: the tiles laid on it in order, and whether its owner's marker says anybody may play on it.

const TRAIN_DEFAULT_OPTIONS

TRAIN_DEFAULT_OPTIONS: TrainOptions

The options a table opens on: every round, one double at a time, and the Mexican Train open from the start.

const TRAIN_DOUBLES

TRAIN_DOUBLES: { readonly one: "one"; readonly chain: "chain"; }

const TRAIN_LENGTHS

TRAIN_LENGTHS: { readonly full: "full"; readonly short: "short"; }

const TRAIN_MEXICAN

TRAIN_MEXICAN: { readonly any: "any"; readonly ownFirst: "ownFirst"; }

const TRAIN_NAME_MOST

TRAIN_NAME_MOST: 20

The longest name a seat keeps.

const TRAIN_PHASES

TRAIN_PHASES: { readonly playing: "playing"; readonly roundOver: "roundOver"; readonly finished: "finished"; }

MEXICAN TRAIN, THE DOMINO GAME: the rules, and nothing else.

Pure, as the engine is: every function returns a new game and leaves the one it was given untouched. A game is its table (the set, the options, the seed, the seats) and its moves, in order; hands, trains, the boneyard and whose turn it is are always read again from those (replayTrain), so a game read back out of a browser's storage is exactly the game its moves make, or none. Every shuffle is drawn from the game's seed and the round's number, so a reload deals exactly what it dealt before.

The rules as the site plays them, most published rules' own:

- a round is dealt round the engine double, the set's highest in the first round and one fewer each round after, which sits in the hub; - every player has a train of their own out of the hub, and there is one more, the Mexican Train, anybody may play on; - on your turn lay one tile against the open end of your own train, the Mexican Train, or any player's train whose marker is out; - nothing to lay: draw one tile; lay it if it goes, or put your marker out and pass (with nothing to draw, just put it out); lay on your own train and your marker comes in; - a double must be covered before anything else is played anywhere, and whoever lays one lays again to cover it (DoublesRule for the house rule that lets doubles be chained); - a round ends when somebody lays their last tile, or when nobody can lay and there is nothing left to draw; every player scores the pips left in their hand, and after the last round the lowest total wins.

const TRAIN_PIP_BASE

TRAIN_PIP_BASE: 16

The largest set's highest double, and so the base a tile's two ends are written in (tileOf).

const TRAIN_SET_NAMES

TRAIN_SET_NAMES: Record<number, string>

Each set by name, for the set-up's tiles and the card on My games.

const TRAIN_SETS

TRAIN_SETS: { readonly nine: 9; readonly twelve: 12; readonly fifteen: 15; }

THE SETS OFFERED, by their highest double. Double-twelve is the set Mexican Train is sold with and the one most published rules are written for, so it is the default; double-nine for a quicker game with fewer, larger pips, and double-fifteen for a long evening. These are the party game's "sizes" (PARTY_SPECS), since the set is what the table is played on.

function trainAgain

trainAgain(game: TrainGame, seed: number): TrainGame

The same table again, the same seats and options, with a fresh shuffle.

type TrainGame

type TrainGame = { /** The set, by its highest double: 9, 12 or 15. The party game's "board size". */ set: number; options: TrainOptions; /** What every shuffle of this game is drawn from. */ seed: number; /** The names given at the table, in seat order: "" for one left blank. */ players: readonly string[]; /** Which seats a computer plays. */ computers: readonly boolean[]; /** * Every move made, the last first, eac…

A game, as its table and moves make it. Only the set, the options, the seed, the seats and the moves are ever kept (encodeTrain); everything else is read again from them (replayTrain), so a kept game can never hold a hand or a train its moves do not make, and a reload cannot deal again.

type TrainHistory

type TrainHistory = { readonly move: TrainMove; readonly before: TrainHistory | null; /** How many moves the record holds, this one included. */ readonly count: number; };

One link of a game's record: a move, and every move before it.

type TrainLength

type TrainLength = "full" | "short";

How many rounds: all of them, one for every double from the set's highest down to double blank, or half as many.

type TrainMove

type TrainMove = | { kind: "play"; tile: Domino; train: number } | { kind: "draw" } | { kind: "pass" } | { kind: "next" };

One move at the table.

- play: lay a tile from your hand on a train. - draw: take one tile from the boneyard, when you have nothing to play. - pass: nothing to play and nothing to draw (or the tile drawn will not go): your train's marker goes on, and the turn passes. - next: a round is over and everybody has seen how it went; deal the next.

function trainMoves

trainMoves(game: TrainGame): TrainMove[]

Every move the player to move may make now; none once the game is over.

type TrainOptions

type TrainOptions = { length: TrainLength; doubles: DoublesRule; mexican: MexicanStart; };

What the set-up chose, beyond the set and the players.

type TrainPhase

type TrainPhase = "playing" | "roundOver" | "finished";

function trainPlayerName

trainPlayerName(game: Pick<TrainGame, "players" | "computers">, seat: TrainSeat): string

A seat's name as the table reads it: the one given, or "Computer 3" for a computer's seat left blank, "Player 3" for a person's.

type TrainSeat

type TrainSeat = number;

Who sits where: 0 is the first player, round the table in the order the set-up named them.

function trainSetName

trainSetName(set: number): string | null

The set's name, or null for a number that is no set offered.

function trainTotals

trainTotals(game: Pick<TrainGame, "players" | "results">): number[]

Each seat's total over every round played: the lowest wins.

const VERSION

VERSION: "1.0.0"

The package's version.