Last active
August 21, 2020 01:23
-
-
Save szarroug3/1ed00854f363a719c52c00725926bc11 to your computer and use it in GitHub Desktop.
analyze_dota_matches.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import json | |
import os | |
import requests | |
from argparse import ArgumentParser | |
from collections import defaultdict | |
from time import sleep | |
OPENDOTA_BASE_URL = 'https://api.opendota.com/api/{func_name}/{params}' | |
def opendota_api_call(func_name, *params): | |
for retry in range(1, 6): | |
resp = requests.get(OPENDOTA_BASE_URL.format(func_name=func_name, params='/'.join(params))) | |
if resp.ok: | |
break | |
sleep(20) | |
print('Retry #{}'.format(retry)) | |
if not resp.ok: | |
raise Exception('Something went wrong: GET {}: {} {}'.format(func_name, resp.status_code, resp.reason)) | |
return resp.json() | |
def parse_match(match, data={}): | |
for player in match['players']: | |
name = '{} ({})'.format(player['personaname'], player['account_id']) | |
player_data = data.get(name, defaultdict(list)) | |
building_damage = 0 | |
for building, damage in player['damage'].items(): | |
if 'npc_dota_goodguys' not in building: | |
continue | |
if 'tower' not in building and 'rax' not in building: | |
continue | |
building_damage += damage | |
player_data['heroes'] += [player['hero_id']] | |
player_data['kills'] += [player['kills']] | |
player_data['deaths'] += [player['deaths']] | |
player_data['assists'] += [player['assists']] | |
player_data['gpm'] += [player['gold_per_min']] | |
player_data['kda'] += [player['kda']] | |
player_data['cs'] += [player['last_hits']] | |
player_data['building_damage'] += [building_damage] | |
player_data['deaths_to_techies'] += [player['killed_by'].get('npc_dota_hero_techies', 0)] | |
player_data['win'] += [player['win']] | |
player_data['games_played'] = player_data['games_played'] + 1 if player_data['games_played'] else 1 | |
data[name] = player_data | |
return data | |
def parse_matches(matches): | |
unparsed = [] | |
parsed = {} | |
not_found = [] | |
for match in matches: | |
try: | |
fname = os.path.join('match_cache', '{}.json'.format(match)) | |
if not os.path.exists('match_cache'): | |
os.makedirs('match_cache') | |
if os.path.exists(fname): | |
with open(fname) as f: | |
data = json.load(f) | |
else: | |
data = opendota_api_call('matches', match) | |
with open(fname, 'w') as f: | |
json.dump(data, f) | |
except: | |
not_found.append(match) | |
continue | |
if not data.get('picks_bans'): | |
unparsed.append(match) | |
continue | |
parsed = parse_match(data, data=parsed) | |
return unparsed, parsed, not_found | |
def get_midas_touch(data): | |
winner = None | |
value = -1 | |
for player, player_data in data.items(): | |
if player_data['games_played'] < 5: | |
continue | |
player_value = float(sum(player_data['gpm'])) / player_data['games_played'] | |
if player_value > value: | |
value = player_value | |
winner = player | |
elif player_value == value: | |
winner = '{}, {}'.format(winner, player) | |
return '{} - {}'.format(winner, int(value)) | |
def get_scorched_earth(data): | |
winner = None | |
value = -1 | |
for player, player_data in data.items(): | |
if player_data['games_played'] < 5: | |
continue | |
player_value = float(sum(player_data['kda'])) / player_data['games_played'] | |
if player_value > value: | |
value = player_value | |
winner = player | |
elif player_value == value: | |
winner = '{}, {}'.format(winner, player) | |
return '{} - {}'.format(winner, int(value)) | |
def get_hero_puddler(data): | |
winner = None | |
value = 500 | |
for player, player_data in data.items(): | |
if player_data['games_played'] < 5: | |
continue | |
player_value = len(set(player_data['heroes'])) | |
if player_value < value: | |
value = player_value | |
winner = player | |
elif player_value == value: | |
winner = '{}, {}'.format(winner, player) | |
return '{} - {}'.format(winner, value) | |
def get_ocean(data): | |
winner = None | |
value = -1 | |
for player, player_data in data.items(): | |
player_value = len(set(player_data['heroes'])) | |
if player_value > value: | |
value = player_value | |
winner = player | |
elif player_value == value: | |
winner = '{}, {}'.format(winner, player) | |
return '{} - {}'.format(winner, value) | |
def get_no_thoughts_head_empty(data): | |
winner = None | |
value = -1 | |
for player, player_data in data.items(): | |
player_value = float(sum(player_data['cs'])) / player_data['games_played'] | |
if player_value > value: | |
value = player_value | |
winner = player | |
elif player_value == value: | |
winner = '{}, {}'.format(winner, player) | |
return '{} - {}'.format(winner, int(value)) | |
def get_carty_b_award(data): | |
winner = None | |
value = -1 | |
for player, player_data in data.items(): | |
player_value = float(sum(player_data['building_damage'])) / player_data['games_played'] | |
if player_value > value: | |
value = player_value | |
winner = player | |
elif player_value == value: | |
winner = '{}, {}'.format(winner, player) | |
return '{} - {}'.format(winner, int(value)) | |
def get_minesweeper(data): | |
winner = None | |
value = -1 | |
for player, player_data in data.items(): | |
player_value = float(sum(player_data['deaths_to_techies'])) / player_data['games_played'] | |
if player_value > value: | |
value = player_value | |
winner = player | |
elif player_value == value: | |
winner = '{}, {}'.format(winner, player) | |
return '{} - {}'.format(winner, int(value)) | |
def get_space_creator(data): | |
winner = None | |
value = -1 | |
for player, player_data in data.items(): | |
player_value = max([d for d in player_data['deaths'] if player_data['win']]) | |
if player_value > value: | |
value = player_value | |
winner = player | |
elif player_value == value: | |
winner = '{}, {}'.format(winner, player) | |
return '{} - {}'.format(winner, value) | |
def get_i_like_the_cha_ching_sound(data): | |
winner = None | |
value = -1 | |
for player, player_data in data.items(): | |
player_value = max(player_data['gpm']) | |
if player_value > value: | |
value = player_value | |
winner = player | |
elif player_value == value: | |
winner = '{}, {}'.format(winner, player) | |
return '{} - {}'.format(winner, value) | |
def get_kills_secured(data): | |
winner = None | |
value = -1 | |
for player, player_data in data.items(): | |
player_value = max(player_data['kills']) | |
if player_value > value: | |
value = player_value | |
winner = player | |
elif player_value == value: | |
winner = '{}, {}'.format(winner, player) | |
return '{} - {}'.format(winner, value) | |
def get_its_okay_take_the_kills(data): | |
winner = None | |
value = -1 | |
for player, player_data in data.items(): | |
player_value = max(player_data['assists']) | |
if player_value > value: | |
value = player_value | |
winner = player | |
elif player_value == value: | |
winner = '{}, {}'.format(winner, player) | |
return '{} - {}'.format(winner, value) | |
def get_me_hit_creep(data): | |
winner = None | |
value = -1 | |
for player, player_data in data.items(): | |
player_value = max(player_data['cs']) | |
if player_value > value: | |
value = player_value | |
winner = player | |
elif player_value == value: | |
winner = '{}, {}'.format(winner, player) | |
return '{} - {}'.format(winner, value) | |
def get_objective_gamer(data): | |
winner = None | |
value = -1 | |
for player, player_data in data.items(): | |
player_value = max(player_data['building_damage']) | |
if player_value > value: | |
value = player_value | |
winner = player | |
elif player_value == value: | |
winner = '{}, {}'.format(winner, player) | |
return '{} - {}'.format(winner, value) | |
def get_args(): | |
parser = ArgumentParser() | |
parser.add_argument('-m', '--match', action='append') | |
return parser.parse_args() | |
def process(match): | |
unparsed, parsed, not_found = parse_matches(match) | |
if unparsed: | |
print('The following matches were not parsed by opendota: {}\n'.format(', '.join(unparsed))) | |
if not_found: | |
print('The following matches were not found by opendota: {}\n'.format(', '.join(not_found))) | |
print('Midas Touch - Highest GPM over season (5 game minimum): {}'.format(get_midas_touch(parsed))) | |
print('Scorched Earth - Best KDA during season (5 game minimum): {}'.format(get_scorched_earth(parsed))) | |
print('Hero Puddler - Least diverse (5 games minimum): {}'.format(get_hero_puddler(parsed))) | |
print('Ocean this probably doesnt need a minimum : {}'.format(get_ocean(parsed))) | |
print('No thoughts head empty (most cs in the season): {}'.format(get_no_thoughts_head_empty(parsed))) | |
print('Carty B Award (Most building damage): {}'.format(get_carty_b_award(parsed))) | |
print('Minesweeper (player most killed by techies): {}'.format(get_minesweeper(parsed))) | |
print('Space Creator - Most Deaths in a win: {}'.format(get_space_creator(parsed))) | |
print('I like the Cha-Ching sound - Highest GPM in a Single Game: {}'.format(get_i_like_the_cha_ching_sound(parsed))) | |
print('Kill Secured - Most Kills in a Single Game: {}'.format(get_kills_secured(parsed))) | |
print('It\'s Okay, Take The Kills :) - Most Assists in a Single Game: {}'.format(get_its_okay_take_the_kills(parsed))) | |
print('ME HIT CREEP - Most Last Hits in a Single Game: {}'.format(get_me_hit_creep(parsed))) | |
print('Objective Gamer - Most Building Damage in a Single Game: {}'.format(get_objective_gamer(parsed))) | |
if __name__ == '__main__': | |
process(**vars(get_args())) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment