Bobrokrot

Genshin Impact Wishing model

Sep 13th, 2021
6,128
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.73 KB | None | 0 0
  1. # -*- coding: utf-8 -*-
  2. """
  3. Created on Thu Sep  9 23:09:15 2021
  4.  
  5. @author: Bobrokrot @ Genshin Academy https://vk.com/genshinacademy
  6. Statistical model: Sengalev https://www.hoyolab.com/genshin/article/497840
  7. """
  8.  
  9. from random import random, choice
  10. import matplotlib.pyplot as plt
  11. import numpy as np
  12. import csv
  13.  
  14. class ItemGiver:
  15.     def __init__(self, star = 5, banner = "character"):
  16.         #Items: Upper_case = 5 star, lower case = 4 star
  17.         #A,B are features, Z,Y,X and so on are standard
  18.         self.star = star
  19.         self.banner = banner
  20.         if banner == "character" and star == 5:
  21.             self.rate = 0.5
  22.             self.featured_items = ["A"]
  23.             self.standard_items = ["Q", "R", "S", "T", "U"]
  24.             self.path = False
  25.         elif banner == "weapon" and star == 5:
  26.             self.rate = 0.75
  27.             self.featured_items = ["A", "B"]
  28.             self.standard_items = ["Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
  29.             self.path = True
  30.         elif banner == "character" and star == 4:
  31.             self.rate = 0.5
  32.             self.featured_items = ["a", "b", "c"]
  33.             self.standard_items = ["m"] #too many
  34.             self.path = False
  35.         elif banner == "weapon" and star == 4:
  36.             self.rate = 0.75
  37.             self.featured_items = ["a", "b", "c", "d", "e"]
  38.             self.standard_items = ["m"] #too many
  39.             self.path = False
  40.         elif banner == "standard" and star == 5:
  41.             #untested
  42.             self.rate = 0.5
  43.             self.featured_items = ["A", "B", "C", "D", "E"]
  44.             self.standard_items = ["V", "W", "X", "Y", "Z"]
  45.             self.path = False
  46.         elif banner == "standard" and star == 4:
  47.             self.rate = 0.5
  48.             self.featured_items = ["m"]
  49.             self.standard_items = ["m"]
  50.             self.path = False
  51.         else:
  52.             raise "star or banner is wrong"
  53.         self.last_featured = True
  54.         self.path_counter = 0
  55.         self.history = []
  56.    
  57.     def drop(self):
  58.         if self.path and self.path_counter == 2:
  59.             item = "A"
  60.             self.path_counter = 0
  61.             self.last_featured = True
  62.         else:
  63.             if not self.last_featured or random() < self.rate: #give featured
  64.                 item = choice(self.featured_items)
  65.                 self.last_featured = True
  66.             else:
  67.                 item = choice(self.standard_items)
  68.                 if self.banner != "standard":
  69.                     self.last_featured = False
  70.                 else:
  71.                     self.last_featured = True
  72.             if self.path:
  73.                 if item == "A":
  74.                     self.path_counter = 0
  75.                 else:
  76.                     self.path_counter += 1
  77.        
  78.         self.history.append(item)
  79.         return item
  80.            
  81.            
  82. class Banner:
  83.     def __init__(self, banner = "character"):
  84.         self.banner = banner
  85.         if banner == "character":
  86.             self._rate5 = .006
  87.             self._rate4 = 0.051
  88.             self._pity5 = 73 #pity starts after 73
  89.             self._pity4 = 8
  90.         elif banner == "weapon":
  91.             self._rate5 = .007
  92.             self._rate4 = 0.06
  93.             self._pity5 = 62 #pity starts after 62
  94.             self._pity4 = 7
  95.         else:
  96.             raise "banner is wrong"
  97.         self.giver5 = ItemGiver(5, banner)
  98.         self.giver4 = ItemGiver(4, banner)
  99.         self.roll_counter = 0
  100.         self.pity_counter4 = 1
  101.         self.pity_counter5 = 1
  102.         self.history = ""
  103.        
  104.     def roll(self):
  105.         self.roll_counter += 1
  106.        
  107.         prob5 = self._rate5 + max(0, (self.pity_counter5 - self._pity5)*10*self._rate5)
  108.         prob4 = self._rate4 + max(0, (self.pity_counter4 - self._pity4)*10*self._rate4)
  109.        
  110.         x = random()
  111.         if x < prob5: #5 star drop
  112.             item = self.giver5.drop()
  113.             self.pity_counter5 = 1
  114.             self.pity_counter4 += 1
  115.         elif x < prob4 + prob5: #4 star drop
  116.             item = self.giver4.drop()
  117.             self.pity_counter5 += 1
  118.             self.pity_counter4 = 1
  119.        
  120.         else: #3 star drop
  121.             item = "."
  122.             self.pity_counter5 += 1
  123.             self.pity_counter4 += 1
  124.        
  125.         self.history += item
  126.         return item
  127.        
  128.     def rollN(self, X):
  129.         for i in range(X):
  130.             self.roll()
  131.         return self.history
  132.    
  133.     def rollUntil(self, items = ["A"]):
  134.         #input a list of items
  135.         #stop rolling until an item from the list drops
  136.         voc = self.giver4.featured_items + self.giver4.standard_items \
  137.             + self.giver5.featured_items + self.giver5.standard_items
  138.         for desired in items:
  139.             if desired not in voc:
  140.                 raise "this item is not in the banner!"
  141.        
  142.         while(self.roll() not in items):
  143.             pass
  144.         return self.history
  145.    
  146.     def reset(self):
  147.         self.roll_counter = 0
  148.         self.pity_counter4 = 1
  149.         self.pity_counter5 = 1
  150.         self.history = ""
  151.  
  152. def pullUntil(banner, items = ["A"], constellation = 0, N = 1000000):
  153.     vals = np.zeros(10000) #distribution
  154.     for i in range(N):
  155.         banner.reset()
  156.         for j in range(0, constellation + 1):
  157.             banner.rollUntil(items)
  158.         vals[banner.roll_counter] += 1
  159.     return np.trim_zeros(vals, "b")
  160.  
  161. if __name__ == '__main__':
  162.    
  163.     banner = Banner("weapon") #weapon for weapon banner, character for character banner
  164.     N = 1000000 #size of sample
  165.     desired_items = ["A"] #list of items you are aiming to pull for
  166.     constellation = 0 #number of items+1 until stop
  167.     #vals = pullUntil(banner, ["A"], 4, N) # — pull until R5 banner weapon if C is weapon banner
  168.     #vals = pullUntil(banner, ["A, B"], 0, N) # — pull until either of 5 star weapons drop
  169.     #C = Banner("character")
  170.     #vals = pullUntil(banner, ["A"], 6, N) # — pull until C6 banner charater
  171.     #vals = pullUntil(banner, ["A", "Q", "R", "S", "T", "U"], 0, N) # — pull until either
  172.     # banner character (A) or any of the standard characters (QRSTU)
  173.     #vals = pullUntil(banner, ["a"], 4, N) # — pull until a particular rate-up C4 4 star
  174.    
  175.     vals = pullUntil(banner, desired_items, constellation, N)
  176.     n = vals.shape[0]
  177.     sums = np.zeros(n) #cumulative
  178.     for i in range(n):
  179.         sums[i] = sum(vals[:i+1])
  180.    
  181.     """
  182.     #export to file
  183.     with open('desired5starweapon.csv', 'w', newline="") as f:
  184.         writer = csv.writer(f)
  185.         writer.writerow(["N", "Распределение", "Кумулятивное"])
  186.         for i in range(1,n):
  187.             writer.writerow([i, "{:.7f}".format(vals[i] / N), sums[i] / N])
  188.     """
  189.    
  190.     plt.figure()
  191.     plt.bar(range(0,n), vals / N, width = 1)
  192.    
  193.     xran = np.rint(np.linspace(1, n, 37))
  194.     plt.xticks(xran, list(map(lambda x: '{:.0f}'.format(x), xran)))
  195.     #plt.xticks([1] + list(range(0,181,5))[1:])
  196.     S = np.linspace(0,0.1, 11)
  197.     plt.yticks(S, list(map(lambda x: '{:.0f}%'.format(x*100), S)))
  198.     plt.xlim(0,n)
  199.     plt.ylim(0,S[-1])
  200.     plt.grid()
  201.     plt.title("Распределение вероятности")
  202.    
  203.    
  204.     plt.figure()
  205.     plt.bar(range(0,n), sums / N, width = 1)
  206.     plt.xticks(xran, list(map(lambda x: '{:.0f}'.format(x), xran)))
  207.     S = np.linspace(0,1,21)
  208.     plt.yticks(S, list(map(lambda x: '{:.0f}%'.format(x*100), S)))
  209.     plt.xlim(0,n)
  210.     plt.ylim(0,S[-1])
  211.     plt.grid()
  212.     plt.title("Кумулятивная вероятность")
  213.     plt.show()
  214.    
  215.    
  216.     """
  217.     xran = list(range(1, 81))
  218.     vals = np.zeros(90)
  219.     _rate = .007
  220.     _pity = 62
  221.     sum = 0
  222.     for x in xran:
  223.         prob5 = min(1, _rate + max(0, (x - _pity)*10*_rate))
  224.         vals[x-1] = prob5
  225.     with open('5starweapon_rate.csv', 'w', newline="") as f:
  226.         writer = csv.writer(f, quoting=csv.QUOTE_NONE)
  227.         writer.writerow(["N", "Вероятность"])
  228.         for x in xran:
  229.             writer.writerow([x, vals[x-1]])
  230.     """
  231.    
Advertisement
Add Comment
Please, Sign In to add comment