-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathLondon_mull_Tron.py
139 lines (128 loc) · 5.21 KB
/
London_mull_Tron.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import random
deck = {
'Tower': 4,
'Plant': 4,
'Mine': 4,
'Map': 4,
'Karn': 8,
'Chromatic': 8,
'Scrying': 4,
'Other': 24
}
def binom(n, k):
"""
Parameters:
n - Number of elements of the entire set
k - Number of elements in the subset
It should hold that 0 <= k <= n
Returns - The binomial coefficient n choose k that represents the number of ways of picking k unordered outcomes from n possibilities
"""
answer = 1
for i in range(1, min(k, n - k) + 1):
answer = answer * (n + 1 - i) / i
return int(answer)
def multivariate_hypgeom(deck, needed):
"""
Parameters:
deck - A dictionary of cardname : number of copies
needed - A dictionary of cardname : number of copies
It should hold that the cardname keys of deck and needed are identical
Returns - the multivariate hypergeometric probability of drawing exactly the cards in 'needed' from 'deck' when drawing without replacement
"""
answer = 1
sum_deck = 0
sum_needed = 0
for card in deck.keys():
answer *= binom(deck[card], needed[card])
sum_deck += deck[card]
sum_needed += needed[card]
return answer / binom(sum_deck, sum_needed)
def determine_T3Karn(handsize = 7):
"""
Parameters:
handsize - Should only be used for Vancouver rule. Represents the number of cards you mulligan towards
Returns - a number that represents the probability of finding an opening hand that guarantees a turn-3 payoff card
We keep if and only if our hand contains at least:
- One of each Tron piece and a payoff card,
- Two Tron pieces, an Expedition Map, and a payoff card,
- Two Tron pieces, a Chromatic artifacts, a Sylvan Scrying, and a payoff card.
"""
Tron_success_prob = 0
for Tower in [0, 1, 2, 3, 4]:
for Plant in [0, 1, 2, 3, 4]:
for Mine in [0, 1, 2, 3, 4]:
for Map in [0, 1, 2, 3, 4]:
for Karn in [1, 2, 3, 4]:
for Chromatic in [0, 1, 2, 3]:
for Scrying in [0, 1, 2, 3]:
#Note that a payoff card is guaranteed because we started the list for Karn at 1
NumberTronPieces = min(Plant, 1) + min(Mine, 1) + min(Tower, 1)
keep_condition = NumberTronPieces >= 3 or (NumberTronPieces >= 2 and Map >=1) or (NumberTronPieces >= 2 and Chromatic >=1 and Scrying>=1)
if Tower + Plant + Mine + Map + Karn + Chromatic + Scrying <= handsize and keep_condition:
needed = {}
needed['Tower'] = Tower
needed['Plant'] = Plant
needed['Mine'] = Mine
needed['Karn'] = Karn
needed['Map'] = Map
needed['Chromatic'] = Chromatic
needed['Scrying'] = Scrying
needed['Other'] = handsize - Tower - Plant - Mine - Karn - Map - Chromatic - Scrying
Tron_success_prob += multivariate_hypgeom(deck, needed)
return Tron_success_prob
def simulate_T3Karn(handsize = 7):
"""
Parameters:
handsize - Should only be used for Vancouver rule. Represents the number of cards you mulligan towards
Returns - a number that approximates via simulation the probability of finding an opening hand that guarantees a turn-3 payoff card
We keep if and only if our hand contains at least:
- One of each Tron piece and a payoff card,
- Two Tron pieces, an Expedition Map, and a payoff card,
- Two Tron pieces, a Chromatic artifacts, a Sylvan Scrying, and a payoff card.
"""
num_iterations = 10 ** 6
count_good_hands = 0
for _ in range(num_iterations):
decklist = []
for card in deck.keys():
decklist += [card] * deck[card]
random.shuffle(decklist)
hand = {
'Tower': 0,
'Plant': 0,
'Mine': 0,
'Map': 0,
'Karn': 0,
'Chromatic': 0,
'Scrying': 0,
'Other': 0
}
for _ in range(handsize):
hand[decklist.pop(0)] += 1
NumberTronPieces = min(hand['Plant'], 1) + min(hand['Mine'], 1) + min(hand['Tower'], 1)
if hand['Karn'] >=1 and (NumberTronPieces >= 3 or (NumberTronPieces >= 2 and hand['Map']>=1) or (NumberTronPieces >= 2 and hand['Chromatic']>=1 and hand['Scrying']>=1)):
count_good_hands += 1
return count_good_hands/num_iterations
T3Karn_success = determine_T3Karn()
T3Karn_failure = 1 - T3Karn_success
expected_hand_size = 0
print("London probability of opening hand guaranteeing a turn-3 payoff card?")
for mulligans in range(4):
print(f'When willing to mull down to {7 - mulligans}, probability is {(1 - (T3Karn_failure ** (mulligans + 1))) * 100:.2f}%.')
if (mulligans < 3):
expected_hand_size += (7 - mulligans) * (T3Karn_failure ** mulligans) * T3Karn_success
if (mulligans == 3):
expected_hand_size += 4 * (T3Karn_failure ** 3)
print("Expected hand size when keeping: " + str(round(expected_hand_size, 2)))
print('\nFor verification, simulation shows that the 7-card probability is: ' + str(round(100 * simulate_T3Karn(), 2))+"%")
print("\nVancouver probability of opening hand guaranteeing a turn-3 payoff card?")
Prob_no_Tron_so_far = 1
expected_hand_size = 0
for mulligans in range(4):
if (mulligans < 3):
expected_hand_size += (7 - mulligans) * Prob_no_Tron_so_far * determine_T3Karn(7 - mulligans)
if (mulligans == 3):
expected_hand_size += 4 * Prob_no_Tron_so_far
Prob_no_Tron_so_far = Prob_no_Tron_so_far * (1 - determine_T3Karn(7 - mulligans))
print(f'When willing to mull down to {7-mulligans}, probability is {(1 - Prob_no_Tron_so_far) * 100:.2f}%.')
print("Expected hand size when keeping: " + str(round(expected_hand_size, 2)))