Skip to content

Instantly share code, notes, and snippets.

@Nikolaj-K
Last active March 15, 2022 22:20
Show Gist options
  • Save Nikolaj-K/251d454f8118126c908092882c21cc3b to your computer and use it in GitHub Desktop.
Save Nikolaj-K/251d454f8118126c908092882c21cc3b to your computer and use it in GitHub Desktop.
What's the optimal deck consisting of Lotus & Recall only?
"""
Code discussed in the video
https://youtu.be/s5ud06udqT0
"""
from random import shuffle
NUM_GAME_SIMS: int = 1000
PRINT_LOG: bool = NUM_GAME_SIMS == 1
NUM_RECALLS_RANGE: int = range(60) # Most interesting range: (36, 37, 38, 39, 40)
Lotus, Recall = 'L', 'R'
REQUIRED_RECALLS_TO_WIN: int = 18 # roof of (60 - 7) / 3
class GameState:
def __init__(self, num_of_recalls: int) -> None:
self.num_of_recalls = num_of_recalls # const
self.num_of_lotuses = 60 - num_of_recalls # const
def reset(self) -> None:
self.mana_pool = 0
self.recalls_in_hand = 0
self.deck = self.num_of_recalls * [Recall] + self.num_of_lotuses * [Lotus]
shuffle(self.deck)
def draw(self, num_cards: int) -> None:
for _ in range(num_cards):
card, self.deck = self.deck[0], self.deck[1:]
if card == Recall:
self.recalls_in_hand += 1
else:
self.mana_pool += 3
def play_recall(self) -> None:
self.mana_pool -= 1
self.recalls_in_hand -= 1
self.draw(3)
def run_and_win(self) -> bool:
# Compute num_starting_hand (old school mulligans)
for num_starting_hand in range(7, 0, -1):
if num_starting_hand == 1:
if PRINT_LOG:
print(f"\tToo many mulligans.")
return False
potential_hand = self.deck[:num_starting_hand]
if Lotus in potential_hand and Recall in potential_hand:
break # At least one recall and one lotus => good to go
else:
shuffle(self.deck)
self.draw(num_starting_hand)
if PRINT_LOG:
print("\nShuffled starting deck: " + "".join(potential_hand) + " " + "".join(self.deck))
play = -1
while len(self.deck) >= 3:
play += 1
if PRINT_LOG:
print(f"\t\tPlay {play}, mana: {self.mana_pool}, recalls in hand: {self.recalls_in_hand}")
if self.recalls_in_hand == 0 or self.mana_pool == 0:
if PRINT_LOG:
print(f"\tOut of recalls or out of mana.")
return False
self.play_recall()
if self.recalls_in_hand >= REQUIRED_RECALLS_TO_WIN and self.mana_pool >= REQUIRED_RECALLS_TO_WIN:
if PRINT_LOG:
print(f"\tWin!")
print(f"\t\tTurn {turn}, mana: {self.mana_pool}, recalls in hand: {self.recalls_in_hand}")
return True
if PRINT_LOG:
print(f"\tOut of cards.")
return False # TODO: This assumes 53 cards at start and not more (from potential mulligan)
def reset_run_and_win(self) -> int:
"""
Play a game and return 0 if game is won first turn. Otherwise return 1.
(Assumes no zero mana counterspells from the opponent, etc.)
"""
self.reset()
return self.run_and_win()
for num_recalls in NUM_RECALLS_RANGE:
game_state = GameState(num_recalls)
wins = sum(game_state.reset_run_and_win() for _ in range(NUM_GAME_SIMS))
num_fails = NUM_GAME_SIMS - wins
print(
f"Simulated {NUM_GAME_SIMS} games starting with {num_recalls} recalls. " + \
f"Fails: {num_fails}, or " + "{:3.3f} %".format(100 * num_fails / NUM_GAME_SIMS)
)
"""
num_recalls = 10000
Simulated 10000 games starting with 23 recalls. Fails: 10000, or 100.000 %
Simulated 10000 games starting with 24 recalls. Fails: 10000, or 100.000 %
Simulated 10000 games starting with 25 recalls. Fails: 10000, or 100.000 %
Simulated 10000 games starting with 26 recalls. Fails: 10000, or 100.000 %
Simulated 10000 games starting with 27 recalls. Fails: 10000, or 100.000 %
Simulated 10000 games starting with 28 recalls. Fails: 10000, or 100.000 %
Simulated 10000 games starting with 29 recalls. Fails: 10000, or 100.000 %
Simulated 10000 games starting with 30 recalls. Fails: 10000, or 100.000 %
Simulated 10000 games starting with 31 recalls. Fails: 10000, or 100.000 %
Simulated 10000 games starting with 32 recalls. Fails: 9999, or 99.990 %
Simulated 10000 games starting with 33 recalls. Fails: 9982, or 99.820 %
Simulated 10000 games starting with 34 recalls. Fails: 9780, or 97.800 %
Simulated 10000 games starting with 35 recalls. Fails: 7793, or 77.930 %
Simulated 10000 games starting with 36 recalls. Fails: 3120, or 31.200 %
Simulated 10000 games starting with 37 recalls. Fails: 10, or 0.100 %
Simulated 10000 games starting with 38 recalls. Fails: 13, or 0.130 %
Simulated 10000 games starting with 39 recalls. Fails: 17, or 0.170 %
Simulated 10000 games starting with 40 recalls. Fails: 23, or 0.230 %
Simulated 10000 games starting with 41 recalls. Fails: 43, or 0.430 %
Simulated 10000 games starting with 42 recalls. Fails: 66, or 0.660 %
Simulated 10000 games starting with 43 recalls. Fails: 107, or 1.070 %
Simulated 10000 games starting with 44 recalls. Fails: 148, or 1.480 %
Simulated 10000 games starting with 45 recalls. Fails: 192, or 1.920 %
Simulated 10000 games starting with 46 recalls. Fails: 323, or 3.230 %
Simulated 10000 games starting with 47 recalls. Fails: 682, or 6.820 %
Simulated 10000 games starting with 48 recalls. Fails: 2524, or 25.240 %
Simulated 10000 games starting with 49 recalls. Fails: 7823, or 78.230 %
Simulated 10000 games starting with 50 recalls. Fails: 9738, or 97.380 %
Simulated 10000 games starting with 51 recalls. Fails: 10000, or 100.000 %
Simulated 10000 games starting with 52 recalls. Fails: 10000, or 100.000 %
Simulated 10000 games starting with 53 recalls. Fails: 10000, or 100.000 %
Simulated 10000 games starting with 54 recalls. Fails: 10000, or 100.000 %
Simulated 10000 games starting with 55 recalls. Fails: 10000, or 100.000 %
Simulated 10000 games starting with 56 recalls. Fails: 10000, or 100.000 %
Simulated 10000 games starting with 57 recalls. Fails: 10000, or 100.000 %
Simulated 10000 games starting with 58 recalls. Fails: 10000, or 100.000 %
Simulated 10000 games starting with 59 recalls. Fails: 10000, or 100.000 %
Simulated 10000 games starting with 60 recalls. Fails: 10000, or 100.000 %
num_recalls = 10000000
Simulated 10000000 games starting with 36 recalls. Fails: 3186269, or 31.863 %
Simulated 10000000 games starting with 37 recalls. Fails: 11688, or 0.117 %
Simulated 10000000 games starting with 38 recalls. Fails: 13387, or 0.134 %
"""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment