|
2 | 2 |
|
3 | 3 | This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
4 | 4 |
|
| 5 | +**GitHub Repository**: [`s1m0n38/balatrollm`](https://github.com/s1m0n38/balatrollm) |
| 6 | + |
5 | 7 | ## Overview |
6 | 8 |
|
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. |
| 9 | +BalatroLLM is an LLM-powered bot that autonomously plays Balatro using strategic decision-making. It uses the BalatroBot API (a Lua mod exposing JSON-RPC 2.0 endpoints) to control the game while leveraging large language models for gameplay decisions. |
| 10 | + |
| 11 | +**Key capabilities:** |
| 12 | +- Autonomous gameplay from start to completion |
| 13 | +- LLM-driven strategic decision-making using OpenAI function calling |
| 14 | +- Configurable strategies via Jinja2 templates |
| 15 | +- Parallel execution of multiple game instances |
| 16 | +- Comprehensive data collection for analysis and benchmarking |
| 17 | + |
| 18 | +### Make Commands |
| 19 | + |
| 20 | +Available make targets: |
| 21 | + |
| 22 | +| Target | Description | |
| 23 | +| ---------------- | ------------------------------------------------------- | |
| 24 | +| `make help` | Show all available targets | |
| 25 | +| `make lint` | Run ruff linter (check only) | |
| 26 | +| `make format` | Run ruff and mdformat formatters | |
| 27 | +| `make typecheck` | Run type checker | |
| 28 | +| `make quality` | Run all code quality checks (lint + typecheck + format) | |
| 29 | +| `make all` | Run all quality checks | |
| 30 | +| `make install` | Install dependencies | |
| 31 | + |
| 32 | +**Important rules:** |
| 33 | + |
| 34 | +1. **Only run make commands when explicitly asked.** Do not proactively run `make quality`, `make lint`, etc. |
| 35 | +2. **Never run bare linting/formatting/typechecking tools.** Always use make targets instead: |
| 36 | + - Use `make lint` instead of `ruff check` |
| 37 | + - Use `make format` instead of `ruff format` |
| 38 | + - Use `make typecheck` instead of `ty check` |
| 39 | + - Use `make quality` for all checks combined |
8 | 40 |
|
9 | 41 | ## Architecture |
10 | 42 |
|
11 | | -### Core Components |
| 43 | +### Python Components (`src/balatrollm/`) |
| 44 | + |
| 45 | +The bot is structured as a pipeline: CLI → Executor → Bot → (LLMClient + BalatroClient) → Game. |
| 46 | + |
| 47 | +#### CLI (`cli.py`) |
| 48 | + |
| 49 | +Entry point for the `balatrollm` command. Parses CLI arguments and YAML config files, generates task combinations from game parameters (seed, deck, stake, strategy, model). Config precedence: environment variables < YAML < CLI args. |
| 50 | + |
| 51 | +#### Bot (`bot.py`) |
| 52 | + |
| 53 | +Core game loop with LLM-powered decision-making. Handles game states (SELECTING_HAND, SHOP, ROUND_EVAL, BLIND_SELECT, GAME_OVER). Each decision: sends game state + strategy + memory to LLM → receives tool call → executes via BalatroClient. Tracks consecutive failures (max 3) and logs all data via Collector. |
| 54 | + |
| 55 | +#### Client (`client.py`) |
| 56 | + |
| 57 | +JSON-RPC 2.0 HTTP client for BalatroBot mod. Async client to `http://127.0.0.1:12346` (default 30s timeout). Methods: `gamestate`, `start`, `select`, `cash_out`, `play`, `buy`, `sell`, `use`, `screenshot`, etc. |
| 58 | + |
| 59 | +#### LLMClient (`llm.py`) |
| 60 | + |
| 61 | +Async OpenAI client wrapper with retry logic. Provider-agnostic (works with OpenAI, OpenRouter, any compatible API). Default: OpenRouter. Exponential backoff (max 3 retries), aborts after 3 consecutive timeouts. Default timeout: 240s. |
| 62 | + |
| 63 | +#### StrategyManager (`strategy.py`) |
| 64 | + |
| 65 | +Manages Jinja2 strategy templates. Each strategy folder contains: `STRATEGY.md.jinja` (game rules/tactics), `GAMESTATE.md.jinja` (current state), `MEMORY.md.jinja` (action history), `TOOLS.json` (function calling tools), `manifest.json` (metadata). Current: `default` (conservative, financially-focused). |
12 | 66 |
|
13 | | -1. **Client** (`src/balatrollm/client.py`): |
| 67 | +#### Config (`config.py`) |
14 | 68 |
|
15 | | - - JSON-RPC 2.0 over HTTP client using `httpx`. |
16 | | - - Communicates with BalatroBot (Lua) on port 12346 request/response. |
17 | | - - Handles `BalatroError` exceptions. |
| 69 | +Multi-source configuration. Precedence: environment variables < YAML < CLI args. Key env vars: `BALATROLLM_API_KEY`, `BALATROLLM_BASE_URL`, `BALATROLLM_MODEL`, `BALATROLLM_SEED/DECK/STAKE`, `BALATROLLM_STRATEGY`, `BALATROLLM_PARALLEL`, `BALATROLLM_HOST/PORT`. |
18 | 70 |
|
19 | | -2. **Bot** (`src/balatrollm/bot.py`): |
| 71 | +#### Executor (`executor.py`) |
20 | 72 |
|
21 | | - - Main game loop implementation. |
22 | | - - Flow: Fetch state -> Render Strategy -> Query LLM -> Execute Action. |
23 | | - - Manages game lifecycle (start, play, game over). |
| 73 | +Orchestrates parallel execution. Manages pool of Balatro instances via `balatrobot` package, assigns consecutive ports (12346, 12347, ...), uses task queue with worker pool pattern. |
24 | 74 |
|
25 | | -3. **Strategies** (`src/balatrollm/strategies/`): |
| 75 | +#### Collector (`collector.py`) |
26 | 76 |
|
27 | | - - Template-based prompt generation system. |
28 | | - - `GAMESTATE.md.jinja`: Renders current game state for LLM. |
29 | | - - `STRATEGY.md.jinja`: High-level strategic guidance. |
30 | | - - `TOOLS.json`: JSON-RPC tool definitions exposed to the LLM. |
| 77 | +Data collection and statistics. Output: `runs/v1.0.0/{strategy}/{vendor}/{model}/{timestamp}_{deck}_{stake}_{seed}/`. JSONL format, OpenAI Batch API compatible. |
31 | 78 |
|
32 | | -### JSON-RPC Protocol |
| 79 | +### Strategy System |
33 | 80 |
|
34 | | -- **Transport**: HTTP POST to `http://127.0.0.1:12346` |
35 | | -- **Format**: `{"jsonrpc": "2.0", "method": "...", "params": {...}, "id": 1}` |
36 | | -- **Key Endpoints**: |
37 | | - - `start`: Start a new run (params: `deck`, `stake`, `seed`). |
38 | | - - `gamestate`: specific get state call. |
39 | | - - `play` / `discard`: Action on selected cards. |
40 | | - - `buy` / `sell` / `use`: Shop and consumable actions. |
41 | | - - `select` / `skip`: Blind interactions. |
| 81 | +Template-based system using Jinja2. Templates render strategy guidance, game state, and action history into LLM prompts. At each decision point: render templates → send to LLM with tools → execute tool call. Custom strategies: create folder in `src/balatrollm/strategies/` with required template files. |
42 | 82 |
|
43 | | -## Key Changes in V1 |
| 83 | +### Relationship with BalatroBot |
44 | 84 |
|
45 | | -- **State Enum**: Game states are now string enums (e.g., `SELECTING_HAND`, `SHOP`, `GAME_OVER`) instead of integers. |
46 | | -- **Card Structure**: 0-based indexing. Structure is flattened with short codes (e.g., Suit "H", Rank "A", Edition "FOIL"). |
47 | | -- **Split Actions**: Complex actions are split (e.g., `shop` -> `buy`, `reroll`, `next_round`). |
| 85 | +**BalatroBot** is a Lua mod that runs inside the Balatro game and exposes a JSON-RPC 2.0 HTTP API for programmatic control. |
| 86 | + |
| 87 | +**How BalatroLLM uses BalatroBot:** |
| 88 | + |
| 89 | +``` |
| 90 | +┌─────────────────────────────────────────┐ |
| 91 | +│ BalatroLLM (Python) │ |
| 92 | +│ │ |
| 93 | +│ ┌────────┐ ┌──────────────────┐ │ |
| 94 | +│ │ Bot │──────│ LLMClient │ │◄──── OpenAI API |
| 95 | +│ └───┬────┘ └──────────────────┘ │ (OpenRouter, etc.) |
| 96 | +│ │ │ |
| 97 | +│ ┌───▼────────────────────────────┐ │ |
| 98 | +│ │ BalatroClient │ │ |
| 99 | +│ │ (JSON-RPC 2.0 HTTP Client) │ │ |
| 100 | +│ └───┬────────────────────────────┘ │ |
| 101 | +└──────┼──────────────────────────────────┘ |
| 102 | + │ HTTP POST |
| 103 | + │ JSON-RPC 2.0 |
| 104 | +┌──────▼──────────────────────────────────┐ |
| 105 | +│ BalatroBot (Lua Mod) │ |
| 106 | +│ JSON-RPC 2.0 HTTP API Server │ |
| 107 | +└──────┬──────────────────────────────────┘ |
| 108 | + │ |
| 109 | +┌──────▼──────────────────────────────────┐ |
| 110 | +│ Balatro Game │ |
| 111 | +│ (Love2D) │ |
| 112 | +└─────────────────────────────────────────┘ |
| 113 | +``` |
| 114 | + |
| 115 | +**Integration**: BalatroLLM imports `BalatroInstance` from `balatrobot` to manage game instances. `BalatroClient` sends JSON-RPC requests to BalatroBot's HTTP server (default port 12346). |
| 116 | + |
| 117 | +## Data Collection & Output Structure |
| 118 | + |
| 119 | +Output directory: `runs/v1.0.0/{strategy}/{vendor}/{model}/{timestamp}_{deck}_{stake}_{seed}/` |
| 120 | + |
| 121 | +**Files generated:** |
| 122 | +- `requests.jsonl` - LLM requests (OpenAI Batch API format) |
| 123 | +- `responses.jsonl` - LLM responses with timing/token data |
| 124 | +- `gamestates.jsonl` - Game state snapshots after each action |
| 125 | +- `screenshots/` - PNG screenshots at each decision point |
| 126 | +- `task.json` - Run configuration |
| 127 | +- `strategy.json` - Strategy metadata |
| 128 | +- `stats.json` - Aggregated statistics (outcome, tokens, timing, cost, provider distribution) |
48 | 129 |
|
49 | 130 | ## Key Files |
50 | 131 |
|
51 | | -- `src/balatrollm/client.py`: JSON-RPC client implementation. |
52 | | -- `src/balatrollm/bot.py`: Main bot logic. |
53 | | -- `src/balatrollm/data_collection.py`: Run logging and stats. |
54 | | -- `balatro.sh`: Helper script to manage Balatro game instances. |
55 | | -- `runs/`: Directory where game execution logs and artifacts are stored. |
| 132 | +### Python |
| 133 | + |
| 134 | +| File | Purpose | |
| 135 | +|------|---------| |
| 136 | +| `src/balatrollm/cli.py` | CLI entry point and argument parsing | |
| 137 | +| `src/balatrollm/bot.py` | Core game loop and LLM integration | |
| 138 | +| `src/balatrollm/client.py` | BalatroBot JSON-RPC client | |
| 139 | +| `src/balatrollm/llm.py` | OpenAI client wrapper with retry logic | |
| 140 | +| `src/balatrollm/strategy.py` | Strategy template management | |
| 141 | +| `src/balatrollm/config.py` | Multi-source configuration management | |
| 142 | +| `src/balatrollm/executor.py` | Parallel execution orchestration | |
| 143 | +| `src/balatrollm/collector.py` | Data collection and statistics | |
| 144 | + |
| 145 | +### Configuration |
| 146 | + |
| 147 | +| File | Purpose | |
| 148 | +|------|---------| |
| 149 | +| `pyproject.toml` | Python dependencies and tool configuration | |
| 150 | +| `config/example.yaml` | Example YAML configuration file | |
| 151 | +| `Makefile` | Development commands (lint, format, typecheck) | |
| 152 | + |
| 153 | +### Strategies |
| 154 | + |
| 155 | +| File | Purpose | |
| 156 | +|------|---------| |
| 157 | +| `src/balatrollm/strategies/default/` | Default strategy templates | |
| 158 | + |
| 159 | +## Error Handling |
| 160 | + |
| 161 | +Tracks consecutive failures (max 3 before aborting). Two failure types: **error calls** (invalid LLM response) and **failed calls** (valid tool call but execution failed). LLM retry with exponential backoff (max 3 retries), aborts after 3 consecutive timeouts. Error context included in next prompt's memory for recovery. |
| 162 | + |
| 163 | +## Configuration |
| 164 | + |
| 165 | +Config precedence: environment variables < YAML config file < CLI arguments (highest priority). |
| 166 | + |
| 167 | +**Environment variables** (prefix: `BALATROLLM_`): |
| 168 | +- `API_KEY`, `BASE_URL`, `MODEL` - LLM configuration |
| 169 | +- `SEED`, `DECK`, `STAKE`, `STRATEGY` - Game parameters |
| 170 | +- `PARALLEL`, `HOST`, `PORT` - Execution settings |
| 171 | + |
| 172 | +**Usage**: `balatrollm config/example.yaml --model openai/gpt-4o --seed AAAAAAA --deck RED --stake WHITE` |
0 commit comments