Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # -*- coding: utf-8 -*-
- """
- Created on Sun Oct 11 15:11:05 2020
- @author: Alex Majlaton
- """
- import random, copy
- import pandas as pd
- from statistics import mean
- from time import process_time
- ##### MULLIGAN ENGINES #####
- # Here I'm writing a few functions that implement various mulligan strategies.
- # Note: The way these functions are implemented, the cards you remove from
- # your mulligan hands are never actually put on the bottom of your deck.
- # I don't expect this to matter for these simulations, but it's
- # important to note in case this code gets reused again somewhere.
- # This function is what it sounds like - you never mulligan.
- def neverMull(deck):
- starting_deck = copy.copy(deck)
- random.shuffle(starting_deck)
- opening_hand = starting_deck[:7]
- starting_deck = starting_deck[7:]
- return starting_deck, opening_hand
- # This function takes a starting deck, a starting hand size, and a starting
- # number of bolts as an input and generates that starting hand.
- # The purpose is to use it in a series of loops to simulate games with
- # every possible opening hand.
- def startingHandSize(deck, n, b):
- starting_deck = copy.copy(deck)
- opening_hand = []
- for i in range(b):
- starting_deck.remove(2)
- opening_hand.append(2)
- for j in range(n-b):
- starting_deck.remove(1)
- opening_hand.append(1)
- random.shuffle(starting_deck)
- return starting_deck, opening_hand
- # This was the original policy I wrote, before I simulated every hand.
- # It was based only on my intuition after considering every hand.
- # A rough description is this:
- #
- # 7 cards: Keep 4, 5, or 6 bolts.
- # 6 cards: Keep 4, 5, or 6 bolts and start 4/2 if possible, then 5/1.
- # 5 cards: Keep 3, 4, 5, or 6 bolts and start 4/1 if possible, then 3/2.
- # 4 cards: Keep 2-6 bolts and start 3/1 if possible, then 2/2.
- # 3 cards: Keep 2-7 bolts and start 2/1 if possible, then 3/0.
- # 2 cards: Keep 1-7 bolts and start 1/1, if possible, then 2/0.
- # 1 card: Keep and start a bolt if you have one.
- def londonMull(deck):
- mulls = 0
- keep = 0
- while (keep == 0):
- starting_deck = copy.copy(deck)
- random.shuffle(starting_deck)
- opening_hand = starting_deck[:7]
- starting_deck = starting_deck[7:]
- OHB = opening_hand.count(2)
- if mulls == 0:
- if OHB in range(4,7):
- keep = 1
- else:
- mulls += 1
- elif mulls == 1:
- if OHB == 4:
- keep = 1
- opening_hand = [2,2,2,2,1,1]
- elif OHB in range(5,7):
- keep = 1
- opening_hand = [2,2,2,2,2,1]
- else:
- mulls += 1
- elif mulls == 2:
- if OHB in range(4,7):
- keep = 1
- opening_hand = [2,2,2,2,1]
- elif OHB == 3:
- keep = 1
- opening_hand = [2,2,2,1,1]
- else:
- mulls += 1
- elif mulls == 3:
- if OHB in range(3,7):
- keep = 1
- opening_hand = [2,2,2,1]
- elif OHB == 2:
- keep = 1
- opening_hand = [2,2,1,1]
- else:
- mulls += 1
- elif mulls == 4:
- if OHB in range(2,7):
- keep = 1
- opening_hand = [2,2,1]
- elif OHB == 7:
- keep = 1
- opening_hand = [2,2,2]
- else:
- mulls += 1
- elif mulls == 5:
- if OHB == 7:
- keep = 1
- opening_hand = [2,2]
- elif OHB >= 1:
- keep = 1
- opening_hand = [2,1]
- else:
- mulls += 1
- elif mulls == 6:
- keep = 1
- if OHB == 7:
- opening_hand = [2]
- else:
- opening_hand = [1]
- return starting_deck, opening_hand
- # This is the policy described in the blog post.
- def mullPolicyTest(deck):
- mulls = 0
- keep = 0
- while (keep == 0):
- starting_deck = copy.copy(deck)
- random.shuffle(starting_deck)
- opening_hand = starting_deck[:7]
- starting_deck = starting_deck[7:]
- OHB = opening_hand.count(2)
- if mulls == 0:
- # here is where you can adjust the range of 7 card keeps.
- # 4 and 5 represent number of bolts.
- # to keep 6 bolts, add a 6 to the list.
- if OHB in [4,5]:
- keep = 1
- else:
- mulls += 1
- elif mulls == 1:
- if OHB in [4,5,6]:
- keep = 1
- if OHB == 6:
- opening_hand = [2,2,2,2,2,1]
- else:
- opening_hand = [2,2,2,2,1,1]
- else:
- mulls += 1
- elif mulls == 2:
- if OHB in [3,4,5,6]:
- keep = 1
- if OHB == 3:
- opening_hand = [2,2,2,1,1]
- else:
- opening_hand = [2,2,2,2,1]
- else:
- mulls += 1
- elif mulls == 3:
- keep = 1
- if OHB in [3,4,5,6]:
- opening_hand = [2,2,2,1]
- elif OHB == 2:
- opening_hand = [2,2,1,1]
- else:
- # even though you've kept 4 cards you're adding 1 to the
- # mulls counter to signify that you've kept a "bad" 4 card
- # hand as a way to track how often they occur
- mulls += 1
- if OHB == 7:
- opening_hand = [2,2,2,2]
- elif OHB == 1:
- opening_hand = [2,1,1,1]
- elif OHB == 0:
- opening_hand = [1,1,1,1]
- return starting_deck, opening_hand
- # create empty lists for scorekeeping
- boltsInDeck = []
- avgKillTurn = []
- # use these if you're simulating every hand
- # numStartingCards = []
- # numStartingBolts = []
- # timekeeping
- total_time = float(0)
- # If you're simulating every hand, uncomment these lines and use them to
- # loop with the startingHandSize function.
- # for cards in range(8):
- # for spells in range(cards+1):
- # The big loop begins here.
- # Looping through desired values of lands and bolts in the deck.
- # These ranges can be adjusted.
- for i in range(8,25):
- t1_start = process_time()
- # create empty starting deck list
- deck = []
- lands = i
- # adjust deck size here. 60 = regular constructed deck, 99 = EDH, etc.
- bolts = 60 - i
- # populate the deck with lands and bolts
- for l in range(lands):
- deck.append(1)
- for b in range(bolts):
- deck.append(2)
- # create empty list for scorekeeping
- scores = []
- # choose number of iterations to run here
- for j in range(10000):
- # this is where you decide what mulligan policy you're testing
- starting_deck, opening_hand = mullPolicyTest(deck)
- #starting_deck, opening_hand = startingHandSize(deck, cards, spells)
- #starting_deck, opening_hand = londonMull(deck)
- #starting_deck, opening_hand = neverMull(deck)
- turn = 0
- # adjustable to any life total
- oppLife = 20
- # adjustable to any damage spell - Shock would be 2 for ex.
- boltDmg = 3
- # using the lists to define the starting gameplay values
- landsInPlay = 0
- landsInHand = opening_hand.count(1)
- boltsInHand = opening_hand.count(2)
- while oppLife > 0:
- turn += 1
- # strategy for turn 1:
- # if you have a land, play it
- # if did and you have a bolt, cast it
- if turn == 1:
- if landsInHand >= 1:
- landsInPlay += 1
- landsInHand -= 1
- manaLeft = landsInPlay
- if boltsInHand >= 1 and manaLeft == 1:
- oppLife -= boltDmg
- manaLeft -= 1
- boltsInHand -= 1
- # general strategy after turn 1:
- # play a land if you have one
- # then play every single bolt you have mana for
- if turn > 1:
- # pop(0) pulls from the front of the list
- drawStep = starting_deck.pop(0)
- if drawStep == 1:
- landsInHand += 1
- if drawStep == 2:
- boltsInHand += 1
- if landsInHand > 0:
- landsInPlay += 1
- landsInHand -= 1
- manaLeft = landsInPlay
- if boltsInHand >= manaLeft:
- boltsInHand -= manaLeft
- oppLife -= (manaLeft * boltDmg)
- elif boltsInHand < manaLeft:
- oppLife -= (boltsInHand * boltDmg)
- boltsInHand = 0
- # discard to hand size
- if boltsInHand > 7:
- boltsInHand = 7
- # add the score (i.e. the turn you killed on) to the scores list
- scores.append(turn)
- # append the results to two lists, use to make a dataframe
- # if you want CSV output
- boltsInDeck.append(bolts)
- avgKillTurn.append(mean(scores))
- # use these if you're simulating every hand
- #numStartingCards.append(cards)
- #numStartingBolts.append(spells)
- t1_stop = process_time()
- total_time += (t1_stop - t1_start)
- print("Split time:", t1_stop - t1_start)
- print("Elapsed time:", total_time)
- print("Total time:", total_time)
- # if you want a dataframe & CSV output
- results = pd.DataFrame(
- {'bolts in deck': boltsInDeck,
- 'average kill turn': avgKillTurn
- # use these if you're simulating every hand, don't forget the comma above
- #'starting hand size': numStartingCards,
- #'starting bolts': numStartingBolts
- })
- results.to_csv("YOUR DIRECTORY HERE/bolt_sim_allhands_60_100k.csv", index=False)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement