function agreeDraw
agreeDraw(game: GameState): GameState
The game drawn by agreement.
@johnmorrisdotca/sugoroku 1.0.0 · 5 entry points · 170 exports
@johnmorrisdotca/sugorokuagreeDraw allHome applyMove BAR beaverDouble boardPoint canBeaver canDouble canPlayAll chanceOfWinning checkersOf choosePlay computerAction ComputerAction ComputerOptions concede concedeKind countAt Cube CUBE_LIMIT cubeInPlay cubeIsDead DEFAULT_RULES diceMustPlay DiceSource diceToPlay Direction dropDouble endTurn evaluate evaluateFor EVALUATION_WEIGHTS EvaluationWeights finishGame formatMove formatOpening formatPlay formatPosition formatRecordHeader formatResult formatTurn GameRecorder GameResult GameState Goal hashSeed Hop HowItEnded inContact inPlay isVariantKey keithCount legalMoves legalPlays legalPlaysOf Match mirrorPoint Move movesOfDie multiplierOf newGame NewGameOptions newMatch nextMoves offerDouble onBoard Opening openingFrom opponentAt otherSide parseHops parsePlay parsePosition Phase pipCount Play playMove playMoves playTurn POINTS pointsToGo Position positionFromId positionId positionKey Preset presetByKey presetByName PRESETS Random randomDice RECORD_VERSION RecordSeed ReplayedGame ReplayError ReplayOk replayRecord resolveRules restartTurn rollDice rollFrom rollOpening Rules rulesProblems seededDice seededRandom Settings settingsFor Side sideIndex SIDES sideToAct startCounts startGame startPosition startReserve stepComputer Strength STRENGTHS takeDouble turnIsPlayed undoMove VARIANT_KEYS VariantKey VARIANTS variantSpec VariantSpec VERSION wantsToDouble wantsToTake winChance winKind WinKind withCounts
agreeDraw(game: GameState): GameState
The game drawn by agreement.
allHome(position: Position, side: Side): boolean
Whether every checker of side still in play is in its home board (own points 1 to 6): the condition for bearing off.
applyMove(spec: VariantSpec, position: Position, side: Side, move: Move): Position
The position after a move. Does not check that the move is legal.
BAR: 25
The number a move's from has when a checker comes from the bar (or from off the board at the start of a race).
beaverDouble(game: GameState): GameState
Beaver: the side that was doubled redoubles and keeps the cube; the side that doubled must now take it or drop it.
boardPoint(spec: VariantSpec, side: Side, own: number): number
The point on the board, 1 to 24 counting the way white counts, that is side's own point own. White's own numbers are the board's; black's are the board's reversed when the sides go opposite ways round.
canBeaver(game: GameState): boolean
Whether the side that was doubled may beaver: redouble at once and keep the cube. Only where the rules allow it.
canDouble(game: GameState): boolean
Whether the side on turn may offer a double now.
canPlayAll(spec: VariantSpec, position: Position, side: Side, need: readonly number[], failed?: Set<string>): boolean
Whether every die of need (highest first) can be played from position, in some order.
chanceOfWinning(game: GameState, side: Side): number
The chance, 0 to 1, that side wins from here, by the evaluation, or by the Keith count's rule of thumb in a race (as 0.5 plus a share of the count's lead).
checkersOf(position: Position, side: Side): number
The whole count of each side's checkers: on the board, on the bar, yet to enter, and off. Always the variant's checkers.
choosePlay(game: GameState, options?: ComputerOptions): Play
The play the computer makes for the rest of the turn of the side on turn, from the plays the rules leave it.
computerAction(game: GameState, options?: ComputerOptions): ComputerAction | null
What the side that has to act in a game would do, if the computer were playing it. Nothing is done: the game is not changed. Null when the game is over.
type ComputerAction = | { kind: "throw" } | { kind: "double" } | { kind: "roll" } | { kind: "play"; play: Play } | { kind: "end" } | { kind: "take" } | { kind: "drop" };
What the computer does next in a game, for the side it plays.
type ComputerOptions = { /** The strength. Default `strong`. */ strength?: Strength; /** Where its choices come from, for `random` and for choosing between plays that score the same. Default `Math.random`. */ random?: Random; /** * The longest, in milliseconds, it spends looking ahead at one move: it looks at its best plays in order and stops when the time is * up (always having looked at the best two). Default 60, …
concede(game: GameState, side: Side, kind?: WinKind): GameState
Give up the game: side loses. kind is what it is giving up, from concedeKind's worst down to a single game.
concedeKind(game: GameState, side: Side): WinKind
What a side gives up if it gives up the game now, which is the worst the position could come to: a single game once it has borne a checker off; a gammon if nothing of the other side's can still hit it; otherwise a backgammon where it may have a checker caught in the other side's home board.
countAt(position: Position, side: Side, own: number): number
How many of side's checkers stand on its own point own (1 to 24).
type Cube = { readonly value: number; readonly owner: Side | null };
The doubling cube: what it shows, and who may turn it (null while it stands in the middle, for either).
CUBE_LIMIT: 64
The most the cube can show.
cubeInPlay(rules: Rules, crawfordGame: boolean): boolean
Whether the cube is in play in a game of a match, given whether this is the Crawford game.
cubeIsDead(game: GameState): boolean
The cube is dead when turning it could not matter: it already shows enough for either side to win the match with a win. Never in money play.
DEFAULT_RULES: Rules
The rules a variant is played with when nothing else is asked: a single game with no cube and no gammons.
diceMustPlay(spec: VariantSpec, position: Position, side: Side, dice: readonly number[]): number[]
WHICH DICE MUST BE PLAYED. A side must play as many of its dice as it can; where it cannot play them all and could play either of two (or more) sets, it must play the higher: with two dice, if only one can be played, it is the larger one when it can. Returned highest first, from the dice as diceToPlay gives them.
type DiceSource = () => number;
Where dice come from: each call is the number on one die, 1 to 6.
diceToPlay(spec: VariantSpec, roll: readonly number[]): number[]
The dice a roll gives to play, highest first: a double of a game that plays doubles four times is four of them.
type Direction = /** Opposite ways round, as in backgammon: one side's 24-point is the other's 1-point. */ | "opposed" /** The same way round, from the same start, as in Tabula: both sides' own point 24 is the one place. */ | "same";
How the sides' tracks lie on the board.
dropDouble(game: GameState): GameState
Drop the double that was offered: the side that drops it loses the game, at the cube's value before the double, or for a beaver at its value once the double was taken.
endTurn(game: GameState): GameState
End the side on turn's turn, once everything it had to play has been played. The other side is to move.
evaluate(spec: VariantSpec, position: Position, side: Side, weights?: EvaluationWeights): number
The score of a position for side, with the other side to move, as a game where bearing off first wins. Large positive or negative numbers are a game won or lost; evaluateFor turns it over for a game played to lose.
evaluateFor(spec: VariantSpec, position: Position, side: Side, weights?: EvaluationWeights): number
The score as side plays to win or to lose: a game played to lose is judged the other way up.
EVALUATION_WEIGHTS: EvaluationWeights
The weights the computer plays with: set by hand and then held against random changes of themselves, none of which won more often over two thousand games.
type EvaluationWeights = { /** What it is worth to have the roll, which the side that does not have it must give up. */ onRoll: number; /** Per checker still to bear off, in a race. */ raceChecker: number; /** What a made point in the home board is worth, by the point, 1 to 6. */ home: readonly [number, number, number, number, number, number]; /** A made point in the outfield (7 to 12), and in the other outfield (13…
The numbers the evaluation is made of, each a number of pips.
finishGame(match: Match, result: GameResult): Match
The match after a game's result: the score added up, the Crawford game accounted for, and the match over if it is decided.
formatMove(move: Move): string
One move as text: 24/18, bar/22*, 6/off.
formatOpening: (throws: readonly [number, number]) => string
One line of a game: the opening throw.
formatPlay(moves: readonly Move[]): string
The text of a whole turn: moves of one checker joined, equal moves counted, the farthest-back checker first. A turn of no moves is -.
formatPosition(position: Position): string
A position as text, for any variant: each side's checkers as point:count in that side's own numbering, highest point first, then the bar, those yet to enter and those borne off, [white, black] each.
white=24:2,13:5,8:3,6:5 black=24:2,13:5,8:3,6:5 bar=0,0 reserve=0,0 off=0,0
formatRecordHeader(settings: Settings, seed?: RecordSeed): string[]
The header lines of a record: its version, the variant, the rules and the seed.
formatResult(result: GameResult): string
The line that closes a game, with what it came to.
formatTurn: (side: Side, dice: readonly number[], moves: readonly Move[]) => string
One line of a game: a turn, with its dice and its play.
GameRecorder: typeof GameRecorder
A record written as a game is played. Call its methods as the game goes, in the order things happen, and text() is the record so far; a game not yet finished is a record that replays to a game still in play.
type GameResult = { /** The side that won, or null for a draw. */ readonly winner: Side | null; readonly how: HowItEnded; readonly kind: WinKind; /** 1 for a single win, 2 for a gammon, 3 for a backgammon, after the Jacoby rule and the choice of rules. */ readonly multiplier: number; /** The cube's value when it ended. */ readonly cube: number; /** What the winner scores: the multiplier times the cube. 0 for a draw.…
type GameState = { readonly settings: Settings; /** Whether this is the Crawford game of a match: no cube. */ readonly crawford: boolean; /** The match score as the game began, `[white, black]`: what the dead cube is judged by. */ readonly score: readonly [number, number]; readonly position: Position; /** The side to move; null until the opening throw has decided. */ readonly turn: Side | null; readonly phase: Phase…
type Goal = /** Bear off every checker first. */ | "first-off" /** Be last to bear off: whoever bears off every checker first loses (Anti-Backgammon). */ | "last-off";
What it takes to win.
hashSeed(seed: number | string): number
A whole number from a seed given as a number or as text: the same seed is always the same number, in every browser and every Node.
type Hop = { readonly from: number; readonly to: number };
A step of a written play: one checker, one die's distance (or off), as written.
type HowItEnded = /** Somebody bore off every checker. */ | "bear-off" /** A double was dropped. */ | "drop" /** A side gave up the game. */ | "concede" /** Neither side won. */ | "draw";
How a game came to its end.
inContact(spec: VariantSpec, position: Position): boolean
Whether the two sides can still hit one another. Once every checker of one side has gone past every checker of the other there is nothing more to play for but the race, and the game is a race.
inPlay(position: Position, side: Side): number
Every checker of side that is still in the game: on the board, on the bar or yet to enter.
isVariantKey(key: unknown): key is VariantKey
Whether key names a variant.
keithCount(position: Position, side: Side, onRoll: boolean): number
The Keith count of a side, a pip count corrected for how badly the checkers are placed for bearing off: plus 2 for each checker beyond the first on the 1-point, 1 for each beyond the first on the 2-point and beyond three on the 3-point, and 1 for each empty point among the 4, 5 and 6, and then the side on roll adds a seventh (rounded down). For races only.
legalMoves(game: GameState): Move[]
The moves the side on turn may make next. Empty when the turn is played out, or nothing can be played.
legalPlays(spec: VariantSpec, position: Position, side: Side, roll: readonly number[]): Play[]
Every different turn a side may make with a roll: one for each position that playing the roll as the rules require can leave, with one way of getting there. A side that cannot move has the one play of no moves.
legalPlaysOf(game: GameState): Play[]
Every different way the side on turn may play out the rest of its turn: for the dice it has yet to play, from where the position stands now.
type Match = { readonly settings: Settings; /** The score, `[white, black]`. */ readonly score: readonly [number, number]; /** Every game finished so far. */ readonly results: readonly GameResult[]; /** Whether the next game is the Crawford game. */ readonly crawfordNext: boolean; /** Whether the Crawford game has been played: there is one in a match. */ readonly crawfordPlayed: boolean; /** Whether the match is dec…
A MATCH: games played one after another to a number of points, or without end in money play. It keeps the score and says which game is the Crawford game. Like everything here it is plain data, and never changed: each function gives back a new match.
mirrorPoint(spec: VariantSpec, own: number): number
The other side's own number for the point that is side's own point own. With the sides going opposite ways round, a side's 24-point is the other's 1-point; going the same way, a point has the one number for both.
type Move = { readonly from: number; readonly to: number; /** The number on the die that moved it. A checker borne off with a larger die than it needed has `die` greater than `from`. */ readonly die: number; /** Whether it hit a checker, which went to the other side's bar. */ readonly hit: boolean; };
ONE CHECKER MOVED BY ONE DIE, in the mover's own numbering: from its own point from (or the BAR, 25, for a checker entering from the bar or from off the board at the start of a race) to its own point to (0 for borne off). A move that lands on a lone checker of the other side hits it.
movesOfDie(spec: VariantSpec, position: Position, side: Side, die: number): Move[]
Every move of one die that side may make from position, one for each point a checker can start from. A side with a checker on the bar must enter it first; a side that has not borne off may start a checker off the board (in a race) whenever it likes; and a side whose checkers are all home may bear off, by the exact die, or by a larger one from the highest point it has a checker on.
multiplierOf(game: GameState, kind: WinKind): number
What a win of kind is worth, after the rules: a single win when gammons do not count, or when the Jacoby rule holds them back because the cube was never turned.
newGame(settings: Settings, options?: NewGameOptions): GameState
A new game, waiting for the opening throw.
type NewGameOptions = { /** Whether it is the Crawford game of a match: played without the cube. */ crawford?: boolean; /** The score of the match so far, `[white, black]`. Default 0 to 0. */ score?: readonly [number, number]; };
Options for a new game.
newMatch(settings: Settings): Match
A match at 0 to 0.
nextMoves(spec: VariantSpec, position: Position, side: Side, need: readonly number[]): Move[]
The moves that may be made next, by a side that still has the dice need (highest first) to play from position: every move that leaves the rest of need playable. Where one checker could be borne off by either of two dice, both moves are offered.
offerDouble(game: GameState): GameState
Offer a double: the other side must now take it or drop it.
onBoard(position: Position, side: Side): number
How many checkers side has on the board's points, not counting the bar, the reserve and those borne off.
type Opening = /** Each side throws one die; the higher plays both dice as the first roll of the game, as in backgammon. */ | "roll-off" /** Each side throws one die; the higher goes first and then rolls a roll of their own. */ | "die-each";
How a game is opened.
openingFrom(source: DiceSource): [number, number]
An opening throw from a source: one die for white and one for black.
opponentAt(spec: VariantSpec, position: Position, side: Side, own: number): number
How many of the other side's checkers stand on the point that is side's own point own.
otherSide(side: Side): Side
The other side.
parseHops(text: string): Hop[] | null
The hops a written play names, in the order written: a chain of three points is two hops, and 13/11(2) is two. Returns null for text that is not a play. -, pass, and the empty text are the play of no moves.
parsePlay(spec: VariantSpec, position: Position, side: Side, roll: readonly number[], text: string): Move[] | null
The moves a written play makes with a roll, or null if it is not a legal play of that roll. Checks every rule: it is found by making each named move in some order, each one legal when it is made, until the roll is played as the rules require. A checker that plays both dice may be written as one move, 24/14 for a 6 and a 4, as well as through the point it passes.
parsePosition(text: string): Position | null
The position that text of formatPosition's kind stands for, or null if it is not one. bar, reserve and off may be left out.
type Phase = /** Nobody has the move yet: the sides are throwing a die each to see who starts. */ | "opening" /** The side on turn may double, or must roll. */ | "before-roll" /** The side on turn has offered a double, and the other must take it or drop it. */ | "double-offered" /** The side that was doubled has beavered (redoubled, keeping the cube) and the side on turn must take it or drop it. */ | "beaver-offered…
Where a game has got to.
pipCount(position: Position, side: Side): number
The pip count of a side: the sum of every checker's distance from being borne off. A checker on the bar or yet to enter counts 25.
type Play = { readonly moves: readonly Move[]; readonly position: Position; };
A WHOLE TURN: the moves a side makes with a roll, in the order it makes them, and the position they leave.
playMove(game: GameState, step: { from: number; to: number; }): GameState
Make one move for the side on turn, given as the checker's own point it leaves and the one it goes to. The move must be one of legalMoves. Where a checker could be borne off by either of two dice, the smaller is used.
playMoves(spec: VariantSpec, position: Position, side: Side, moves: readonly Move[]): Position
The position after a list of moves, made one after another.
playTurn(game: GameState, moves: readonly Move[]): GameState
Make a whole play (the moves of legalPlays) for the side on turn and end the turn.
POINTS: 24
The points on the board.
pointsToGo(match: Match, side: Side): number
How many points a side still needs to win the match: 0 in money play, where there is no such thing.
type Position = { /** `[white, black]`, each 24 counts, index 0 being own point 1. */ readonly points: readonly [readonly number[], readonly number[]]; /** Checkers hit and waiting to enter, `[white, black]`. */ readonly bar: readonly [number, number]; /** Checkers that have never entered, `[white, black]`: all of them at the start of a race. */ readonly reserve: readonly [number, number]; /** Checkers borne off, `[…
WHERE THE CHECKERS ARE. Every count is in the side's own numbering: own point 1 is the one nearest its bearing-off and own point 24 the one farthest from it, so points[side][n - 1] is how many of that side's checkers stand on its own point n. A position is plain data and is never changed: every function that moves a checker returns a new one.
positionFromId(id: string, onRoll: Side, checkers?: number): Position | null
The position an ID stands for, with onRoll to move, or null if the text is not an ID. The ID does not say how many checkers each side began with: pass the variant's (15 for the standard board), and what is not on the board is counted as borne off.
positionId(position: Position, onRoll: Side): string | null
The ID of a position with onRoll to move, or null if it cannot be written in this form.
positionKey(position: Position): string
A position as a short string that is the same for the same position, for keeping a set of them.
type Preset = { /** In kebab case. */ key: string; variant: VariantKey; rules: Rules; /** The names this goes by, as the sites print them. */ names: readonly string[]; };
A NAMED WAY OF PLAYING: a variant with its rules. The names on the sites these games were played on (ItsYourTurn.com and GoldToken.com) each lead to one of these through presetByName, and docs/VARIANTS.md says how each was read.
presetByKey(key: string): Preset | undefined
The preset with this key, or undefined.
presetByName(name: string): Preset | undefined
The preset a site's printed name leads to, or undefined. Case and spacing around the name do not matter.
PRESETS: readonly Preset[]
Every named way of playing, in the order of docs/VARIANTS.md.
type Random = () => number;
A source of random numbers from 0 up to but not including 1.
randomDice(): DiceSource
Dice that are not seeded: from the device's own random numbers.
RECORD_VERSION: 1
The record format's version, the number after sugoroku on the first line.
type RecordSeed = number | string;
A seed in a record: all digits means a number, anything else is text.
type ReplayedGame = { readonly result: GameResult; /** The turns each side took. */ readonly turns: readonly [number, number]; /** The position the game ended in. */ readonly position: Position; };
One game of a replayed record, finished.
type ReplayError = { readonly ok: false; /** The line the record went wrong on, counting from 1. */ readonly line: number; readonly reason: string; };
type ReplayOk = { readonly ok: true; readonly settings: Settings; readonly seed: RecordSeed | null; /** The match as it stands after every finished game. */ readonly match: Match; /** Every finished game, in order. */ readonly games: readonly ReplayedGame[]; /** The game still in play when the record ends, or null if the last was finished. */ readonly current: GameState | null; };
replayRecord(text: string): ReplayOk | ReplayError
Replay a record against the rules. Every move is checked to be legal when it was made, every double to have been allowed, and each game's result and the match's score are worked out, not read: a result line is only checked. Where the record has a seed, the dice are checked to be the seed's. Gives the games and the match, or the first line that goes wrong, and why.
resolveRules(rules?: Partial<Rules>, variant?: VariantSpec): Rules
The rules with anything left out filled from DEFAULT_RULES. Throws if the result is not a set of rules that can be played.
restartTurn(game: GameState): GameState
Take back every move of the turn.
rollDice(game: GameState, dice: readonly number[]): GameState
Roll the dice for the side on turn, which must not be waiting on an answer to a double.
rollFrom(source: DiceSource, count?: number): number[]
A roll of count dice from a source.
rollOpening(game: GameState, throws: readonly [number, number]): GameState
The opening throw: one die for white and one for black. The higher starts. A tie changes nothing but opening, and the sides throw again. In a game opened with a roll-off the starter plays the two dice as the first roll; otherwise the starter then rolls as usual.
type Rules = { /** * The match length in points. 1 is a single game. 0 is money play: games * go on one after another, each scored alone, and nothing ends the session. */ points: number; /** Whether the doubling cube is played. Never in a one-point game, which is its own Crawford game. */ cube: boolean; /** The Crawford rule: the game after a side first comes within a point of winning the match is played without the…
HOW A GAME IS SCORED AND STAKED, beside the variant's board. A variant says where the checkers stand and how they move; these say what a win is worth.
rulesProblems(rules: Rules, variant?: VariantSpec): string[]
What goes wrong with a set of rules, as sentences; empty when they are fine.
seededDice(seed: number | string): DiceSource
A SEEDED DICE SEQUENCE: the same seed is the same dice, one die after another, in every browser and every Node, so a game can be played again from its seed and a server can tell whether the dice a game says it had are the dice it should have had. Dice are taken in the order a game asks for them: the opening throw (white's die, then black's, and again for a tie), then every roll in turn, each as many dice as the variant rolls.
seededRandom(seed: number | string): Random
The mulberry32 stream: the same seed makes the same numbers everywhere.
type Settings = { variant: VariantSpec; rules: Rules; };
The settings of a game: the variant's row and the rules. What every game and match is made from.
settingsFor(variant: VariantKey | VariantSpec, rules?: Partial<Rules>): Settings
Settings from a variant key (or a row) and rules, left-out rules filled in.
type Side = "white" | "black";
THE TWO SIDES. A game has a white and a black player. Every count in a position is kept per side, in that side's own numbering of the points: a side's own point 1 is the one nearest its bearing-off, its own point 24 the one farthest from it, whichever way the board is drawn.
sideIndex(side: Side): 0 | 1
0 for white and 1 for black: where a side's counts are kept in a position.
SIDES: readonly [Side, Side]
Both sides, white first.
sideToAct(game: GameState): Side | null
Whether a side is the one the computer would act for at this moment of a game: the side on turn, or the side that must answer a double.
startCounts(spec: VariantSpec): number[]
Which points a side's checkers start on, as an array indexed by own point 1 to 24 (index 0 is point 1).
startGame(match: Match): GameState
The first game, or the next game, of a match.
startPosition(spec: VariantSpec): Position
The position a variant starts from.
startReserve(spec: VariantSpec): number
How many checkers each side begins off the board, to be entered with the dice.
stepComputer(game: GameState, dice: DiceSource, options?: ComputerOptions): { game: GameState; action: ComputerAction; } | null
Carry out one step of what the computer would do, with dice from dice. Returns the game after it, and what was done, so a caller can write it down or show it. Which side plays is up to the caller: this plays for whichever side is to act.
type Strength = "random" | "greedy" | "careful" | "strong";
THE COMPUTER PLAYER, in four strengths. Each is the one before it with more care, and none of them is slow: a move takes a few milliseconds, and the strongest well under a tenth of a second in a browser.
- random: any legal play, with equal chance. It never doubles, and takes every double. - greedy: the play that leaves the best position by the evaluation (evaluate), looking no further. - careful: weighs its best four plays by what the other side's best reply to each roll would leave. - strong: the same over its best ten plays.
Doubling is simple, and works as documented in the README: in a race the Keith count (see keithCount), where the rules are Keith's: double when the count of the side on roll is within 4 of the other's, redouble within 3, take when the doubler's is 2 or more above the taker's. In a position where the sides are still in contact (careful and strong only), the evaluation is turned into a chance of winning (winChance) and a side doubles at 70% and takes at 25%.
STRENGTHS: readonly Strength[]
The strengths, weakest first.
takeDouble(game: GameState): GameState
Take the double that was offered. The cube turns and goes to the side that took it, and the side on turn rolls. (A beaver taken turns it twice, and leaves it with the side that beavered.)
turnIsPlayed(game: GameState): boolean
Whether the turn is played out and the side on turn has only to end it.
undoMove(game: GameState): GameState
Take back the last move made this turn. Does nothing if none has been made.
VARIANT_KEYS: readonly VariantKey[]
The variants' keys, in the order the demo lists them.
type VariantKey = "backgammon" | "backgammon-race" | "anti-backgammon" | "nackgammon" | "long-gammon" | "hypergammon" | "tabula";
THE VARIANTS, AS ROWS OF SETTINGS. Nothing in the engine branches on a variant's name: it reads the row. A new game on the same board is a new row.
VARIANTS: Readonly<Record<VariantKey, VariantSpec>>
Every variant. Where each comes from, and where the sources disagree or say nothing, is in docs/VARIANTS.md.
variantSpec(key: VariantKey): VariantSpec
The row for a variant.
type VariantSpec = { readonly key: VariantKey; /** Checkers each side has. */ readonly checkers: number; /** Where the checkers are at the start, as `[own point, how many]`. Empty for a start with every checker off the board. */ readonly layout: readonly (readonly [number, number])[]; /** How many dice a roll is: two, or three for Tabula. */ readonly dice: 2 | 3; /** Whether doubles are played four times (`four`) or…
VERSION: "1.0.0"
The package's version.
wantsToDouble(game: GameState, options?: ComputerOptions): boolean
Whether the computer, to move before it rolls, should offer a double.
wantsToTake(game: GameState, options?: ComputerOptions): boolean
Whether the computer, doubled, should take.
winChance(score: number): number
A chance of winning, 0 to 1, that a score in pips stands for: the logistic curve, which a lead of 25 pips in a position of this kind takes to about three in four.
winKind(game: GameState, winner: Side): WinKind
The kind of win a game that winner has won by bearing off is: single, gammon or backgammon, before the rules say what they count for.
type WinKind = "single" | "gammon" | "backgammon";
What kind of win it was, which is how many times the cube's value it scores.
withCounts(position: Position, changes: { points?: [number[], number[]]; bar?: [number, number]; reserve?: [number, number]; off?: [number, number]; }): Position
A copy of a position with some counts changed.
@johnmorrisdotca/sugoroku/drawBoardLayout BoardOptions boardSize Box colourProperty colourStyle drawSugoroku MOST_DRAWN Orientation SUGOROKU_BOARD_NAMES SUGOROKU_BOARDS SUGOROKU_CHECKER_SET_NAMES SUGOROKU_CHECKER_SETS SUGOROKU_COLOUR_NAMES SUGOROKU_STYLE SugorokuBoardName SugorokuCheckerSetName SugorokuColours SugorokuDrawOptions
BoardLayout: typeof BoardLayout
The layout of a board, a function of its options: where each point, the bar, each tray, the rail and the dice sit, all in landscape units.
type BoardOptions = { /** Whether the board has a rail for the doubling cube. */ cube: boolean; /** Which side's home board is at the bottom of the drawing. */ view: Side; /** Which end the trays are at. */ home: "left" | "right"; orientation: Orientation; };
boardSize(options: Pick<BoardOptions, "cube" | "orientation">): { width: number; height: number; }
The width and height the board is drawn in, which is the other way about when it stands up.
type Box = { x: number; y: number; w: number; h: number };
A box: where something is, in landscape units before any turning.
colourProperty(name: keyof SugorokuColours): string
The custom property a colour is set on.
colourStyle(options: Pick<SugorokuDrawOptions, "board" | "checkers" | "colours">): string
The inline custom properties of a drawing: the named board and checkers, then any colour given.
drawSugoroku(position: Position, options?: SugorokuDrawOptions): string
The board as SVG text for position. The variant says how the sides' points line up; pass the same one the game is played with.
MOST_DRAWN: 5
How many checkers are drawn on a stack before the last one carries the count.
type Orientation = "landscape" | "portrait";
Whether the board lies across the page or stands up in it.
SUGOROKU_BOARD_NAMES: readonly SugorokuBoardName[]
The names of the boards.
SUGOROKU_BOARDS: Readonly<Record<SugorokuBoardName, Pick<SugorokuColours, "frame" | "felt" | "pointA" | "pointB" | "bar" | "tray">>>
The boards the drawing knows by name, set against any theme. Each changes only the surfaces: the frame, the felt, the points, the bar and the trays.
SUGOROKU_CHECKER_SET_NAMES: readonly SugorokuCheckerSetName[]
The names of the checker sets.
SUGOROKU_CHECKER_SETS: Readonly<Record<SugorokuCheckerSetName, Pick<SugorokuColours, "white" | "black" | "whiteEdge" | "blackEdge">>>
The checkers the drawing knows by name. contrast is for readers who tell colours apart with difficulty: pale and near-black, with strong rims.
SUGOROKU_COLOUR_NAMES: readonly (keyof SugorokuColours)[]
The names of the colours, as the custom properties spell them (--sg- and the name in kebab case).
SUGOROKU_STYLE: "\n.sugoroku {\n --sg-frame: #5b3a1f; --sg-felt: var(--felt, #2f5d4a); --sg-point-a: #e7d8b1; --sg-point-b: #8e3a2b; --sg-bar: #4a2f19; --sg-tray: #24493a;\n --sg-white: #f6f0df; --sg-white-edge: #b3a888; --sg-black: #2b2724; --sg-black-edge: #0d0b0a;\n --sg-number: #e8dcc0; --sg-selected: #ffd23f; --sg-target: #7fe3a1; --sg-die: #fbf8f1; --sg-pip: #1f2320; --sg-cube: #fbf8f1; --sg-cube-ink: #1f2320;…
THE STYLE a Sugoroku board is drawn with: colours as custom properties (--sg-felt, --sg-white and the rest of SugorokuColours), light by default and dark when the device is, and the one rule that matters for a game played with fingers and a mouse: nothing on the board can be selected, dragged or double-tapped.
Put it in the page once. Every colour is a custom property on .sugoroku, so a page's own style needs only to set the ones it wants different; the felt follows --felt where a page defines one.
type SugorokuBoardName = "green" | "blue" | "red" | "black" | "wood";
A named board, a set of colours for the board and its points.
type SugorokuCheckerSetName = "classic" | "red-and-white" | "gold-and-blue" | "contrast";
A named pair of checker colours.
type SugorokuColours = { /** The frame the board is set in. */ frame: string; /** The playing surface. */ felt: string; /** The points, alternately. */ pointA: string; pointB: string; /** The bar, and the trays' floor. */ bar: string; tray: string; /** The checkers: the face and the rim of each side's. */ white: string; whiteEdge: string; black: string; blackEdge: string; /** The numbers on the points, and a count o…
THE COLOURS OF A BOARD. Every colour the drawing uses is one of these, set as a custom property on the drawing (--sg-felt and so on), so a page's own style may change any of them, and drawSugoroku takes any of them as an option. Where a colour is left out the drawing uses the theme's: light by default, dark when the device is (see SUGOROKU_STYLE).
type SugorokuDrawOptions = { /** The variant, for how its sides' points line up. Default `backgammon`. */ variant?: VariantKey | VariantSpec; /** Whose home board is at the bottom of the drawing. Default white. */ view?: Side; /** Which end of the board the trays are at. Default right. */ home?: "left" | "right"; /** Lying across the page (default) or standing up, a better shape for a phone. */ orientation?: Orienta…
What to draw with.
@johnmorrisdotca/sugoroku/playcreateSounds ensureSugorokuPlayStyle mountSugoroku packagedSounds SoundName SoundPlayer SoundUrls SUGOROKU_PLAY_STYLE SUGOROKU_STRINGS SugorokuEventDetail SugorokuLanguage sugorokuLanguageOf SugorokuLook SugorokuMount SugorokuMountOptions sugorokuSay
createSounds(urls: SoundUrls): SoundPlayer
A player for the sounds, which makes its audio only when first asked to play, as browsers insist on a gesture first. Plays nothing where there is no audio.
ensureSugorokuPlayStyle(host: Element): void
Put the style in the page once: in the document's head, or in the shadow root the host is in.
mountSugoroku(host: HTMLElement, options?: SugorokuMountOptions): SugorokuMount
Mount a board into host.
packagedSounds(): SoundUrls
The sounds' addresses beside the package's code, or an empty set where import.meta.url is not an address (some test runners).
type SoundName = "clack" | "hit" | "dice" | "cube";
THE SOUNDS OF A BOARD, optional: a checker set down, a checker hit, the dice and the cube. The files are in the package's sounds/ folder (short WAV clips from Kenney's "Casino Audio", Creative Commons Zero: see sounds/CREDITS.txt) and are found beside the code with import.meta.url, so a bundler that follows that pattern ships them. A page that serves them from somewhere else gives their addresses.
type SoundPlayer = { play: (name: SoundName) => void; /** Stop and let go of everything. */ destroy: () => void; };
type SoundUrls = Partial<Record<SoundName, string>>;
The address of each sound.
SUGOROKU_PLAY_STYLE: "\n.sugoroku {\n --sg-frame: #5b3a1f; --sg-felt: var(--felt, #2f5d4a); --sg-point-a: #e7d8b1; --sg-point-b: #8e3a2b; --sg-bar: #4a2f19; --sg-tray: #24493a;\n --sg-white: #f6f0df; --sg-white-edge: #b3a888; --sg-black: #2b2724; --sg-black-edge: #0d0b0a;\n --sg-number: #e8dcc0; --sg-selected: #ffd23f; --sg-target: #7fe3a1; --sg-die: #fbf8f1; --sg-pip: #1f2320; --sg-cube: #fbf8f1; --sg-cube-ink: #1f…
THE STYLE a playable Sugoroku board wears (mountSugoroku, <sugoroku-board>): the drawing's own (SUGOROKU_STYLE), the board's box, its buttons and its lines of words. Colours are custom properties on .sugoroku-play (--sgp-ink, --sgp-muted, --sgp-rule, --sgp-surface, --sgp-accent, --sgp-good) so a page sets only what it wants different.
Nothing moves when something happens: the board is one box of one shape (set from the drawing's own proportions), the line of words keeps room for two lines, and the buttons are three of one size in the same places whatever they say. Nothing the player touches can be selected.
SUGOROKU_STRINGS: Record<SugorokuLanguage, Record<string, string>>
type SugorokuEventDetail = { /** The side it is about; null for the opening throw. */ side: Side | null; game: GameState; match: Match; /** Dice: for a roll, those rolled; for a turn, the roll played. */ dice?: readonly number[]; /** A move in standard notation, or a turn's play. */ notation?: string; move?: Move; moves?: readonly Move[]; /** What was done to the cube. */ action?: "double" | "take" | "drop" | "beave…
What every event of the board carries.
type SugorokuLanguage = "en" | "ja";
THE WORDS A SUGOROKU BOARD SAYS, in English and Japanese: the buttons, the line that says whose turn it is and what to do, and the result of a game. Plain data, so a page can read them, replace a few or add a language of its own beside these two.
{name} in a line is a value filled in. The Japanese is a first draft that no native reader has yet checked.
sugorokuLanguageOf(element: Element | null): SugorokuLanguage
The language an element or its page is in: its own lang, or the nearest above, and English unless that begins with ja.
type SugorokuLook = { board?: SugorokuBoardName; checkers?: SugorokuCheckerSetName; colours?: Partial<SugorokuColours>; theme?: "auto" | "light" | "dark"; /** Which end of the board the trays are at. */ home?: "left" | "right"; /** Whose home board is at the bottom: `auto` (the default) is the side a person plays when the computer plays the other, otherwise white. */ view?: Side | "auto"; /** Numbers on the points. …
How the board looks, apart from the game.
type SugorokuMount = { readonly host: HTMLElement; /** The game as it stands. */ game: () => GameState; match: () => Match; /** The match so far as a record a server can replay (see `replayRecord`). */ record: () => string; /** Roll for the side that is to: with no dice, from the seed or at random; with them, the dice the page gives (two for the opening throw, one for white and one for black). */ roll: (dice?: reado…
type SugorokuMountOptions = SugorokuLook & { /** The variant. Default `backgammon`. */ variant?: VariantKey; /** The rules left out are a single game with no cube. */ rules?: Partial<Rules>; /** A named way of playing (a `PRESETS` key, such as `backgammon-5`) instead of `variant` and `rules`. */ preset?: string; /** Dice from a seed, so the game can be played again; left out they are random. */ seed?: RecordSeed; /*…
sugorokuSay(language: SugorokuLanguage, key: string, values?: Record<string, string | number>): string
A line in a language with its {name}s filled in. A name left unfilled stays as written, so a missing value is visible.
@johnmorrisdotca/sugoroku/elementSugorokuBoard: typeof SugorokuBoard
@johnmorrisdotca/sugoroku/element/defineこの日本語は、まだ日本語を母語とする方の確認を受けていません。訂正を歓迎します。