AI-powered autonomous player for Cataclysm: Dark Days Ahead
CDDAi is an autonomous AI agent that plays the roguelike survival game CDDA. It combines hybrid data capture (ASCII terminal screen + JSON game state parsing) with LLM-powered decision-making for goal-directed gameplay.
- Hybrid Perception: Captures both ASCII terminal output and parses JSON save files for complete game state awareness
- Goal-Directed Gameplay: Set high-level objectives like "survive 3 days" and watch the AI work towards them
- Decision Logging: Every action is logged with reasoning, latency metrics, and game state snapshots
- Multi-Provider Support: Works with OpenRouter, Anthropic API, or file-based mode for testing
- Extensible Knowledge Base: Add domain knowledge to improve decision quality
-
Clone and setup Python environment:
git clone <repository-url> cd CDDAi python3 -m venv .venv source .venv/bin/activate pip install --upgrade pip pip install -r requirements.txt
-
Setup CDDA (one command):
python scripts/setup_cdda.py
This script will:
- Detect your platform (macOS/Linux)
- Find existing CDDA installation or provide download instructions
- Copy CDDA to project-local
cdda/directory - Validate the installation
-
Verify setup:
python scripts/validate_cdda.py pytest
-
Install the CLI:
pip install -e .
CDDAi provides a command-line interface for running autonomous gameplay sessions with goal tracking and decision logging.
Get help:
cddai --help
cddai agent --help
cddai agent start --helpRun a 3-day survival session:
cddai agent start --goal "survive 3 days"Run with custom settings:
cddai agent start \
--goal "survive 3 days" \
--max-turns 500 \
--cdda-path /path/to/cataclysm \
--data-path /path/to/cdda/data \
--log-dir data/logs \
--kb-path data/knowledge| Option | Default | Description |
|---|---|---|
--goal |
required | High-level objective (e.g., "survive 3 days", "find vehicle") |
--max-turns |
1000 | Maximum turns before stopping |
--cdda-path |
auto-detected | Path to CDDA executable |
--data-path |
auto-detected | Path to CDDA data directory |
--log-dir |
data/logs | Directory for decision logs |
--kb-path |
data/knowledge | Path to knowledge base directory |
During execution, the CLI displays real-time progress:
[Turn 001] [14:23:15] [Action: e] (EXECUTED) Check water source
├─ State: Health 100 | Thirst Thirsty | Hunger Hungry | Location Shelter
├─ Latency: 350 ms
└─ Progress: Day 0.01 / 3.00 (0% complete)
At the end, a summary is displayed:
============================================================
SESSION SUMMARY
============================================================
Turns executed: 287
Average latency: 342.5 ms
Final state: Health 85, Thirst Hydrated, Hunger Satisfied, Location Forest
Goal progress: Day 3.02 / 3.00 (100% complete)
Log file: data/logs/session_20251012_143215.jsonl
Decision logs are saved in JSONL format (one JSON object per line) for easy streaming and analysis.
Example log entry:
{
"turn": 1,
"timestamp": "2025-10-12T14:23:15.123456",
"execution_time_ms": 350,
"action": "e",
"reasoning": "Check water source to address thirst",
"context_tags": ["thirst_mechanics", "water_sources"],
"game_time_minutes": 60,
"state_snapshot": {
"health": 100,
"thirst": "Thirsty",
"hunger": "Hungry",
"fatigue": "Rested",
"morale": 10,
"location": "Shelter",
"visible_items": ["water"],
"visible_threats": []
}
}Read logs line-by-line with streaming:
cat data/logs/session_20251012_143215.jsonl | jq '.'Extract specific fields:
cat data/logs/session_*.jsonl | jq '.action'Count decisions by action:
cat data/logs/session_*.jsonl | jq -r '.action' | sort | uniq -cCLI not found after installation:
pip install -e .
# Or use hash -r to refresh shell command cacheCDDA executable not detected:
- Use
--cdda-pathto specify explicit path - Ensure CDDA is installed (run
python scripts/setup_cdda.py)
Session fails to start:
- Check that CDDA data directory is accessible
- Verify knowledge base directory exists or will be created
- Review logs for permission errors
Ctrl+C handling:
- Sessions can be interrupted with Ctrl+C
- Decision logs are preserved up to the interruption point
- Final summary will still be displayed
CDDAi/
├── cdda/ # CDDA distribution (created by setup script)
├── src/cddai/ # Main package
│ ├── config.py # Configuration (CDDA paths, defaults)
│ ├── execution/ # Terminal control, session management
│ ├── perception/ # Screen capture, JSON parsing
│ └── models/ # Data models
├── scripts/ # Setup and validation scripts
├── tests/ # Test suite
└── docs/ # Documentation and stories
- Ensure Python 3.11 or newer is installed (
python3 --version). - Create a virtual environment in the repository root:
python3 -m venv .venv
- Activate the virtual environment:
source .venv/bin/activate - Upgrade
pipand install dependencies:pip install --upgrade pip pip install -r requirements.txt
The requirements.txt file pins pexpect==4.9.0 for process control and pytest==7.4.3 for tests that will be introduced in later stories.
After activating the virtual environment, run the test suite with:
pytestFuture stories will add the implementation and tests referenced in this guide.
The project includes a lightweight coverage helper powered by Python's built-in trace module. Run it from the repository root to verify Acceptance Criterion 9:
python tools/run_coverage.pyThe script executes pytest, reports per-file coverage for src/cddai/, and enforces an 80% minimum threshold. Adjust --pytest-args to forward additional options to pytest if needed.
After running python scripts/setup_cdda.py, all components will automatically use the project-local CDDA installation:
from cddai.execution.terminal_controller import TerminalController
from cddai.execution.session_manager import SessionManager
from cddai.perception import JSONReader
# No path needed - uses config default
controller = TerminalController()
session = SessionManager()
reader = JSONReader()Override the default CDDA location using environment variable:
export CDDA_PATH="/custom/path/to/Cataclysm.app"Or pass explicit path to components:
from pathlib import Path
controller = TerminalController(Path("/custom/path/to/cataclysm"))The controller validates that the file exists and is executable before attempting to spawn the process.
The perception layer introduces a ScreenCapture component that waits for terminal output to stabilize before returning the buffer. Inject an active TerminalController and call capture_screen() whenever you need the latest ASCII snapshot:
raw_buffer = capture.capture_screen()The method logs at DEBUG when a stable screen is captured and warns at WARNING if the output flaps beyond the 500 ms timeout. When no new output is available, it returns the last successfully captured frame to keep downstream parsing resilient.
Use ScreenCapture.strip_ansi_codes() to remove color/style escape sequences prior to parsing:
clean_buffer = capture.strip_ansi_codes(raw_buffer)The helper relies on the regex pattern \033\[[0-9;]*m, covering common CDDA color encodings and safely returning plain text when no escape codes are present.
JSONReader provides structured access to CDDA save files, item definitions, and crafting recipes. Instantiate it with the root of the CDDA data directory—the same location that contains the save/ folder and data/json/ tree:
from pathlib import Path
from cddai.perception import JSONReader
reader = JSONReader(Path("/Applications/Cataclysm.app/Contents/Resources/data"))
player_state = reader.read_save_file("default")
if player_state:
print(player_state["health"]["torso"])
water = reader.get_item_definition("water_clean")
stone_recipe = reader.get_recipe("stone_knife")Core behaviors:
- Automatically scans
save/and its world subdirectories for{character}.sav, selecting the most recently updated file when duplicates exist. - Save files are treated as dynamic data and re-read on every call. Missing saves return
{}with a warning, while JSON decoding issues raiseValueErrorthat includes the filename for rapid debugging. - Item and recipe lookups are cached after the first hit to avoid repeatedly traversing the
data/jsontree. Subsequent calls are returned immediately from memory. - The reader tolerates both list-based and single-object JSON layouts (a common CDDA quirk) and skips any corrupted definition files with a warning, continuing the search.
- Recipes are matched on their
resultfield per CDDA conventions rather thanid. - Missing save fields fall back to safe defaults (e.g., vitals reset to healthy values, counters default to zero) so downstream consumers always receive a complete payload.
.gitignoreexcludes virtual environment directories, Python cache files, IDE metadata, and common build artifacts.- Documentation for story context lives under
docs/. Story execution updates the relevant markdown files in that directory.
This project is licensed under the MIT License (see LICENSE file).
This project uses Cataclysm: Dark Days Ahead (CDDA), which is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License (CC BY-SA 3.0).
- Project: Cataclysm: Dark Days Ahead
- License: CC BY-SA 3.0
- Source: https://github.com/CleverRaven/Cataclysm-DDA
- Website: https://cataclysmdda.org/
- License Details: https://creativecommons.org/licenses/by-sa/3.0/
Attribution: Cataclysm: Dark Days Ahead is developed by the CDDA community and CleverRaven. CDDAi uses CDDA's game executable and data files for automated gameplay testing and AI agent development.
License Requirements: Under CC BY-SA 3.0, any modifications or derivatives of CDDA content must also be shared under the same license. CDDAi itself is a separate project that interacts with CDDA but does not modify CDDA's game content.
See THIRD_PARTY_LICENSES.md for complete license text and additional third-party components.