LoginSignup
0
0

More than 1 year has passed since last update.

データサイエンスでDota2強くなるかも説(2)~ビッグデータ取得&定期保存~

Last updated at Posted at 2022-09-06

はじめに

最近,Dota2を始めましたが全く勝てません
ハードボットにボコボコにされます.

色々と調べても「死ぬな」くらいのことしか分らず苦戦しています.

データサイエンスでDota2強くなるかも説

そこで,データサイエンスの力を借りて,どのような状況なら勝っているか?や前回に比べてどのように振舞ったから勝てたのか?ということを数値化して分析していけば強くなるのでは!と考えました.本企画はその仮説を検証していく企画です.

前回までのあらすじ

Dota2の情報をPythonで取得できるような環境を作成しました.

今回の概要

今回は,Dota2からデータを取得し,定期的に保存する機構を作っていきます.

Dota2 解析プログラム

Import

import dota2gsi

Demo program

まずはDota2と接続していることを確認.

def demo_handle_state(last_state, state):
    # Use nested gets to safely extract data from the state
    hero_name = state.get('hero', {}).get('name')
    health_percent = state.get('hero', {}).get('health_percent')
    max_health = state.get('hero', {}).get('max_health')
    max_health = state.get('hero', {}).get('max_health')
    health = state.get('hero', {}).get('health')
    level = state.get('hero', {}).get('level')
    mana = state.get('hero', {}).get('mana')
    mana_percent = state.get('hero', {}).get('mana_percent')
    gold = state.get('player', {}).get('gold')
    xpm = state.get('player', {}).get('xpm')
    
    clock_time = state.get('map', {}).get('clock_time')
    
    # If the attributes exist, print them
    if health_percent and max_health:
        health = int(max_health * health_percent/100)
        # print(f"{hero_name}'s current health: {health}/{max_health}")
        print("{}:{}, {}:{}, {}:{}, {}:{},  {}:{},  {}:{}, ".format("clock_time", clock_time, "health", health, "gold", gold, "mana", mana, "level", level, "xpm", xpm))
server = dota2gsi.Server(ip='0.0.0.0', port=3000)
server.on_update(demo_handle_state)
server.start()

    DotA 2 GSI server listening on 0.0.0.0:3000 - CTRL+C to stop
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    clock_time:2887, health:1020, gold:1, mana:375,  level:7,  xpm:72, 
    Server stopped.
    

Read data name file

Create data id csv

detaのIDリストのファイルを作成します.

player
assists
camps_stacked
deaths
denies
gold
gold_reliable
gold_unreliable
gpm
hero_damage
kill_list:victimid_#
kill_streak
kills
last_hits
net_worth
pro_name
runes_activated
support_gold_spent
wards_destroyed
wards_placed
wards_purchased
xpm

Glob file

csv ファイルを探索します.

import glob
import pandas as pd

datasets_path = "datasets"
data_file_list = glob.glob(datasets_path + "/*.csv")

ファイル一覧です.

data_file_list
    ['datasets\\hero.csv', 'datasets\\player.csv']

Read file

CSVファイルを読み込みます.

for file_path in data_file_list:
    df_data = pd.read_csv(file_path)
    
    print("---------------------")
    print(df_data.head(5))
    ---------------------
                   hero
    0             alive
    1             break
    2      buyback_cost
    3  buyback_cooldown
    4          disarmed
    ---------------------
              player
    0        assists
    1  camps_stacked
    2         deaths
    3         denies
    4           gold

dataframe2dict

DataFrameを辞書型に変形します.

data_dict = {}

for file_path in data_file_list:
    df_data = pd.read_csv(file_path)
    
    data_dict[df_data.columns[0]] = list(df_data[df_data.columns[0]].values)

変形後のデータです.

data_dict
    {'hero': ['alive',
      'break',
      'buyback_cost',
      'buyback_cooldown',
      'disarmed',
      'has_debuff',
      'health',
      'health_percent',
      'hexed',
      'id',
      'level',
      'magicimmune',
      'mana',
      'mana_percent',
      'max_health',
      'max_mana',
      'muted',
      'name',
      'respawn_seconds',
      'selected_unit',
      'silenced',
      'stunned',
      'talent_1',
      'talent_2',
      'talent_3',
      'talent_4',
      'talent_5',
      'talent_6',
      'talent_7',
      'talent_8',
      'xpos',
      'ypos'],
     'player': ['assists',
      'camps_stacked',
      'deaths',
      'denies',
      'gold',
      'gold_reliable',
      'gold_unreliable',
      'gpm',
      'hero_damage',
      'kill_list:victimid_#',
      'kill_streak',
      'kills',
      'last_hits',
      'net_worth',
      'pro_name',
      'runes_activated',
      'support_gold_spent',
      'wards_destroyed',
      'wards_placed',
      'wards_purchased',
      'xpm']}

Get Dota2 data

ここから,Dota2のデータを取得します.

data name loop

先ほどの辞書からデータの名前を1つづ取り出すコードを作成します.

for data_kind_name in data_dict.keys():
    print("===================")
    print(data_kind_name)
    print("--------------------")
    for data_name in data_dict[data_kind_name]:
        print(data_name)
        #hero_name = state.get(data_kind_name, {}).get(data_name)
    ===================
    hero
    --------------------
    alive
    break
    buyback_cost
    buyback_cooldown
    disarmed
    has_debuff
    health
    health_percent
    hexed
    id
    level
    magicimmune
    mana
    mana_percent
    max_health
    max_mana
    muted
    name
    respawn_seconds
    selected_unit
    silenced
    stunned
    talent_1
    talent_2
    talent_3
    talent_4
    talent_5
    talent_6
    talent_7
    talent_8
    xpos
    ypos
    ===================
    player
    --------------------
    assists
    camps_stacked
    deaths
    denies
    gold
    gold_reliable
    gold_unreliable
    gpm
    hero_damage
    kill_list:victimid_#
    kill_streak
    kills
    last_hits
    net_worth
    pro_name
    runes_activated
    support_gold_spent
    wards_destroyed
    wards_placed
    wards_purchased
    xpm
    

Define handle

これをハンドラーに組み込みます.

def realtime_handle_state(last_state, state):
    # Use nested gets to safely extract data from the state
    
    for data_kind_name in data_dict.keys():
        print("===================")
        print(data_kind_name)
        
        for data_name in data_dict[data_kind_name]:
            print("--------------------")
            print(data_name)
            data_value = state.get(data_kind_name, {}).get(data_name)
            print(data_value)
    
    

Run server test

サーバーを起動させます.

server = dota2gsi.Server(ip='0.0.0.0', port=3000)
server.on_update(realtime_handle_state)
server.start()
    DotA 2 GSI server listening on 0.0.0.0:3000 - CTRL+C to stop
    ===================
    hero
    --------------------
    alive
    True
    --------------------
    break
    False
    --------------------
    buyback_cost
    233
    ....
    None
    --------------------
    wards_destroyed
    None
    --------------------
    wards_placed
    None
    --------------------
    wards_purchased
    None
    --------------------
    xpm
    23
    Server stopped.
   ..... 

これでデータ取得する部分ができました.

Adjust state data

次に,取得したデータを整形していきます.
stateから1行のDataFrameに変形します.

def state2df(state):
    
    df_list = []
    for data_kind_name in data_dict.keys():
        #print("===================")
        #print(data_kind_name)
        
        data_name_list = []
        data_value_list = []
        
        for data_name in data_dict[data_kind_name]:
            #print("--------------------")
            #print(data_name)
            data_name_list.append(data_name)
            data_value = state.get(data_kind_name, {}).get(data_name)
            #print(data_value)
            data_value_list.append(data_value)
            
        #df_data = pd.Dataframe(data_value_list)
        df_data = pd.DataFrame(data_value_list).T
        df_data.columns = data_name_list
        #print(df_data)
        df_list.append(df_data)
    
    df_merge = pd.concat(df_list, axis=1)
    print(df_merge)
    

これを新しいハンドラ2を作成し組み込みます.

def realtime_handle_state2(last_state, state):
    # Use nested gets to safely extract data from the state
    df_state = state2df(state)
    
    df_data_base = pd.concat([df_data_base, df_state])
    print(df_data_base)

以上を踏まえてクラスを作成します.

Define class

stateを取得し,DataFrameに変形するクラスです.

定期的保存機能

30秒ごとに保存する機構を加えます.
これで,後から解析できます.

if(self.df_data_base['clock_time'].values[-1] % 30 == 0):
    print("===================")
    print(self.df_data_base['clock_time'].values[-1])
    print(self.df_data_base.tail(5))
    self.df_data_base.to_csv('log_{}.csv'.format(self.df_data_base['matchid'].values[-1]))

コード全体

import dota2gsi
import glob
import csv
import pprint
pd.set_option('display.max_columns', 5)

class Dota2Analy:
    
    def __init__(self, funname="dota2"):
        self.funname = funname
        
        self.df_data_base = pd.DataFrame({})
        
        datasets_path = "datasets"
        data_file_list = glob.glob(datasets_path + "/*.csv")
        
        # --------------------------------------
        self.data_dict = {}
        for file_path in data_file_list:
            df_data = pd.read_csv(file_path)
            self.data_dict[df_data.columns[0]] = list(df_data[df_data.columns[0]].values)
            
        

    def state2df(self, state):

        df_list = []
        for data_kind_name in self.data_dict.keys():
            #print("===================")
            #print(data_kind_name)

            data_name_list = []
            data_value_list = []

            for data_name in self.data_dict[data_kind_name]:
                #print("--------------------")
                #print(data_name)
                data_name_list.append(data_name)
                data_value = state.get(data_kind_name, {}).get(data_name)
                #print(data_value)
                data_value_list.append(data_value)

            #df_data = pd.Dataframe(data_value_list)
            df_data = pd.DataFrame(data_value_list).T
            df_data.columns = data_name_list
            #print(df_data)
            df_list.append(df_data)

        df_merge = pd.concat(df_list, axis=1)
        #print(df_merge)
        return df_merge

    def _realtime_handle_state2(self, last_state, state):
        # Use nested gets to safely extract data from the state
        df_state = self.state2df(state)

        self.df_data_base = pd.concat([self.df_data_base, df_state])
        
        
        if(self.df_data_base['clock_time'].values[-1] % 30 == 0):
            print("===================")
            print(self.df_data_base['clock_time'].values[-1])
            print(self.df_data_base.tail(5))
            self.df_data_base.to_csv('log_{}.csv'.format(self.df_data_base['matchid'].values[-1]))
        
    def run(self):
        server = dota2gsi.Server(ip='0.0.0.0', port=3000)
        server.on_update(self._realtime_handle_state2)
        server.start()

if __name__ == '__main__':
    D2A = Dota2Analy()
    D2A.run()

実行するとこのように,時刻ごとに追加されていきます.

    DotA 2 GSI server listening on 0.0.0.0:3000 - CTRL+C to stop
    ===================
    270
      alive  break  ... wards_purchased    xpm
    0  True  False  ...             NaN  328.0
    0  True  False  ...             NaN  328.0
    0  True  False  ...             NaN  328.0
    0  True  False  ...             NaN  328.0
    0  True  False  ...             NaN  327.0
    
    [5 rows x 66 columns]
    ===================
    270
      alive  break  ... wards_purchased    xpm
    0  True  False  ...             NaN  328.0
    0  True  False  ...             NaN  328.0
    0  True  False  ...             NaN  328.0
    0  True  False  ...             NaN  327.0
    0  True  False  ...             NaN  327.0
    
    [5 rows x 66 columns]
    ....

プログラム

コードはこちらです.

おわりに

今回は,Dota2からstateを取得し,csvに記載されているIDを使ってデータを取得しました.
取得したデータが1行のDataFrameに変形し追加し,定期的に保存する機構を作ることができました.

次回は保存したデータをプロットしていこうと思います.

参考サイト

最新情報

速報はTwitterやブログにて

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0