Advertisement
tzoonami

OWL ELO

Feb 26th, 2019
119
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 14.93 KB | None | 0 0
  1. import requests,random
  2. from operator import itemgetter
  3. from timeit import default_timer as timer
  4. from itertools import repeat as rep
  5. import multiprocessing
  6. import numpy as np
  7. from functools import total_ordering
  8. import copy
  9.  
  10. @total_ordering
  11. class Team:
  12.     def __init__(self, guid, name, abbrev,maps,wins,losses,mapwins,maplosses,division):
  13.         self.guid=guid
  14.         self.name=name
  15.         self.abbrev=abbrev
  16.         self.elo=1000
  17.         self.maps = maps
  18.         self.assaultdraws = 0
  19.         self.assaultdrawable = 0
  20.         self.hybriddraws = 0
  21.         self.hybriddrawable = 0
  22.         self.wins = wins
  23.         self.losses = losses
  24.         self.mapwins = mapwins
  25.         self.maplosses = maplosses
  26.         self.division = division
  27.         self.teamrecords = {}
  28.  
  29.     def __eq__(self,other):
  30.         return teamcmp(self,other)==0
  31.  
  32.     def __ne__(self,other):
  33.         return teamcmp(self,other)!=0
  34.  
  35.     def __lt__(self,other):
  36.         return teamcmp(self,other)<0
  37.  
  38.  
  39.     def updaterecord(self,other,scores):
  40.         if other.guid not in self.teamrecords.keys():
  41.             self.teamrecords[other.guid] = HTH()
  42.         if scores[0] > scores[1]:
  43.             self.teamrecords[other.guid].wins += 1
  44.             self.teamrecords[other.guid].mapwins += scores[0]
  45.             self.teamrecords[other.guid].maplosses += scores[1]
  46.         else:
  47.             self.teamrecords[other.guid].losses += 1
  48.             self.teamrecords[other.guid].mapwins += scores[0]
  49.             self.teamrecords[other.guid].maplosses += scores[1]
  50.            
  51.        
  52.  
  53. class HTH:
  54.     def __init__(self):
  55.         self.wins = 0
  56.         self.losses = 0
  57.         self.mapwins = 0
  58.         self.maplosses = 0
  59.  
  60.     def __str__(self):
  61.         return "{}-{} M {}-{}".format(self.wins,self.losses,self.mapwins,self.maplosses)
  62.  
  63. def teamcmp(A,B):
  64.     Adiff = A.mapwins - A.maplosses
  65.     Bdiff = B.mapwins - B.maplosses
  66.     if B.guid in A.teamrecords.keys():
  67.         AHdiff = A.teamrecords[B.guid].mapwins - A.teamrecords[B.guid].maplosses
  68.         BHdiff = B.teamrecords[A.guid].mapwins - B.teamrecords[A.guid].maplosses
  69.     else:
  70.         AHdiff = 0
  71.         BHdiff = 0
  72.     if (A.wins-A.losses) != (B.wins-B.losses):
  73.         retvalue = 1 if (A.wins-A.losses) > (B.wins-B.losses) else -1
  74.         return retvalue
  75.     elif Adiff != Bdiff:
  76.         retvalue = 1 if Adiff > Bdiff else -1
  77.         return retvalue
  78.     elif AHdiff != BHdiff:
  79.         retvalue = 1 if AHdiff > BHdiff else -1
  80.         return retvalue
  81.     else:
  82.         return 0 # god I don't know how to do this shit
  83.  
  84.  
  85. class Match:
  86.     def __init__(self,A,B,gamemaps,expectations):
  87.         self.A=A
  88.         self.B=B
  89.         self.gamemaps=gamemaps
  90.         self.expecations=expectations
  91.        
  92.     def simulate(self):
  93.         return scorekey[np.random.choice(range(len(scorekey)),p=expectations)]
  94.    
  95.        
  96.  
  97. def expected(A, B):
  98.     return 1 / (1 + 10 ** ((B - A) / 400))
  99. def elo(old, exp, score, k=24):
  100.     return old + k * (score - exp)
  101.  
  102. control = ["0x08000000000007E2","0x080000000000066D","0x08000000000004B7"]
  103. def tiebreakmap(mcmap):
  104.     tiebreaks = []
  105.     for cmap in control:
  106.         if mcmap != cmap:
  107.             tiebreaks.append(cmap)
  108.     return random.choice(tiebreaks)
  109. trials = 500000
  110. ptrials = 20000
  111. def simulate(A, B, gamemaps):
  112.     scorehist = {}
  113.     expectations = []
  114.     assaultdrawchance = (((float(A.assaultdraws)/A.assaultdrawable)+(float(B.assaultdraws)/B.assaultdrawable))/2)
  115.     hybriddrawchance = (((float(A.hybriddraws)/A.hybriddrawable)+(float(B.hybriddraws)/B.hybriddrawable)/2))
  116.     for map in gamemaps:
  117.         expectations.append(expected(A.maps[map],B.maps[map]))
  118.     for i in range(trials):
  119.         score = (0,0)
  120.         for i in range(len(gamemaps)):
  121.  
  122.             if gamemaps[i] in assault:
  123.                 if random.random() < assaultdrawchance:
  124.                     continue
  125.             if gamemaps[i] in hybrid:
  126.                 if random.random() < hybriddrawchance:
  127.                     continue
  128.                
  129.            
  130.             if random.random() < expectations[i]:
  131.                 score = (score[0]+1,score[1])
  132.             else:
  133.                 score = (score[0],score[1]+1)
  134.  
  135.         if score[0] == score[1]: # just in case of a (1,1) draw
  136.             tiebreak = tiebreakmap(gamemaps[0])
  137.             if random.random() < expected(A.maps[tiebreak],B.maps[tiebreak]):
  138.                 score = (score[0]+1,score[1])
  139.             else:
  140.                 score = (score[0],score[1]+1)
  141.  
  142.         if score in scorehist.keys():
  143.             scorehist[score] += 1
  144.         else:
  145.             scorehist[score] = 1
  146.  
  147.     return scorehist
  148.  
  149. scorekey = [(0,2),(0,3),(0,4),(1,2),(1,3),(2,0),(2,1),(2,3),(3,0),(3,1),(3,2),(4,0)]
  150. def addtrial(id,A,B,gamemaps,assault,assaultdrawchance,hybrid,hybriddrawchance,expectations):
  151.     score = (0,0)
  152.     for i in range(len(gamemaps)):
  153.  
  154.         if gamemaps[i] in assault:
  155.             if random.random() < assaultdrawchance:
  156.                 continue
  157.         if gamemaps[i] in hybrid:
  158.             if random.random() < hybriddrawchance:
  159.                 continue
  160.                
  161.            
  162.         if random.random() < expectations[i]:
  163.             score = (score[0]+1,score[1])
  164.         else:
  165.             score = (score[0],score[1]+1)
  166.  
  167.     if score[0] == score[1]: # just in case of a (1,1) draw
  168.         tiebreak = tiebreakmap(gamemaps[0])
  169.         if random.random() < expected(A.maps[tiebreak],B.maps[tiebreak]):
  170.             score = (score[0]+1,score[1])
  171.         else:
  172.             score = (score[0],score[1]+1)
  173.     return score
  174.  
  175. def createhist(results):
  176.     scorehist = {}
  177.     for res in results:
  178.         if res not in scorehist.keys():
  179.             scorehist[res] = 1
  180.         else:
  181.             scorehist[res] += 1
  182.     return scorehist
  183.  
  184. def simulate_multi(A, B, gamemaps):
  185.     expectations = []
  186.     assaultdrawchance = (((float(A.assaultdraws)/A.assaultdrawable)+(float(B.assaultdraws)/B.assaultdrawable))/2)
  187.     hybriddrawchance = (((float(A.hybriddraws)/A.hybriddrawable)+(float(B.hybriddraws)/B.hybriddrawable)/2))
  188.     for map in gamemaps:
  189.         expectations.append(expected(A.maps[map],B.maps[map]))
  190.     pool = multiprocessing.Pool(4)
  191.     res = pool.starmap(addtrial,zip(range(trials),rep(A),rep(B),rep(gamemaps),rep(assault),rep(assaultdrawchance),rep(hybrid),rep(hybriddrawchance),rep(expectations)))
  192.     scorehist = createhist(res)
  193.     return scorehist
  194.        
  195.  
  196. def printhistogram(hist):
  197.     for score in hist.keys():
  198.         print("{0}-{1}: {2:.3f}".format(score[0],score[1],float(hist[score])/trials))
  199.  
  200.  
  201. def getplayoffs(teamlist):
  202.     teamlist = sorted(teamlist,reverse=True)
  203.     playoffs = []
  204.     for div in (79,80): # find division winners
  205.         for i in range(len(teamlist)):
  206.             if teamlist[i].division == div:
  207.                 playoffs.append(teamlist[i])
  208.                 del teamlist[i]
  209.                 break
  210.     playoffs += teamlist[:6]
  211.     return sorted(playoffs,reverse=True)
  212.        
  213.                
  214.    
  215.  
  216.  
  217. if __name__ == "__main__":
  218.     teamresp = requests.get('https://api.overwatchleague.com/ranking')
  219.     if teamresp.status_code != 200:
  220.         raise ApiError('GET /ranking {}'.format(teamresp.status_code))
  221.     mapresp = requests.get('https://api.overwatchleague.com/maps')
  222.     if mapresp.status_code != 200:
  223.         raise ApiError('GET /ranking {}'.format(mapresp.status_code))
  224.     maps = {}
  225.     assault = []
  226.     hybrid = []
  227.     for map in mapresp.json():
  228.         maps[map['guid']]=map['name']['en_US']
  229.         if map['type'] == "hybrid":
  230.             hybrid.append(map['guid'])
  231.         if map['type'] == "assault":
  232.             assault.append(map['guid'])
  233.     #print(hybrid,assault)
  234.     #print(maps)
  235.     #print(teamresp.json()['content'][0])
  236.     teams = {}
  237.     for team in teamresp.json()['content']:
  238.         teammaps={}
  239.         for map in maps.keys():
  240.             teammaps[map] = 1000
  241.            
  242.         for division in team['competitor']['divisions']:
  243.             division = division['division']['id']
  244.             if division == 79 or division == 80:
  245.                 div = division
  246.        
  247.         records = team['records'][0]
  248.         teams[team['competitor']['id']] = Team(team['competitor']['id'],team['competitor']['name'],team['competitor']['abbreviatedName'],teammaps,records['matchWin'],records['matchLoss'],records['gameWin'],records['gameLoss'],div)
  249.     #for teamid in teams.keys():
  250.         #div = "PAC" if teams[teamid].division == 80 else "ATL"
  251.         #print(teamid,teams[teamid].name,div, "{}-{} {}".format(teams[teamid].wins,teams[teamid].losses,(teams[teamid].mapwins-teams[teamid].maplosses)))
  252.     resp = requests.get('https://api.overwatchleague.com/schedule')
  253.     if resp.status_code != 200:
  254.         raise ApiError('GET /schedule {}'.format(resp.status_code))
  255.     #print(resp.json()['data']['stages'][0]['matches'][0]['games'][0]['attributes']['mapGuid'])
  256.     for match in resp.json()['data']['stages'][0]['matches']:
  257.         if match['state'] != "CONCLUDED":
  258.             break
  259.         A = teams[match['competitors'][0]['id']]
  260.         B = teams[match['competitors'][1]['id']]
  261.         #print(A.name, "vs", B.name)
  262.         scores = [score['value'] for score in match['scores']]
  263.         A.updaterecord(B,scores)
  264.         B.updaterecord(A,[scores[1],scores[0]])
  265.  
  266.         #print(str(A.teamrecords[B.guid]))
  267.         #print(str(B.teamrecords[A.guid]))
  268.         #print(A>B)
  269.        
  270.         for game in match['games']:
  271.             exp = expected(A.elo,B.elo)
  272.             gamemap = game['attributes']['mapGuid']
  273.             mapexp = expected(A.maps[gamemap],B.maps[gamemap])
  274.             #print("{0} vs {1} map {2} - {3} EV {4:.3f} map EV {5:.3f}".format(A.abbrev,B.abbrev,game['number'],maps[gamemap],exp,mapexp))
  275.  
  276.             assaultflag = False
  277.             hybridflag = False
  278.             if gamemap in assault:
  279.                 A.assaultdrawable += 1
  280.                 B.assaultdrawable += 1
  281.                 assaultflag = True
  282.             if gamemap in hybrid:
  283.                 A.hybriddrawable += 1
  284.                 B.hybriddrawable += 1
  285.                 hybridflag = True
  286.            
  287.             points = game['points']
  288.             if points[0] > points[1]:
  289.                 A.elo = elo(A.elo,exp,1)
  290.                 A.maps[gamemap] = elo(A.maps[gamemap],mapexp,1)
  291.                 B.elo = elo(B.elo,1-exp,0)
  292.                 B.maps[gamemap] = elo(B.maps[gamemap],1-mapexp,0)
  293.                 #print("{0} wins, ELOs {1:.1f} {2:.1f} {3:.1f} {4:.1f}".format(A.abbrev,A.elo,B.elo,A.maps[gamemap],B.maps[gamemap]))
  294.             elif points[0] < points[1]:
  295.                 A.elo = elo(A.elo,exp,0)
  296.                 A.maps[gamemap] = elo(A.maps[gamemap],mapexp,0)
  297.                 B.elo = elo(B.elo,1-exp,1)
  298.                 B.maps[gamemap] = elo(B.maps[gamemap],1-mapexp,1)
  299.                 #print("{0} wins, ELOs {1:.1f} {2:.1f} {3:.1f} {4:.1f}".format(B.abbrev,A.elo,B.elo,A.maps[gamemap],B.maps[gamemap]))
  300.             else:
  301.                 A.elo = elo(A.elo,exp,0.5)
  302.                 A.maps[gamemap] = elo(A.maps[gamemap],mapexp,0.5)
  303.                 B.elo = elo(B.elo,1-exp,0.5)
  304.                 B.maps[gamemap] = elo(B.maps[gamemap],1-mapexp,0.5)
  305.                 #print("draw, ELOs {0:.1f} {1:.1f} {2:.1f} {3:.1f}".format(A.elo,B.elo,A.maps[gamemap],B.maps[gamemap]))
  306.                 if assaultflag:
  307.                     A.assaultdraws += 1
  308.                     B.assaultdraws += 1
  309.                 if hybridflag:
  310.                     A.hybriddraws += 1
  311.                     B.hybriddraws += 1
  312.             #print()
  313.     i=1
  314.     matches=[]
  315.     start=timer()
  316.     for match in resp.json()['data']['stages'][0]['matches']:
  317.         if match['state'] == "CONCLUDED":
  318.             continue
  319.         A = teams[match['competitors'][0]['id']]
  320.         B = teams[match['competitors'][1]['id']]
  321.         #print(A.name, "vs", B.name," - prediction EV ","{0:.3f}".format(expected(A.elo,B.elo)))
  322.         #print()
  323.         gamemaps = []
  324.         for game in match['games']:
  325.             gamemap = game['attributes']['mapGuid']
  326.             gamemaps.append(gamemap)
  327.             mapexp = expected(A.maps[gamemap],B.maps[gamemap])
  328.             #print("{0} vs {1} map {2} - {3} map EV {4:.3f}".format(A.abbrev,B.abbrev,game['number'],maps[gamemap],mapexp))
  329.             #if gamemap in hybrid:
  330.                 #print("Hybrid draw chance: {0} {1:.3f} {2} {3:.3f}".format(A.abbrev,float(A.hybriddraws)/A.hybriddrawable,B.abbrev,float(B.hybriddraws)/B.hybriddrawable))
  331.             #if gamemap in assault:
  332.                 #print("Assault draw chance: {0} {1:.3f} {2} {3:.3f}".format(A.abbrev,float(A.assaultdraws)/A.assaultdrawable,B.abbrev,float(B.assaultdraws)/B.assaultdrawable))
  333.             #print()
  334.         #start = timer()
  335.         #histogram=simulate(A,B,gamemaps)
  336.         #printhistogram(histogram)
  337.         #end = timer()
  338.         #print("{} iterations in {}s".format(trials,(end-start)))
  339.         #print()
  340.         #start = timer()
  341.         histogram=simulate_multi(A,B,gamemaps)
  342.         #printhistogram(histogram)
  343.         #end = timer()
  344.         #print("{} iterations in {}s".format(trials,(end-start)))
  345.         expectations = [0.0]*len(scorekey)
  346.         for score in scorekey:
  347.             if score in histogram.keys():
  348.                 expectations[scorekey.index(score)] = float(histogram[score])/trials
  349.         amatch = Match(A,B,gamemaps,expectations)
  350.         matches.append(amatch)
  351.         #print()
  352.         #print()
  353.     end=timer()
  354.     print("{} matches simulated in {}s".format(len(matches),(end-start)))
  355.     phistogram = {}
  356.     start=timer()
  357.     for i in range(ptrials):
  358.         teams2 = copy.deepcopy(teams)
  359.         for match in matches:
  360.             A=teams2[match.A.guid]
  361.             B=teams2[match.B.guid]
  362.             scores = match.simulate()
  363.             #print("simulating {} vs {} score {}-{}".format(A.abbrev,B.abbrev,scores[0],scores[1]))
  364.             A.updaterecord(B,scores)
  365.             B.updaterecord(A,[scores[1],scores[0]])
  366.             if scores[0] > scores[1]:
  367.                 A.wins += 1
  368.                 B.losses += 1
  369.             else:
  370.                 A.losses += 1
  371.                 B.wins += 1
  372.             A.mapwins += scores[0]
  373.             A.maplosses += scores[1]
  374.             B.mapwins += scores[1]
  375.             B.maplosses += scores[0]
  376.            
  377.         teamlist = [teams2[teamid] for teamid in teams2.keys()]
  378.         simplayoff = getplayoffs(teamlist)
  379.         simplayoff = [team.guid for team in simplayoff]
  380.         simplayoff = tuple(simplayoff)
  381.         #print(simplayoff)
  382.         if simplayoff in phistogram.keys():
  383.             phistogram[simplayoff] += 1
  384.         else:
  385.             phistogram[simplayoff] = 1
  386.     end=timer()
  387.     print("{} playoffs simulated in {}s".format(ptrials,(end-start)))
  388.  
  389.     for team in teams.keys():
  390.         playoffcount = 0
  391.         for playoffsim in phistogram.keys():
  392.             if teams[team].guid in playoffsim:
  393.                 playoffcount += 1
  394.         print("{0} {1:.3f}".format(teams[team].abbrev,float(playoffcount)/ptrials))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement