|
| 1 | +# Architecture |
| 2 | + |
| 3 | +This document explains the architecture and design of the Balatrobot system, including how components interact and communicate. |
| 4 | + |
| 5 | +## 📋 Table of Contents |
| 6 | + |
| 7 | +- [System Overview](#system-overview) |
| 8 | +- [Component Architecture](#component-architecture) |
| 9 | +- [Communication Flow](#communication-flow) |
| 10 | +- [Game State Management](#game-state-management) |
| 11 | +- [Action Processing](#action-processing) |
| 12 | +- [Performance Optimizations](#performance-optimizations) |
| 13 | + |
| 14 | +## System Overview |
| 15 | + |
| 16 | +Balatrobot consists of two main components working together: |
| 17 | + |
| 18 | +1. **Lua Mod (Game-side)**: Runs inside Balatro via Steamodded |
| 19 | +2. **Python Bot (External)**: Contains bot logic and decision-making |
| 20 | + |
| 21 | +These components communicate via UDP sockets to achieve real-time bot control. |
| 22 | + |
| 23 | +``` |
| 24 | +┌─────────────────┐ UDP Socket ┌─────────────────┐ |
| 25 | +│ Balatro Game │ ◄──────────────► │ Python Bot │ |
| 26 | +│ (Lua Mod) │ Port 12345+ │ (Bot Logic) │ |
| 27 | +└─────────────────┘ └─────────────────┘ |
| 28 | +``` |
| 29 | + |
| 30 | +## Component Architecture |
| 31 | + |
| 32 | +### Lua Mod Components |
| 33 | + |
| 34 | +The Lua mod consists of several interconnected modules: |
| 35 | + |
| 36 | +``` |
| 37 | +main.lua |
| 38 | +├── config.lua (Configuration) |
| 39 | +├── lib/ (External Libraries) |
| 40 | +│ ├── hook.lua (Function hooking) |
| 41 | +│ ├── sock.lua (UDP networking) |
| 42 | +│ ├── json.lua (JSON serialization) |
| 43 | +│ ├── list.lua (Data structures) |
| 44 | +│ └── bitser.lua (Binary serialization) |
| 45 | +└── src/ (Core Modules) |
| 46 | + ├── api.lua (UDP API server) |
| 47 | + ├── bot.lua (Action definitions) |
| 48 | + ├── middleware.lua (Game hooks) |
| 49 | + ├── botlogger.lua (Logging/replay) |
| 50 | + └── utils.lua (Utility functions) |
| 51 | +``` |
| 52 | + |
| 53 | +#### Key Modules |
| 54 | + |
| 55 | +- **`api.lua`**: UDP server that handles communication with Python bots |
| 56 | +- **`middleware.lua`**: Hooks into game functions to detect decision points |
| 57 | +- **`bot.lua`**: Defines available actions and validation logic |
| 58 | +- **`utils.lua`**: Game state extraction and data formatting |
| 59 | +- **`botlogger.lua`**: Action logging and replay functionality |
| 60 | + |
| 61 | +### Python Bot Components |
| 62 | + |
| 63 | +The Python side provides the bot framework and decision-making logic: |
| 64 | + |
| 65 | +``` |
| 66 | +Bot Framework |
| 67 | +├── bot.py (Base Bot class) |
| 68 | +├── gamestates.py (State caching) |
| 69 | +├── bot_example.py (Simple example) |
| 70 | +└── flush_bot.py (Advanced example) |
| 71 | +``` |
| 72 | + |
| 73 | +#### Base Bot Class |
| 74 | + |
| 75 | +The `Bot` class provides: |
| 76 | +- UDP communication with the game |
| 77 | +- Game state processing |
| 78 | +- Action validation and sending |
| 79 | +- Instance management |
| 80 | + |
| 81 | +## Communication Flow |
| 82 | + |
| 83 | +The communication between components follows this pattern: |
| 84 | + |
| 85 | +### 1. Initialization |
| 86 | + |
| 87 | +``` |
| 88 | +Python Bot Lua Mod |
| 89 | + │ │ |
| 90 | + ├─── HELLO ──────────────► │ |
| 91 | + │ ├─── Game State ───► |
| 92 | + ◄─── Game State ───────────┤ |
| 93 | + │ │ |
| 94 | +``` |
| 95 | + |
| 96 | +### 2. Decision Loop |
| 97 | + |
| 98 | +``` |
| 99 | +Python Bot Lua Mod |
| 100 | + │ │ |
| 101 | + │ ├─── Wait for Action |
| 102 | + │ ├─── Send Game State ──► |
| 103 | + ◄─── Game State ───────────┤ |
| 104 | + ├─── Process State │ |
| 105 | + ├─── Make Decision │ |
| 106 | + ├─── Send Action ────────► │ |
| 107 | + │ ├─── Execute Action |
| 108 | + │ ├─── Update Game |
| 109 | + │ └─── Loop |
| 110 | +``` |
| 111 | + |
| 112 | +### 3. Game State Structure |
| 113 | + |
| 114 | +The game state sent to the bot contains: |
| 115 | + |
| 116 | +```json |
| 117 | +{ |
| 118 | + "waitingFor": "select_cards_from_hand", |
| 119 | + "waitingForAction": true, |
| 120 | + "state": 1, |
| 121 | + "hand": [ |
| 122 | + { |
| 123 | + "label": "base_card", |
| 124 | + "name": "3 of Hearts", |
| 125 | + "suit": "Hearts", |
| 126 | + "value": 3, |
| 127 | + "card_key": "H_3" |
| 128 | + } |
| 129 | + ], |
| 130 | + "jokers": [...], |
| 131 | + "shop": {...}, |
| 132 | + "ante": {...}, |
| 133 | + "current_round": {...} |
| 134 | +} |
| 135 | +``` |
| 136 | + |
| 137 | +## Game State Management |
| 138 | + |
| 139 | +### State Extraction |
| 140 | + |
| 141 | +The `Utils.getGamestate()` function extracts comprehensive game information: |
| 142 | + |
| 143 | +- **Hand**: Current cards in hand |
| 144 | +- **Jokers**: Active jokers and their properties |
| 145 | +- **Consumables**: Tarot cards, planet cards, etc. |
| 146 | +- **Shop**: Available cards, boosters, vouchers |
| 147 | +- **Ante**: Current blind information |
| 148 | +- **Round**: Discards left, hands played, etc. |
| 149 | +- **Game**: Dollars, round number, state flags |
| 150 | + |
| 151 | +### State Validation |
| 152 | + |
| 153 | +Before sending actions, the system validates: |
| 154 | +- Action is valid for current game state |
| 155 | +- Parameters are within acceptable ranges |
| 156 | +- Required resources are available |
| 157 | + |
| 158 | +## Action Processing |
| 159 | + |
| 160 | +### Action Definition |
| 161 | + |
| 162 | +Actions are defined in `bot.lua` with validation rules: |
| 163 | + |
| 164 | +```lua |
| 165 | +Bot.ACTIONPARAMS[Bot.ACTIONS.PLAY_HAND] = { |
| 166 | + num_args = 2, |
| 167 | + func = "select_cards_from_hand", |
| 168 | + isvalid = function(action) |
| 169 | + -- Validation logic |
| 170 | + return G.GAME.current_round.hands_left > 0 and |
| 171 | + Utils.isTableInRange(action[2], 1, #G.hand.cards) |
| 172 | + end, |
| 173 | +} |
| 174 | +``` |
| 175 | + |
| 176 | +### Action Execution |
| 177 | + |
| 178 | +Actions are queued and executed through the middleware system: |
| 179 | + |
| 180 | +1. **Validation**: Check if action is valid for current state |
| 181 | +2. **Queueing**: Add to appropriate execution queue |
| 182 | +3. **Execution**: Execute via game's event system |
| 183 | +4. **State Update**: Game state changes trigger next decision point |
| 184 | + |
| 185 | +### Available Actions |
| 186 | + |
| 187 | +| Action | Description | Parameters | |
| 188 | +|--------|-------------|------------| |
| 189 | +| `SELECT_BLIND` | Choose to play the current blind | None | |
| 190 | +| `SKIP_BLIND` | Skip the current blind | None | |
| 191 | +| `PLAY_HAND` | Play selected cards | Card indices | |
| 192 | +| `DISCARD_HAND` | Discard selected cards | Card indices | |
| 193 | +| `BUY_CARD` | Purchase shop card | Card index | |
| 194 | +| `REROLL_SHOP` | Reroll shop contents | None | |
| 195 | +| `END_SHOP` | Leave shop | None | |
| 196 | +| `USE_CONSUMABLE` | Use tarot/planet card | Card index, targets | |
| 197 | +| `SELL_JOKER` | Sell joker card | Joker index | |
| 198 | + |
| 199 | +## Performance Optimizations |
| 200 | + |
| 201 | +### Game Speed Optimizations |
| 202 | + |
| 203 | +The mod includes several performance enhancements: |
| 204 | + |
| 205 | +#### Time Manipulation |
| 206 | +```lua |
| 207 | +-- Speed up game time |
| 208 | +if BALATRO_BOT_CONFIG.dt then |
| 209 | + love.update = Hook.addbreakpoint(love.update, function(dt) |
| 210 | + return BALATRO_BOT_CONFIG.dt -- Default: 8/60 seconds |
| 211 | + end) |
| 212 | +end |
| 213 | +``` |
| 214 | + |
| 215 | +#### Visual Optimizations |
| 216 | +```lua |
| 217 | +-- Disable FPS cap |
| 218 | +G.FPS_CAP = 999999.0 |
| 219 | + |
| 220 | +-- Instant movement (no animations) |
| 221 | +function Moveable.move_xy(self, dt) |
| 222 | + self.VT.x = self.T.x |
| 223 | + self.VT.y = self.T.y |
| 224 | +end |
| 225 | + |
| 226 | +-- Render only every Nth frame |
| 227 | +frame_ratio = 100 -- Render every 100th frame |
| 228 | +``` |
| 229 | + |
| 230 | +#### Network Optimization |
| 231 | +```lua |
| 232 | +-- Non-blocking UDP receive |
| 233 | +BalatrobotAPI.socket:settimeout(0) |
| 234 | + |
| 235 | +-- Minimal network overhead |
| 236 | +data, msg_or_ip, port_or_nil = BalatrobotAPI.socket:receivefrom() |
| 237 | +``` |
| 238 | + |
| 239 | +### Multi-Instance Support |
| 240 | + |
| 241 | +The system supports multiple bot instances by: |
| 242 | +- Using different UDP ports for each instance |
| 243 | +- Process isolation between game instances |
| 244 | +- Independent state management |
| 245 | + |
| 246 | +## Error Handling |
| 247 | + |
| 248 | +### Action Validation |
| 249 | +- Comprehensive parameter checking |
| 250 | +- Game state consistency verification |
| 251 | +- Graceful error responses |
| 252 | + |
| 253 | +### Network Resilience |
| 254 | +- Timeout handling for UDP communication |
| 255 | +- Connection recovery mechanisms |
| 256 | +- Robust message parsing |
| 257 | + |
| 258 | +### Logging Integration |
| 259 | +- Complete action history logging |
| 260 | +- Replay capability for debugging |
| 261 | +- Performance metrics tracking |
| 262 | + |
| 263 | +--- |
| 264 | + |
| 265 | +*This architecture enables fast, reliable, and scalable bot development for Balatro.* |
0 commit comments