|
2 | 2 |
|
3 | 3 | This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
4 | 4 |
|
5 | | -## Project Overview |
| 5 | +## Overview |
6 | 6 |
|
7 | | -BalatroLLM is an LLM-powered bot that plays Balatro (a roguelike poker deck-building game) using the BalatroBot client. The bot uses OpenAI-compatible APIs to communicate with various LLM providers and makes strategic decisions based on game state analysis. |
| 7 | +BalatroLLM is an LLM-powered bot that plays Balatro using the BalatroBot JSON-RPC API. It communicates with the game via HTTP/JSON-RPC 2.0 and uses OpenAI-compatible APIs (OpenAI, OpenRouter, etc.) for strategic decision making. |
8 | 8 |
|
9 | | -## Development Commands |
10 | | - |
11 | | -### Environment Setup |
12 | | - |
13 | | -```bash |
14 | | -uv sync --all-extras --group dev |
15 | | -cp .envrc.example .envrc |
16 | | -# edit .envrc with your OpenRouter API key |
17 | | -source .envrc |
18 | | -``` |
19 | | - |
20 | | -### Running the Application |
21 | | - |
22 | | -**balatrollm** - Main bot CLI: |
23 | | - |
24 | | -``` |
25 | | -usage: balatrollm [-h] [-m MODEL] [-l] [-s STRATEGY] [-u BASE_URL] |
26 | | - [-k API_KEY] [-d RUNS_DIR] [-r RUNS_PER_SEED] |
27 | | - [--seeds SEEDS] [-p PORTS] [--no-screenshot] |
28 | | - [--use-default-paths] |
29 | | -
|
30 | | -LLM-powered Balatro bot |
31 | | -
|
32 | | -options: |
33 | | - -h, --help show this help message and exit |
34 | | - -m, --model MODEL Model name to use from OpenAI-compatible API (required, uses BALATROLLM_MODEL env var if set) |
35 | | - -l, --list-models List available models from OpenAI-compatible API and exit |
36 | | - -s, --strategy STRATEGY |
37 | | - Name of the strategy to use (default: default) |
38 | | - -u, --base-url BASE_URL |
39 | | - OpenAI-compatible API base URL (required, uses BALATROLLM_BASE_URL env var if set) |
40 | | - -k, --api-key API_KEY |
41 | | - API key (default: BALATROLLM_API_KEY env var) |
42 | | - -d, --runs-dir RUNS_DIR |
43 | | - Base directory for storing run data (default: current directory) |
44 | | - -r, --runs-per-seed RUNS_PER_SEED |
45 | | - Number of runs per seed (default: 1) |
46 | | - --seeds SEEDS Comma-separated list of seeds (e.g., AAAA123,BBBB456,CCCC789) |
47 | | - -p, --ports PORTS Comma-separated list of ports for BalatroBot client connections (default: 12346, e.g., 12346,12347,12348) |
48 | | - --no-screenshot Disable taking screenshots during gameplay |
49 | | - --use-default-paths Use BalatroBot's default storage paths for screenshots and game logs |
50 | | -``` |
51 | | - |
52 | | -**balatrobench** - Benchmark analysis CLI: |
53 | | - |
54 | | -``` |
55 | | -usage: balatrobench [-h] (--models | --strategies) [--input-dir INPUT_DIR] |
56 | | - [--output-dir OUTPUT_DIR] [--avif] |
57 | | -
|
58 | | -Analyze BalatroLLM runs and generate benchmark leaderboards |
59 | | -
|
60 | | -options: |
61 | | - -h, --help show this help message and exit |
62 | | - --models Analyze by models (compare models within strategies) |
63 | | - --strategies Analyze by strategies (compare strategies for each model) |
64 | | - --input-dir INPUT_DIR |
65 | | - Input directory with run data (default: runs/v{version}) |
66 | | - --output-dir OUTPUT_DIR |
67 | | - Output directory for benchmark results (default: benchmarks/[models|strategies]/v{version}) |
68 | | - --avif Convert PNG screenshots to AVIF format after analysis |
69 | | -``` |
70 | | - |
71 | | -### Development |
72 | | - |
73 | | -``` |
74 | | -BalatroLLM Development Makefile |
75 | | -
|
76 | | -Available targets: |
77 | | - help Show this help message |
78 | | - install Install dependencies |
79 | | - lint Run ruff linter with auto-fixes |
80 | | - format Run ruff formatter |
81 | | - typecheck Run type checker |
82 | | - quality Run all code quality checks |
83 | | - setup Kill previous instances and start Balatro (INSTANCES=1) |
84 | | - teardown Stop Balatro processes |
85 | | -``` |
86 | | - |
87 | | -### Game Automation |
88 | | - |
89 | | -``` |
90 | | -Usage: ./balatro.sh [OPTIONS] |
91 | | - ./balatro.sh -p PORT [OPTIONS] |
92 | | - ./balatro.sh --kill |
93 | | - ./balatro.sh --status |
94 | | -
|
95 | | -Options: |
96 | | - -p, --port PORT Specify port for Balatro instance (can be used multiple times) |
97 | | - Default: 12346 if no port specified |
98 | | - --headless Enable headless mode (sets BALATROBOT_HEADLESS=1) |
99 | | - --fast Enable fast mode (sets BALATROBOT_FAST=1) |
100 | | - --audio Enable audio (disabled by default, sets BALATROBOT_AUDIO=1) |
101 | | - --kill Kill all running Balatro instances and exit |
102 | | - --status Show information about running Balatro instances |
103 | | - -h, --help Show this help message |
104 | | -
|
105 | | -Examples: |
106 | | - ./balatro.sh # Start single instance on default port 12346 |
107 | | - ./balatro.sh -p 12347 # Start single instance on port 12347 |
108 | | - ./balatro.sh -p 12346 -p 12347 # Start two instances on ports 12346 and 12347 |
109 | | - ./balatro.sh --headless --fast # Start with headless and fast mode on default port |
110 | | - ./balatro.sh --audio # Start with audio enabled on default port |
111 | | - ./balatro.sh --kill # Kill all running Balatro instances |
112 | | - ./balatro.sh --status # Show running instances |
113 | | -``` |
114 | 9 |
|
115 | 10 | ## Architecture |
116 | 11 |
|
117 | | -**LLMBot (`src/balatrollm/bot.py`)**: Main bot class with clean architecture featuring Config integration, StrategyManager for template rendering, LLM decision-making with retry logic, response history tracking, and BalatroClient integration. |
118 | | - |
119 | | -**CLI Entry Point (`src/balatrollm/__init__.py`)**: Simplified command-line interface with argument parsing and async game execution using modern Python patterns. |
120 | | - |
121 | | -**Configuration (`src/balatrollm/config.py`)**: Config dataclass with metadata fields (name, description, author, tags) for enhanced run tracking and version management. |
122 | | - |
123 | | -**Strategy System (`src/balatrollm/strategies.py`)**: Lightweight StrategyManager class for managing Jinja2-based strategy templates with built-in strategy support only. |
124 | | - |
125 | | -**Data Collection (`src/balatrollm/data_collection.py`)**: RunStatsCollector for structured game execution logging, comprehensive performance tracking, and organized run data storage. |
126 | | - |
127 | | -**Benchmarking (`src/balatrollm/benchmark.py`)**: Comprehensive benchmark analysis system with aggregated statistics, leaderboard generation, and hierarchical result organization. |
128 | | - |
129 | | -**Strategies (`src/balatrollm/strategies/`)**: Strategy-based organization: |
130 | | - |
131 | | -- `default/`: Conservative strategy (financial discipline) |
132 | | -- `aggressive/`: High-risk, high-reward strategy |
133 | | - |
134 | | -Each strategy contains: |
135 | | - |
136 | | -- `STRATEGY.md.jinja`: Strategy-specific guide |
137 | | -- `GAMESTATE.md.jinja`: Game state representation |
138 | | -- `MEMORY.md.jinja`: Response history tracking |
139 | | -- `TOOLS.json`: Strategy-specific function definitions |
140 | | - |
141 | | -**Key Dependencies**: balatrobot, openai, jinja2, httpx |
142 | | - |
143 | | -**Game Flow**: |
144 | | - |
145 | | -1. Initialize game with run directory and data collection setup |
146 | | -2. Main game loop: Get state → Render strategy templates → Send to LLM with retry logic → Parse response → Execute action |
147 | | -3. Handle game states: BLIND_SELECT, SELECTING_HAND, SHOP, ROUND_EVAL, GAME_OVER |
148 | | -4. Collect comprehensive statistics and generate run reports |
149 | | - |
150 | | -**Available Models** (via OpenRouter): |
151 | | - |
152 | | -- **OpenAI**: openai/gpt-oss-20b (default), openai/gpt-oss-120b |
153 | | -- **Qwen**: qwen/qwen3-235b-a22b-thinking-2507, qwen/qwen3-235b-a22b-2507 |
154 | | -- **X-AI**: x-ai/grok-code-fast-1 |
155 | | -- **Google**: google/gemini-2.5-flash |
156 | | - |
157 | | -**API Key**: |
158 | | - |
159 | | -- `OPENROUTER_API_KEY` (provides access to all providers) |
160 | | - |
161 | | -## Code Quality |
162 | | - |
163 | | -Uses Ruff (linting/formatting), basedpyright (type checking), pytest (testing), conventional commits, and Release Please automation. |
164 | | - |
165 | | -**Modern Type Hints**: Use built-in types for Python 3.9+ instead of typing module equivalents: |
166 | | - |
167 | | -- Use `dict` instead of `Dict` |
168 | | -- Use `list` instead of `List` |
169 | | -- Use `tuple` instead of `Tuple` |
170 | | -- Use `set` instead of `Set` |
171 | | -- Use `str | None` instead of `Optional[str]` |
172 | | -- Use `int | str` instead of `Union[int, str]` |
173 | | - |
174 | | -## Project Structure |
175 | | - |
176 | | -``` |
177 | | -src/balatrollm/ |
178 | | -├── __init__.py # CLI entry point with argument parsing |
179 | | -├── bot.py # Main LLMBot class with game logic |
180 | | -├── config.py # Configuration dataclass |
181 | | -├── strategies.py # StrategyManager for Jinja2 templates |
182 | | -├── data_collection.py # RunDataCollector for game logging |
183 | | -└── strategies/ # Strategy-based templates |
184 | | - ├── default/ # Conservative strategy |
185 | | - └── aggressive/ # High-risk strategy |
186 | | - ├── manifest.json # Strategy metadata |
187 | | - ├── STRATEGY.md.jinja # Strategy guide |
188 | | - ├── GAMESTATE.md.jinja # Game state representation |
189 | | - ├── MEMORY.md.jinja # Response history |
190 | | - └── TOOLS.json # Function definitions |
191 | | -
|
192 | | -balatro.sh # Game automation script |
193 | | -runs/ # Game execution logs (organized by version/strategy/provider/model) |
194 | | -benchmarks/ # Benchmark results (organized by version/strategy/provider/model) |
195 | | -tests/test_llm.py # Test suite |
196 | | -``` |
197 | | - |
198 | | -## Context7 Library IDs |
199 | | - |
200 | | -**Core**: `/coder/balatrobot`, `/pallets/jinja`, `/openai/openai-python`, `/encode/httpx` |
201 | | -**Dev**: `/detachhead/basedpyright`, `/pytest-dev/pytest`, `/pytest-dev/pytest-asyncio`, `/astral-sh/ruff`, `/astral-sh/uv` |
202 | | - |
203 | | -## Results Tracking |
204 | | - |
205 | | -**Run Data Structure:** |
206 | | - |
207 | | -- `runs/v{version}/{strategy}/{vendor}/{model}/{timestamp}_{deck}_s{stake}_{seed}/` |
208 | | -- Each run directory contains: config.json, strategy.json, stats.json, gamestates.jsonl, requests.jsonl, responses.jsonl, run.log, screenshots/ |
209 | | -- Strategy-first organization enables easy comparison across vendors/models within strategies |
210 | | - |
211 | | -**Benchmark Results Structure (Dual Modes):** |
212 | | - |
213 | | -Benchmarks can be generated in two modes using the `balatrobench` CLI: |
214 | | - |
215 | | -**By Models Mode** (`--models`): |
216 | | -- `benchmarks/models/v{version}/{strategy}/leaderboard.json` - Models ranked within each strategy |
217 | | -- `benchmarks/models/v{version}/{vendor}/{model}/{strategy}/stats.json` - Detailed model stats per strategy |
218 | | -- Compare different models within each strategy |
219 | | - |
220 | | -**By Strategies Mode** (`--strategies`): |
221 | | -- `benchmarks/strategies/v{version}/{strategy}/leaderboard.json` - Models ranked within strategy |
222 | | -- `benchmarks/strategies/v{version}/{vendor}/{model}.json` - Model stats across strategies |
223 | | -- Compare different strategies for each model |
224 | | - |
225 | | -## Strategy System |
| 12 | +### Core Components |
226 | 13 |
|
227 | | -The `--strategy` flag accepts built-in strategy names: |
| 14 | +1. **Client** (`src/balatrollm/client.py`): |
| 15 | + - JSON-RPC 2.0 over HTTP client using `httpx`. |
| 16 | + - Communicates with BalatroBot (Lua) on port 12346 request/response. |
| 17 | + - Handles `BalatroError` exceptions. |
228 | 18 |
|
229 | | -**Built-in Strategies**: |
| 19 | +2. **Bot** (`src/balatrollm/bot.py`): |
| 20 | + - Main game loop implementation. |
| 21 | + - Flow: Fetch state -> Render Strategy -> Query LLM -> Execute Action. |
| 22 | + - Manages game lifecycle (start, play, game over). |
230 | 23 |
|
231 | | -- **Default** (`--strategy default`): Conservative, financially disciplined approach |
232 | | -- **Aggressive** (`--strategy aggressive`): High-risk, high-reward approach with aggressive spending |
| 24 | +3. **Strategies** (`src/balatrollm/strategies/`): |
| 25 | + - Template-based prompt generation system. |
| 26 | + - `GAMESTATE.md.jinja`: Renders current game state for LLM. |
| 27 | + - `STRATEGY.md.jinja`: High-level strategic guidance. |
| 28 | + - `TOOLS.json`: JSON-RPC tool definitions exposed to the LLM. |
233 | 29 |
|
234 | | -Each strategy directory must contain **5 required files**: |
| 30 | +### JSON-RPC Protocol |
235 | 31 |
|
236 | | -- `manifest.json`: Strategy metadata (name, description, author, version, tags) |
237 | | -- `STRATEGY.md.jinja`: Strategy-specific guide |
238 | | -- `GAMESTATE.md.jinja`: Game state representation |
239 | | -- `MEMORY.md.jinja`: Response history tracking |
240 | | -- `TOOLS.json`: Strategy-specific function definitions |
| 32 | +- **Transport**: HTTP POST to `http://127.0.0.1:12346` |
| 33 | +- **Format**: `{"jsonrpc": "2.0", "method": "...", "params": {...}, "id": 1}` |
| 34 | +- **Key Endpoints**: |
| 35 | + - `start`: Start a new run (params: `deck`, `stake`, `seed`). |
| 36 | + - `gamestate`: specific get state call. |
| 37 | + - `play` / `discard`: Action on selected cards. |
| 38 | + - `buy` / `sell` / `use`: Shop and consumable actions. |
| 39 | + - `select` / `skip`: Blind interactions. |
241 | 40 |
|
242 | | -**manifest.json Structure**: |
| 41 | +## Key Changes in V1 |
243 | 42 |
|
244 | | -```json |
245 | | -{ |
246 | | - "name": "Default", |
247 | | - "description": "Conservative, financially disciplined approach to Balatro", |
248 | | - "author": "BalatroBench", |
249 | | - "version": "0.1.0", |
250 | | - "tags": ["conservative", "financial"] |
251 | | -} |
252 | | -``` |
| 43 | +- **State Enum**: Game states are now string enums (e.g., `SELECTING_HAND`, `SHOP`, `GAME_OVER`) instead of integers. |
| 44 | +- **Card Structure**: 0-based indexing. Structure is flattened with short codes (e.g., Suit "H", Rank "A", Edition "FOIL"). |
| 45 | +- **Split Actions**: Complex actions are split (e.g., `shop` -> `buy`, `reroll`, `next_round`). |
253 | 46 |
|
254 | | -All 5 fields are required. Strategy versions are independent from BalatroLLM versions. |
| 47 | +## Key Files |
255 | 48 |
|
256 | | -For comprehensive strategy documentation including Jinja2 templates, validation, and contribution guidelines, see [docs/strategies.md](https://s1m0n38.github.io/balatrollm/strategies/). |
| 49 | +- `src/balatrollm/client.py`: JSON-RPC client implementation. |
| 50 | +- `src/balatrollm/bot.py`: Main bot logic. |
| 51 | +- `src/balatrollm/data_collection.py`: Run logging and stats. |
| 52 | +- `balatro.sh`: Helper script to manage Balatro game instances. |
| 53 | +- `runs/`: Directory where game execution logs and artifacts are stored. |
0 commit comments