Skip to content

Commit 3b3c487

Browse files
committed
feat(strategies): add pack endpoint support
1 parent d10e026 commit 3b3c487

4 files changed

Lines changed: 130 additions & 16 deletions

File tree

src/balatrollm/bot.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ async def _run_game_loop(self, gamestate: dict[str, Any]) -> None:
178178
await self._balatro.call("gamestate")
179179

180180
match current_state:
181-
case "SELECTING_HAND" | "SHOP":
181+
case "SELECTING_HAND" | "SHOP" | "SMODS_BOOSTER_OPENED":
182182
response = await self._get_llm_response(gamestate)
183183
gamestate = await self._execute_tool_call(response)
184184
case "ROUND_EVAL":

src/balatrollm/strategies/default/GAMESTATE.md.jinja

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -193,15 +193,48 @@ Here are the vouchers that can be redeemed:
193193
{%- endfor %}
194194
{% endif %}
195195

196-
{#- Packs purchase not yet supported
197196
{% if G.packs.cards %}
198-
Here are the booster packs that can be opened:
197+
### Booster Packs
198+
199199
{% for pack in G.packs.cards -%}
200-
- {{ loop.index0 }}: {{ pack.label }}
200+
- {{ loop.index0 }}: **{{ pack.label }}**
201+
- **Effect**: {{ pack.value.effect }}
201202
- **Cost**: ${{ pack.cost.buy }}
202203
{% endfor %}
203204
{% endif %}
204-
-#}
205+
{%- endif %}
206+
207+
{% if G.state == "SMODS_BOOSTER_OPENED" -%}
208+
## Pack Selection
209+
210+
You have opened a booster pack. Choose a card or skip.
211+
212+
### Available Cards in Pack
213+
214+
{% for card in G.pack.cards -%}
215+
{% if card.set == "JOKER" %}
216+
- {{ loop.index0 }}: **{{ card.label }}** (Joker)
217+
- **Effect**: {{ card.value.effect }}
218+
- **Rarity**: {{ card.value.rarity|default('Common') }}
219+
{% elif card.set in ["TAROT", "PLANET", "SPECTRAL"] %}
220+
- {{ loop.index0 }}: **{{ card.label }}** ({{ card.set|title }})
221+
- **Effect**: {{ card.value.effect }}
222+
{% if card.value.min_highlighted %} - **Targets**: {{ card.value.min_highlighted }}-{{ card.value.max_highlighted }} hand cards{% endif %}
223+
{% elif card.set in ["DEFAULT", "ENHANCED"] %}
224+
- {{ loop.index0 }}: **{{ card.value.rank }} of {{ card.value.suit }}**
225+
{% if card.set == "ENHANCED" %} - **Enhancement**: {{ card.value.enhancement }}{% endif %}
226+
{% if card.value.edition %} - **Edition**: {{ card.value.edition }}{% endif %}
227+
{% if card.value.seal %} - **Seal**: {{ card.value.seal }}{% endif %}
228+
{% endif %}
229+
{%- endfor %}
230+
231+
{% if G.hand and G.hand.cards %}
232+
### Your Hand (for targeting)
233+
234+
{% for card in G.hand.cards -%}
235+
- {{ loop.index0 }}: {{ card.value.rank }} of {{ card.value.suit }}{% if card.set == "ENHANCED" %} ({{ card.value.enhancement }}){% endif %}
236+
{% endfor %}
237+
{% endif %}
205238
{%- endif %}
206239

207240
{% if G.state == "BLIND_SELECT" -%}
@@ -247,11 +280,12 @@ You **MUST** use one of the following tools to perform the intended action:
247280
- `consumables`: (list) New order of consumables (0-based indices, must include all consumables)
248281
- `reasoning`: (string) Strategic reasoning for this arrangement
249282
{% elif G.state == "SHOP" -%}
250-
- Buy a card or voucher from the shop
283+
- Buy a card, voucher, or pack from the shop
251284
- Function: `buy`
252285
- Parameters (provide exactly one of):
253286
- `card`: (integer) 0-based index of shop card to buy
254287
- `voucher`: (integer) 0-based index of voucher to buy
288+
- `pack`: (integer) 0-based index of booster pack to buy and open
255289
- `reasoning`: (string) Strategic reasoning for this purchase
256290
- Reroll the shop items
257291
- Function: `reroll`
@@ -276,6 +310,18 @@ You **MUST** use one of the following tools to perform the intended action:
276310
- Function: `skip`
277311
- Parameters:
278312
- `reasoning`: (string) Strategic reasoning for skipping this blind
313+
{% elif G.state == "SMODS_BOOSTER_OPENED" -%}
314+
- Select a card from the pack
315+
- Function: `pack`
316+
- Parameters:
317+
- `card`: (integer) 0-based index of card to select
318+
- `targets`: (list, optional) 0-based indices of hand cards to target (for Tarot/Spectral that require selection)
319+
- `reasoning`: (string) Strategic reasoning for this selection
320+
- Skip the pack selection
321+
- Function: `pack`
322+
- Parameters:
323+
- `skip`: (boolean) Set to true to skip
324+
- `reasoning`: (string) Strategic reasoning for skipping
279325
{% endif -%}
280326
{% if G.state in ["SELECTING_HAND", "SHOP"] -%}
281327
- Sell a joker or consumable for money

src/balatrollm/strategies/default/STRATEGY.md.jinja

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -196,24 +196,16 @@ By default, the shop sells:
196196

197197
- 2 random cards (usually Jokers, but occasionally Tarot Cards and Planets)
198198
- 1 Voucher
199-
200-
{#- Booster Packs not yet supported by BalatroBot API
201199
- 2 random Booster Packs
202-
-#}
203200

204201
All items in the shop have a buy cost (purchase price) and a sell cost (selling value) for the purchased items. The sell cost item is usually half of it buy cost.
205202

206203
### Reroll
207204

208205
The player may pay money to reroll the shop an unlimited number of times, removing any of the remaining random cards and replacing them with 2 new random cards. The reroll cost starts at $5 and increases by $1 each reroll, resetting to $5 upon entering a new shop.
209206

210-
{#- Pack purchase not yet supported by BalatroBot API
211207
However, rerolling the shop does not restock Booster Packs or the Voucher. Booster packs only restock upon entering a new shop, and the Voucher restocks after defeating the boss blind.
212-
-#}
213-
214-
However, rerolling the shop does not restock the Voucher. The Voucher restocks after defeating the boss blind.
215208

216-
{#- Pack purchase not yet supported by BalatroBot API
217209
### Booster Packs
218210

219211
Two Booster Packs are always offered in each Shop, often but not always of two different types. These come in five categories:
@@ -225,7 +217,29 @@ Two Booster Packs are always offered in each Shop, often but not always of two d
225217
- **Spectral Packs** containing Spectral Cards for immediate use.
226218

227219
A new set of Booster Packs will be offered with each shop. Booster Packs do not refresh when rerolling the Shop.
228-
-#}
220+
221+
#### Booster Pack Strategy
222+
223+
Booster packs provide valuable cards but use them wisely:
224+
225+
**When to Buy Packs:**
226+
- **Buffoon Packs**: Buy when you have joker slots available and need synergy
227+
- **Arcana Packs**: Good for card enhancements early game
228+
- **Celestial Packs**: Buy when you have a dominant hand type to level up
229+
- **Standard Packs**: Useful for building specific suits/ranks
230+
- **Spectral Packs**: High risk/reward - can duplicate jokers or destroy cards
231+
232+
**Pack Selection Tips:**
233+
- For Arcana/Spectral packs, cards are used **immediately**
234+
- Some cards (like The Magician) require selecting target cards from your hand
235+
- Consider skipping if no cards benefit your current strategy
236+
- Buffoon/Standard packs add cards to your collection (not used immediately)
237+
238+
**Targeting for Tarot/Spectral:**
239+
When a card requires targets:
240+
- Check the card's effect for how many cards to select
241+
- Target cards that will benefit most from the enhancement
242+
- For destructive cards, target low-value or duplicate cards
229243

230244
## Jokers
231245

src/balatrollm/strategies/default/TOOLS.json

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@
133133
"type": "function",
134134
"function": {
135135
"name": "buy",
136-
"description": "Buy a card or voucher from the shop. Provide exactly one of: card or voucher.",
136+
"description": "Buy a card, voucher, or pack from the shop. Provide exactly one of: card, voucher, or pack.",
137137
"parameters": {
138138
"type": "object",
139139
"properties": {
@@ -145,6 +145,10 @@
145145
"type": "integer",
146146
"description": "0-based index of voucher to buy"
147147
},
148+
"pack": {
149+
"type": "integer",
150+
"description": "0-based index of booster pack to buy and open"
151+
},
148152
"reasoning": {
149153
"type": "string",
150154
"description": "Strategic reasoning for this purchase"
@@ -302,5 +306,55 @@
302306
}
303307
}
304308
}
309+
],
310+
"SMODS_BOOSTER_OPENED": [
311+
{
312+
"type": "function",
313+
"function": {
314+
"name": "pack",
315+
"description": "Select a card from the opened booster pack. For Arcana/Spectral packs, cards are used immediately and may require targeting hand cards.",
316+
"parameters": {
317+
"type": "object",
318+
"properties": {
319+
"card": {
320+
"type": "integer",
321+
"description": "0-based index of card to select from pack"
322+
},
323+
"targets": {
324+
"type": "array",
325+
"items": {"type": "integer"},
326+
"description": "0-based indices of hand cards to target (required for some Tarot/Spectral cards)"
327+
},
328+
"reasoning": {
329+
"type": "string",
330+
"description": "Strategic reasoning for this selection"
331+
}
332+
},
333+
"required": ["card", "reasoning"]
334+
}
335+
}
336+
},
337+
{
338+
"type": "function",
339+
"function": {
340+
"name": "pack",
341+
"description": "Skip the pack selection without taking any card.",
342+
"parameters": {
343+
"type": "object",
344+
"properties": {
345+
"skip": {
346+
"type": "boolean",
347+
"const": true,
348+
"description": "Set to true to skip the pack"
349+
},
350+
"reasoning": {
351+
"type": "string",
352+
"description": "Strategic reasoning for skipping"
353+
}
354+
},
355+
"required": ["skip", "reasoning"]
356+
}
357+
}
358+
}
305359
]
306360
}

0 commit comments

Comments
 (0)