A marriage simulator that demonstrates attractor-based narrative generation — a multi-agent architecture that breaks LLM text adventures out of autoregressive local minima.
Single-prompt LLM games suffer from context poisoning: the model conditions on its own outputs, converging to whatever tone was established early. A passive player gets a pleasant, flat experience with no surprise. The narrative falls into a local minimum.
This game uses three independent LLM agents with different objectives:
- Narrative Model — writes the scene the player sees
- Director — a separate LLM that reads the full context and generates pressure directives (confrontations, disruptions, revelations)
- Partner Agent — a background scorer that tracks the partner's emotional state (drift, tension) and maintains attractors: unresolved relationship patterns that accumulate charge when ignored
When an attractor's charge reaches threshold, it mandates — the narrative model is forced to cash it out as a concrete in-scene event. This breaks the autoregressive feedback loop because the pressure comes from outside the conversation context.
Requires Claude Code installed and authenticated.
git clone https://github.com/NicholasARossi/first-year.git
cd first-year
python play.pypython play.py # full game with attractor system
python play.py --no-attractors # baseline mode (no charge dynamics)
python play.py --no-director # disable the director
python play.py --hide-thinking # hide model reasoning
python play.py --load session.json # resume a saved sessionCompare baseline vs attractor system with passive-player rollouts:
# First: play a few turns, then copy a save as the fixture
cp saves/session_XXXXXXXX_XXXXXX.json backtest_fixtures/session_frozen.json
# Run the backtest
python backtest.py --rollouts 10 --turns 5Results are saved to backtest_results/.
Player Input
│
▼
┌──────────┐ ┌──────────┐
│ Director │────▶│Narrative │────▶ Player sees this
│ (Opus) │ │ Model │
└──────────┘ │ (Opus) │
└──────────┘
│
▼
┌──────────────┐
│Partner Agent │
│ (Opus) │
│ │
│ drift: 4/10 │
│ tension: 6/10│
│ attractors: │
│ - resentment│
│ charge: 8 │
│ MANDATED │
└──────────────┘
The Director and Partner Agent run as separate LLM calls. The Partner Agent runs in a background thread while the player reads and types — zero added latency.
This repo accompanies a blog post on attractor-based narrative generation for LLM games. See rossidata.com.