Skip to content

Commit 56c7c80

Browse files
committed
feat: add extensive logging to python code
1 parent 4f2b206 commit 56c7c80

File tree

4 files changed

+187
-42
lines changed

4 files changed

+187
-42
lines changed

bots/example.py

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
a predefined sequence of actions.
55
"""
66

7+
import argparse
78
import itertools
89
from typing import Any, Iterator
910

10-
from balatrobot import Actions, ActionSchema, Bot, Decks, Stakes
11+
from balatrobot import Actions, ActionSchema, Bot, Decks, Stakes, configure_bot_logging
1112

12-
# Predefined sequence of actions using the ActionSchema format
13+
# Predefined sequence of actions using the ActionCall format
1314
plays: Iterator[ActionSchema] = itertools.cycle(
1415
[
1516
# This sequence of plays is winning for the first round
@@ -22,8 +23,8 @@
2223
)
2324

2425

25-
class MyFirstBot(Bot):
26-
"""Example bot implementation using the ActionSchema API.
26+
class ExampleBot(Bot):
27+
"""Example bot implementation using the ActionCall API.
2728
2829
This bot demonstrates a simple strategy using predefined actions.
2930
It always selects blinds, uses a fixed sequence of plays, and
@@ -42,9 +43,9 @@ def __init__(
4243
"""Initialize the bot with default settings.
4344
4445
Args:
45-
deck (Decks): The deck type to use.
46-
stake (Stakes): The stake level to play at.
47-
seed (str): The random seed for the game.
46+
deck: The deck type to use.
47+
stake: The stake level to play at.
48+
seed: The random seed for the game.
4849
"""
4950
super().__init__(deck=deck, stake=stake, seed=seed)
5051
self.round_count: int = 0
@@ -56,7 +57,7 @@ def skip_or_select_blind(self, env: dict[str, Any]) -> ActionSchema:
5657
env (dict[str, Any]): The current game environment state.
5758
5859
Returns:
59-
ActionSchema: Action to select blind.
60+
ActionCall: Action to select blind.
6061
"""
6162
return {"action": Actions.SELECT_BLIND, "args": None}
6263

@@ -67,7 +68,7 @@ def select_cards_from_hand(self, env: dict[str, Any]) -> ActionSchema:
6768
env (dict[str, Any]): The current game environment state.
6869
6970
Returns:
70-
ActionSchema: Action with card selection from predefined sequence.
71+
ActionCall: Action with card selection from predefined sequence.
7172
"""
7273
return next(plays)
7374

@@ -78,7 +79,7 @@ def select_shop_action(self, env: dict[str, Any]) -> ActionSchema:
7879
env (dict[str, Any]): The current game environment state.
7980
8081
Returns:
81-
ActionSchema: Action to end shop.
82+
ActionCall: Action to end shop.
8283
"""
8384
return {"action": Actions.END_SHOP, "args": None}
8485

@@ -89,7 +90,7 @@ def select_booster_action(self, env: dict[str, Any]) -> ActionSchema:
8990
env (dict[str, Any]): The current game environment state.
9091
9192
Returns:
92-
ActionSchema: Action to skip booster pack.
93+
ActionCall: Action to skip booster pack.
9394
"""
9495
return {"action": Actions.SKIP_BOOSTER_PACK, "args": None}
9596

@@ -100,7 +101,7 @@ def sell_jokers(self, env: dict[str, Any]) -> ActionSchema:
100101
env (dict[str, Any]): The current game environment state.
101102
102103
Returns:
103-
ActionSchema: Action to sell jokers with empty list.
104+
ActionCall: Action to sell jokers with empty list.
104105
"""
105106
return {"action": Actions.SELL_JOKER, "args": []}
106107

@@ -111,7 +112,7 @@ def rearrange_jokers(self, env: dict[str, Any]) -> ActionSchema:
111112
env (dict[str, Any]): The current game environment state.
112113
113114
Returns:
114-
ActionSchema: Action to rearrange jokers with empty list.
115+
ActionCall: Action to rearrange jokers with empty list.
115116
"""
116117
return {"action": Actions.REARRANGE_JOKERS, "args": []}
117118

@@ -122,7 +123,7 @@ def use_or_sell_consumables(self, env: dict[str, Any]) -> ActionSchema:
122123
env (dict[str, Any]): The current game environment state.
123124
124125
Returns:
125-
ActionSchema: Action to use consumables with empty list.
126+
ActionCall: Action to use consumables with empty list.
126127
"""
127128
return {"action": Actions.USE_CONSUMABLE, "args": []}
128129

@@ -133,7 +134,7 @@ def rearrange_consumables(self, env: dict[str, Any]) -> ActionSchema:
133134
env (dict[str, Any]): The current game environment state.
134135
135136
Returns:
136-
ActionSchema: Action to rearrange consumables with empty list.
137+
ActionCall: Action to rearrange consumables with empty list.
137138
"""
138139
return {"action": Actions.REARRANGE_CONSUMABLES, "args": []}
139140

@@ -144,13 +145,27 @@ def rearrange_hand(self, env: dict[str, Any]) -> ActionSchema:
144145
env (dict[str, Any]): The current game environment state.
145146
146147
Returns:
147-
ActionSchema: Action to rearrange hand with empty list.
148+
ActionCall: Action to rearrange hand with empty list.
148149
"""
149150
return {"action": Actions.REARRANGE_HAND, "args": []}
150151

151152

152153
def main() -> None:
153-
bot = MyFirstBot()
154+
"""Main function to run the example bot with command-line argument support."""
155+
parser = argparse.ArgumentParser(description="Run the example Balatro bot")
156+
parser.add_argument(
157+
"--log",
158+
choices=["DEBUG", "INFO", "WARNING", "ERROR"],
159+
default="DEBUG",
160+
help="Set the console logging level (default: DEBUG)",
161+
)
162+
163+
args = parser.parse_args()
164+
165+
# Configure logging with the specified level
166+
configure_bot_logging(args.log)
167+
168+
bot = ExampleBot()
154169
bot.running = True
155170
bot.run()
156171

src/balatrobot/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from .base import ActionSchema, Bot
22
from .enums import Actions, Decks, Stakes, State
3+
from .utils import configure_bot_logging, get_logger, setup_logging
34

45
__all__ = [
56
"ActionSchema",
@@ -8,6 +9,9 @@
89
"Decks",
910
"Stakes",
1011
"State",
12+
"configure_bot_logging",
13+
"get_logger",
14+
"setup_logging",
1115
]
1216

1317
__version__ = "0.1.0"

src/balatrobot/base.py

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from typing import Any, TypedDict
77

88
from .enums import Actions, Decks, Stakes, State
9+
from .utils import get_logger
910

1011

1112
class ActionSchema(TypedDict):
@@ -67,6 +68,23 @@ def __init__(
6768
self.sock: socket.socket | None = None
6869

6970
self.state: dict[str, Any] = {}
71+
self.logger = get_logger("bot")
72+
73+
# Initialize socket connection
74+
self._initialize_socket()
75+
76+
def _initialize_socket(self) -> None:
77+
"""Initialize the socket connection for bot communication.
78+
79+
Sets up the UDP socket with timeout and connects to the game instance.
80+
"""
81+
self.state = {}
82+
self.G = None
83+
self.running = True
84+
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
85+
self.sock.settimeout(1)
86+
self.sock.connect(self.addr)
87+
self.logger.debug("Socket initialized and connected to %s:%d", *self.addr)
7088

7189
@staticmethod
7290
def random_seed() -> str:
@@ -214,44 +232,48 @@ def chooseaction(self, env: dict[str, Any]) -> ActionSchema:
214232
Returns:
215233
ActionSchema: The action to perform with 'action' and optional 'args'.
216234
"""
217-
print("Choosing action based on game state:", env["state"])
235+
try:
236+
state_name = State(env["state"]).name
237+
except ValueError:
238+
state_name = f"UNKNOWN({env['state']})"
239+
self.logger.debug("Choosing action based on game state: %s", state_name)
218240
if env["state"] == State.GAME_OVER:
219241
self.running = False
220242

221243
match env["waitingFor"]:
222244
case "start_run":
223-
print("Starting run with deck:", self.deck)
245+
self.logger.info("Starting run with deck: %s", self.deck)
224246
seed = self.seed or self.random_seed()
225247
return {
226248
"action": Actions.START_RUN,
227249
"args": [self.stake.value, self.deck.value, seed, self.challenge],
228250
}
229251
case "skip_or_select_blind":
230-
print("Choosing action: skip_or_select_blind")
252+
self.logger.debug("Choosing action: skip_or_select_blind")
231253
return self.skip_or_select_blind(env)
232254
case "select_cards_from_hand":
233-
print("Choosing action: select_cards_from_hand")
255+
self.logger.debug("Choosing action: select_cards_from_hand")
234256
return self.select_cards_from_hand(env)
235257
case "select_shop_action":
236-
print("Choosing action: select_shop_action")
258+
self.logger.debug("Choosing action: select_shop_action")
237259
return self.select_shop_action(env)
238260
case "select_booster_action":
239-
print("Choosing action: select_booster_action")
261+
self.logger.debug("Choosing action: select_booster_action")
240262
return self.select_booster_action(env)
241263
case "sell_jokers":
242-
print("Choosing action: sell_jokers")
264+
self.logger.debug("Choosing action: sell_jokers")
243265
return self.sell_jokers(env)
244266
case "rearrange_jokers":
245-
print("Choosing action: rearrange_jokers")
267+
self.logger.debug("Choosing action: rearrange_jokers")
246268
return self.rearrange_jokers(env)
247269
case "use_or_sell_consumables":
248-
print("Choosing action: use_or_sell_consumables")
270+
self.logger.debug("Choosing action: use_or_sell_consumables")
249271
return self.use_or_sell_consumables(env)
250272
case "rearrange_consumables":
251-
print("Choosing action: rearrange_consumables")
273+
self.logger.debug("Choosing action: rearrange_consumables")
252274
return self.rearrange_consumables(env)
253275
case "rearrange_hand":
254-
print("Choosing action: rearrange_hand")
276+
self.logger.debug("Choosing action: rearrange_hand")
255277
return self.rearrange_hand(env)
256278
case _:
257279
raise ValueError(f"Unhandled waitingFor state: {env['waitingFor']}")
@@ -262,14 +284,8 @@ def run_step(self) -> None:
262284
This method handles socket communication, receives game state updates,
263285
and sends actions back to the game.
264286
"""
265-
if self.sock is None:
266-
self.state = {}
267-
self.G = None
268-
269-
self.running = True
270-
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
271-
self.sock.settimeout(1)
272-
self.sock.connect(self.addr)
287+
if not self.running or self.sock is None:
288+
return
273289

274290
if self.running:
275291
self.sock.send(bytes("HELLO", "utf-8"))
@@ -279,7 +295,7 @@ def run_step(self) -> None:
279295
env = json.loads(data)
280296

281297
if "response" in env:
282-
print(env["response"])
298+
self.logger.info("Game response: %s", env["response"])
283299
else:
284300
if env["waitingForAction"]:
285301
action = self.chooseaction(env)
@@ -290,11 +306,9 @@ def run_step(self) -> None:
290306
raise ValueError("All actions must return a value!")
291307

292308
except socket.error as e:
293-
print(e)
294-
print("Socket error, reconnecting...")
295-
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
296-
self.sock.settimeout(1)
297-
self.sock.connect(self.addr)
309+
self.logger.error("Socket error: %s", e)
310+
self.logger.warning("Socket error, reconnecting...")
311+
self._initialize_socket()
298312

299313
def run(self) -> None:
300314
"""Run the bot's main game loop.

0 commit comments

Comments
 (0)