|
| 1 | +"""Simple bot example demonstrating the balatrobot API. |
| 2 | +
|
| 3 | +This example shows how to create a basic bot that plays Balatro using |
| 4 | +a predefined sequence of actions. |
| 5 | +""" |
| 6 | + |
1 | 7 | import itertools |
| 8 | +from typing import Any, Iterator |
2 | 9 |
|
3 | 10 | from balatrobot import Actions, Bot, Decks, Stakes |
| 11 | +from balatrobot.base import ActionSchema |
4 | 12 |
|
5 | | -plays = itertools.cycle( |
| 13 | +# Predefined sequence of actions using the ActionSchema format |
| 14 | +plays: Iterator[ActionSchema] = itertools.cycle( |
6 | 15 | [ |
7 | 16 | # This sequence of plays is winning for the first round |
8 | 17 | # for the seed "EXAMPLE" and the deck "Red Deck" with stake 1. |
9 | | - [Actions.DISCARD_HAND, [2, 3, 4, 6]], |
10 | | - [Actions.DISCARD_HAND, [1, 2, 6, 8]], |
11 | | - [Actions.PLAY_HAND, [2, 3, 5, 6, 7]], |
12 | | - [Actions.PLAY_HAND, [3, 4, 7, 8]], |
| 18 | + {"action": Actions.DISCARD_HAND, "args": [2, 3, 4, 6]}, |
| 19 | + {"action": Actions.DISCARD_HAND, "args": [1, 2, 6, 8]}, |
| 20 | + {"action": Actions.PLAY_HAND, "args": [2, 3, 5, 6, 7]}, |
| 21 | + {"action": Actions.PLAY_HAND, "args": [3, 4, 7, 8]}, |
13 | 22 | ] |
14 | 23 | ) |
15 | 24 |
|
16 | 25 |
|
17 | 26 | class MyFirstBot(Bot): |
18 | | - def __init__(self, deck=Decks.RED, stake=Stakes.WHITE, seed="EXAMPLE"): |
| 27 | + """Example bot implementation using the ActionSchema API. |
| 28 | +
|
| 29 | + This bot demonstrates a simple strategy using predefined actions. |
| 30 | + It always selects blinds, uses a fixed sequence of plays, and |
| 31 | + skips most optional actions. |
| 32 | +
|
| 33 | + Attributes: |
| 34 | + round_count (int): The current round number. |
| 35 | + """ |
| 36 | + |
| 37 | + def __init__( |
| 38 | + self, |
| 39 | + deck: Decks = Decks.RED, |
| 40 | + stake: Stakes = Stakes.WHITE, |
| 41 | + seed: str = "EXAMPLE", |
| 42 | + ) -> None: |
| 43 | + """Initialize the bot with default settings. |
| 44 | +
|
| 45 | + Args: |
| 46 | + deck (Decks): The deck type to use. |
| 47 | + stake (Stakes): The stake level to play at. |
| 48 | + seed (str): The random seed for the game. |
| 49 | + """ |
19 | 50 | super().__init__(deck=deck, stake=stake, seed=seed) |
20 | | - self.round_count = 0 |
| 51 | + self.round_count: int = 0 |
| 52 | + |
| 53 | + def skip_or_select_blind(self, env: dict[str, Any]) -> ActionSchema: |
| 54 | + """Always select blinds to play them. |
21 | 55 |
|
22 | | - def skip_or_select_blind(self, G): |
23 | | - """Always select blinds to play them""" |
24 | | - return [Actions.SELECT_BLIND] |
| 56 | + Args: |
| 57 | + env (dict[str, Any]): The current game environment state. |
25 | 58 |
|
26 | | - def select_cards_from_hand(self, G): |
27 | | - """Simple strategy: play the first card""" |
| 59 | + Returns: |
| 60 | + ActionSchema: Action to select blind. |
| 61 | + """ |
| 62 | + return {"action": Actions.SELECT_BLIND, "args": None} |
| 63 | + |
| 64 | + def select_cards_from_hand(self, env: dict[str, Any]) -> ActionSchema: |
| 65 | + """Simple strategy: use predefined card selection sequence. |
| 66 | +
|
| 67 | + Args: |
| 68 | + env (dict[str, Any]): The current game environment state. |
| 69 | +
|
| 70 | + Returns: |
| 71 | + ActionSchema: Action with card selection from predefined sequence. |
| 72 | + """ |
28 | 73 | return next(plays) |
29 | 74 |
|
30 | | - def select_shop_action(self, G): |
31 | | - """Always leave the shop immediately""" |
32 | | - return [Actions.END_SHOP] |
| 75 | + def select_shop_action(self, env: dict[str, Any]) -> ActionSchema: |
| 76 | + """Always leave the shop immediately. |
33 | 77 |
|
34 | | - def select_booster_action(self, G): |
35 | | - """Skip all booster packs""" |
36 | | - return [Actions.SKIP_BOOSTER_PACK] |
| 78 | + Args: |
| 79 | + env (dict[str, Any]): The current game environment state. |
37 | 80 |
|
38 | | - def sell_jokers(self, G): |
39 | | - """Don't sell any jokers""" |
40 | | - return [Actions.SELL_JOKER, []] |
| 81 | + Returns: |
| 82 | + ActionSchema: Action to end shop. |
| 83 | + """ |
| 84 | + return {"action": Actions.END_SHOP, "args": None} |
41 | 85 |
|
42 | | - def rearrange_jokers(self, G): |
43 | | - """Don't rearrange jokers""" |
44 | | - return [Actions.REARRANGE_JOKERS, []] |
| 86 | + def select_booster_action(self, env: dict[str, Any]) -> ActionSchema: |
| 87 | + """Skip all booster packs. |
45 | 88 |
|
46 | | - def use_or_sell_consumables(self, G): |
47 | | - """Don't use consumables""" |
48 | | - return [Actions.USE_CONSUMABLE, []] |
| 89 | + Args: |
| 90 | + env (dict[str, Any]): The current game environment state. |
49 | 91 |
|
50 | | - def rearrange_consumables(self, G): |
51 | | - """Don't rearrange consumables""" |
52 | | - return [Actions.REARRANGE_CONSUMABLES, []] |
| 92 | + Returns: |
| 93 | + ActionSchema: Action to skip booster pack. |
| 94 | + """ |
| 95 | + return {"action": Actions.SKIP_BOOSTER_PACK, "args": None} |
53 | 96 |
|
54 | | - def rearrange_hand(self, G): |
55 | | - """Don't rearrange hand""" |
56 | | - return [Actions.REARRANGE_HAND, []] |
| 97 | + def sell_jokers(self, env: dict[str, Any]) -> ActionSchema: |
| 98 | + """Don't sell any jokers. |
57 | 99 |
|
| 100 | + Args: |
| 101 | + env (dict[str, Any]): The current game environment state. |
58 | 102 |
|
59 | | -# Run the bot |
60 | | -if __name__ == "__main__": |
| 103 | + Returns: |
| 104 | + ActionSchema: Action to sell jokers with empty list. |
| 105 | + """ |
| 106 | + return {"action": Actions.SELL_JOKER, "args": []} |
| 107 | + |
| 108 | + def rearrange_jokers(self, env: dict[str, Any]) -> ActionSchema: |
| 109 | + """Don't rearrange jokers. |
| 110 | +
|
| 111 | + Args: |
| 112 | + env (dict[str, Any]): The current game environment state. |
| 113 | +
|
| 114 | + Returns: |
| 115 | + ActionSchema: Action to rearrange jokers with empty list. |
| 116 | + """ |
| 117 | + return {"action": Actions.REARRANGE_JOKERS, "args": []} |
| 118 | + |
| 119 | + def use_or_sell_consumables(self, env: dict[str, Any]) -> ActionSchema: |
| 120 | + """Don't use consumables. |
| 121 | +
|
| 122 | + Args: |
| 123 | + env (dict[str, Any]): The current game environment state. |
| 124 | +
|
| 125 | + Returns: |
| 126 | + ActionSchema: Action to use consumables with empty list. |
| 127 | + """ |
| 128 | + return {"action": Actions.USE_CONSUMABLE, "args": []} |
| 129 | + |
| 130 | + def rearrange_consumables(self, env: dict[str, Any]) -> ActionSchema: |
| 131 | + """Don't rearrange consumables. |
| 132 | +
|
| 133 | + Args: |
| 134 | + env (dict[str, Any]): The current game environment state. |
| 135 | +
|
| 136 | + Returns: |
| 137 | + ActionSchema: Action to rearrange consumables with empty list. |
| 138 | + """ |
| 139 | + return {"action": Actions.REARRANGE_CONSUMABLES, "args": []} |
| 140 | + |
| 141 | + def rearrange_hand(self, env: dict[str, Any]) -> ActionSchema: |
| 142 | + """Don't rearrange hand. |
| 143 | +
|
| 144 | + Args: |
| 145 | + env (dict[str, Any]): The current game environment state. |
| 146 | +
|
| 147 | + Returns: |
| 148 | + ActionSchema: Action to rearrange hand with empty list. |
| 149 | + """ |
| 150 | + return {"action": Actions.REARRANGE_HAND, "args": []} |
| 151 | + |
| 152 | + |
| 153 | +def main() -> None: |
61 | 154 | bot = MyFirstBot() |
62 | 155 | bot.running = True |
63 | 156 | bot.run() |
| 157 | + |
| 158 | + |
| 159 | +if __name__ == "__main__": |
| 160 | + main() |
0 commit comments