Skip to main content
Glama
README.md
# Elmridge — an AI town sim

A browser town-simulation game built with **three.js**: a small economy runs day
and night, citizens hold jobs, buy food, chat, sleep, and get unhappy when the
bakery runs out of bread. Two AI surfaces sit on top:

1. **An MCP server** — any MCP-capable agent (Claude, Hermes, …) gets 25 tools to
   observe and puppet the town: spawn citizens, assign jobs, move people, set
   policies, build houses, trigger festivals, or rewrite the schedule.
2. **TypeSafe Jev** (“System One”) — an optional LLM decision layer on the
   OpenRouter-compatible endpoint that steers every citizen once per game hour
   with typed, confidence-gated choices.

![Elmridge by day](screenshots/01-day.png)
![Elmridge at night](screenshots/02-night.png)
![Citizen detail card](screenshots/03-citizen.png)

## Quick start

```bash
npm install
npm start          # game on http://localhost:8080
```

Drag to orbit, scroll to zoom, click a citizen to open their card.
Keys: `space` pause · `1/2/3` → 1×/2×/4× · `H` help · `Esc` deselect.

## Let AI play with the town (MCP)

The stdio MCP server (`src/server/mcp-server.js`) bridges to the running game
over WebSocket:

```bash
# Hermes
hermes mcp add sim-game --command node --args C:/path/to/sim-game/src/server/mcp-server.js

# Claude Desktop / any MCP client
{
  "mcpServers": {
    "sim-game": {
      "command": "node",
      "args": ["C:/path/to/sim-game/src/server/mcp-server.js"]
    }
  }
}
```

25 tools, all listed in `scripts/mcp-test.js`:

| group | tools |
|---|---|
| read | `get_town` `get_characters` `get_character` `get_buildings` `get_economy` `get_events` `get_ai` |
| people | `spawn_character` `set_job` `move_character` `set_activity` `release_character` `set_need` `give_money` `give_food` |
| world | `build` `demolish` `set_policy` `set_time` `set_speed` `select` `announce` `trigger_event` |
| AI layer | `set_ai` `ask_jev` |

Actions stream into the UI as “🤖 AI · tool ✓” chips, so you can watch an agent
work in real time. `select` also highlights the citizen in the scene.

## TypeSafe Jev decision layer

Set an OpenRouter API key on the game server, then ask your agent to call `set_ai`:

```bash
OPENROUTER_API_KEY=sk-or-… npm start
```

Once enabled, once per game hour the world state is flattened into a dense text
snapshot and every citizen is asked a typed **System One** question
(`{ _action: choice, _content: noul, … }`). Answers above the confidence gate
(0.5) become visible directives: the citizen leaves their schedule, walks
somewhere, eats, socialises, or idles — and the HUD card shows the directive
with its confidence. `ask_jev` exposes the raw passthrough for free-form
queries. Costs/tokens are tracked (`get_ai`). Without a key the layer stays
offline and the game runs exactly the same.

The key is read from the environment or from `%LOCALAPPDATA%/hermes/.env`
(never printed), so this repo ships with **no** secrets.

## The simulation

Everything tick-based in `src/sim/` runs headless and deterministic (seeded
mulberry32) — the browser is only a viewer.

- **Jobs** — mayor, farmer, baker, merchant, craftsman, bartender, unemployed.
  Jobs have wages, workplaces, opening hours, and production recipes.
- **Economy** — taxes fund the treasury, wages and unemployment pay push coins
  back out; shops adjust prices daily toward a target sales rate; farms
  wholesale vegetables to the bakery each morning; treasury pays wages
  (partial-pay keeps everyone afloat in a shortfall).
- **Needs** — hunger, energy, social, morale. Citizens eat at home, buy meals
  at the market, collapse from hunger, and cheer at festivals.
- **Day cycle** — 100 ms ticks, sunrise/sunset lighting, lamps and windows at
  night, sleep at home, dawn commutes.

All balance numbers (wages, prices, opening hours, build costs, Jev cadence)
live in one file: `src/sim/config.js`.

Stability check: 20 simulated days from seed 1337 end with **0 deaths, 0 broke
citizens, treasury 500 → 485**.

## Tests

```bash
npm test           # both suites
# or individually:
node scripts/check.js      # 52 headless assertions: economy, jobs, needs,
                           # commands, events, Jev layer (mocked System One)
node scripts/mcp-test.js   # boots the game, real MCP handshake, all 25 tools
```

## Layout

```
src/sim/        world, grid/A*, characters, economy, Jev layer  (no I/O)
src/server/     static+WS server · stdio MCP bridge
client/         three.js scene, HUD, websocket client
scripts/        check.js · mcp-test.js
screenshots/    README images
```

MIT — see [LICENSE](LICENSE).