Skip to content

Pompidup/KDomino

Repository files navigation

Kingdomino Engine

Node.js CI Branches Functions Lines Statements Coverage total

A TypeScript engine for the Kingdomino board game and its expansions.

Overview

This engine provides the core logic for Kingdomino: state management, rule enforcement, scoring, and turn flow. It is not a UI — consumers drive the game by calling engine methods and passing game state back in.

The engine is stateless: each method takes the current game state as input and returns a new game state. It is your responsibility to persist the state between calls.

Requirements

  • Node.js >= 20

Installation

npm install @pompidup/kingdomino-engine

Quick Start

import { createGameEngine, isGameWithNextAction } from "@pompidup/kingdomino-engine";

const engine = createGameEngine({});

// Create and set up a game
let game = engine.createGame({ mode: "Classic" });
game = engine.addPlayers({ game, players: ["Alice", "Bob"] });
game = engine.startGame({ game });

// Game loop
while (isGameWithNextAction(game)) {
  const { nextLord, nextAction } = game.nextAction;

  if (nextAction === "pickDomino") {
    const domino = game.currentDominoes.find((d) => !d.picked);
    if (domino) {
      game = engine.chooseDomino({ game, lordId: nextLord, dominoPick: domino.domino.number });
    }
  } else if (nextAction === "placeDomino") {
    const lord = game.lords.find((l) => l.id === nextLord);
    const player = game.players.find((p) => p.id === lord?.playerId);
    if (lord?.dominoPicked && player) {
      const placements = engine.getValidPlacements({ kingdom: player.kingdom, domino: lord.dominoPicked });
      if (placements.length > 0) {
        game = engine.placeDomino({ game, lordId: nextLord, position: placements[0].position, rotation: placements[0].rotation });
      } else {
        game = engine.discardDomino({ game, lordId: nextLord });
      }
    }
  } else {
    game = engine.discardDomino({ game, lordId: nextLord });
  }
}

const results = engine.getResults({ game });
console.log(results.result);

Game Modes

Mode Description
Classic Original Kingdomino rules. 1-4 players, 5x5 kingdom, terrain-based scoring.
QueenDomino Full Queendomino expansion with buildings, coins, knights, the Queen and the Dragon.
KingdominoOrigins-Discovery Origins expansion — volcanoes, fire tokens, fire-based scoring.
KingdominoOrigins-Totem Origins — Discovery + wooden resources + totem majority tiles.
KingdominoOrigins-Tribe Origins — Discovery + resources + cave board + cavemen recruitment.
AgeOfGiants Age of Giants extension — giants, quest tiles, 5-player support, 12 new dominos on Classic.
AgeOfGiants-QueenDomino Age of Giants combined with QueenDomino.
let game = engine.createGame({ mode: "Classic" });
let game = engine.createGame({ mode: "QueenDomino" });
let game = engine.createGame({ mode: "KingdominoOrigins-Discovery" });
let game = engine.createGame({ mode: "AgeOfGiants" });

See Game Modes for rules, player counts, and extra rules details.

Queendomino

The QueenDomino mode adds significant new mechanics to the base game:

  • Construction squares on some domino tiles where buildings can be placed
  • Coins (7 starting) used to purchase buildings or use the Dragon
  • Knights (max 3) placed on dominos to collect tax from construction squares
  • Buildings (18 types) purchased from the Builders Board, granting crowns, towers, and end-game bonuses
  • The Queen goes to the player with the most towers, granting +1 crown to the best territory
  • The Dragon burns a building tile from the Builders Board

The turn flow is extended with optional actions after placing a domino:

placeDomino → placeKnight → constructBuilding → useDragon → pickDomino
               (optional)      (optional)         (optional)
// Handle QueenDomino optional actions in your game loop
if (nextAction === "placeKnight" || nextAction === "constructBuilding" || nextAction === "useDragon") {
  game = engine.skipOptionalAction({ game, lordId: nextLord });
}

See Queendomino Guide for the complete rules, building list, and game loop example.

Kingdomino Origins

Origins introduces prehistoric terrains and fire-based scoring across three modes:

  • Discovery: Volcanoes provide fire tokens. Scoring = region size × fire symbols.
  • Totem: Discovery + wooden resources + totem majority tiles for bonus points.
  • Tribe: Discovery + resources + cave board with 22 cavemen to recruit.
if (nextAction === "placeFireToken") {
  game = engine.placeFireToken({ game, lordId: nextLord, position });
}
if (nextAction === "recruitCaveman") {
  game = engine.recruitCaveman({ game, lordId: nextLord, cavemanId, position, resourcePositions });
}

See Origins Guide for the complete rules and mechanics.

Age of Giants

Age of Giants extends Classic (or QueenDomino) with giants, quest tiles, and 5-player support:

  • 60 dominos (48 original + 12 new with giant/footprint symbols)
  • Giants cover crowns on your kingdom, reducing opponents' scores
  • Quest tiles (2 per game) provide end-game bonus scoring conditions
  • 5-player support with 5 dominos revealed per turn
if (nextAction === "placeGiant") {
  game = engine.placeGiant({ game, lordId: nextLord, position });
}
if (nextAction === "sendGiant") {
  game = engine.sendGiant({ game, lordId: nextLord, giantIndex, targetPlayerId, targetCrownPosition });
}

See Age of Giants Guide for the complete rules and mechanics.

Extra Rules

Rule Description Restriction
The Middle Kingdom +10 points if castle is centered
Harmony +5 points if no dominos were discarded
The Mighty Duel All 48 dominos, 7x7 kingdom 2 players only
Dynasty Play 3 games, highest total wins
game = engine.addExtraRules({ game, extraRules: ["The middle Kingdom", "Harmony"] });

// Dynasty: aggregate results from multiple games
const dynastyResults = engine.getDynastyResults({ games: [game1, game2, game3] });

See Game Modes for details.

Engine Options

const engine = createGameEngine({
  logging: true,                          // Console logging
  logger: { info: console.log, error: console.error },  // Custom logger
  shuffleMethod: (arr) => arr,            // Custom shuffle (e.g. no-op for testing)
  uuidMethod: () => "custom-uuid",        // Custom UUID generator
  events: { onGameEnd: () => {} },        // Event callbacks
  debug: true,                            // Debug mode
});

API Overview

Core Methods

Method Description
getModes Get available game modes
createGame Create a new game
addPlayers Add 1-4 players
addExtraRules Add optional extra rules
startGame Start the game
chooseDomino Pick a domino from the revealed set
placeDomino Place a domino on the kingdom
discardDomino Discard when no valid placement exists
getResults Get final rankings and scores
calculateScore Calculate score for any kingdom
getValidPlacements Find all valid placements for a domino
canPlaceDomino Check if any placement exists
serialize / deserialize Save and restore game state
getDynastyResults Aggregate scores from multiple games

Queendomino Methods

Method Description
placeKnight Place a knight and collect tax
constructBuilding Buy and place a building
useDragon Destroy a building tile from the board
skipOptionalAction Skip the current optional action

Origins Methods

Method Description
placeFireToken Place a fire token after placing a volcano domino
recruitCaveman Recruit a caveman from the cave board (Tribe mode)

Age of Giants Methods

Method Description
placeGiant Place a giant on a crown in your kingdom
sendGiant Send a giant to an opponent's kingdom

See API Reference for complete method signatures, types, and error codes.

Additional Features

  • Bot / AI: 4 built-in strategies (randomStrategy, greedyStrategy, advancedStrategy, expertStrategy) with playBotTurn() helper
  • Mixed games: Human and bot players in the same game via bot: { strategyName } on player input
  • Undo / Redo: Snapshot-based history with createGameHistory(), pushState(), undo(), redo()
  • Action Log: Immutable log via wrapWithActionLog(engine) for replay with replayActions()
  • Events: Callbacks via EngineConfig.events (onDominoPlaced, onGameEnd, etc.)
  • Debug Mode: wrapWithDebug(engine) with 3 verbosity levels
  • Seeded games: Deterministic shuffle via createGame({ mode, seed: "my-seed" })
  • Serialization: serializeGame() / deserializeGame() and save points
  • WASM Build: pnpm build:wasm produces a WebAssembly module usable from any language

Documentation

Document Content
Game Modes Modes, rules by player count, extra rules, game flow
Queendomino Guide Complete Queendomino rules, mechanics, and examples
Origins Guide Kingdomino Origins rules, fire tokens, resources, cavemen
Age of Giants Guide Age of Giants rules, giants, quest tiles, 5-player support
API Reference All methods, types, error codes, and utilities
WASM Build Cross-language usage via WebAssembly

Development

pnpm install          # Install dependencies
pnpm test             # Run tests (watch mode)
pnpm test -- --run    # Run tests once
pnpm coverage         # Tests with coverage
pnpm typecheck        # Type check (tsc)
pnpm lint             # Lint (biome)
pnpm build            # Build (ESM to dist/)
pnpm build:wasm       # Build WASM module (requires javy)

Contributing

  1. Fork the repository
  2. Create a branch (git checkout -b feature/your-feature)
  3. Make changes with tests (pnpm test -- --run && pnpm typecheck)
  4. Open a Pull Request

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages