Town Simulation with AI Characters
← Project Index Town Simulation with AI Characters

Town Simulation with AI Characters

A small town of LLM-driven characters who live daily lives -; each with their own personality, needs and routine -; bumping into each other and holding generated conversations that nudge their relationships and emergent stories.

Archived Started: Summer 2025 Updated: Summer 2025

Overview

A Python desktop app in the generative-agents lineage: roughly a dozen hand-authored characters move around a grid town, satisfy needs, form relationships, and talk to one another using an LLM -; Claude or a local Ollama model. It runs with a tkinter GUI (split-pane map plus info panels) or fully headless, and persists everything to a SQL Server database. A captured run shows it genuinely working: "Successfully loaded 11 characters... Status: Sunday 01:24 AM."

Background

The concept sits squarely in the wake of Stanford's generative-agents work -; LLM NPCs with memory, relationships and routines producing emergent storytelling -; but this is a from-scratch implementation, not a fork: tkinter and SQL Server rather than a web stack. It was built solo with Claude Code as a sandbox for watching characters' social lives unfold on their own.

One honest note: an internal hub document oversells the scope (zoning, job markets, a mayor role, dynasties). None of that is in the code. The real, working scope is needs, emotions, movement, and LLM conversations for about eleven characters -; which is plenty interesting on its own.

How It Works

A tick loop advances an in-world clock (1 real second ≈ 2 sim minutes), updates weather hourly, then steps every character and every active conversation. Each character carries five decaying needs (energy, hunger, social, entertainment, hygiene), a state machine (sleeping, eating, working, socializing…), and twelve emotional states with their own intensity and decay.

Personalities are written as natural-language bios, and a keyword scan derives numeric traits from the prose -; so the same text drives both the behaviour model and the LLM prompt. When two characters come within two grid cells and both want company, a conversation spins up. The LLM must answer in structured JSON:

# every generated line comes back structured
{
  "dialogue": "...",
  "emotional_tone": "friendly",      # sets memory importance
  "suggested_next_speaker": "...",
  "end_conversation": false
}
# tone nudges the pairwise relationship (+1 friendly, -5 hostile),
# which re-enters the next prompt -- a closed social-feedback loop

Memories carry importance scores; the five most recent feed each prompt. Weather is a first-class emotional driver -; introverts get cozy in the rain, extroverts get bored -; scaled by each character's weather-sensitivity trait. Movement uses a cost-weighted A* so characters prefer sidewalks over roads. There's even an adaptive clock that slows in-world time when a local LLM's request queue backs up, so slow models don't break causality.

Current Status

Archived as a working prototype. It runs and produces conversations, but several pieces are stubbed.

  • Needs, emotions, traits, relationships, weather and the LLM conversation loop all function.
  • Schedule generation isn't wired up; activity choice falls back to time-of-day plus needs heuristics.
  • No save/load of sim state; "New Town," "Load Town" and the town editor are all "not implemented yet." Needs a SQL Server instance to run.