# The Chaos — Game Architecture This document describes the system architecture for developers and AI agents working on the codebase. ## Design Principle The Chaos is a **self-contained terminal game**. The TUI owns the full game loop — including LLM calls — so there is no split between a "DM agent" in chat and a "dashboard" in the terminal. The player runs one command: ```bash python3 tools/run.py ``` Everything — narrative, choices, character sheet, log, archive, ambience — lives in that process. ## Project Layout ``` the-chaos/ ├── rules/ # LOCKED — game rules, do not modify │ ├── deck/ # Card tables │ └── mechanics.md # Core mechanics reference ├── tools/ # Game system code │ ├── __init__.py │ ├── engine.py # Game engine (prompt builder, LLM client, parser, state) │ ├── run.py # TUI (Textual app, game loop, narrative, input) │ ├── ambience.py # CLI shortcut for ambience switching │ ├── draw.py # Card drawing tool │ ├── music-fetch.py # YouTube audio downloader │ ├── roll.py # Dice rolling tool │ └── store_turn.py # DEPRECATED — use engine.py archive_turn instead ├── scripts/ # UNLOCKED — helper scripts ├── run.sh # Entry point (just calls tools/run.py) └── session/ # Game state (read/write by engine) ├── config.json # LLM provider config ├── character.md # Player character sheet ├── world.md # Keep & Realm state ├── book.md # Story book (append-only turn archive) ├── journal.md # TODO / DONE tracking ├── ambience.md # Current ambience name ├── ambience_options.md # Ambience → file mapping ├── ambience_sources.md # Track source URLs ├── tweaks.md # House rules log ├── audio/ # Music files └── log/ # Session logs by date ``` ## How It Works ### Tools | Tool | Role | |------|------| | `tools/engine.py` | Game engine. Owns the LLM interaction, prompt assembly, response parsing, and state persistence. Can be used standalone from the CLI for debugging. | | `tools/run.py` | TUI (Textual app). Owns the game loop: display narrative → get player input → call engine → display result. | ### The Game Loop (run.py) 1. **Mount**: Load engine, build system prompt (rules + character + world + log). 2. **Scene**: Call `engine.generate()` → receive narrative + choices. 3. **Display**: Show narrative in main pane, render choice buttons. 4. **Input**: Player clicks a choice or types free text, presses Enter. 5. **Resolve**: Call `engine.generate(player_action)` → receive outcome + state changes. 6. **Archive**: Append the full turn (scene + action + outcome) to `book.md`. 7. **Apply**: Write state changes to `character.md`, `world.md`, `log/`, `ambience.md`, `journal.md`. 8. **Loop**: Display the next scene → go to step 3. ### The Engine (engine.py) - `GameEngine` class loads config from `session/config.json`. - `build_system_prompt()` assembles the DM prompt from game rules + current state. - `build_user_message()` builds the per-turn message with player action context. - `generate()` calls litellm, returns parsed `GenerationResult`. - `parse_response()` extracts the JSON block from the LLM response. - `apply_state()` writes state changes to session files. - `archive_turn()` appends the narrative to `book.md`. ### LLM Output Format The LLM must end every response with a JSON fenced code block: ```json { "choices": ["Choice 1", "Choice 2"], "log_entry": "- **time** — description.", "ambience": "ambience_name_or_null", "character_updates": null, "world_updates": null, "journal_add": [], "journal_done": [] } ``` - `choices`: 2-4 action options for the player. - `log_entry`: Single-line summary appended to today's log. - `ambience`: One of: silence, calm, combat, dungeon, forest, tavern, tension, town, wilds. - `character_updates`: Full character sheet markdown only if HP/cash/gear/stats changed. - `world_updates`: Full world markdown only if NPCs/locations/threads changed. - `journal_add` / `journal_done`: TODO list management. ### Session Config ```json { "llm": { "model": "ollama/llama3.1", "api_key": null, "api_base": null, "temperature": 0.8 } } ``` The `model` field accepts any litellm provider string: `openai/gpt-4`, `anthropic/claude-sonnet-4-20250514`, `ollama/llama3.1`, `groq/llama3-70b-8192`, etc. Set `api_key` for remote providers. ## Files Still Used By Tools | File | Purpose | Written By | |------|---------|------------| | `session/config.json` | LLM provider config | Manual edit | | `session/character.md` | PC state | engine.py | | `session/world.md` | Realm state | engine.py | | `session/book.md` | Story archive | engine.py | | `session/journal.md` | TODO/DONE | engine.py | | `session/ambience.md` | Current ambience | engine.py | | `session/log/.md` | Session log | engine.py | | `session/tweaks.md` | House rules | Manual edit | ## Running ```bash # Start the game ./run.sh # Or directly python3 tools/run.py # No music python3 tools/run.py --no-music # Test a generation from CLI (no TUI) python3 tools/engine.py --action "I head to the market" ```