Skip to content

About

Island empire browser strategy game in the classic early-2000s style. Zero dependencies, bots play by the same rules.

Resources

Stars

0 stars

Watchers

0 watching

Forks

 
 

Repository files navigation

Tideholm

A browser-based island empire game in the spirit of the classic early-2000s German browser strategy games — slow real-time timers, table-driven UI, alliances-and-raids gameplay. Original name, assets, text and numbers.

Bots play alongside humans using the exact same rules and actions.

Play it live: https://nostr.social/tideholm/ — sign in with a pod (create one).

Your island The world map
Island overview World map

Available in English, German and Czech — pick a language on the login screen or in-game; the whole UI, error messages and even battle reports are rendered in each player's own language (public/i18n.js holds the dictionary; adding a language is one new block there plus a help page).

Run

node server.js

Then open http://localhost:3000 and register with any name + password.

No dependencies. State is saved to data/world.json every 30 seconds and on shutdown. Delete that file to start a fresh world.

Config (env vars)

World & server

Var Default Meaning
PORT 3000 HTTP port
GAME_SPEED 5 Multiplies production, divides build times. 1 ≈ classic pace (hours), 5 = minutes. Lower it (2–3) for a slower, more deliberate world.
BOTS 20 Bots spawned when a new world is created
FREE_ISLES 30 Uncharted (colonizable) islands on a new world
LAND_RESPAWN 0 (off) Uncharted islands added when the map runs out of them. Fires on world load, i.e. a server restart — so with it on, a deploy can reshape a live map and move the dominance threshold (all islands count toward WIN_SHARE). Off by default; see #36.
WORLD_THEME generated Map backdrop: generated (seeded fictional chart) or aegean (real coastlines; build other regions with tools/build-region.js)
WIN_SHARE 0.6 Island share a player/alliance must hold for a dominance victory
WONDER_WIN_LEVEL 5 Great Beacon level that wins the world. Raising it stretches the endgame: each level costs 1.55× more and takes 1.5× longer, and every completed level is announced world-wide.
NIGHT_BONUS — Night window HH-HH (e.g. 22-6) where defenders get a bonus
ADMIN_TOKEN — Enables the admin panel at /admin.html (stats, announce, reset)

Season lifecycle (#8, #2)

Var Default Meaning
WORLD_START now Launch time (epoch ms or ISO date). Before it, the world is frozen and shows a signup countdown so everyone starts together.
SEASON_AUTO off Auto-recycle a finished world into the next season
SEASON_END_GRACE_HOURS 48 Bragging window a won world lingers before auto-reset
SEASON_SIGNUP_HOURS 24 Pregame countdown for the next auto-started season

Balance / difficulty (#7, #13) — all default to prior behavior

Var Default Meaning
PROTECT_GRACE_HOURS 72 ÷ speed Newcomer shield in real hours (independent of speed). Set e.g. 48 for two real days.
PROTECTED_POINTS 40 Points below which a player can't be attacked
BULLY_RATIO 3 Attack point-band: no punching up/down past this ratio
MORALE_FLOOR 0.3 Min attack-power multiplier when outweighing the defender
BOT_MORALE_FLOOR = MORALE_FLOOR Same, but vs bot defenders. Season 6 ran at 1 (the leader fights bots at full power) until 2026-09-08, then 0.3 like humans: a big player farming small bots pays the same morale tax as bullying a small human.
WONDER_WIN_COUNT 1 How many winning-level Beacons ONE captain must hold at once. At 2, build both (~2× the wonder bill — the pacifist premium) or build one and capture a rival's: conquest carries buildings intact, so an enemy's rising tower is a prize as much as a threat, and stealing a lone finished beacon no longer wins by itself. Wonder stays personal — alliances have the crown. Clamped [1, 5]. See #178.
WIN_BASIS all What WIN_SHARE is a share of: all islands on the map, or populated (inhabited only). Under season-6 curves a crown priced against the whole map is a decoy — the flagship ladder to 23 islands costs a second beacon — while half the living world tracks beacon-cost all season and rises as colonies land. See #177.
POOL_TRADE_SPEED / POOL_TRAVEL_MIN / POOL_TRAVEL_MAX 8 / 30 / off The Tidepool's sails (#176): minutes per field for pool deliveries, the floor, and an optional ceiling in minutes (0 = uncapped). Defaults reproduce #76's behaviour exactly (pool boats match TRADE_SPEED, 30-min floor, no cap). Distance stays real — the #30 brake holds — but the market's boats can be tuned apart from merchant convoys. Loosening mid-season strands nothing, so these are env knobs, not world stamps. Season 6 runs 2 / 10 / 100.
TRANSIT_POP_FACTOR 0 (free) What fraction of its population an army costs while at sea — troops aboard attack, return, scout and colonize voyages charge pop × factor (ceil) against their home farm. 0 is the old behaviour: transit was population-free, so perpetual raiding pipelined a standing force unboundedly past the cap (train into the gap while the army sails; nothing re-checks on return). 0.5 — half rations at sea — keeps campaign surges possible while bounding the pipeline at ~2× cap; 1 is strict. Support is #40's domain at full cost under its own world flag. Clamped to [0, 1], junk falls back to free. See #174.
FLAGSHIP_COST_GROWTH 1 (flat) The conquest unit rides the same positional curve as the Colony Ship, under its own knob — position is islands owned plus flagships already paid for (garrison, queue, aboard any movement), so batches cannot dodge. With settling taxed and flagships flat, width-by-conquest bypassed the expansion tax ~10× cheaper at position 10. Tune softer than COLONY_COST_GROWTH: conquest already pays in blood (1.2 puts the 10th flagship at ~29k all-in, the 15th ~73k). Same [1, 3] clamp as #61. See #173.
FLAGSHIP_STORAGE_CLAMP 1 (on) A flagship's price is clamped to the launching isle's storehouse, resource by resource, while at least one of the three still fits. Under #173 every resource climbs the same ladder, so wood overflows first and gold four rungs later; those four rungs used to be unbuyable for a non-money reason ("no storehouse can hold it") and now cost a full storehouse of the overflowed resource. Once all three overflow the bare curve returns — unaffordable until the storehouse grows. Net: four more flagships per storehouse level, ~8% off the bill to isle 24, nothing off the calendar. 0 restores the bare curve. See #182.
BOT_RESPAWN 1 (on) Whether a bot wiped to its last island receives a refuge. Set 0 and a wiped bot is eliminated — the season's BOTS become a finite population and attrition consolidates the endgame into PvP. Never gates humans. See #172.
BOT_GARRISON_RATIO ∞ Cap a bot's standing defence to islandPoints × ratio (raiders exempt). Set e.g. 12 to stop bots turtling into unbeatable fortresses.
TRADE_SLOTS_PER_HARBOR 1 Merchant slots granted per Harbor level (#30). A shipment occupies one slot for the round trip, so distance costs throughput, not just patience — supplying a distant island is genuinely expensive. Applies to trade and market shipments; the Tidepool is exempt. New worlds only: existing seasons load unlimited (tradeSlots: null) so a restart cannot tighten logistics under plans already in motion. 0 grants no slots at all.
COLONY_COST_GROWTH 1 (flat) Colony Ship price multiplier per step along the expansion curve — cost × growth^(position-1), stepped per ship. Position is islands owned plus every colony ship already paid for (in a garrison, in a training queue, or sailing to settle), so ordering singly costs exactly the same as ordering a batch — counting islands alone let a player split orders for a 25% discount. 1.3 is the tuned brake on going wide: the 10th step costs ~29k all-in instead of 2.7k, the 15th ~106k. Steeper bites much harder than it looks (1.6 puts the 15th at 1.9M). Clamped to [1, 3] — anything outside falls back to flat, because an unbounded value made the ship unbuildable at any wealth while reporting "not enough resources" (#61). The train form quotes the real batch total, which is dearer than the single-ship price times the count (#62). Only meaningful from a fresh world.

Example — a fair, self-running "casual" world:

GAME_SPEED=3 \
WORLD_START=2026-07-20T18:00:00Z \
PROTECT_GRACE_HOURS=48 BOT_GARRISON_RATIO=12 BOT_MORALE_FLOOR=1 \
SEASON_AUTO=1 SEASON_SIGNUP_HOURS=24 SEASON_END_GRACE_HOURS=48 \
ADMIN_TOKEN=... node server.js

Gameplay

  • One island per player on a 40×40 ocean grid.
  • Three resources — wood 🪵, stone 🪨, gold 🪙 — produced in real time by the Lumberyard, Quarry and Gold Mine.
  • Storehouse caps how much you can hold; Island Hall speeds up construction.
  • Sequential build queue (max 3), costs rise ~1.55× per level.
  • Build a Barracks to train troops: Spearman (all-rounder), Raider (offense + big carry), Sentinel (defense).
  • Click any island on the map to attack it. Travel time depends on distance and your slowest unit; the battle resolves on arrival, the winner loots proportionally to surviving carry capacity, and survivors march home.
  • Battle reports for both sides; incoming attacks show origin and ETA.
  • Build a Harbor to launch Colony Ships, then click an uncharted (gray) island to settle it. First ship to land claims it; latecomers sail home. Switch between your islands with the dropdown in the header.
  • Conquest is loyalty-based: every island has loyalty 0-100. Each winning attack with a surviving Flagship breaks 25-40 of it; at 0 the island is captured — buildings, stores and all — and the surviving army garrisons it. Loyalty regenerates over time, so conquest takes waves. A fresh conquest starts restive at 25. Armies returning to a lost island scatter. A player whose last island falls respawns on a fresh one.
  • Support: station troops on any inhabited island as defenders (allies, or your own islands as transfers), recall them anytime. Stationed troops fight and die with the garrison; their owner gets battle reports. They also keep consuming the population of the island that raised them, from the moment they sail until they are recalled — so helping an ally costs you the farm space at home, and sending troops away never frees room to train more. Attacks are exempt. (Supporting your own island is a transfer: the troops join that island's garrison and cost population there instead.) Per-world (supportCostsPop): on for new seasons, off for seasons that began before the rule existed, so a restart cannot change the terms mid-season.
  • Scouts spy out garrisons, stores, loyalty and buildings — contested by the defender's own scouts (counter-espionage).
  • Wall strengthens all defenders and takes damage when the island is sacked. Farm caps population: every unit costs pop, bigger armies need bigger farms.
  • Trade: ship resources to any inhabited island (allies or your own) from the Harbor — capacity 250 × 1.5^(level-1) per shipment. The Harbor also grants merchant slots (one per level by default), and a shipment holds one until the merchants sail home — so a far-off delivery ties up a merchant for twice the travel time, and how much you can move at once is finite. Market trades charge both sides a slot; Tidepool swaps are exempt.
  • Quality of life: rename your islands, a battle simulator in the attack panel, the map centers on your island, and unicode player names.
  • Morale: attacking a much smaller player blunts your attack (down to MORALE_FLOOR, 30% by default). Optional night defense bonus via NIGHT_BONUS=22-6.
  • Victory: a player or alliance holding WIN_SHARE (default 60%) of all islands wins the world — or raises the Great Beacon to WONDER_WIN_LEVEL. Either way it's announced to everyone, with a banner in the UI. Start the next season from the admin panel's reset (which archives the old world and can open a signup countdown), or let SEASON_AUTO=1 recycle a won world on its own.
  • Bots wage real war: they scout targets and remember the intel, skip fortresses, favor soft targets, and run flagship conquest campaigns against empires their own size — never against small humans (<150 pts).
  • Bot personalities (#22): every bot rolls an archetype and temperament at spawn. Most are settlers (varied build biases, own sleep phase and tempo); a few are unmarked warlords who hunt above their weight; some are barbarians — marked on the map, never attack, never retaliate: a safe farm loop. Grudges pierce all safety bands: farm a bot, expect an answer. Mix via BOT_PERSONAS, global pace via BOT_TEMPO.
  • Rankings tab: every player by total points. Mail tab: write to any human player. Alliance tab: found an alliance (name + tag), invite players, accept or decline invitations. Allies cannot attack each other, and alliance tags show on the map and rankings.
  • Bots play for real: they grow their economy, keep garrisons, raid nearby islands for loot, hold grudges and strike back at whoever attacks them, and expand to uncharted islands (up to 3 each) — all through the same game actions humans use. Beginner protection: bots ignore players under 40 points and won't attack anyone more than 3× their size.

Combat model

Attack power A vs defense power D (home garrison defends). If A > D the attacker wins and loses fraction (D/A)^1.5 of the army; the defender's garrison is wiped. Otherwise the attacker is wiped and the defender loses (A/D)^1.5. Loot is capped by the survivors' carry capacity, drawn proportionally from the target's stocks.

Defense counts the garrison plus all stationed support, then the Wall: D = (defenders + 15×wall) × (1 + 0.08×wall). A sacked island loses one wall level.

If a Flagship survives a winning attack it breaks 25-40 loyalty; at 0 the island changes owner: the Flagship is consumed, survivors stay as the garrison, and the island's buildings and resources transfer. Loyalty regenerates at 2/h (×speed). An attack that arrives at an island you already own (e.g. captured by an earlier wave) reinforces it.

Roadmap

  1. ✅ Core loop: login, resources, buildings, queue, map, bots
  2. ✅ Units + combat: barracks, troops, attacks with travel time, battle reports
  3. ✅ Colonization: harbor, colony ships, uncharted islands, multi-island play
  4. ✅ Conquest: flagships, island capture, respawn after elimination
  5. ✅ Bot war AI: raids, grudges and retaliation, bot expansion
  6. ✅ Alliances, messaging, rankings
  7. ✅ War depth: loyalty conquest, support troops, scouts, wall, farm cap
  8. ✅ Trade shipments + QoL: renaming, battle simulator, map centering
  9. ✅ Bot war college, dominance victory, morale, night bonus

Running it for real

See deploy/DEPLOY.md — a systemd + Caddy recipe for a VPS, or Fly.io. The server enforces a single instance via a lockfile, keeps rolling world backups in data/backups/, rate-limits registration/login per IP and actions per player, and logs actions and errors (not polling) to stdout. Set TRUST_PROXY=1 behind a reverse proxy so rate limiting sees real client IPs and session cookies are marked Secure.

Run the engine test suite with node tests.js. Balance-test a simulated world with node playtest.js [days] [bots].

Set ADMIN_TOKEN to enable the admin panel at /admin.html: world stats, announcements to all players, and season reset (archives the old world to data/backups/, keeps the hall of fame). Without the env var the admin API is disabled entirely.

Run inside a Solid server (JSS)

Tideholm doubles as "plugin zero" for JSS's plugin system (JavaScriptSolidServer#206): mounted at /tideholm inside a pod server, where your WebID is your player — no password, auto-provisioned on first request. See jss-plugin/ (adapter + manifest) and run the end-to-end proof with npm install && node jss-plugin/demo.js (JSS >= 0.0.214 comes from the devDependency; the game itself stays zero-dependency).

Layout

  • tests.js / tests.app.js — engine invariants and app-layer (HTTP, prefix mount, host identity) tests: node tests.js && node tests.app.js
  • game.js — pure game engine (formulas, lazy time resolution, actions, persistence)
  • bots.js — bot spawn + decision tick, built on the same engine actions
  • app.js — application core: world lifecycle, sessions, JSON API, static files. createApp({ basePath, identify, ... }) is transport-agnostic so a host process (e.g. a JSS plugin) can mount the game under a prefix with its own identity provider (WebID → player)
  • server.js — thin standalone launcher: socket, signals, port hopping, rate limiting, lockfile, backups
  • public/ — vanilla JS single-page client (+ help.html)
  • tests.js — engine math and invariant tests
  • AMM.md — the Tidepool: how the constant-product pool works, how its parameters were derived, and what to lift if you want one elsewhere
  • TRAIL.md — the Trail: peg-in/peg-out, the signed courier loop, Bitcoin anchoring, threat model, and the open trust boundary (#154) — the spec for sealed gold's whole lifecycle
  • NEWS.md — announcements sent to players in-game, with the exact text and what each one shipped
  • deploy/ — systemd unit, Caddyfile, Dockerfile, fly.toml, deploy guide

About

Island empire browser strategy game in the classic early-2000s style. Zero dependencies, bots play by the same rules.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages