<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300..700&amp;display=swap" />
workspace / traingame

Rust2026

TrainGame

A life sim on an endless train: hundreds of NPCs with jobs, a closed economy and gangs, and an LLM that proposes one novelty a day, which the engine checks before it becomes real.

Source code Bevy · Deterministic simulation · SQLite · LLM

The problem

I wanted a world that keeps changing without me writing every item and event, and without handing the game to a language model. A model is good at ideas and bad at rules: left alone it invents items that don't exist, breaks the economy, and no two runs of the same save match.

What I built

A Rust workspace of six crates: sim, the deterministic world with no graphics; game, the Bevy front end; history, every event in SQLite, so the dead stay queryable and family trees span generations; llm, an OpenAI-compatible client with a call budget, recording and replay; narrator, the prompts and checks around the model; and sim-laya, an optional NPC brain on a small choice model run with candle. People eat in meal shifts, queue in a full canteen, work, trade, marry, quarrel and form gangs that extort and protect their members. With no model configured, the game runs entirely on its fast brains.

  1. 01Deterministic simulation with no graphics or AI dependencies
  2. 02Closed economy: food, goods, wages and taxes, with prices that grow with distance from where an item is made
  3. 03An LLM proposes one novelty a day; the engine validates it, or refuses it with a reason
  4. 04Every event in SQLite, and every model answer recorded, so a game replays identically
A dormitory carriage and the canteen of the train, with NPCs talking, eating and cooking

Decisions

  1. 01

    The AI proposes, the engine decides

    If the model writes the world directly, every bad answer becomes a bug in the save, and the simulation stops being reproducible.

    ChoiceThe simulation never calls the model. The Narratore reads a compact summary of the train and asks for one novelty a day: an item, a recipe, a job or an event. A precheck rejects answers with the wrong shape and gives the model one retry. Then the Custode, inside the simulation, checks the draft against the live catalogs and applies it at a game minute fixed in advance, or refuses it with a reason. If the model is late, the fast brains carry on.

  2. 02

    Decide only when an action ends

    A train with hundreds of NPCs can't run a decision for each of them every frame, and the carriages off screen still have to live.

    ChoiceAn NPC only knows which carriage it is in and which action it performs until when. It decides again only when that action ends, so off-screen carriages never need catching up, and the renderer maps carriage, action and station to sprites.

What I learned

Where to draw the line between a model and a rule engine: the model is a source of proposals with a budget, a schema and a fallback, never the authority. The same idea as the agents in Trama, inside a game.

Under the hood

The code

Bevy · Deterministic simulation · SQLite · LLM