The Desk

A single-player browser game. You rule the fictional Republic of Valdoria for five in-game years from a wooden desk: a yes/no proposal lands in your tray every week, you set the budget and taxes every quarter, and the country answers. Debt, unrest and repression can end your term early. English and French.

The rules are in web/data/rules/rules.en.md and rules.fr.md. They are plain Markdown, and the game shows the same files under Menu → Rules.

Run it

You need Docker with Compose.

docker compose up --build -d     # build and start
# open http://localhost:8080
docker compose down              # stop

The container listens on port 8080 (change the left side of ports in docker-compose.yml to use another host port). It restarts unless stopped and has a health check (/healthz, probed by the binary itself because the image has no shell).

Without Docker (Go 1.23+):

go run ./server                  # PORT=3000 go run ./server to pick a port

There is nothing to configure: no database, no environment variables except the optional PORT. Saves live in the player's browser (localStorage), with three manual slots and export/import as text.

Stack

  • Server: Go, standard library only. It serves the files under web/ (embedded in the binary) and /healthz. No game logic runs on the server.
  • Game: vanilla JavaScript ES modules and CSS. No framework, no bundler, no npm, no runtime dependency. Charts are hand-drawn SVG, sounds are synthesised with the Web Audio API, fonts are bundled (OFL).
  • Image: multi-stage build into gcr.io/distroless/static (a few MB).

Folders

server/            Go entry point (main.go) and the content tests (*_test.go)
web/
  embed.go         go:embed of everything below
  index.html       the page
  css/             desk, papers, ledger, decision, menu, endgame, anim (motion), mobile (phone layout)
  fonts/ img/      bundled fonts and small images
  js/
    main.js        boot, lifecycle, the real-time loop (frames + a worker heartbeat for background play)
    timer-worker.js  heartbeat that keeps the game running in a hidden tab
    engine/        the game rules: pure functions, no DOM, no network (state, economy, sectors,
                   indicators, cards, gameover, score, world, projection, validate...)
    ui/            DOM panels: dashboard, decision (the tray), calendar (dial clock), ledger, newspaper,
                   telegram, endgame, menu, shelf (phone), audio, markdown
    save/          save codec (CG1:<crc>:<base64>) with migrations, and storage with fallbacks
    i18n/          interface strings, en.json and fr.json
  data/
    balance.json   every tunable number
    scenarios/     the country at the start (default.json, index.json)
    cards/         the proposal pool, one file per category (900+ cards, EN and FR)
    characters.json  newspaper.json  endgame.json  rules/
  dev/             headless simulation page (sim.html)
tools/             Node scripts: tests, balance, card and scenario helpers (below)
docs/              PLAN.md (plan and decisions log), BALANCE.md, CARD-WRITING.md, SCENARIO-WRITING.md

The engine never touches the DOM and the UI never changes the state directly: it calls engine actions. That keeps the simulation testable headless.

Test and tune

go test ./server                      # validates every card, scenario, balance section, rules file and string
node tools/engine-check.mjs           # engine rules, saves, tray, clock, game overs
node tools/balance-check.mjs 24 --check   # plays full terms with bots and checks the difficulty bands
node tools/card-stats.mjs             # overview of the card pool
node tools/add-cards.mjs <category> <batch.json>   # merge a batch of new cards
node tools/scenario-check.mjs [id]    # start-state report for a scenario
node tools/bal-edit.mjs path=value    # edit balance.json keeping its layout

Guides for contributors (and agents): docs/BALANCE.md for difficulty, docs/CARD-WRITING.md for new proposals, docs/SCENARIO-WRITING.md for new countries.

Handy URL parameters for development (they skip the menu and never touch the real autosave): ?dev (exposes window.__game), ?seed=N, ?lang=fr, ?pronouns=she, ?skip=DAYS, ?alloc=K, ?card=ID, ?msPerDay=N, ?menu.

Licence

Public domain, see LICENSE. The fictional countries, people and events are invented.

S
Description
No description provided
Readme Unlicense
63 MiB
Languages
JavaScript 71.5%
CSS 16.3%
Go 10.4%
HTML 1.7%