Have you ever played bala...precipice???
nc precipice.challs.m0lecon.it 14615
We're given a lengthy Python interface that looks like this:
#!/usr/bin/env python3
from numpy import float32
from game_state import GameState
from scoring import base_scores
def play_round(state):
assert not state.round_over
last_cmd = ""
try:
while not state.round_over:
ts = input(">")
if len(ts) == 0:
ts = last_cmd
for t in ts.split(" "):
if len(t) == 0:
continue
elif t in {"?", "h", "help", }:
print(
" \"?\" or \"h\" or \"help\" to show this message",
" \"v\" or \"verbose\" to toggle verbose ui",
" \"s\" or \"sort\" to toggle sorting by rank or by suit",
" <a number> to select card by number",
" \"p\" or \"play\" to play hand",
" \"d\" or \"discard\" to discard hand",
" \"a\" or \"ability\" to see the jokers abilities",
" \"l\" or \"level\" to see hand level and scoring",
sep="\n"
)
elif t in {"v", "verbose", }:
state.toggle_verbose_ui()
elif t in {"s", "sort", }:
state.toggle_sorting_order()
elif t in {"p", "play", }:
state.play()
elif t in {"d", "discard", }:
state.discard()
elif t.isdecimal():
state.select(int(t)-1)
elif t in {"a", "ability", }:
for i, j in enumerate(state.jokers, start=1):
print(
f" -{i}: {j.name}: {j.description} {j.currently(state)}")
elif t in {"l", "level", }:
for hand_type in base_scores.keys():
hand_level = state.hand_levels[hand_type]
chips, mult = [
b * hand_level for b in base_scores[hand_type]]
print(f" {hand_type}: {hand_level}: ${chips} X{mult}")
else:
print(f"command not supported: {t!r}")
last_cmd = ts
except KeyboardInterrupt:
pass
def play_game(state):
state.in_shop()
last_cmd = ""
try:
while not state.game_over:
t = input(">").strip()
if len(t) == 0:
t = last_cmd
if t in {"?", "h", "help", }:
print(
" \"?\" or \"h\" or \"help\" to show this message",
" \"v\" or \"verbose\" to toggle verbose ui",
" \"p\" or \"play\" to buy in and play the next ante",
" \"f\" or \"forfit\" to forfit (Loser)",
f" \"r\" or \"reroll\" to reroll the shop (${state.reroll_shop_price})",
" <a number> to select shop joker by number",
" \"b\" or \"buy\" to buy select jokers",
" -<a number> to select owned joker by number",
" \"s\" or \"sell\" to sell select jokers",
sep="\n"
)
elif t in {"v", "verbose", }:
state.toggle_verbose_ui()
elif t in {"p", "play", }:
state.out_shop()
state.buy_in()
play_round(state)
state.round_ended()
state.in_shop()
elif t in {"f", "forfit", }:
state.quit()
break
elif t in {"r", "reroll", }:
state.reroll_shop()
elif t.isdecimal():
state.select_shop_joker(int(t)-1)
elif t[0] == "-" and t[1:].isdecimal():
state.select_joker(int(t[1:])-1)
elif t in {"b", "buy", }:
state.buy_jokers()
elif t in {"s", "sell", }:
state.sell_jokers()
else:
print(f"command not supported: {t!r}")
last_cmd = t
except KeyboardInterrupt:
pass
if __name__ == "__main__":
state = GameState(
initial_score=0,
initial_hands=5,
initial_discards=5,
)
play_game(state)Reading through the source code, it looks like the server implements a game similar to Balatro, and the goal is to beat the last ante for the flag:
@renders
def round_ended(self):
assert not self.game_over
assert self.round_over
assert self.hand is not None
if self.current_ante == len(self.antes) - 1:
import os
# import sys
self.header += [
CardClass.joker_art,
choice([
"You Aced it!",
"You dealt with that pretty well!",
"Looks like you weren't bluffing!",
"Too bad these chips are all virtual...",
"How the turn tables.",
"Looks like I've taught you well!",
"You made some heads up plays!",
"Good thing I didn't bet against you!",
]),
]
self.message.append("GG")
with open("/flag", "r") as f:
flag = f.read()
self.message.append(flag)
import sys
print("FLAGGED", file=sys.stderr)
print("ante:", self.current_ante, file=sys.stderr)
print("score:", self.score, file=sys.stderr)
print("deck_len:", len(self.game_deck), file=sys.stderr)
list(map(lambda j: print(j.name, j.description, j.currently(self), file=sys.stderr), self.jokers))
self.game_over = True
return
self.game_deck += self.hand
self.game_deck += self.discard_pile
self.discard_pile.clear()
# ...antes = [
0,
100,
300,
800,
2000,
5000,
11000,
20000,
35000,
50000,
110000,
1560000,
17200000,
1300000000,
147000000000,
12.9e13,
17.7e16,
18.6e20,
14.2e25,
19.2e30,
19.2e36,
24.3e43,
29.7e50,
21.0e59,
25.8e67,
21.6e77,
22.4e87,
21.9e98,
28.4e109,
22.0e122,
22.7e135,
32.1e149,
39.9e163,
32.7e179,
34.4e195,
34.4e212,
32.8e230,
31.1e249,
32.7e268,
34.5e288,
34.8e309,
]But how do we score 34.8e309? In regular Balatro, scoring close to inf is only possible by stacking retriggers and xMult via for e.g. Perkeo + Observatory, Baron + Mime + steel cards + red seals, etc. Looking in cards.py, however, it's clear that many of these strategies are simply not implemented in this limited recreation of Balatro:
from enum import Enum
from itertools import chain
cards_mode = "txt"
cards_mode = "utf"
class CardEnhancement(Enum):
NONE = 0
GOLD = 1
STONE = 2
STEEL = 3
# ANSI escape codes for colors
COLORS = {
"reset": "\033[0m",
"black": "\033[90m",
"red": "\033[91m",
"green": "\033[92m",
"yellow": "\033[93m",
"blue": "\033[94m",
"purple": "\033[95m",
"white_background": "\033[107m",
}
SUIT_COLORS = {
None: COLORS["reset"],
0: COLORS["red"],
1: COLORS["purple"],
2: COLORS["blue"],
3: COLORS["black"],
}
class Card:
suits = None
figures = None
ranks = None
ranks_sorting_map = {r: i for i, r in enumerate(
chain(range(2, 13+1), range(1, 1+1)),
start=2
)} | {None: 0}
is_joker = False
@classmethod
def clean_art(cls, art):
# Remove all ANSI color codes for rank/suit extraction
for color in COLORS.values():
art = art.replace(color, "")
return art
@classmethod
def color_art(cls, art, suit=None, rank=None, enhancement=CardEnhancement.NONE):
if enhancement == CardEnhancement.GOLD:
return f"{COLORS['yellow']}{art}{COLORS['reset']}"
elif enhancement == CardEnhancement.STONE:
return f"{COLORS['white_background']}{COLORS['black']}{art}๐ชจ{COLORS['reset']}"
assert enhancement == CardEnhancement.NONE
return f"{SUIT_COLORS[suit]}{art}{COLORS['reset']}"
@classmethod
def art_factory(cls, suit, rank, enhancement=CardEnhancement.NONE):
raise NotImplementedError()
@classmethod
def rank_from_art(cls, art):
raise NotImplementedError()
@classmethod
def suit_from_art(cls, art):
raise NotImplementedError()
def __init__(self, suit, rank, chips_bonus=0, mult_bonus=1, enhancement=CardEnhancement.NONE):
self.rank = rank
self.sorting_rank = self.ranks_sorting_map[self.rank]
self.suit = suit
self.chips_bonus = chips_bonus
self.mult_bonus = mult_bonus
self.enhancement = enhancement
self.art = self.art_factory(suit, rank, enhancement)
def __str__(self) -> str:
return self.art
def __repr__(self) -> str:
return self.__str__()
def is_face_card(self):
return self.rank in [11, 12, 13]
# ...
if cards_mode == "utf":
CardClass = UTFCard
elif cards_mode == "txt":
CardClass = TXTCard
else:
raise NotImplementedError(f"rendering mode {cards_mode!r} not supported")
cards = [CardClass(s, r) for s in range(4) for r in range(1, 13+1)]
assert len(CardClass.suits) == 4, [CardClass.suits, len(CardClass.suits)]
assert len(CardClass.figures) == 3, [CardClass.figures, len(CardClass.figures)]
assert len(CardClass.ranks) == 13, [CardClass.ranks, len(CardClass.ranks)]
assert len(cards) == len(CardClass.ranks) * \
len(CardClass.suits), [cards, len(cards)]
def sort_hand(hand, reverse=False, suit_first=False, ace_after_king=True):
if suit_first:
return sorted(hand, reverse=reverse, key=lambda c: (c.suit, c.sorting_rank if ace_after_king else c.rank))
else:
return sorted(hand, reverse=reverse, key=lambda c: (c.sorting_rank if ace_after_king else c.rank, c.suit))Indeed, we only have two "real" card enhancements: Gold and Stone (steel cards are unimplemented, and if one somehow gets created the server simply crashes). Furthermore, tarot cards, spectral cards, planets, and seals are all missing.
(As a side note, steel cards ended up being quite a bit of a red herring, as the server also defines a custom non-Balatro joker meant to create steel cards which also crashes the game if it was ever triggered; luckily, this joker was removed from the joker map and couldn't appear in a shop, unlike some other perpetrators...)
from cards import CardEnhancement
from .joker_base import JokerClass, JokerRarity
class SteelMask(JokerClass):
description = "All face cards become Steel cards when played"
rarity = JokerRarity.UNCOMMON
price = 7
def on_card_scoring(self, state, i, card, ignore_jokers=None):
if state.is_face_card(card):
raise NotImplementedError() # TODONote also that scoring is tied to your money (or more precisely, buying from the shop simply subtracts from your score), and score carries over between rounds. As opposed to normal Balatro, this allows us to pretty much buy anything we want from the shop, as hand scores are in the O(100) even on the first round, and rerolls cost a static $5 with jokers costing < $10.
@renders
def buy_jokers(self):
assert self.jokers_in_shop is not None
assert self.jokers_selected_in_shop is not None
if len(self.jokers_selected_in_shop) > self.max_joker_slots - len(self.jokers):
self.message.append(f"You can't have more than {self.max_joker_slots} jokers.")
return
price = 0
for joker_class in self.jokers_selected_in_shop:
price += joker_class.price
if price > self.score:
self.message.append(f"You don't have enough money to buy the selected jokers (${price!r}).")
return
while len(self.jokers_selected_in_shop) > 0:
joker = self.jokers_selected_in_shop.pop(0)
self.score -= joker.price
self.jokers.append(joker)
self.jokers_in_shop.remove(joker)
for joker in self.jokers:
joker.on_joker_buy(self, joker)
self.check_game_over()But even so, how do we even score close to e309? The key idea is this: jokers that provide retriggers are implemented in the game by re-calling the method on state that runs a specific phase of scoring.
from .joker_base import JokerClass, JokerRarity
class HangingChad(JokerClass):
description = "Retrigger first played card used in scoring 2 additional times"
rarity = JokerRarity.COMMON
price = 4
def on_card_scoring(self, state, i, card, ignore_jokers=None):
if i == 0:
# Retrigger 2 additional times
for _ in range(2):
state.message.append(f"{self.name}: retrigger card")
if ignore_jokers is None:
ignore_jokers = set()
ignore_jokers.add(self)
state.compute_card_score(i, card, ignore_jokers=ignore_jokers)
ignore_jokers.discard(self)(here, the Hanging Chad calls state.compute_card_score() again, which as a side effect re-triggers all jokers' on_card_scoring() callbacks). To avoid infinitely looping with themselves, jokers like the Hanging Chad add themselves to an ignore_jokers list, for which all jokers on the list are skipped in the rerun scoring phase.
Enter the Blueprint:
from copy import deepcopy
from .joker_base import JokerClass, JokerRarity
class Blueprint(JokerClass):
description = "Copies ability of Joker to the right"
rarity = JokerRarity.RARE
price = 10
def currently(self, state):
if self.copied_joker is None:
return "(none)"
return f"(currently: {self.copied_joker.name})"
def __init__(self):
super().__init__()
self.copied_joker = None
def find_joker_to_copy(self, state):
self.copied_joker = None
found = False
for joker in state.jokers:
if found:
self.copied_joker = deepcopy(joker)
break
if joker is self:
found = True
continue
def on_joker_buy(self, state, joker):
self.find_joker_to_copy(state)
if self.copied_joker is not None:
self.copied_joker.on_joker_buy(state, joker)
def on_joker_sell(self, state, joker):
self.find_joker_to_copy(state)
if self.copied_joker is not None:
self.copied_joker.on_joker_sell(state, joker)
def on_card_scoring(self, state, i, card, ignore_jokers=None):
if self.copied_joker is not None:
self.copied_joker.on_card_scoring(
state, i, card, ignore_jokers=ignore_jokers)
def on_hand_scoring(self, state, ignore_jokers=None):
if self.copied_joker is not None:
self.copied_joker.on_hand_scoring(
state, ignore_jokers=ignore_jokers)
def on_in_hand_card_scoring(self, state, i, card, ignore_jokers=None):
if self.copied_joker is not None:
self.copied_joker.on_in_hand_card_scoring(
state, i, card, ignore_jokers=ignore_jokers)
def on_in_hand_scoring(self, state, ignore_jokers=None):
if self.copied_joker is not None:
self.copied_joker.on_in_hand_scoring(
state, ignore_jokers=ignore_jokers)
def on_round_start(self, state):
if self.copied_joker is not None:
self.copied_joker.on_round_start(state)
def on_round_end(self, state):
if self.copied_joker is not None:
self.copied_joker.on_round_end(state)
def on_discard(self, state):
if self.copied_joker is not None:
self.copied_joker.on_discard(state)
def on_play(self, state):
if self.copied_joker is not None:
self.copied_joker.on_play(state)
def on_hand_updated(self, state):
if self.copied_joker is not None:
self.copied_joker.on_hand_updated(state)
def on_card_added_to_deck(self, state, card):
if self.copied_joker is not None:
self.copied_joker.on_card_added_to_deck(state, card)
def on_card_removed_from_deck(self, state, card):
if self.copied_joker is not None:
self.copied_joker.on_card_removed_from_deck(state, card)
def overrides_is_face_card(self, state, card):
if self.copied_joker is not None:
return self.copied_joker.overrides_is_face_card(state, card)
return super().overrides_is_face_card(state, card)Namely, Blueprint "copies" all the callbacks of self.copied_joker by simply passing down all arguments to self.copied_joker's callbacks. But in the ignore_joker case, if we e.g. have Blueprint copying Hanging Chad,
- On card score, we will call
blueprint.on_card_scoring() - Blueprint calls
blueprint.copied_joker.on_card_scoring()(where copied joker is Hanging Chad) - The copied callback adds
blueprint.copied_jokerto theignore_jokerslist and re-triggersstate.compute_card_score() - Since Blueprint is not in the
ignore_jokerslist, we callblueprint.on_card_scoring() - Blueprint calls
blueprint.copied_joker.on_card_scoring() - ...
for infinite recursion. As a remark, note that the joker order matters: any jokers to the left of the Blueprint will have their on_card_scoring() callbacks execute before Blueprint's, allowing them to actually happen before Blueprint triggers another recursion. Since there is no mechanism for reordering jokers, we need to be careful about the order we buy jokers in.
So how do we leverage this for high scoring? In the Blueprint + Hanging Chad case, we can essentially infinitely trigger any joker's .on_card_scoring() callback (until a stack overflow exception is thrown, which the game simply quietly catches and moves on to the next round):
def compute_score(self):
self.hand_chips, self.hand_mult = 0, 1
# print("initial scoring: ", self.hand_chips, self.hand_mult)
try: # fail safe on unimplemented jokers
self.compute_hand_score()
# print("hand_value scoring: ", self.hand_chips, self.hand_mult)
self.compute_cards_score()
# print("cards_value scoring:", self.hand_chips, self.hand_mult)
self.compute_in_hand_cards_score()
# print("in_hand_cards_value scoring:", self.hand_chips, self.hand_mult)
except Exception as e:
print(repr(e), file=stderr)For .on_card_scoring(), the best joker to retrigger is Photograph, giving us x2 mult per loop:
from .joker_base import JokerClass, JokerRarity
class Photograph(JokerClass):
description = "First played face card gives X2 Mult when scored"
rarity = JokerRarity.COMMON
price = 5
def __init__(self):
super().__init__()
self.is_first_face = False
def on_hand_scoring(self, state, ignore_jokers=None):
self.is_first_face = True
def on_card_scoring(self, state, i, card, ignore_jokers=None):
if state.is_face_card(card):
state.message.append(f"{self.name}: X2 Mult for first face card")
state.hand_mult *= 2
self.is_first_face = False
def on_play(self, state):
self.is_first_face = False(since their Photograph implementation is bugged, and never checks self.is_first_face for whether to apply the x2 mult on subsequent face card scorings).
However, even with 3 Photographs (their shop deduplication is broken) + Blueprint + Hanging Chad, due to the maximum recursion depth of 1000 we can only get a score of ~e301.
Instead, we can pivot to another ignore_jokers joker: Mime, which retriggers state.compute_in_hand_cards_score().
from .joker_base import JokerClass, JokerRarity
class Mime(JokerClass):
description = "Retrigger all card held in hand abilities"
rarity = JokerRarity.UNCOMMON
price = 5
def on_in_hand_scoring(self, state, ignore_jokers=None):
state.message.append(f"{self.name}: retrigger hand cards")
if ignore_jokers is None:
ignore_jokers = set()
ignore_jokers.add(self)
state.compute_in_hand_cards_score(ignore_jokers=ignore_jokers)
ignore_jokers.discard(self)Notably, .compute_in_hands_score() retriggers each card in the hand before recursing by triggering jokers, allowing us to get more than x2 mult per recursion:
def compute_in_hand_cards_score(self, ignore_jokers=None):
assert self.hand is not None
assert self.selected is not None
assert self.hand_chips is not None
assert self.hand_mult is not None
if ignore_jokers is None:
ignore_jokers = set()
for i, card in enumerate(self.hand):
if card not in self.selected:
self.compute_in_hand_card_score(
i, card, ignore_jokers=ignore_jokers)
for joker in self.jokers:
if joker not in ignore_jokers:
joker.on_in_hand_scoring(self, ignore_jokers=ignore_jokers)Thus, all we need to do is pair this with a Baron. We can see that with just 2 barons, we get up to ~x11 mult per loop with just 3 kings in hand:
from .joker_base import JokerClass, JokerRarity
class Baron(JokerClass):
description = "Each King held in hand gives X1.5 Mult"
rarity = JokerRarity.RARE
price = 8
def on_in_hand_card_scoring(self, state, i, card, ignore_jokers=None):
# King has rank 13
if card.rank == 13:
state.message.append(f"{self.name}: X1.5 Mult for King")
state.hand_mult *= 1.51.5^2 = 2.25 (K=1)
(1.5^2)^2 = 5.0625 (K=2)
(1.5^2)^3 = 11.390625 (K=3)
So the plan of attack is as follows:
- Play the first round randomly. (if we play 5 cards at a time, we're almost guaranteed to have enough money by the end of round)
- Reroll until we get the following jokers, in order:
[Baron, Baron, Blueprint, Mime] - On the next round, discard for 3 kings and play any hand; the infinite recursion should trigger, giving you
infscore. - Play out the rest of the antes until you win and get the flag.
and all thats left is scripting. For this challenge, though, even scripting is non-trivial and mildly annoying due to their game interface and PoW. Even funnier, due to other bugs in their implementation, the moment you try to select an Erosion in the shop the server immediately crashes; since you can't tell which jokers are which before attempting to select them, this is a completely random element that you just need to get lucky enough to avoid.
After some lengthy trial and error, we can create a script like so:
import subprocess
import pwn
# queue = ['Photograph', 'Photograph', 'Photograph', 'Blueprint', 'Hanging Chad']
queue = ['Baron', 'Baron', 'Blueprint', 'Mime']
def skip_shop():
conn.recvuntil(b'\n $')
money = conn.recvline()
print(f'shop: ${money.decode().strip()}')
conn.sendline(b'p')
def handle_shop():
conn.recvuntil(b'\n $')
money = conn.recvline()
print(f'shop: ${money.decode().strip()}')
# Query both jokers, reroll until we get all the jokers we want
while True:
b = query_shop_joker('1')
query_shop_joker('2' if not b else '1')
if len(queue) == 0:
break
conn.sendline(b'r')
conn.recvuntil(b'\n $')
money = int(conn.recvline())
print(f'reroll: ${money}')
if money < 100:
break
conn.recvuntil(b'\n>')
conn.sendline(b'p')
def query_shop_joker(index: str):
if len(queue) == 0:
return False
conn.recvuntil(b'\n>')
conn.sendline(index.encode())
conn.recvuntil(f'{index}: '.encode())
line = conn.recvline().decode()
name = line.partition(':')[0]
print('->', name)
want = queue[0]
if want == name:
queue.pop(0)
conn.sendline(b'b')
print(f'purchased {name}')
return True
else:
# Deselect the joker
conn.sendline(index.encode())
return False
def play_game():
for _ in range(5):
conn.recvuntil(b'\n>')
conn.sendline(b'a 1 2 3 4 5')
conn.recvuntil(b'\n>')
conn.sendline(b'p')
def play_game_kings():
for _ in range(5):
hand = conn.recvuntil(b'\n 1').decode().split('\n')[-2]
kings = hand.count('๐ฎ') + hand.count('๐พ') + hand.count('๐') + hand.count('๐')
aces = hand.count('๐ก') + hand.count('๐ฑ') + hand.count('๐') + hand.count('๐')
conn.recvuntil(b'\n>')
print(hand, kings, aces)
if kings >= 3:
break
# Find the indices to discard; we probably want to discard on the right first
# since we play cards from the left.
remove = list(range(1, 9))[-aces:] + list(range(1, 9))[:8 - kings - aces]
remove = remove[:5]
conn.sendline(f'a {" ".join(map(str, remove))}'.encode())
conn.recvuntil(b'\n>')
conn.sendline(b'd')
else:
print('not able to discard :(')
for _ in range(5):
conn.sendline(b'a 1')
conn.recvuntil(b'\n>').decode()
conn.sendline(b'p')
conn = pwn.remote('precipice.challs.m0lecon.it', 14615)
# Do PoW :)
cmd = conn.recvuntil(b'Result: ').decode().split('\n')[3]
res = subprocess.run(cmd, capture_output=True, shell=True).stdout.decode()
conn.sendline(res.encode())
skip_shop()
while len(queue) > 0:
play_game()
handle_shop()
while True:
play_game_kings()
if conn.recvuntil(b'GG', timeout=0.5) != b'':
print(conn.recvall().decode())
break
skip_shop()which we can run repeatedly (praying it never hits an Erosion)
reroll: $226
-> Riff Raff
-> Gros Michel
reroll: $221
-> Gros Michel
-> Baron
reroll: $216
-> Stone Joker
Traceback (most recent call last):
File "/mnt/c/Users/kevin/Downloads/ctf/balatro.py", line 112, in <module>
handle_shop()
File "/mnt/c/Users/kevin/Downloads/ctf/balatro.py", line 23, in handle_shop
query_shop_joker('2' if not b else '1')
File "/mnt/c/Users/kevin/Downloads/ctf/balatro.py", line 46, in query_shop_joker
conn.recvuntil(f'{index}: '.encode())
File "/home/ky28059/.local/lib/python3.12/site-packages/pwnlib/tubes/tube.py", line 381, in recvuntil
res = self.recv(timeout=self.timeout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/ky28059/.local/lib/python3.12/site-packages/pwnlib/tubes/tube.py", line 146, in recv
return self._recv(numb, timeout) or b''
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/ky28059/.local/lib/python3.12/site-packages/pwnlib/tubes/tube.py", line 216, in _recv
if not self.buffer and not self._fillbuffer(timeout):
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/ky28059/.local/lib/python3.12/site-packages/pwnlib/tubes/tube.py", line 195, in _fillbuffer
data = self.recv_raw(self.buffer.get_fill_size())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/ky28059/.local/lib/python3.12/site-packages/pwnlib/tubes/sock.py", line 56, in recv_raw
raise EOFError
EOFError
[*] Closed connection to precipice.challs.m0lecon.it port 14615until finally, we win:
[+] Opening connection to precipice.challs.m0lecon.it on port 14615: Done
shop: $0.0
shop: $366
-> Brainstorm
-> Burnt Joker
reroll: $366
-> Dusk
-> Hanging Chad
reroll: $361
-> Cavendish
-> Blueprint
reroll: $356
-> Flower Pot
-> Acrobat
reroll: $351
-> Hiker
-> Midas Mask
reroll: $346
-> Certificate
-> Ancient Joker
reroll: $341
-> Banner
-> Sock And Buskin
reroll: $336
-> Ramen
-> Brainstorm
reroll: $331
-> Misprint
-> Banner
reroll: $326
-> Scary Face
-> Sock And Buskin
reroll: $321
-> Vampire
-> Joker
reroll: $316
-> Abstract Joker
-> Midas Mask
reroll: $311
-> Abstract Joker
-> Ramen
reroll: $306
-> Mime
-> Smiley Face
reroll: $301
-> Baron
purchased Baron
-> Turtle Bean
reroll: $288
-> Drunkard
-> Hologram
reroll: $283
-> Baron
purchased Baron
-> Burnt Joker
reroll: $270
-> Certificate
-> Sock And Buskin
reroll: $265
-> Brainstorm
-> Joker
reroll: $260
-> Stone Joker
-> Joker Stencil
reroll: $255
-> Hiker
-> Hiker
reroll: $250
-> Joker Stencil
-> Hiker
reroll: $245
-> Loyalty Card
-> Golden Ticket
reroll: $240
-> Gros Michel
-> Certificate
reroll: $235
-> Blueprint
purchased Blueprint
-> Midas Mask
reroll: $220
-> Drunkard
-> Gros Michel
reroll: $215
-> Blue Joker
-> Ramen
reroll: $210
-> Hologram
-> Dusk
reroll: $205
-> Shoot The Moon
-> Gros Michel
reroll: $200
-> Blue Joker
-> Abstract Joker
reroll: $195
-> Raised Fist
-> Loyalty Card
reroll: $190
-> Baron
-> Dusk
reroll: $185
-> Sock And Buskin
-> Mime
purchased Mime
๐ข ๐ ๐น ๐ ๐พ ๐ ๐ ๐ก 1 3
๐ด ๐ต ๐ท ๐ง ๐น ๐ ๐ฝ ๐พ 1 0
๐ ๐ค ๐ ๐ฉ ๐ ๐ฝ ๐พ ๐ 2 0
๐ณ ๐ ๐ ๐ ๐ป ๐ฝ ๐พ ๐ 2 0
๐ ๐ ๐ ๐ช ๐ฝ ๐พ ๐ ๐ฑ 2 1
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐น ๐ ๐ฉ ๐ป ๐ฝ ๐ ๐ฑ 1 1
๐
๐ ๐ซ ๐ฝ ๐ญ ๐ ๐ฑ 1 1
๐ณ ๐ ๐ ๐ ๐ ๐ญ ๐ 1 0
๐ ๐ ๐ ๐ ๐ ๐ญ ๐ 1 0
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ ๐จ ๐น ๐ ๐ป ๐ ๐ 0 0
๐ ๐ป ๐ ๐ ๐ ๐พ ๐ 1 1
๐ ๐ถ ๐ ๐ ๐ ๐พ ๐ 1 1
๐ ๐ฅ ๐ ๐ ๐ ๐พ ๐ 1 1
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ข ๐ฃ ๐ ๐ ๐ถ ๐ ๐ 0 1
๐ด ๐ถ ๐ ๐ ๐ ๐ ๐ 0 1
๐ ๐ต ๐ ๐ ๐ ๐ ๐ 1 0
๐ง ๐ ๐ซ ๐ ๐ญ ๐พ ๐ 2 0
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ด ๐ท ๐ ๐ช ๐ ๐พ ๐ 1 1
๐ต ๐บ ๐ซ ๐ ๐พ ๐ฎ ๐ฑ 2 1
๐ข ๐ ๐ ๐ญ ๐พ ๐ฎ ๐ฑ 2 1
๐ค ๐ ๐ง ๐ ๐ป ๐พ ๐ฎ 2 0
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ ๐จ ๐ ๐ ๐ญ ๐ ๐ 2 0
๐ฆ ๐ธ ๐ป ๐ญ ๐ ๐ ๐ 2 1
๐ ๐ฅ ๐ ๐ง ๐น ๐ ๐ 2 0
๐ถ ๐ท ๐น ๐ ๐ ๐ ๐ก 2 1
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ฃ ๐ ๐ฅ ๐ ๐ฆ ๐ ๐ฎ 1 0
๐ง ๐ ๐ช ๐ป ๐ ๐ฎ ๐ก 2 1
๐ท ๐ ๐ ๐ ๐ฝ ๐ ๐ฎ 2 0
๐ฝ ๐ ๐ญ ๐ ๐ ๐ฎ ๐ 3 1
shop: $inf
๐ ๐ 0 0
๐ ๐ข ๐ ๐ต ๐ฅ ๐ ๐ฉ 0 0
๐ฅ ๐ ๐ฉ ๐ ๐ ๐ ๐ 1 1
๐ด ๐ ๐ฆ ๐ ๐ ๐ ๐ 1 0
๐ ๐ ๐ ๐ ๐ ๐ญ ๐ 1 0
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ข ๐
๐ ๐ ๐น ๐ฉ ๐ฝ 0 0
๐ ๐ ๐ฆ ๐น ๐ฉ ๐ฝ ๐ 0 0
๐ ๐ฉ ๐ช ๐ฝ ๐ ๐พ ๐ 1 1
๐ ๐ฅ ๐ง ๐ ๐ ๐พ ๐ฎ 2 0
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ณ ๐ ๐จ ๐ ๐ ๐ช ๐ 1 0
๐ ๐ ๐ ๐ช ๐ ๐ซ ๐ 1 0
๐ ๐ง ๐ ๐ ๐ซ ๐ฝ ๐ 1 0
๐ ๐ฅ ๐ซ ๐ฝ ๐ ๐ญ ๐ 1 0
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ ๐ธ ๐ ๐ ๐ ๐ฎ ๐ 2 1
๐ ๐ฆ ๐ซ ๐ ๐ ๐ฎ ๐ 3 1
shop: $inf
๐ ๐ 0 0
๐ฆ ๐ ๐ ๐ป ๐ ๐ฑ ๐ 0 2
๐ ๐ ๐ ๐ ๐ฝ ๐พ ๐ 1 1
๐ฒ ๐ณ ๐ท ๐จ ๐บ ๐ ๐ฝ 0 0
๐ ๐ ๐ง ๐บ ๐ช ๐ ๐ฝ 0 0
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ ๐ธ ๐ ๐ช ๐ ๐ ๐ 1 1
๐
๐ง ๐ ๐ ๐ ๐ ๐ฑ 1 1
๐ฒ ๐ฃ ๐ ๐ ๐ญ ๐ ๐ฑ 1 1
๐ ๐ฅ ๐ถ ๐ ๐ญ ๐ ๐ฑ 1 1
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ฅ ๐ ๐ท ๐ ๐ช ๐ ๐ 0 1
๐ด ๐ ๐ ๐ ๐ช ๐ ๐ฝ 0 0
๐ง ๐บ ๐ช ๐ ๐ฝ ๐ฑ ๐ก 0 2
๐ ๐ ๐ ๐ ๐ฝ ๐ ๐ก 1 1
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ฃ ๐ ๐ ๐ ๐ ๐ง ๐ 0 0
๐ ๐ง ๐ธ ๐จ ๐บ ๐ ๐ 0 0
๐ต ๐ฆ ๐บ ๐ ๐ ๐พ ๐ 2 0
๐ ๐ฝ ๐ ๐ ๐พ ๐ ๐ฎ 3 0
shop: $inf
๐ ๐ 0 0
๐ ๐ธ ๐ ๐ ๐ ๐ญ ๐ 1 0
๐ ๐ ๐จ ๐ ๐ญ ๐พ ๐ 2 0
๐ถ ๐น ๐ ๐ญ ๐พ ๐ ๐ 2 1
๐ณ ๐
๐ ๐ ๐พ ๐ ๐ 2 1
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ ๐ฃ ๐
๐ ๐ฆ ๐ ๐ฑ 0 1
๐ด ๐ถ ๐ฆ ๐ ๐ ๐ ๐ 0 0
๐ ๐ธ ๐บ ๐ ๐ ๐ ๐ก 0 1
๐ข ๐ ๐ ๐ฝ ๐ ๐ ๐ฎ 1 0
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ ๐ต ๐ท ๐ ๐ป ๐ ๐ 1 0
๐ ๐จ ๐ป ๐ ๐ ๐ ๐ 1 0
๐บ ๐ ๐ ๐ฝ ๐ ๐ญ ๐ 1 0
๐ ๐ ๐ซ ๐ ๐ญ ๐ ๐ 1 1
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ณ ๐ ๐ ๐ ๐น ๐ฎ ๐ก 1 1
๐ ๐
๐ ๐ท ๐น ๐ฎ ๐ 1 1
๐ ๐ ๐ง ๐น ๐ซ ๐ฎ ๐ฑ 1 1
๐ ๐ ๐ป ๐ ๐ซ ๐ ๐ฎ 2 0
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ฆ ๐ ๐ง ๐ ๐ ๐ ๐ก 1 1
๐ ๐ ๐ธ ๐ ๐ ๐ ๐ 2 0
๐ ๐ ๐ ๐ ๐ ๐ฑ ๐ 2 2
๐ฒ ๐ข ๐น ๐ช ๐ญ ๐ ๐ 1 1
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐จ ๐ ๐ ๐ช ๐พ ๐ ๐ฑ 2 1
๐ ๐ ๐ค ๐ง ๐ธ ๐พ ๐ 2 0
๐ ๐ท ๐ธ ๐ ๐ญ ๐พ ๐ 2 0
๐ข ๐ ๐ฃ ๐ ๐ญ ๐พ ๐ 2 0
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ ๐ ๐ด ๐ ๐ฉ ๐ช ๐ 0 0
๐ท ๐ฉ ๐ช ๐ป ๐ ๐ ๐ฑ 1 1
๐ฅ ๐จ ๐ซ ๐ฝ ๐ ๐ ๐ฑ 1 1
๐ ๐ง ๐ ๐ ๐พ ๐ ๐ฑ 2 1
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ฃ ๐ ๐ค ๐ถ ๐ธ ๐ ๐ 1 0
๐
๐ธ ๐ฉ ๐ ๐ซ ๐พ ๐ 2 0
๐ข ๐น ๐ ๐ซ ๐พ ๐ ๐ฎ 3 0
shop: $inf
๐ ๐ 0 0
๐ข ๐ค ๐ถ ๐ ๐ท ๐ ๐ญ 0 0
๐ท ๐ธ ๐ ๐ ๐ญ ๐ ๐ก 0 2
๐ต ๐ฆ ๐ง ๐ฝ ๐ญ ๐ ๐ก 0 2
๐ฃ ๐ ๐ฝ ๐ ๐ ๐ญ ๐ 0 1
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ฃ ๐ ๐ต ๐ ๐ธ ๐ช ๐ญ 0 0
๐ณ ๐ค ๐
๐ธ ๐ช ๐ ๐ญ 0 0
๐ ๐ ๐ ๐ช ๐ ๐ญ ๐ 1 0
๐ถ ๐ง ๐ ๐ ๐ญ ๐ ๐ก 1 1
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ด ๐
๐ธ ๐ ๐ ๐ ๐ 0 0
๐ง ๐ ๐ ๐ ๐ฝ ๐พ ๐ 2 0
๐น ๐ฝ ๐พ ๐ ๐ ๐ฎ ๐ฑ 4 1
shop: $inf
๐ ๐ 0 0
๐ ๐ต ๐ป ๐ ๐ ๐พ ๐ 1 1
๐ ๐ข ๐ถ ๐ง ๐ ๐ ๐พ 1 0
๐ ๐ ๐บ ๐ ๐ ๐ญ ๐พ 1 0
๐ ๐ ๐ฆ ๐จ ๐ ๐ญ ๐พ 1 0
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ด ๐ต ๐
๐ธ ๐ฉ ๐ช ๐ 0 0
๐ ๐ฃ ๐ ๐ฉ ๐ ๐ช ๐ 0 0
๐ ๐ ๐ ๐ช ๐ ๐ญ ๐พ 1 0
๐ถ ๐ท ๐ ๐ ๐ญ ๐พ ๐ 2 0
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ข ๐ ๐ ๐ง ๐ ๐ฉ ๐ซ 0 0
๐ ๐ฉ ๐ซ ๐ฝ ๐พ ๐ ๐ฎ 3 0
shop: $inf
๐ ๐ 0 0
๐ด ๐
๐ฅ ๐ฆ ๐ท ๐จ ๐ 0 0
๐ท ๐จ ๐ ๐ ๐ ๐ ๐ 1 0
๐ ๐ ๐ญ ๐พ ๐ ๐ ๐ก 2 2
๐ ๐ต ๐ถ ๐ ๐ ๐ฎ ๐ก 2 1
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ค ๐ฅ ๐ถ ๐ท ๐ ๐ ๐ 0 1
๐ ๐ ๐ธ ๐น ๐ ๐ฉ ๐ 0 0
๐ ๐
๐ ๐ ๐ฉ ๐ ๐ 1 0
๐ ๐ฉ ๐ ๐ช ๐ ๐ ๐ 1 0
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐
๐ ๐ ๐ ๐ ๐ ๐ฎ 1 0
๐ค ๐ ๐ง ๐ช ๐ ๐ ๐ฎ 1 0
๐ ๐ ๐ป ๐ ๐ ๐ ๐ฎ 2 0
๐ท ๐ ๐ ๐ญ ๐ ๐ ๐ฎ 3 0
shop: $inf
๐ ๐ 0 0
๐ ๐ ๐ ๐ ๐น ๐ซ ๐ 0 0
๐น ๐ ๐ป ๐ซ ๐ ๐ ๐ 1 0
๐ข ๐ ๐ ๐ ๐ ๐ ๐ 1 0
๐ธ ๐ฝ ๐ ๐ ๐ญ ๐ ๐ 2 0
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ด ๐ท ๐ ๐ฝ ๐ญ ๐พ ๐ฑ 1 1
๐
๐ ๐ ๐ ๐ญ ๐พ ๐ 2 0
๐ต ๐ฅ ๐ฉ ๐ช ๐ญ ๐พ ๐ 2 0
๐ ๐จ ๐ ๐ญ ๐พ ๐ ๐ก 2 1
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ ๐ ๐ ๐ ๐พ ๐ฎ ๐ 2 1
๐ง ๐ ๐น ๐ ๐พ ๐ฎ ๐ฑ 2 1
๐ ๐ ๐ฝ ๐พ ๐ ๐ฎ ๐ฑ 3 1
shop: $inf
๐ ๐ 0 0
๐ ๐ ๐ช ๐ป ๐ ๐ซ ๐ 1 0
๐ ๐ธ ๐ ๐ฉ ๐ ๐ซ ๐ 1 0
๐ ๐บ ๐ ๐ซ ๐พ ๐ ๐ฎ 3 0
shop: $inf
๐ ๐ 0 0
๐ ๐ ๐ ๐ช ๐ป ๐ซ ๐ฎ 1 0
๐ฃ ๐ท ๐ป ๐ซ ๐ ๐ฎ ๐ฑ 1 1
๐ ๐ ๐ธ ๐ ๐ ๐ฎ ๐ฑ 1 1
๐ฒ ๐ ๐จ ๐ ๐ ๐ ๐ฎ 2 0
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ด ๐ ๐ธ ๐ ๐ ๐ญ ๐ 1 0
๐ค ๐ฅ ๐ ๐บ ๐ป ๐ญ ๐ 1 0
๐จ ๐ ๐ป ๐ ๐ญ ๐ ๐ฑ 1 1
๐ฒ ๐ ๐ ๐ ๐น ๐ญ ๐ 1 0
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ ๐ ๐ ๐ฉ ๐ช ๐ฑ ๐ 0 2
๐ ๐ ๐ฃ ๐ช ๐ป ๐ญ ๐ 0 1
๐ฒ ๐ต ๐ฅ ๐ ๐ช ๐ป ๐ญ 0 0
๐ถ ๐ ๐บ ๐ ๐ช ๐ป ๐ญ 0 0
not able to discard :(
shop: $inf
๐ ๐ 0 0
๐ ๐ด ๐ถ ๐ ๐ ๐ฝ ๐พ 1 0
๐ ๐ ๐ ๐ ๐ฝ ๐พ ๐ฑ 1 1
๐ ๐
๐ฆ ๐ ๐ฝ ๐ ๐พ 1 0
๐ต ๐ธ ๐จ ๐บ ๐ฝ ๐ ๐พ 1 0
not able to discard :(
[+] Receiving all data: Done (116B)
[*] Closed connection to precipice.challs.m0lecon.it port 14615
ptm{7urns_0u7_7h3r3_w@s_p@p3r0nis_m0n3y_p00l_@7_7h3_b0770m_0f_7h3_pr3cipic3}
\x1b[2J
~$inf
$3.308591879916669e+233