Self-hosted multiplayer lobby for Landlord Game, Stones of the Sea, Tic-Tac-Toe, and shared NPC players
  • JavaScript 92.7%
  • CSS 5.5%
  • HTML 1.7%
Find a file
Bram 0d93ff8b65
Some checks are pending
CI / Node 24 (push) Waiting to run
CI / Node 26 (push) Waiting to run
docs: refresh legible 1904 board proof
2026-08-16 18:30:36 +02:00
.github/workflows feat: turn Landlord rules into an extensible game 2026-08-16 14:07:12 +02:00
client fix(landlord): improve 1904 board legibility 2026-08-16 18:26:38 +02:00
docs docs: refresh legible 1904 board proof 2026-08-16 18:30:36 +02:00
locales/stones-of-the-sea fix(stones): make language selector discoverable 2026-08-16 17:07:28 +02:00
server fix(landlord): improve 1904 board legibility 2026-08-16 18:26:38 +02:00
.dockerignore chore: add production container image 2026-08-16 15:31:23 +02:00
.editorconfig improvements 2026-05-13 11:27:06 -06:00
.env.example feat: turn Landlord rules into an extensible game 2026-08-16 14:07:12 +02:00
.git-blame-ignore-revs chore: add .git-blame-ignore-revs 2026-05-25 10:06:09 -06:00
.gitignore chore: add MIT LICENSE and update package.json metadata 2026-05-27 12:28:32 -06:00
.nvmrc feat: turn Landlord rules into an extensible game 2026-08-16 14:07:12 +02:00
CLAUDE.md feat: unify multi-game lobby and NPC framework 2026-08-16 15:23:12 +02:00
Dockerfile fix: support Coolify runtime health checks 2026-08-16 15:56:20 +02:00
LICENSE chore: add MIT LICENSE and update package.json metadata 2026-05-27 12:28:32 -06:00
package.json refactor(i18n): drive Stones translations from PO 2026-08-16 16:51:42 +02:00
README.md feat: unify multi-game lobby and NPC framework 2026-08-16 15:23:12 +02:00

Gamagie

One self-hosted multiplayer lobby for Landlord's Game, Stones of the Sea, and other turn-based games.

Gamagie keeps accounts, open tables, spectators, chat, persistence, and computer-player orchestration in one shared framework. Each game contributes only its server logic, configuration, help, and client renderer. Landlord's Game remains the default table and adds its versioned rule laboratory inside that boundary.

This repository is based on LAN Games and preserves its MIT-licensed history.

License: MIT Node Code style: Prettier

Unit and integration suites · one self-hosted Node.js server

The waiting room shows every enabled rule and table setting

The waiting room repeats the exact versioned selection that was persisted when the table was created.

The Landlord's Game board with Prosperity and the issuer ledger enabled

Start a table

Prerequisites: Node.js 24 LTS or newer and the native build tools required by better-sqlite3 and bcrypt.

git clone <repository-url> gamagie
cd gamagie
npm ci --prefix server
node -e "console.log('JWT_SECRET=' + require('crypto').randomBytes(48).toString('hex'))" > .env
npm start

Open http://localhost:3000. Before inviting LAN players, verify the host is connected to the intended trusted LAN or VPN; do not expose the server directly to the public internet.

The first table takes four steps:

  1. Each player registers a local username and password, or signs back in.
  2. The host names the table, chooses a preset, and optionally changes compatible switches. The explanation card updates immediately.
  3. Other players open http://<host-ip>:3000 and join from the lobby. NPC-ready games can fill seats with Chaos NPC or—when configured—the external n8n Director. Landlord tables additionally offer Prudent Steward; Stones tables offer Steady Navigator.
  4. Everyone reviews the persisted explanation card in the waiting room; the host starts the game.

Windows, reset, and connection details are covered in Installation details.

The rule laboratory

There is one selectable extension—Monopolomics Laboratory v1—with a complete option matrix. Sets are convenience presets over that matrix, not separate engines or rule forks.

Switch What it changes
Kitchen table Disables compulsory auctions, routes eligible payments to a pot, and permits one issue-backed rescue per player.
Prosperity Collects site rent publicly, permits public infrastructure acquisition, and replaces last-player-standing with a shared cash target.
Issuer's ledger Journals Bank-boundary flows and reconciles the table each full round.
Issuer rescue exercise Enables the documented rescue loan/grant exercise. This switch requires the Issuer's ledger.
Missing column Adds finite construction capacity plus verified retrofit, reversal, and repair claims.

Bundled presets are Set 1 (rules as written), Set 2 (Kitchen table), Set 3 (Prosperity), Set 4 (Issuer's ledger), Set 5 (Missing column), Sets 3 + 4, and Sets 5 + 4. Presets remain editable before the table is created.


The Games

Landlord's Game
28 players · ~90 min
Landlord's Game
Risk
26 players · ~120 min
Risk
The Game of Life
26 players · ~45 min
Life
Battleship
2 players · ~15 min
Battleship
Yahtzee
18 players · ~20 min
Yahtzee
Checkers
2 players · ~15 min
Checkers
Connect Four
2 players · ~5 min
Connect Four
Tic-Tac-Toe
2 players · ~2 min
Tic-Tac-Toe
Stones of the Sea
25 players · ~75 min

Every game supports save/resume, in-game chat, and spectator mode. Games with hidden information (Battleship, Risk, Life) enforce privacy server-side — each player sees only what they're allowed to.


Table of Contents

Start a table · Rule laboratory · The Games · Features · Installation details

In this README: Project Structure · Architecture · Security Notes · Development · Documentation · Roadmap

Standalone docs: How to Play · Configuration · Adding a New Game · API Reference · Socket.io Events


Features

Framework

  • Multi-game — add any turn-based game by implementing one interface file; no framework changes required
  • Real-time multiplayer — all clients sync instantly via Socket.io WebSockets
  • Player accounts — register/login with username + password; JWT persisted in localStorage
  • Lobby — create, browse, and join open games; game-type badge shown on every card
  • Save & Resume — pause any in-progress game and continue it later from the lobby
  • Auto-reconnect — disconnected players are marked AFK; their turn is auto-skipped after 30 s
  • In-game chat — room-scoped, real-time, 300-character cap
  • Spectator mode — anyone logged in can watch any game in progress (👁 Spectate button on every in-progress lobby card). Spectators see the full unfiltered state (including hidden information for games like Battleship, Risk, and Life), chat with players, and watch the action log; they cannot take actions. Players are notified when spectators join. See the "Spectator mode" section below.
  • Configurable — content and tuning live in JSON; Landlord policy is a discoverable, versioned server-side extension
  • Shared NPC controller — allowlisted, conformance-tested games offer local and authenticated n8n play without trusting a controller to invent moves; strategies see the same player-filtered state as a human seat

Landlord's Game

  • One bundled Monopolomics Laboratory extension contains the whole rules experiment; Sets 15 and the documented combinations are presets, not separate engines
  • Composable switches cover Kitchen Table, Prosperity, the Issuer's Ledger, issuer rescue, and the Missing Column; declared requirements such as issuer rescue → Issuer's Ledger are validated before play
  • Live explanation cards show the selected preset, every switch, and the exact table settings in the create flow and waiting room
  • Each open seat can be filled independently with a host-selected computer strategy. Faker gives every NPC a persisted fictional, locale-aware name plus randomly selected country and sex metadata; these cosmetics never affect play. Prudent Steward preserves liquidity, values income and complete groups, liquidates conservatively, and follows cooperative rule objectives; Chaos NPC samples the same server-approved action catalog randomly; the optional authenticated n8n Director lets a private workflow select only from pseudonymized, server-approved candidates and falls back locally on failure
  • Exact extension version, preset, and complete validated option matrix are pinned per table, so saved games cannot change meaning after deployment
  • Full property engine: dice, property buying, auctions, rent, color groups, even building, mortgages, cards, trades, and bankruptcy
  • Canonical accounts, transaction journal, settlement phase, institutional ownership, full-round lifecycle, and extension-requested random facts keep composed rules auditable
  • Visual CSS Grid board with color bands, player tokens, house/hotel indicators, and ownership dots

Connect Four

  • Standard 7 × 6 board; drop pieces by clicking column buttons
  • Win detection: horizontal, vertical, and both diagonals
  • Draw detection when the board is full

Risk

  • Classic 42-territory world map across 6 continents; 26 players
  • Three-phase turns: reinforce → attack → fortify
  • Auto-distributed initial setup; armies and territories dealt evenly to all players
  • Dice combat: attacker rolls up to 3, defender auto-rolls up to 2; ties go to defender
  • Continent bonuses (NA 5, SA 2, EU 5, AF 3, AS 7, AU 2) applied at the start of every reinforce phase
  • 44-card deck (42 territory + 2 wild); valid sets (3 of a kind, 3 different, or any 2 + wild) traded for escalating bonus armies (4, 6, 8, 10, 12, 15, then +5 each)
  • Player elimination transfers all cards to the conqueror; last player standing wins by world domination
  • First game with hidden information — each player's hand is private, enforced server-side by the game's getStateForPlayer filter

Tic-Tac-Toe

  • Classic 3 × 3 board; 2 players take turns marking cells with and
  • Win detection: rows, columns, both diagonals
  • Draw detection when the board fills
  • Supports shared local and n8n-directed NPC seats

Stones of the Sea

  • Cooperative-competitive island stewardship across six changing seasons
  • Shared reef, forest, and trust tracks; specialist lineages; voyages; gatherings; and community construction
  • Public rai expeditions separate material stones, witnessed loss, provenance, and recognized title
  • Supports shared local and n8n-directed NPC seats
  • Steady Navigator protects food, gathering cadence, shared tracks, hosting, and rai-expedition progress instead of merely sampling legal moves

Other NPC-ready games

Yahtzee, Checkers, and The Game of Life use the reconciled descriptor adapter to materialize complete legal moves for Chaos NPC and the n8n Director. Risk, Battleship, and Connect Four remain fail-closed until their complete NPC-driving surfaces pass the same full-game conformance gate.

Yahtzee

  • Standard 5-dice, 13-category, three-rolls-per-turn rules; 18 players (solo play supported)
  • Up to 3 rolls per turn with arbitrary holds between rolls
  • All 13 categories (six upper + three-/four-of-a-kind, full house, two straights, Yahtzee, chance)
  • Upper-section bonus (+35 when subtotal ≥ 63); ties produce a shared-winner array
  • Two-click commit on category selection prevents accidental score-locking
  • First non-board game — UI is a shared score sheet (players as columns, categories as rows) plus a dice tray, all inside the renderer-owned board area

Battleship

  • Classic 2-player, 10×10 grid; five ships per side (Carrier 5, Battleship 4, Cruiser 3, Submarine 3, Destroyer 2)
  • Drag-and-drop ship placement with R-key rotation during drag
  • Two-grid firing layout — your fleet on the left, opponent's waters on the right; fixed positions across turns with active/inactive treatment that flips
  • Both fleets revealed at game over (winner and loser see the layout that beat them)
  • First game with a simultaneous private setup phase — both players place ships in parallel; the phase transitions to firing on a barrier (both Ready) rather than via a turn. Modelled as status='playing' + turnState.phase='setup' with currentPlayerIndex=null
  • First game with fully hidden state per player — opponents' ship positions are stripped by getStateForPlayer on every emit, not masked

Checkers

  • Classic American Checkers on an 8×8 board; 12 pieces per side, 2 players
  • Diagonal movement with mandatory captures and multi-jump chain captures
  • King coronation on reaching the back row (kings move forward and backward, one square at a time — not "flying" kings)
  • Stalemate-as-loss: player with no legal move loses
  • Click-driven move selection consuming server-supplied action descriptors — the renderer highlights only legal pieces and destinations
  • Animated piece movement, capture fading, chain-capture sequencing, and king coronation visual

The Game of Life

  • 64-square branching board with a start fork (Career vs College), main track, and two-path retirement choice (Countryside Acres vs Millionaire Estates)
  • 110 spinner (no dice) — CSS-animated wheel that decelerates and settles on the result
  • College path costs $40,000 in loans but unlocks degree-required careers; career path skips the loan but is locked out of degree careers
  • Marriage adds a spouse peg and collects $5,000 from each other player as wedding gifts
  • Children mechanic — single births (+$5k/player) and twins (+$10k/player); each child counts toward final scoring at $50k each
  • Insurance — auto and life, optional out-of-band purchases that nullify the matching accident squares
  • Stocks — pick a number 110; collect $10,000 whenever any player's spinner matches it (cross-turn payouts)
  • House purchase — pick from 23 offered house cards (cost vs scoring value); contributes to final score
  • Retirement is the strategic crux: Countryside Acres draws life tiles from a shrinking deck; Millionaire Estates is a cash gamble that resolves at game over
  • First game with a branching, non-grid board — squares are graph nodes with explicit next[] adjacency; the renderer places squares at hand-tuned grid coordinates with arrows showing direction
  • First game with deferred-resolution game over — ME retirees' win/loss is undetermined until the last player retires; documented in docs/renderer-contract.md
  • First game with a "retired-but-still-in-game" pattern — retired players stay in state.players and turn rotation skips past them; the game ends when every player is retired
  • Life tiles are hidden information per player (count visible to opponents, values masked until game over); revealed with a per-tile flip animation during the final score reveal

Installation details

Prerequisites

  • Node.js 24+ (Node 24 LTS recommended)
  • C++ build toolchainbetter-sqlite3 and bcrypt are native modules compiled during npm install. If the build fails, install the toolchain for your OS:
    • Windows: npm install -g windows-build-tools or install the "Desktop development with C++" workload from Visual Studio Build Tools
    • macOS: xcode-select --install
    • Linux (Debian/Ubuntu): sudo apt install build-essential python3
  • No external database — SQLite is embedded via better-sqlite3

Install and run

npm ci --prefix server
node -e "console.log('JWT_SECRET=' + require('crypto').randomBytes(48).toString('hex'))" > .env
npm start

The server binds to 0.0.0.0:3000 by default — reachable on your entire LAN.

The root scripts use POSIX shell syntax. On Windows, run them from Git Bash or WSL, or set JWT_SECRET in the current shell and run npm --prefix server start directly.

Connect

Who URL
Host machine http://localhost:3000
LAN players http://<host-ip>:3000

Find <host-ip> with ip addr (Linux), ipconfig getifaddr en0 (macOS), or ipconfig (Windows).

Spectator mode

Anyone logged in can watch any in-progress game without playing.

  • In the lobby, every game in playing status has a 👁 Spectate button next to the Join/Rejoin button.
  • A spectator sees the full unfiltered state — including hidden information like Battleship ship positions, Risk card hands, and Life tiles. (The point is to enjoy watching strategy unfold; spectators are watching, not playing.)
  • Spectators can chat in the same room as players; their messages are prefixed with 👁 so players know which lines come from the gallery.
  • Spectators see the action log, the player roster, and any in-progress modals.
  • The action panel is hidden; the "You're spectating" banner persists at the top of the play area.
  • Players are notified when a spectator joins (log entry). Leaves are silent.
  • A spectator can leave any time via the ✕ Leave button (replacing the host's Save/Quit). Leaving has no effect on the game.

Spectators cannot take actions. The server enforces this (any game:action from a spectator is rejected with a game:error); the client gates click handlers as a UX layer so spectator clicks don't appear to do anything.

A user who is already a player in a game cannot also spectate it — the Spectate button is suppressed in that case, and the server rejects an explicit attempt with "You are already a player in this game".


Project Structure

The repository has three top-level directories: server/ (Node.js backend with game-logic modules under server/games/ and framework under server/src/), client/ (single-page application with per-game renderers under client/js/games/), and docs/ (design notes and reference documentation).

See docs/project-structure.md for the full annotated directory tree.


Architecture

The platform is a single Node.js server (Express + Socket.io) backed by embedded SQLite. Game logic lives in pure-function modules behind a small interface; the framework handles persistence, real-time sync, authentication, and the lobby. Each client renders its game type through a registry-dispatched renderer.

Landlord's Game adds a second boundary inside its property engine: a trusted, versioned extension owns switchable economic policy, while the engine owns shared board mechanics. Named sets are complete presets over one option matrix. The browser builds controls and explanation cards from server metadata and executes no extension code.

See docs/architecture.md for the component diagram, data-flow walkthrough, database schema, and common GameState shape.


Security Notes

LAN Games is designed for trusted local networks. Authentication uses JWT tokens (the server refuses to start without a JWT_SECRET) and bcrypt-hashed passwords. Every game action is validated server-side, and every state-bearing emission routes through getStateForPlayer to protect hidden information. A full state-emission audit documents the boundary.

The platform is offline by design — once installed, the server and client need zero internet connectivity. All assets are served locally, there are no CDN dependencies, no telemetry, and no outbound HTTP calls.

See docs/security.md for the complete security notes including JWT configuration, LAN binding warnings, and the offline-by-design inventory.


Development

Scripts

npm start              # run the server
npm run dev            # run with nodemon (auto-restart)
npm test               # unit tests (778 across 20 suites)
npm run test:integration  # integration tests (45 across 6 suites)
npm run lint           # ESLint
npm run format:check   # Prettier (verify)
npm run format:write   # Prettier (auto-fix)
npm run reset-db       # drop all tables and recreate schema
npm run reset-db:hard  # delete the .db file entirely
npm run screenshot:rules  # regenerate the rule-card and board screenshots
npm run screenshots       # regenerate the complete game gallery

See docs/development.md for environment variables, port configuration, test details, hot-reloading, and the new-game checklist.


Documentation

Guides

  • How to Play — rules and controls for the original eight games; Stones of the Sea also ships contextual help in its game module
  • Adding a New Game — interface contract, state versioning, renderer registration
  • Development — environment variables, scripts, testing, hot-reload, new-game checklist

Reference

Design Documents


Roadmap

  • More games — Chess, Scrabble, Catan, Coup, Liar's Dice, …
  • Action descriptor contract — implemented across six of the nine games (Battleship, Checkers, Risk, Yahtzee, The Game of Life, and Stones of the Sea). Connect Four and Tic-Tac-Toe deliberately keep their trivial action surfaces (dropPiece, markCell) on getValidActions. See docs/action-descriptors.md for the contract.
  • Turn timer UI — server emits absolute-deadline warnings via game:turn_warning; the client-side countdown bar is implemented in client/js/turn-warning.js.
  • Spectator mode — shipped; see the Spectator mode section above
  • NPC tournament and rollout tools — benchmark Prudent Steward across rule matrices and add bounded Monte Carlo evaluation without moving authority out of the server
  • Custom board themes — CSS variable overrides per game type
  • Mobile optimisation — touch-friendly controls for handheld players
  • HTTPS / mDNS — easier LAN discovery and secure transport without manual IP lookup