Advertisement
Guest User

Gloomhaven Monte Carlo analysis of attack modifiers 0.0.4

a guest
Mar 29th, 2018
529
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.88 KB | None | 0 0
  1. # I, the copyright holder of this work, release this work into the public domain. This applies worldwide.
  2. #
  3. # In some countries this may not be legally possible; if so:
  4. # I grant anyone the right to use this work for any purpose, without any conditions, unless such conditions are required by law.
  5.  
  6. # Version 0.0.4
  7.  
  8. import sys
  9. import random
  10.  
  11. misses = 0.0
  12.  
  13. # Return the value of an attack, given a base attack value, the card drawn, the deck of remaining cards and the discard pile.
  14. # Also returns the deck with the card removed and all the discarded cards, including the card being considered.
  15. def cardValue(base, card, deck, discard):
  16.     global misses
  17.     discard.append(card)
  18.  
  19.     if card == '0':
  20.         return base
  21.     elif card == '+1':
  22.         return base + 1
  23.     elif card == '-1':
  24.         return base - 1
  25.     elif card == '+2':
  26.         return base + 2
  27.     elif card == '-2':
  28.         return base - 2
  29.     elif card == 'MISS':
  30.         misses += 1
  31.         deck += discard
  32.         random.shuffle(deck)
  33.         assert len(deck) == len(amd)
  34.         discard[:] = []
  35.         return 0
  36.     elif card == 'CURSE':
  37.         misses += 1
  38.         return 0        
  39.     elif card == '2x':
  40.         deck += discard
  41.         random.shuffle(deck)
  42.         assert len(deck) == len(amd)
  43.         discard[:] = []
  44.         return 2*base
  45.     elif card == 'BLESS':
  46.         return 2*base
  47.     elif card == 'R+1':
  48.         card2 = deck.pop()
  49.         return cardValue(base+1,card2,deck,discard)
  50.     elif card == 'R+0':
  51.         card2 = deck.pop()
  52.         return cardValue(base,card2,deck,discard)
  53.     else:
  54.         raise ValueError            
  55.  
  56. # Return the value of an attack with advantage, given a base attack value, the cards drawn, the deck of remaining cards and the
  57. # discard pile.  Also returns the deck with the cards removed and all the discarded cards, including the cards being considered.
  58. def advantageValue(base,card1, card2, deck, discard):
  59.     if card1 == 'R+1':
  60.         discard.append(card1)
  61.         return cardValue(base+1,card2,deck,discard)
  62.     elif card2 == 'R+1':
  63.         discard.append(card2)
  64.         return cardValue(base+1,card1,deck,discard)
  65.     if card1 == 'R+0':
  66.         discard.append(card1)
  67.         return cardValue(base,card2,deck,discard)
  68.     elif card2 == 'R+0':
  69.         discard.append(card2)
  70.         return cardValue(base,card1,deck,discard)
  71.     elif card1 == 'MISS':
  72.         discard.append(card1)
  73.         cv = cardValue(base,card2,deck,discard)
  74.         deck += discard
  75.         random.shuffle(deck)
  76.         assert len(deck) == len(amd)
  77.         discard[:] = []
  78.         return cv
  79.     elif card2 == 'MISS':
  80.         discard.append(card2)
  81.         cv = cardValue(base,card1,deck,discard)
  82.         deck += discard
  83.         random.shuffle(deck)
  84.         assert len(deck) == len(amd)
  85.         discard[:] = []
  86.         return cv
  87.     elif card1  == 'CURSE':
  88.         discard.append(card1)
  89.         cv = cardValue(base,card2,deck,discard)
  90.         return cv
  91.     elif card2  == 'CURSE':
  92.         discard.append(card2)
  93.         cv = cardValue(base,card1,deck,discard)
  94.         return cv
  95.  
  96.  
  97.     discard.append(card1)
  98.     discard.append(card2)
  99.  
  100.     assert len(deck)+len(discard) == len(amd)
  101.  
  102.     if card1 == '2x' or card2 == '2x':
  103.         deck += discard
  104.         random.shuffle(deck)
  105.         assert len(deck) == len(amd)
  106.         discard[:] = []
  107.         return 2*base
  108.     elif card1 == 'BLESS' or card2 == 'BLESS':
  109.         return 2*base
  110.    
  111.     elif card1 == '+2' or card2 == '+2':
  112.         return 2+base
  113.     elif card1 == '+1' or card2 == '+1':
  114.         return 1+base
  115.     elif card1 == '0' or card2 == '0':
  116.         return base
  117.     elif card1 == '-1' or card2 == '-1':
  118.         return base - 1
  119.    
  120.     assert card1 == '-2' or card2 == '-2'
  121.     return base - 2
  122.    
  123.  
  124. # Return the value of an attack with disadvantage, given a base attack value, the cards drawn, the deck of remaining cards and the
  125. # discard pile.  Also returns the deck with the cards removed and all the discarded cards, including the cards being considered.
  126. def disadvantageValue(base,card1, card2, deck, discard):
  127.     global misses
  128.    
  129.     if card1 == 'MISS' or card2 == 'MISS':
  130.         discard.append(card1)
  131.         discard.append(card2)
  132.         misses += 1
  133.         deck += discard
  134.         random.shuffle(deck)
  135.         assert len(deck) == len(amd)
  136.         discard[:] = []
  137.         return 0
  138.     if card1 == 'CURSE' or card2 == 'CURSE':
  139.         discard.append(card1)
  140.         discard.append(card2)
  141.         # We still need to shuffle if the 2nd card is a 2x, even without using it
  142.         if card1 == '2x' or card2 == '2x':
  143.             deck += discard
  144.             random.shuffle(deck)
  145.             assert len(deck) == len(amd)
  146.             discard[:] = []
  147.         misses += 1
  148.         return 0
  149.     elif card1 == 'R+1':
  150.         discard.append(card1)
  151.         return cardValue(base+1,card2,deck,discard)
  152.     elif card2 == 'R+1':
  153.         discard.append(card2)
  154.         return cardValue(base+1,card1,deck,discard)
  155.     if card1 == 'R+0':
  156.         discard.append(card1)
  157.         return cardValue(base,card2,deck,discard)
  158.     elif card2 == 'R+0':
  159.         discard.append(card2)
  160.         return cardValue(base,card1,deck,discard)
  161.  
  162.  
  163.     discard.append(card1)
  164.     discard.append(card2)
  165.  
  166.     # Even if we don't use the 'x2', we still need to reshuffle
  167.     if card1 == '2x' or card2 == '2x':
  168.         deck += discard
  169.         random.shuffle(deck)
  170.         assert len(deck) == len(amd)
  171.         discard[:] = []
  172.  
  173.     assert len(deck)+len(discard) == len(amd)
  174.  
  175.     if card1 == '-2' or card2 == '-2':
  176.         return base - 2
  177.     elif card1 == '-1' or card2 == '-1':
  178.         return base - 1
  179.     elif card1 == '0' or card2 == '0':
  180.         return base
  181.     elif card1 == '+1' or card2 == '+1':
  182.         return 1+base
  183.     elif card1 == '+2' or card2 == '+2':
  184.         return 2+base
  185.  
  186.     # You need either one 2x and one bless or 2 bless
  187.     assert (card1 == '2x' or 'BLESS') and (card2 == '2x' or 'BLESS')
  188.     return 2*base
  189.  
  190. # Given an attack modifier deck, amd, determine the average attack value with and without advantage.
  191. # Optionally takes a base attack value, defaulting to 3 if none is provided.
  192. def calculateAverageAttack(amd, base = None, normal = True, advantage = False, disadvantage = False):
  193.     global misses
  194.     if base is None:
  195.         base = 3
  196.     deck = list(amd)
  197.     random.shuffle(deck)
  198.     discard = []
  199.    
  200.     count = 1000000
  201.  
  202.     # Run without advantage or disadvantage
  203.     if normal == True:
  204.         total = 0.0 # A running total of the all "damage" calculated
  205.         misses = 0.0
  206.        
  207.         for x in range(0,count):
  208.             card = deck.pop()
  209.             total = total + cardValue(base, card, deck, discard)
  210.  
  211.         print ("Average attack:  ", (total / count))    # the average attack value
  212.         print ("Miss frequency:  ", (misses/count)) # percent of attacks that pull a null or curse
  213.  
  214.  
  215.     # Run with advantage
  216.     if advantage == True:
  217.         # reset
  218.         total = 0.0
  219.         misses = 0.0  
  220.         deck += discard
  221.         random.shuffle(deck)
  222.         discard[:] = []
  223.  
  224.         for x in range(0,count):
  225.             card1 = deck.pop()
  226.             card2 = deck.pop()
  227.             total = total + advantageValue(base, card1,card2, deck,discard);
  228.  
  229.         print ("Average attack with advantage:  ",total / count)  # the average attack value
  230.         print ("Miss frequency:  ", misses/count) # percent of attacks that pull a null or curse
  231.  
  232.     # Run with disadvantage
  233.     if disadvantage == True:
  234.         # reset
  235.         total = 0.0
  236.         misses = 0.0  
  237.         deck += discard
  238.         random.shuffle(deck)
  239.         discard[:] = []
  240.  
  241.         for x in range(0,count):
  242.             card1 = deck.pop()
  243.             card2 = deck.pop()
  244.             total = total + disadvantageValue(base, card1, card2, deck, discard);
  245.  
  246.         print ("Average attack with disadvantage:  ",total / count)  # the average attack value
  247.         print ("Miss frequency:  ", misses/count) # percent of attacks that pull a null or curse
  248.    
  249.  
  250. # Basic attack deck
  251. amd = ['0','0','0','0','0','0','+1','+1','+1','+1','+1','-1','-1','-1','-1','-1','-2','+2','MISS','2x']
  252. print ("   Base attack deck")
  253. print (amd)
  254. calculateAverageAttack(amd, advantage = True, disadvantage = True)
  255.  
  256. # Fully cursed deck
  257. 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']
  258. print ("   Ten curses")
  259. print (amd)
  260. calculateAverageAttack(amd, disadvantage = True)
  261.  
  262. # Out of curiosity, the difference between curse and miss
  263. 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']
  264. print ("   Eleven miss")
  265. print (amd)
  266. calculateAverageAttack(amd, disadvantage = True)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement