function areNeighbours
areNeighbours(a: number, b: number, map?: TenkaMap): boolean
Whether two territories are neighbours, by land or by sea.
@johnmorrisdotca/tenka 1.2.1 · 4 entry points · 152 exports
@johnmorrisdotca/tenkaareNeighbours armiesHeld attacksOpen battleLosses beginTurn boardOf cardKind cardTerritory choiceNow cleanTenkaName connectedOwn continentNameIn continentsHeld counted decodeTenka defendDice encodeTenka endOfTurn finished isSet isTenkaSeed isTenkaTable isTerritory isWild marksFor mostAttackDice mustTrade nextRandom nextSeatIn NO_CHOICE playTenka randomBelow readTenkaMove reinforcementFor replayTenka sensibleTenkaMove setsIn shuffled startTenka tapTerritory TENKA_ATTACK_DICE TENKA_CARD_KINDS TENKA_CONTINENTS TENKA_CSV_COLUMNS TENKA_DECK TENKA_DEFEND_DICE TENKA_EXPORT_FORMAT TENKA_FEWEST_PLAYERS TENKA_LEAST_REINFORCEMENT TENKA_LENGTHS TENKA_MAP_LIST TENKA_MAPS TENKA_MEDIUM_ROUNDS TENKA_MOST_PLAYERS TENKA_MOVES TENKA_MUST_TRADE_AT TENKA_NAME_MOST TENKA_NEUTRAL TENKA_PHASES TENKA_PLACING TENKA_SEED_MOST TENKA_SHORT_ROUNDS TENKA_STARTING_ARMIES TENKA_STRINGS TENKA_TERRITORIES TENKA_TERRITORIES_PER_ARMY TENKA_TERRITORY_CARD_BONUS TENKA_TERRITORY_COUNT TENKA_TRADE_STEP TENKA_TRADE_VALUES TENKA_VERSION TENKA_WILD TENKA_WILD_CARDS TENKA_WORLD_ROUNDS tenkaAgain TenkaCard TenkaCardKind TenkaChoice tenkaContinent TenkaContinent TenkaContinentKey tenkaDeckFor TenkaEuropeRegionKey tenkaExported TenkaExported tenkaFromJSON TenkaGame TenkaLocale TenkaMap TenkaMapKey TenkaMapMarks tenkaMapOf TenkaMove TenkaMoveKind tenkaMoves tenkaNeighbours tenkaOver TenkaOwner TenkaPhase TenkaPlacing tenkaPlayerName tenkaRecord TenkaRecordEntry TenkaRoll tenkaSay TenkaSeat TenkaShapes tenkaStrings TenkaStrings TenkaTerritoryData tenkaToCSV tenkaToJSON tenkaToText TenkaTrade TenkaWorldContinentKey territoriesHeld territoryNameIn throwDice tradeValue writeTenkaMove
areNeighbours(a: number, b: number, map?: TenkaMap): boolean
Whether two territories are neighbours, by land or by sea.
armiesHeld(game: Pick<TenkaGame, "owners" | "armies">, owner: TenkaOwner): number
How many armies an owner has on the map.
attacksOpen(game: TenkaGame): TenkaMove[]
Every attack open now: from each territory of theirs with armies to spare, on each neighbour somebody else holds, with each number of dice — and the same attack again and again until it is decided.
battleLosses(attack: readonly number[], defend: readonly number[]): { attackerLost: number; defenderLost: number; }
WHO LOSES WHAT: the highest die of each side compared, then the next highest, as many pairs as the side with fewer dice threw. The higher die wins its pair; a tie goes to the defender. Each lost pair is one army. Both lists are highest first.
beginTurn(game: TenkaGame, seat: TenkaSeat, round: number): TenkaGame
A turn starting for seat: its armies worked out, nothing yet taken, nothing rolled.
boardOf(map: unknown): TenkaMap
The map an argument names, and the world for anything that is not one: these functions are handed to an array's map and filter (deck.map(cardKind)), which pass an index where the map would go.
cardKind(card: TenkaCard, map?: TenkaMap): TenkaCardKind
What a card shows: land, sea or air for a territory's card, in turn round the map; wild for a wild card.
cardTerritory(card: TenkaCard, map?: TenkaMap): number | null
The territory a card shows, or null for a wild card.
choiceNow(game: TenkaGame, choice: TenkaChoice): TenkaChoice
A choice made on an earlier map, read against this one: only what still stands, and a number of armies the move can take.
cleanTenkaName(name: string): string
A name as the table typed it, tidied: spaces run together, trimmed, cut to TENKA_NAME_MOST.
connectedOwn(owners: readonly TenkaOwner[], from: number, map?: TenkaMap): number[]
Every territory of the same owner reachable from this one through that owner's own territories, in order; never the territory itself.
continentNameIn(strings: TenkaStrings, key: string): string
A continent's name in a table of strings, by its key ("northAmerica", "asia"…).
continentsHeld(owners: readonly TenkaOwner[], seat: TenkaOwner, map?: TenkaMap): TenkaContinent[]
The continents a seat holds every territory of.
counted(game: TenkaGame): TenkaGame
THE COUNT, when the last round is over: whoever holds the most territories wins; level on territories, whoever has more armies on the map; level on both, they share the win.
decodeTenka(text: string | null): TenkaGame | null
A kept game read back, or null for nothing kept, or for text that is not a game these rules can play out again: a browser's storage is somebody's to edit, and a half-understood game is worse than none.
defendDice(armies: number): number
The dice a defender holding armies throws: as many as allowed, since more never hurts the defence.
encodeTenka(game: TenkaGame): string
A game as text to keep: its table and its moves.
endOfTurn(game: TenkaGame): TenkaGame
THE END OF A TURN: a card for a player who took a territory in it, then the next player still in. Passing the first player's place starts a new round, and past the last round the game is counted.
finished(game: TenkaGame, winners: readonly TenkaSeat[]): TenkaGame
The game ended, with these seats winning.
isSet(cards: readonly TenkaCard[], map?: TenkaMap): boolean
Whether three cards make a set: three of one kind, one of each kind, or any two with a wild card (a wild card stands for whatever the set needs).
isTenkaSeed(seed: number): boolean
Whether a number is a seed a game can be dealt from: a whole number from 0 to TENKA_SEED_MOST.
isTenkaTable(rounds: number, count: number): boolean
Whether a game this long for this many players is one Tenka is offered for.
isTerritory(territory: number, map?: TenkaMap): boolean
Whether a number is a territory of the map.
isWild(card: TenkaCard, map?: TenkaMap): boolean
Whether a card is one of the two wild cards.
marksFor(game: TenkaGame, choice: TenkaChoice): TenkaMapMarks
What the map lights up for a choice.
mostAttackDice(armies: number): number
The most dice an attacker may throw from a territory holding armies: one army always stays behind.
mustTrade(game: TenkaGame): boolean
Whether the player to move must trade cards before anything else: five or more in hand while reinforcing.
nextRandom(state: number): { value: number; state: number; }
The next number from the random at state, in [0, 1), and the state after it.
nextSeatIn(game: TenkaGame, seat: TenkaSeat): TenkaSeat
The next player still in after seat, round the table; the neutral army never takes a turn.
NO_CHOICE: TenkaChoice
Nothing chosen: where a turn, and a table, start.
playTenka(game: TenkaGame, move: TenkaMove): TenkaGame | null
The game after the player to move makes move, or null when they may not: a move for another phase, a territory not theirs, a neighbour that is not one, dice they have not the armies for, a set that is not a set.
randomBelow(state: number, below: number): { value: number; state: number; }
A whole number from 0 to below - 1, and the state after it.
readTenkaMove(kept: unknown): TenkaMove | null
A kept move read back, or null for anything that is not one.
reinforcementFor(owners: readonly TenkaOwner[], seat: TenkaSeat, map?: TenkaMap): number
THE ARMIES A TURN BRINGS: one for every three territories held, never fewer than three, and each continent held whole adds its bonus. Cards traded in come on top (tenka.ts).
replayTenka(table: { rounds: number; players: readonly string[]; seed: number; placing: TenkaPlacing; map?: TenkaMapKey; }, moves: readonly TenkaMove[]): TenkaGame | null
A game made again from its table and its moves, or null if any move could not have been made when it was.
sensibleTenkaMove(game: TenkaGame, random: (): number) => TenkaMove
The computer player's move: one the rules accept, chosen as a person might. random is any function returning a number from 0 up to 1, such as Math.random; give it a seeded one and the computer plays the same game every time.
setsIn(hand: readonly TenkaCard[], map?: TenkaMap): TenkaCard[][]
Every set a hand holds, each as its three cards smallest first, in order.
shuffled<T>(state: number, items: readonly T[]): { value: T[]; state: number; }
A list in a random order (Fisher and Yates), and the state after it; the list given is left alone.
startTenka(rounds: number, players: readonly string[], seed: number, placing?: TenkaPlacing, map?: TenkaMapKey): TenkaGame | null
A NEW GAME, all of it drawn from seed: who goes first; the territories shuffled and dealt round the table one at a time from that player, each with one army on it (at a table of two, dealt three ways, the neutral army taking every third); the cards shuffled; and each player's starting armies (TENKA_STARTING_ARMIES) less the ones already on their territories — scattered at random when placing is auto, or left to be placed by hand, one at a time round the table. The neutral army's are always scattered.
Null for a table the game is not offered for, or a seed that is not one.
tapTerritory(game: TenkaGame, choice: TenkaChoice, territory: number): { choice: TenkaChoice; move: TenkaMove | null; }
What tapping territory does now: a new choice, and the move to make at once, if the tap is one.
TENKA_ATTACK_DICE: 3
The most dice an attacker throws.
TENKA_CARD_KINDS: readonly TenkaCardKind[]
The three kinds a territory's card shows, in turn round the map, and the wild card that is any of them.
TENKA_CONTINENTS: readonly TenkaContinent[]
The world's six continents, each with the territories in it.
TENKA_CSV_COLUMNS: readonly string[]
The columns of the CSV export, in order.
TENKA_DECK: readonly number[]
Every card in the game: the territories' cards, then the wild cards.
TENKA_DEFEND_DICE: 2
The most dice a defender throws.
TENKA_EXPORT_FORMAT: 1
The shape of the JSON this package writes. It goes up only when a reader of the old shape would be wrong about the new one.
TENKA_FEWEST_PLAYERS: 2
The fewest at a table: two, joined by the neutral army.
TENKA_LEAST_REINFORCEMENT: 3
The fewest armies a turn brings, however little is held.
TENKA_LENGTHS: readonly number[]
The lengths of game startTenka takes, in rounds: 10, 20 and 60.
TENKA_MAP_LIST: readonly TenkaMapKey[]
The maps in the order a set-up offers them: the world first, the usual one.
TENKA_MAPS: Readonly<Record<TenkaMapKey, TenkaMap>>
Every map a game may be played on, by key.
TENKA_MEDIUM_ROUNDS: 20
The medium game: twenty rounds, then the count.
TENKA_MOST_PLAYERS: 6
The most at a table: six.
TENKA_MOVES: { readonly place: "place"; readonly trade: "trade"; readonly attack: "attack"; readonly blitz: "blitz"; readonly occupy: "occupy"; readonly endAttack: "endAttack"; readonly fortify: "fortify"; readonly shift: "shift"; readonly endTurn: "endTurn"; }
The kinds of move, by name: what goes in a move's kind.
TENKA_MUST_TRADE_AT: 5
With this many cards in hand a player must trade before placing; after knocking somebody out, down to fewer than this.
TENKA_NAME_MOST: 20
The longest name a seat keeps.
TENKA_NEUTRAL: -1
The owner of the territories nobody at a table of two holds: the neutral army, which never takes a turn.
TENKA_PHASES: { readonly setUp: "setUp"; readonly reinforce: "reinforce"; readonly attack: "attack"; readonly occupy: "occupy"; readonly fortify: "fortify"; readonly shift: "shift"; readonly over: "over"; }
The phases of a turn, by name: compare game.phase with these rather than with text typed out.
TENKA_PLACING: { readonly auto: "auto"; readonly hand: "hand"; }
How the starting armies are placed: scattered at random (auto), or by the players in turn (hand).
TENKA_SEED_MOST: 4294967295
The largest seed a game keeps: the random's state is one 32-bit number.
TENKA_SHORT_ROUNDS: 10
The short game: ten rounds, then the count.
TENKA_STARTING_ARMIES: Readonly<Record<number, number>>
THE ARMIES EACH PLAYER STARTS WITH, by how many are playing: the standard table. Two players are joined by a neutral army with forty of its own, holding a third of the world, which never moves and only defends.
TENKA_STRINGS: Readonly<Record<TenkaLocale, TenkaStrings>>
The package's words in each language it speaks: TENKA_STRINGS.en, TENKA_STRINGS.ja.
TENKA_TERRITORIES: readonly TenkaTerritoryData[]
The world's territories: the map a game is played on unless it says otherwise.
TENKA_TERRITORIES_PER_ARMY: 3
How many territories held bring one army each turn: three.
TENKA_TERRITORY_CARD_BONUS: 2
The armies placed straight onto a territory the trader holds, when a card of the set shows it.
TENKA_TERRITORY_COUNT: number
How many territories the world has: forty-two.
TENKA_TRADE_STEP: 5
How much more each set is worth than the last, once TENKA_TRADE_VALUES runs out: 20, 25, 30…
TENKA_TRADE_VALUES: readonly number[]
WHAT A SET OF CARDS IS WORTH, by how many sets anybody has traded in before it: 4, 6, 8, 10, 12, 15, and five more for every set after that — the escalating standard schedule. And two armies more, placed straight onto it, when a card in the set shows a territory the trader holds.
TENKA_VERSION: "1.2.1"
The version of this package, as package.json has it. A test holds the two together.
TENKA_WILD: TenkaCardKind
The kind of a wild card, which stands for whatever a set needs.
TENKA_WILD_CARDS: 2
How many wild cards are in the deck.
TENKA_WORLD_ROUNDS: 60
The whole world: played to the last player standing, and counted after sixty rounds if nobody is.
tenkaAgain(game: TenkaGame, seed: number): TenkaGame | null
The same table again, from nothing, with a new seed: a new deal, a new first player, new dice.
type TenkaCard = number;
A territory's card (0–41) or a wild card (42, 43).
type TenkaCardKind = "land" | "sea" | "air" | "wild";
What a card shows: one of three kinds, or a wild card that stands for any of them.
type TenkaChoice = { from: number | null; to: number | null; /** How many armies move, when there is a number to choose. */ armies: number; /** The last territory a reinforcing army was placed on, for "all the rest here". */ placedOn: number | null; };
A choice made on the map and not yet a move: where from, where to, and how many armies (tapTerritory).
tenkaContinent(key: TenkaContinentKey, map?: TenkaMap): TenkaContinent
A continent of a map by its key.
type TenkaContinent = { key: TenkaContinentKey; name: string; kanji: string; bonus: number; territories: readonly number[]; };
A continent: its name, the armies holding all of it is worth each turn, and its territories.
type TenkaContinentKey = TenkaWorldContinentKey | TenkaEuropeRegionKey;
A continent of any map, by key: the world's continents and Europe's regions.
tenkaDeckFor(map?: TenkaMap): TenkaCard[]
Every card of a game on this map: a card for each territory, numbered as the territories are, then the wild cards.
type TenkaEuropeRegionKey = | "britishIsles" | "scandinavia" | "iberia" | "maghreb" | "france" | "centralEurope" | "italyBalkans" | "danube" | "easternEurope" | "russia" | "anatolia";
Europe's eleven regions, by key: the continents of the Europe map.
tenkaExported(game: TenkaGame): TenkaExported
A game as the JSON export's object: its table, its moves, and where it stands.
type TenkaExported = { /** The shape's number: `TENKA_EXPORT_FORMAT`. */ format: typeof TENKA_EXPORT_FORMAT; /** Always `"tenka"`, so a file of this shape is not mistaken for another game's. */ game: "tenka"; /** The package and version that wrote it, such as `"tenka 1.1.0"`. For people; never read back. */ generator: string; /** The seed every deal, shuffle and die is drawn from. */ seed: number; /** The names roun…
A whole JSON export of one game: what tenkaToJSON writes and tenkaFromJSON reads.
tenkaFromJSON(text: string): TenkaGame | null
A game from JSON that tenkaToJSON wrote, or text that encodeTenka did. Nothing in it is trusted: the game is dealt again from the seed and every move is played through the rules, so what comes back is a game these rules made. Null when the text is not JSON, is of a later format than this version reads, is another game's, or holds a move that could not have been made when it was.
type TenkaGame = { /** The map it is played on; left out for the world, so a game saved before there was a choice reads as it did. */ map?: TenkaMapKey; /** The seed every shuffle, deal and die of this game is drawn from. */ seed: number; /** The names given at the table, in seat order: "" for one left blank. */ players: readonly string[]; /** How many rounds before the count: `TENKA_WORLD_ROUNDS` for the whole worl…
A game, as its moves make it. Only the table — seed, players, rounds, placing — and moves are ever kept (encodeTenka); everything else is read again from them, dice and all, since every die is drawn from the game's own seeded random (rng). A kept game can never hold a world its moves do not make.
type TenkaLocale = "en" | "ja";
The languages the package speaks: English and Japanese.
type TenkaMap = { key: TenkaMapKey; name: string; kanji: string; territories: readonly TenkaTerritoryData[]; continents: readonly TenkaContinent[]; neighbours: readonly (readonly number[])[]; };
A map as the rules read it: its territories, its continents and each territory's neighbours by land and by sea.
type TenkaMapKey = "world" | "europe";
The maps a game may be played on.
type TenkaMapMarks = { chosen: number | null; reach: readonly number[]; target: number | null; };
What the map lights up for a choice (marksFor): the territory chosen, what it can reach, and the target.
tenkaMapOf(game: Pick<TenkaGame, "map"> | TenkaMapKey | undefined): TenkaMap
The map a game is played on: the one it was started on, and the world for a game saved before there was a choice.
type TenkaMove = | { kind: "place"; territory: number; armies: number } | { kind: "trade"; cards: readonly TenkaCard[] } | { kind: "attack"; from: number; to: number; dice: number } /** Attack again and again with every die allowed, until the territory falls or only one army is left to attack with. */ | { kind: "blitz"; from: number; to: number } | { kind: "occupy"; armies: number } | { kind: "endAttack" } | { kind:…
A move, as playTenka takes it. Which kinds may be made depends on the phase: place and trade while reinforcing (place alone while setting up), attack, blitz and endAttack while attacking, occupy after taking a territory, fortify and endTurn while fortifying, and shift once a fortifying move is chosen. tenkaMoves(game) lists the ones open.
type TenkaMoveKind = TenkaMove["kind"];
The kinds of move, by name.
tenkaMoves(game: TenkaGame): TenkaMove[]
Every move the player to move may make now, each one playTenka accepts. Placing is listed as one army or all still waiting, on each territory held, since any spread is a run of those. None once the game is over.
tenkaNeighbours(territory: number, map?: TenkaMap): readonly number[]
A territory's neighbours, by land and by sea.
tenkaOver(game: TenkaGame): boolean
Whether the game is over.
type TenkaOwner = number;
Who holds a territory: a seat, or TENKA_NEUTRAL.
type TenkaPhase = "setUp" | "reinforce" | "attack" | "occupy" | "fortify" | "shift" | "over";
Where a turn is. setUp is the placing of the starting armies by hand, one each round the table; reinforce the placing of a turn's new armies (and trading cards for more); attack rolling against a neighbour; occupy choosing how many move into a territory just taken; fortify choosing one move between two of your own connected territories, and shift how many armies it moves; over the end.
type TenkaPlacing = "auto" | "hand";
Starting armies placed at random, or by hand in turn.
tenkaPlayerName(game: Pick<TenkaGame, "players">, seat: TenkaSeat): string
A seat's name as the table reads it: the one given, or "Player 3".
tenkaRecord(game: TenkaGame): TenkaRecordEntry[]
THE RECORD OF A GAME, move by move, with the dice and everything else each move brought: made by playing the game again from its seed. A game whose moves do not replay from its table (one put together by hand, in a test) is recorded as far as they do.
type TenkaRecordEntry = { /** The move's number, from 1. */ n: number; /** The round it was made in. */ round: number; /** The seat that made it. */ seat: TenkaSeat; /** The move itself. */ move: TenkaMove; /** The dice of an attack: the one throw, or the last of a blitz with the losses of all of them. */ roll?: TenkaRoll; /** The set traded in, what it was worth, and the territory that got two more. */ trade?: Tenk…
One move of a game's record, with what came of it: what the text and the CSV are written from.
type TenkaRoll = { from: number; to: number; attacker: TenkaSeat; defender: TenkaOwner; attackDice: readonly number[]; defendDice: readonly number[]; /** Armies lost, over every throw of the run. */ attackerLost: number; defenderLost: number; /** How many times the dice were thrown: one for a single attack. */ throws: number; /** Whether this roll took the territory. */ took: boolean; };
One roll of the dice — or the last of a run of them (blitz) — what each side threw, highest first, and what each lost in all.
tenkaSay(line: string, values?: Readonly<Record<string, string | number>>): string
A line from a table of strings with its braces filled in: tenkaSay(strings.moveIn, { n: 3 }) is "Move 3 in". A brace with no value is left as it is.
type TenkaSeat = number;
A player's place round the table: 0 for the first name given, up to 5.
type TenkaShapes = { width: number; height: number; /** Where each territory's army counter stands. */ labels: readonly (readonly number[])[]; /** Each territory's extent, left, top, right, bottom: what a view frames to show it. */ boxes: readonly (readonly number[])[]; /** Each sea link's dashed line, x1 y1 x2 y2; the crossing of the Bering Strait is two, one off each edge. */ seaLines: readonly (readonly number[])…
How the world is drawn, in map units (tenkaShapes.data.ts, written by scripts/map.mjs).
tenkaStrings(locale: TenkaLocale | string | undefined, own?: Partial<TenkaStrings>): TenkaStrings
The table of strings for a locale with a page's own words laid over it. An unknown locale is English.
type TenkaStrings = { [Name in keyof typeof EN]: string };
Every word the package shows, by name: one table of these for each language.
type TenkaTerritoryData = { key: string; name: string; continent: TenkaContinentKey; land: readonly number[]; sea: readonly number[]; };
One territory as the map script writes it: neighbours by land and by sea are indices into the same list.
tenkaToCSV(game: TenkaGame): string
A game as CSV, a row to a move, for a spreadsheet: TENKA_CSV_COLUMNS. Territories are written by key (alaska, westernCanada), seats from 1, dice as they fell, highest first and spaced. Lines end CRLF, as RFC 4180 has them, and a player's name that a spreadsheet would run as a formula is made safe with a leading apostrophe.
tenkaToJSON(game: TenkaGame): string
A game as JSON, two spaces deep, with the format's number first. tenkaFromJSON reads it back.
tenkaToText(game: TenkaGame, strings?: TenkaStrings): string
A game as plain text, a line to a move, for a chat or a log: who placed what where, every throw of the dice, every territory taken, and how it ended. In English unless given another table of strings (TENKA_STRINGS.ja). Lines end with a line feed.
type TenkaTrade = { seat: TenkaSeat; cards: readonly TenkaCard[]; armies: number; bonusTerritory: number | null };
The last set of cards traded in, for the table to see: by whom, for how many, and the territory that got two more.
type TenkaWorldContinentKey = "northAmerica" | "southAmerica" | "europe" | "africa" | "asia" | "oceania";
The world's six continents, by key.
territoriesHeld(owners: readonly TenkaOwner[], owner: TenkaOwner): number
How many territories an owner holds.
territoryNameIn(strings: TenkaStrings, key: string): string
A territory's name in a table of strings, by its key (TENKA_TERRITORIES[n].key, such as "alaska"); "" for a key that is no territory's.
throwDice(state: number, count: number): { value: number[]; state: number; }
count dice, highest first, and the state after them.
tradeValue(trades: number): number
What the next set traded in is worth, when trades sets have been traded before it: 4, 6, 8, 10, 12, 15, 20, 25…
writeTenkaMove(move: TenkaMove): Kept
A move as the short list it is kept as — and sent as, to the other devices at a table played on several.
@johnmorrisdotca/tenka/shapesTENKA_EUROPE_SHAPES TENKA_SHAPES TenkaShapes
TENKA_EUROPE_SHAPES: TenkaShapes
How Tenka's Europe is drawn: Miller's projection from 25°W to 60°E and 33.5°N to 71.5°N, 2000 by 1242 units. One outline per territory, in the order of TENKA_EUROPE_TERRITORY_DATA; where its army counter stands; its extent; the sea links' dashed lines; the links that go off one edge and on at the other; and the borders between continents, drawn heavier. Read only by the board in the browser, so none of it is carried by a page the server renders for the rules.
TENKA_SHAPES: TenkaShapes
How Tenka's world is drawn: Miller's projection from 170°W round to 192°E, 2000 by 984 units. One outline per territory, in the order of TENKA_TERRITORY_DATA; where its army counter stands; its extent; the sea links' dashed lines; the links that go off one edge and on at the other; and the borders between continents, drawn heavier. Read only by the board in the browser, so none of it is carried by a page the server renders for the rules.
type TenkaShapes = { width: number; height: number; /** Where each territory's army counter stands. */ labels: readonly (readonly number[])[]; /** Each territory's extent, left, top, right, bottom: what a view frames to show it. */ boxes: readonly (readonly number[])[]; /** Each sea link's dashed line, x1 y1 x2 y2; the crossing of the Bering Strait is two, one off each edge. */ seaLines: readonly (readonly number[])…
How the world is drawn, in map units (tenkaShapes.data.ts, written by scripts/map.mjs).
@johnmorrisdotca/tenka/uicontinentNameIn continentView mountTenka nearestLand NO_MARKS ownerColour TENKA_MAP_SHAPES TENKA_NEUTRAL_COLOUR TENKA_SEAT_COLOURS TENKA_STRINGS TENKA_STYLE TenkaLand TenkaLocale tenkaMapModel TenkaMapModel tenkaMapSvg TenkaRing tenkaSay tenkaShapesOf tenkaStrings TenkaStrings TenkaTableHandle TenkaTableOptions TenkaView territoryNameIn
continentNameIn(strings: TenkaStrings, key: string): string
A continent's name in a table of strings, by its key ("northAmerica", "asia"…).
continentView(key: TenkaContinentKey | null, map?: TenkaMapKey): TenkaView
The part of the map that frames a continent, with a margin, widened to the map's own shape so it fills the same box; the whole world for null.
mountTenka(target: HTMLElement, options?: TenkaTableOptions): TenkaTableHandle
A WHOLE TABLE OF TENKA IN PLAIN DOM: the map, whose turn it is and what to do, the players, your cards and the last dice, played against the computer (or by people taking turns on one device, with computers all false). No framework and no server; everything is in the element given.
Tap your own territory to place an army; to attack or move, tap where from, then where to, and choose from the buttons under the map. Under the players is the record of the game, which saves as JSON, text or CSV and loads a saved game back.
nearestLand(x: number, y: number, reach: number, map?: TenkaMapKey): number | null
The territory whose counter is nearest a point of the map, within reach map units, or null.
NO_MARKS: TenkaMapMarks
Nothing lit up.
ownerColour(owner: number, colours?: readonly string[]): string
The colour a territory's owner is drawn in.
TENKA_MAP_SHAPES: Readonly<Record<TenkaMapKey, TenkaShapes>>
How each map is drawn.
TENKA_NEUTRAL_COLOUR: "#9a9a92"
The grey of the neutral army.
TENKA_SEAT_COLOURS: readonly string[]
THE COLOURS A TABLE IS DRAWN IN: one for each of six seats, and a grey for the neutral army that holds a third of the world in a game for two. Any list of CSS colours may be given instead; a seat past its end wraps round.
TENKA_STRINGS: Readonly<Record<TenkaLocale, TenkaStrings>>
The package's words in each language it speaks: TENKA_STRINGS.en, TENKA_STRINGS.ja.
TENKA_STYLE: "\n.tk-root {\n --tk-sea: #b9d3dc; --tk-ink: #1f2320; --tk-panel: #f7f3ea; --tk-line: rgba(20,20,20,.55);\n --tk-border: rgba(10,10,10,.8); --tk-link: #1d3440; --tk-accent: #2f5d4a; --tk-accent-ink: #fff;\n --tk-ring: #111; --tk-ring-target: #fff; --tk-counter-edge: #111; --tk-counter-ink: #fff;\n --tk-attack: #c8463d; --tk-attack-ink: #fff; --tk-defend: #f4efe4; --tk-defend-ink: #1f2320;\n --tk-radius:…
The table's own styles, scoped to .tk-root, with every colour a CSS variable so a page can wear it in its own colours (set them on the element or any ancestor, or pass them as theme). Light and dark follow the reader's system. Everything to be tapped is at least 44px.
type TenkaLand = { /** Its number. */ territory: number; /** Its key, such as `"alaska"`. */ key: string; /** Its name in English. */ name: string; /** Its outline, one SVG path in map units. */ outline: string; /** The colour of whoever holds it. */ fill: string; /** How it is ringed, if it is. */ ring: TenkaRing; /** Where its counter stands, in map units. */ at: readonly [number, number]; /** The armies standing …
One territory as a drawing needs it.
type TenkaLocale = "en" | "ja";
The languages the package speaks: English and Japanese.
tenkaMapModel(game: Pick<TenkaGame, "owners" | "armies" | "map">, marks?: TenkaMapMarks, colours?: readonly string[]): TenkaMapModel
EVERYTHING A DRAWING OF THE WORLD NEEDS, for one game: each territory's outline in its owner's colour, its counter's place and armies, and its ring. Shared by the plain-DOM table and the React map, so both draw the same world from the same few rules.
type TenkaMapModel = { width: number; height: number; lands: readonly TenkaLand[]; seaLines: readonly (readonly number[])[]; continentBorders: string; };
The whole world as a drawing needs it, in map units: tenkaMapModel makes one, tenkaMapSvg and TenkaMap draw it.
tenkaMapSvg(model: TenkaMapModel, options?: { label?: string; view?: TenkaView; pixels?: number; }): SVGSVGElement
THE WORLD AS AN SVG ELEMENT, drawn from a model (tenkaMapModel): the sea, each territory in its owner's colour, the continents' borders and the sea links, the rings of a choice, and a counter of armies on each. Every territory's shape and counter carries data-territory with its number, so one listener on the element can tell which was pressed, and each shape data-owner and data-armies as well.
label is the drawing's accessible name; view the part of the map to show (continentView); pixels the drawing's width on the screen, so that counters and rings are one size whatever is shown.
type TenkaRing = "chosen" | "reach" | "target" | null;
How a territory is ringed: the one chosen, one it can reach, or the target.
tenkaSay(line: string, values?: Readonly<Record<string, string | number>>): string
A line from a table of strings with its braces filled in: tenkaSay(strings.moveIn, { n: 3 }) is "Move 3 in". A brace with no value is left as it is.
tenkaShapesOf(map: TenkaMapKey | undefined): TenkaShapes
How a game's map is drawn: the world unless the game says otherwise.
tenkaStrings(locale: TenkaLocale | string | undefined, own?: Partial<TenkaStrings>): TenkaStrings
The table of strings for a locale with a page's own words laid over it. An unknown locale is English.
type TenkaStrings = { [Name in keyof typeof EN]: string };
Every word the package shows, by name: one table of these for each language.
type TenkaTableHandle = { /** The game as it stands. */ game: () => TenkaGame; /** A new game at the same table, with any options changed. */ newGame: (options?: Pick<TenkaTableOptions, "players" | "computers" | "rounds" | "seed" | "map">) => void; /** Put a game on the table: one read back by `tenkaFromJSON` or `decodeTenka`. Seats keep who plays them when the number of players is the same; otherwise every seat but…
What mountTenka hands back: the game being played, and the ways to change it from outside.
type TenkaTableOptions = { /** The names round the table, in seat order: two to six. */ players?: readonly string[]; /** Which seats the computer plays. By default every seat but the first. */ computers?: readonly boolean[]; /** Rounds before the count: 10, 20, or 60 for the whole world. */ rounds?: number; /** The seed every deal and die is drawn from; a new one each game when absent. */ seed?: number; /** The map:…
What mountTenka may be given. Everything is optional: with nothing, it is a game of three against two computer players, to the last player standing.
type TenkaView = readonly [number, number, number, number];
A part of the map to show, in map units: left, top, width, height (an SVG viewBox).
territoryNameIn(strings: TenkaStrings, key: string): string
A territory's name in a table of strings, by its key (TENKA_TERRITORIES[n].key, such as "alaska"); "" for a key that is no territory's.
@johnmorrisdotca/tenka/reactTenkaMap TenkaMapProps TenkaTable TenkaTableProps
TenkaMap({ game, marks, colours, onTerritory, label, ...svg }: TenkaMapProps): import("/home/runner/work/tenka/tenka/node_modules/.pnpm/@types+react@19.3.0/node_modules/@types/react/index").JSX.Element
THE WORLD, AS A REACT COMPONENT: every territory in its owner's colour, its counter of armies, the rings of a choice, and a press on a territory or its counter reported by number. It draws; the rules stay in playTenka, and what a press means in tapTerritory. Scales to its container's width; style .tk-land, .tk-sea and the rest as you like.
type TenkaMapProps = { /** The game to draw: only who holds each territory and with how many armies is read. */ game: Pick<TenkaGame, "owners" | "armies">; /** What to light up: from `marksFor(game, choice)`. */ marks?: TenkaMapMarks; /** A colour for each seat. */ colours?: readonly string[]; /** A territory pressed. Without it the map is only a picture. */ onTerritory?: (territory: number) => void; /** The map's a…
What TenkaMap takes: a game to draw, and any attribute of its <svg>.
TenkaTable({ players, computers, rounds, seed, colours, computerDelayMs, onChange, locale, strings, theme, record, ...element }: TenkaTableProps): import("/home/runner/work/tenka/tenka/node_modules/.pnpm/@types+react@19.3.0/node_modules/@types/react/index").JSX.Element
A whole table against the computer, as a React component: the plain-DOM table (mountTenka) mounted into this component's element once the browser has it. Options are read when it mounts; give it a new key to start over with different ones.
type TenkaTableProps = TenkaTableOptions & Omit<HTMLAttributes<HTMLDivElement>, keyof TenkaTableOptions>;
What TenkaTable takes: the options of mountTenka, and any attribute of its <div>.
この日本語は、まだ日本語を母語とする方の確認を受けていません。訂正を歓迎します。