# I, the copyright holder of this work, release this work into the public domain. This applies worldwide. # # In some countries this may not be legally possible; if so: # I grant anyone the right to use this work for any purpose, without any conditions, unless such conditions are required by law. # Version 0.0.4 import sys import random misses = 0.0 # Return the value of an attack, given a base attack value, the card drawn, the deck of remaining cards and the discard pile. # Also returns the deck with the card removed and all the discarded cards, including the card being considered. def cardValue(base, card, deck, discard): global misses discard.append(card) if card == '0': return base elif card == '+1': return base + 1 elif card == '-1': return base - 1 elif card == '+2': return base + 2 elif card == '-2': return base - 2 elif card == 'MISS': misses += 1 deck += discard random.shuffle(deck) assert len(deck) == len(amd) discard[:] = [] return 0 elif card == 'CURSE': misses += 1 return 0 elif card == '2x': deck += discard random.shuffle(deck) assert len(deck) == len(amd) discard[:] = [] return 2*base elif card == 'BLESS': return 2*base elif card == 'R+1': card2 = deck.pop() return cardValue(base+1,card2,deck,discard) elif card == 'R+0': card2 = deck.pop() return cardValue(base,card2,deck,discard) else: raise ValueError # Return the value of an attack with advantage, given a base attack value, the cards drawn, the deck of remaining cards and the # discard pile. Also returns the deck with the cards removed and all the discarded cards, including the cards being considered. def advantageValue(base,card1, card2, deck, discard): if card1 == 'R+1': discard.append(card1) return cardValue(base+1,card2,deck,discard) elif card2 == 'R+1': discard.append(card2) return cardValue(base+1,card1,deck,discard) if card1 == 'R+0': discard.append(card1) return cardValue(base,card2,deck,discard) elif card2 == 'R+0': discard.append(card2) return cardValue(base,card1,deck,discard) elif card1 == 'MISS': discard.append(card1) cv = cardValue(base,card2,deck,discard) deck += discard random.shuffle(deck) assert len(deck) == len(amd) discard[:] = [] return cv elif card2 == 'MISS': discard.append(card2) cv = cardValue(base,card1,deck,discard) deck += discard random.shuffle(deck) assert len(deck) == len(amd) discard[:] = [] return cv elif card1 == 'CURSE': discard.append(card1) cv = cardValue(base,card2,deck,discard) return cv elif card2 == 'CURSE': discard.append(card2) cv = cardValue(base,card1,deck,discard) return cv discard.append(card1) discard.append(card2) assert len(deck)+len(discard) == len(amd) if card1 == '2x' or card2 == '2x': deck += discard random.shuffle(deck) assert len(deck) == len(amd) discard[:] = [] return 2*base elif card1 == 'BLESS' or card2 == 'BLESS': return 2*base elif card1 == '+2' or card2 == '+2': return 2+base elif card1 == '+1' or card2 == '+1': return 1+base elif card1 == '0' or card2 == '0': return base elif card1 == '-1' or card2 == '-1': return base - 1 assert card1 == '-2' or card2 == '-2' return base - 2 # Return the value of an attack with disadvantage, given a base attack value, the cards drawn, the deck of remaining cards and the # discard pile. Also returns the deck with the cards removed and all the discarded cards, including the cards being considered. def disadvantageValue(base,card1, card2, deck, discard): global misses if card1 == 'MISS' or card2 == 'MISS': discard.append(card1) discard.append(card2) misses += 1 deck += discard random.shuffle(deck) assert len(deck) == len(amd) discard[:] = [] return 0 if card1 == 'CURSE' or card2 == 'CURSE': discard.append(card1) discard.append(card2) # We still need to shuffle if the 2nd card is a 2x, even without using it if card1 == '2x' or card2 == '2x': deck += discard random.shuffle(deck) assert len(deck) == len(amd) discard[:] = [] misses += 1 return 0 elif card1 == 'R+1': discard.append(card1) return cardValue(base+1,card2,deck,discard) elif card2 == 'R+1': discard.append(card2) return cardValue(base+1,card1,deck,discard) if card1 == 'R+0': discard.append(card1) return cardValue(base,card2,deck,discard) elif card2 == 'R+0': discard.append(card2) return cardValue(base,card1,deck,discard) discard.append(card1) discard.append(card2) # Even if we don't use the 'x2', we still need to reshuffle if card1 == '2x' or card2 == '2x': deck += discard random.shuffle(deck) assert len(deck) == len(amd) discard[:] = [] assert len(deck)+len(discard) == len(amd) if card1 == '-2' or card2 == '-2': return base - 2 elif card1 == '-1' or card2 == '-1': return base - 1 elif card1 == '0' or card2 == '0': return base elif card1 == '+1' or card2 == '+1': return 1+base elif card1 == '+2' or card2 == '+2': return 2+base # You need either one 2x and one bless or 2 bless assert (card1 == '2x' or 'BLESS') and (card2 == '2x' or 'BLESS') return 2*base # Given an attack modifier deck, amd, determine the average attack value with and without advantage. # Optionally takes a base attack value, defaulting to 3 if none is provided. def calculateAverageAttack(amd, base = None, normal = True, advantage = False, disadvantage = False): global misses if base is None: base = 3 deck = list(amd) random.shuffle(deck) discard = [] count = 1000000 # Run without advantage or disadvantage if normal == True: total = 0.0 # A running total of the all "damage" calculated misses = 0.0 for x in range(0,count): card = deck.pop() total = total + cardValue(base, card, deck, discard) print ("Average attack: ", (total / count)) # the average attack value print ("Miss frequency: ", (misses/count)) # percent of attacks that pull a null or curse # Run with advantage if advantage == True: # reset total = 0.0 misses = 0.0 deck += discard random.shuffle(deck) discard[:] = [] for x in range(0,count): card1 = deck.pop() card2 = deck.pop() total = total + advantageValue(base, card1,card2, deck,discard); print ("Average attack with advantage: ",total / count) # the average attack value print ("Miss frequency: ", misses/count) # percent of attacks that pull a null or curse # Run with disadvantage if disadvantage == True: # reset total = 0.0 misses = 0.0 deck += discard random.shuffle(deck) discard[:] = [] for x in range(0,count): card1 = deck.pop() card2 = deck.pop() total = total + disadvantageValue(base, card1, card2, deck, discard); print ("Average attack with disadvantage: ",total / count) # the average attack value print ("Miss frequency: ", misses/count) # percent of attacks that pull a null or curse # Basic attack deck amd = ['0','0','0','0','0','0','+1','+1','+1','+1','+1','-1','-1','-1','-1','-1','-2','+2','MISS','2x'] print (" Base attack deck") print (amd) calculateAverageAttack(amd, advantage = True, disadvantage = True) # Fully cursed deck amd = ['0','0','0','0','0','0','+1','+1','+1','+1','+1','-1','-1','-1','-1','-1','-2','+2','MISS','2x','CURSE','CURSE','CURSE','CURSE','CURSE','CURSE','CURSE','CURSE','CURSE','CURSE'] print (" Ten curses") print (amd) calculateAverageAttack(amd, disadvantage = True) # Out of curiosity, the difference between curse and miss amd = ['0','0','0','0','0','0','+1','+1','+1','+1','+1','-1','-1','-1','-1','-1','-2','+2','MISS','2x','MISS','MISS','MISS','MISS','MISS','MISS','MISS','MISS','MISS','MISS'] print (" Eleven miss") print (amd) calculateAverageAttack(amd, disadvantage = True)