Skip to content

Commit 01e9727

Browse files
committed
Add hand info, speedup, flushbot, and configuration
1 parent c87b8d3 commit 01e9727

File tree

11 files changed

+420
-102
lines changed

11 files changed

+420
-102
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
__pycache__
1+
__pycache__
2+
Balatro
3+
gamestate_cache

bot.py

Lines changed: 121 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
import socket
66
import time
77
from enum import Enum
8+
from gamestates import cache_state
9+
import subprocess
10+
import random
11+
812

913
class State(Enum):
1014
SELECTING_HAND = 1
@@ -27,6 +31,7 @@ class State(Enum):
2731
BUFFOON_PACK = 18
2832
NEW_ROUND = 19
2933

34+
3035
class Actions(Enum):
3136
SELECT_BLIND = 1
3237
SKIP_BLIND = 2
@@ -49,137 +54,192 @@ class Actions(Enum):
4954
START_RUN = 19
5055
SEND_GAMESTATE = 20
5156

52-
class Bot:
5357

54-
def __init__(self, deck: str, stake: int = 1, seed: str = None, challenge: str = None):
58+
class Bot:
59+
def __init__(
60+
self,
61+
deck: str,
62+
stake: int = 1,
63+
seed: str = None,
64+
challenge: str = None,
65+
bot_port: int = 12346,
66+
):
5567
self.G = None
5668
self.deck = deck
5769
self.stake = stake
5870
self.seed = seed
5971
self.challenge = challenge
6072

61-
self.addr = ("127.0.0.1", 12345)
73+
self.bot_port = bot_port
74+
75+
self.addr = ("localhost", self.bot_port)
6276
self.running = False
77+
self.balatro_instance = None
78+
79+
self.sock = None
80+
81+
self.state = {}
6382

64-
self.state = { }
65-
6683
def skip_or_select_blind(self):
67-
raise NotImplementedError("Error: Bot.skip_or_select_blind must be implemented.")
84+
raise NotImplementedError(
85+
"Error: Bot.skip_or_select_blind must be implemented."
86+
)
6887

6988
def select_cards_from_hand(self):
70-
raise NotImplementedError("Error: Bot.select_cards_from_hand must be implemented.")
89+
raise NotImplementedError(
90+
"Error: Bot.select_cards_from_hand must be implemented."
91+
)
7192

7293
def select_shop_action(self):
7394
raise NotImplementedError("Error: Bot.select_shop_action must be implemented.")
74-
95+
7596
def select_booster_action(self):
76-
raise NotImplementedError("Error: Bot.select_booster_action must be implemented.")
77-
97+
raise NotImplementedError(
98+
"Error: Bot.select_booster_action must be implemented."
99+
)
100+
78101
def sell_jokers(self):
79102
raise NotImplementedError("Error: Bot.sell_jokers must be implemented.")
80103

81104
def rearrange_jokers(self):
82105
raise NotImplementedError("Error: Bot.rearrange_jokers must be implemented.")
83-
106+
84107
def use_or_sell_consumables(self):
85-
raise NotImplementedError("Error: Bot.use_or_sell_consumables must be implemented.")
86-
108+
raise NotImplementedError(
109+
"Error: Bot.use_or_sell_consumables must be implemented."
110+
)
111+
87112
def rearrange_consumables(self):
88-
raise NotImplementedError("Error: Bot.rearrange_consumables must be implemented.")
89-
113+
raise NotImplementedError(
114+
"Error: Bot.rearrange_consumables must be implemented."
115+
)
116+
90117
def rearrange_hand(self):
91118
raise NotImplementedError("Error: Bot.rearrange_hand must be implemented.")
92119

120+
def start_balatro_instance(self):
121+
balatro_exec_path = (
122+
r"C:\Program Files (x86)\Steam\steamapps\common\Balatro\Balatro.exe"
123+
)
124+
self.balatro_instance = subprocess.Popen(
125+
[balatro_exec_path, str(self.bot_port)]
126+
)
127+
128+
def stop_balatro_instance(self):
129+
if self.balatro_instance:
130+
self.balatro_instance.kill()
131+
93132
def sendcmd(self, cmd, **kwargs):
94-
msg = bytes(cmd, 'utf-8')
133+
msg = bytes(cmd, "utf-8")
95134
self.sock.sendto(msg, self.addr)
96135

97136
def actionToCmd(self, action):
98-
result = [ ]
137+
result = []
99138

100139
for x in action:
101140
if isinstance(x, Actions):
102141
result.append(x.name)
103142
elif type(x) is list:
104-
result.append(','.join([str(y) for y in x]))
143+
result.append(",".join([str(y) for y in x]))
105144
else:
106-
result.append(str(x))
145+
result.append(str(x))
107146

108-
return '|'.join(result)
147+
return "|".join(result)
109148

110149
def verifyimplemented(self):
111150
try:
112-
self.skip_or_select_blind(self, { })
113-
self.select_cards_from_hand(self, { })
114-
self.select_shop_action(self, { })
115-
self.select_booster_action(self, { })
116-
self.sell_jokers(self, { })
117-
self.rearrange_jokers(self, { })
118-
self.use_or_sell_consumables(self, { })
119-
self.rearrange_consumables(self, { })
120-
self.rearrange_hand(self, { })
151+
self.skip_or_select_blind(self, {})
152+
self.select_cards_from_hand(self, {})
153+
self.select_shop_action(self, {})
154+
self.select_booster_action(self, {})
155+
self.sell_jokers(self, {})
156+
self.rearrange_jokers(self, {})
157+
self.use_or_sell_consumables(self, {})
158+
self.rearrange_consumables(self, {})
159+
self.rearrange_hand(self, {})
121160
except NotImplementedError as e:
122161
print(e)
123162
sys.exit(0)
124163
except:
125164
pass
126165

166+
def random_seed(self):
167+
# e.g. 1OGB5WO
168+
return "".join(random.choices("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", k=7))
169+
127170
def chooseaction(self):
128-
if self.G['state'] == State.GAME_OVER:
171+
if self.G["state"] == State.GAME_OVER:
129172
self.running = False
130173

131-
match self.G['waitingFor']:
132-
case 'start_run':
133-
return [ Actions.START_RUN, self.stake, self.deck, self.seed, self.challenge ]
134-
case 'skip_or_select_blind':
174+
match self.G["waitingFor"]:
175+
case "start_run":
176+
seed = self.seed
177+
if seed is None:
178+
seed = self.random_seed()
179+
return [
180+
Actions.START_RUN,
181+
self.stake,
182+
self.deck,
183+
seed,
184+
self.challenge,
185+
]
186+
case "skip_or_select_blind":
135187
return self.skip_or_select_blind(self, self.G)
136-
case 'select_cards_from_hand':
188+
case "select_cards_from_hand":
137189
return self.select_cards_from_hand(self, self.G)
138-
case 'select_shop_action':
190+
case "select_shop_action":
139191
return self.select_shop_action(self, self.G)
140-
case 'select_booster_action':
192+
case "select_booster_action":
141193
return self.select_booster_action(self, self.G)
142-
case 'sell_jokers':
194+
case "sell_jokers":
143195
return self.sell_jokers(self, self.G)
144-
case 'rearrange_jokers':
196+
case "rearrange_jokers":
145197
return self.rearrange_jokers(self, self.G)
146-
case 'use_or_sell_consumables':
198+
case "use_or_sell_consumables":
147199
return self.use_or_sell_consumables(self, self.G)
148-
case 'rearrange_consumables':
200+
case "rearrange_consumables":
149201
return self.rearrange_consumables(self, self.G)
150-
case 'rearrange_hand':
202+
case "rearrange_hand":
151203
return self.rearrange_hand(self, self.G)
152204

153-
def run(self):
154-
self.verifyimplemented()
155-
self.state = { }
205+
def run_step(self):
206+
if self.sock is None:
207+
self.verifyimplemented()
208+
self.state = {}
209+
self.G = None
156210

157-
self.running = True
211+
self.running = True
212+
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
213+
self.sock.settimeout(1)
214+
self.sock.connect(self.addr)
158215

159-
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
160-
self.sock.settimeout(1.0)
216+
if self.running:
217+
self.sendcmd("HELLO")
161218

162-
while self.running:
163-
self.sendcmd('HELLO')
164-
165-
jsondata = { }
219+
jsondata = {}
166220
try:
167-
data = self.sock.recv(4096)
221+
data = self.sock.recv(65536)
168222
jsondata = json.loads(data)
169223

170-
if 'response' in jsondata:
171-
print(jsondata['response'])
224+
if "response" in jsondata:
225+
print(jsondata["response"])
172226
else:
173227
self.G = jsondata
174-
if self.G['waitingForAction']:
175-
# Choose next action
228+
if self.G["waitingForAction"]:
229+
cache_state(self.G["waitingFor"], self.G)
176230
action = self.chooseaction()
177231
if action == None:
178232
raise ValueError("All actions must return a value!")
179-
233+
180234
cmdstr = self.actionToCmd(action)
181-
print(f'CMD: {cmdstr}')
182235
self.sendcmd(cmdstr)
183-
except socket.error:
236+
except socket.error as e:
237+
print(e)
238+
print("Socket error, reconnecting...")
184239
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
185-
self.sock.settimeout(1.0)
240+
self.sock.settimeout(1)
241+
self.sock.connect(self.addr)
242+
243+
def run(self):
244+
while self.running:
245+
self.run_step()

0 commit comments

Comments
 (0)