Last active
March 15, 2022 22:20
-
-
Save Nikolaj-K/251d454f8118126c908092882c21cc3b to your computer and use it in GitHub Desktop.
What's the optimal deck consisting of Lotus & Recall only?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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