Last active
March 10, 2024 13:03
-
-
Save Nikolaj-K/b9015c76e72f58020b8051c2f8f8f6ab to your computer and use it in GitHub Desktop.
Computing the best ratio of lotus to recall among lotus-recall-only decks
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
""" | |
Script discussed in | |
https://youtu.be/s5ud06udqT0 | |
""" | |
import random | |
NUM_GAME_SIMS = 10 ** 7 | |
Lotus = 'Lotus' | |
Recall = 'Recall' | |
DECK_SIZE = 60 | |
NUM_CARDS_STARTING_HAND = 7 | |
REQUIRED_RECALLS_TO_WIN = 18 # roof of (DECK_SIZE - NUM_CARDS_STARTING_HAND) / 3 | |
class Game: | |
def __init__(self, num_of_recalls): | |
self.num_of_recalls = num_of_recalls | |
self.num_of_lotuses = DECK_SIZE - num_of_recalls | |
def draw(self, num_cards): | |
for _ in range(num_cards): | |
if self.deck[0] == Recall: | |
self.recalls_in_hand += 1 | |
else: | |
self.mana_pool += 3 | |
self.deck = self.deck[1:] | |
def play_recall(self): | |
self.mana_pool -= 1 | |
self.recalls_in_hand -= 1 | |
self.draw(3) | |
def run(self): | |
self.deck = self.num_of_recalls * [Recall] + self.num_of_lotuses * [Lotus] | |
self.recalls_in_hand = 0 | |
self.mana_pool = 0 | |
# Get starting hand | |
for num_starting_hand in range(NUM_CARDS_STARTING_HAND + 1)[::-1]: | |
if num_starting_hand == 1: | |
return 1 # Too many mulligans, first turn kill not possibly anymore | |
random.shuffle(self.deck) | |
potential_hand = self.deck[:num_starting_hand] | |
if Lotus in potential_hand and Recall in potential_hand: | |
self.draw(num_starting_hand) | |
break # At least one recall and one lotus => good to go | |
while len(self.deck) >= 3: | |
if self.recalls_in_hand == 0 or self.mana_pool == 0: | |
return 1 # Out of recalls or out of mana | |
self.play_recall() | |
if self.recalls_in_hand >= REQUIRED_RECALLS_TO_WIN and self.mana_pool >= REQUIRED_RECALLS_TO_WIN: | |
return 0 # Win | |
return 1 # Milled yourself to death (TODO: This assumes 53 cards at start and not more (from potential mulligan)) | |
for num_recalls in (36, 37, 38, 39, 40): | |
game = Game(num_recalls) | |
fails = 0 | |
for _ in range(NUM_GAME_SIMS): | |
fails += game.run() | |
percent = "{:3.3f}".format(100 * fails / NUM_GAME_SIMS) | |
print(f"Simulated {NUM_GAME_SIMS} games starting with {num_recalls} recalls. Fails: {fails}, or {percent} %") | |
exit("Done.") | |
""" | |
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