データサイエンスでDota2強くなるかも説(6)~環境データ(タワー)の取得~

Dota2データサイエンス

はじめに

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

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

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

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

file

前回までのあらすじ

環境構築編

Dota2の情報をPythonで取得できるような環境を作成し*1,データを取得+定期的に保存する機構を作りました*2

データ解析~ミディアムとハード編~

実際にBotとの対戦をプロットしてみて客観的に解析してみたところ*3,ミディアムボットとの対戦をしてもハードボットとの対戦ではそこまで効果がない可能性が発見されました*4

データ解析~ハード編~

ハードボットとの対戦を解析してみた結果,勝利している方が敗北している試合よりもレベル,ゴールドを多く稼いでいると思っていたのですが,その仮説とは相反した結果が得られ味方の振る舞いの影響が大きく,自分の行動は勝利に直結していないという悲しい結果になりました*5

今回の概要

そこで,原点に立ち返って,環境データを考慮して解析をしてみようと思いました.
今回は,環境データを含むデータの取得機構を再度検討&実装していこうと思います.

準備

cfgファイルの修正

buildingsを追加します.

"Python Dota 2 GSI Integration"
{
    "uri"       "http://localhost:3000"
    "timeout"   "5.0"
    "buffer"    "0.1"
    "throttle"  "0.1"
    "heartbeat" "30.0"
    "data"
    {
        "buildings"     "1"
        "provider"      "1"
        "map"           "1"
        "player"        "1"
        "hero"          "1"
        "abilities"     "1"
        "items"         "1"
        "draft"         "1"
        "wearables"     "1"
    }
}

GSI環境

GSI環境が整っていることを確認しておいてください.
まだの方はこちらの記事参照

データサイエンスでDota2強くなるかも説(1)~環境設定~
Dota2の情報をPythonで取得するまでをやっていきます.

ロガープログラム

プログラム本体

下記のDota2Logger.pyを作成します.

import dota2gsi
import glob
import csv
import pprint
import pandas as pd
import os
from datetime import datetime as dt
import json

pd.set_option('display.max_columns', 5)

class Dota2Analy:

    def __init__(self, funname="dota2"):
        self.funname = funname

        tdatetime = dt.now()

        self.log_dir    = "logs"
        self.tstr       = tdatetime.strftime('%Y%m%d_%H%M%S')
        self.save_dir   = "{}/{}".format(self.log_dir, self.tstr) 

        self.time_offset = 80

        os.makedirs(self.save_dir, exist_ok=True)

    def _realtime_handle_state2(self, last_state, state):

        if("hero" in state.keys()):
            if("name" in state["hero"].keys()):

                clock_time2 = "{:09d}".format(state["map"]["clock_time"]+self.time_offset)
                hero_name   = "{}".format(state["hero"]["name"])
                save_file_path = "{}/{}_{}.json".format(self.save_dir, clock_time2, hero_name)

                print("-------------------------------------")
                # print(state["map"]["clock_time"])
                print(clock_time2)

                with open(save_file_path, 'w') as f:
                    json.dump(state, f, indent=4)

    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()

フォルダ構造

こんな感じのフォルダ構造になります.
logsフォルダにログが記録されていきます.

├─.ipynb_checkpoints
├─doc
│  └─doc003
├─ipynb
└─logs
    ├─20220909_232301_W
    ├─20220910_001319_W
    └─20220910_011144_W
Dota2Logger.py

ロガープログラム実行

実行すると,ゲーム内のクロック時間が表示されます.
この表示された時刻のデータはjsonファイルにて保存されています.

D:\Local_Project\5000_HaMaruki\5000.004_Dota2\Dota2-GSI-Science>python Dota2Logger.py
DotA 2 GSI server listening on 0.0.0.0:3000 - CTRL+C to stop

....

-------------------------------------
000000058
-------------------------------------
000000058
-------------------------------------
000000058
-------------------------------------
000000058
-------------------------------------

...

出力情報

出力場所は実行したタイミングの時刻のフォルダが作成されます.

file

その中にjsonファイルが毎秒作成されます.

中身はこんな感じ

{
    "buildings": {
        "radiant": {
            "dota_goodguys_tower1_top": {
                "health": 1800,
                "max_health": 1800
            },
            "dota_goodguys_tower2_top": {
                "health": 2500,
                "max_health": 2500
            },
            "dota_goodguys_tower3_top": {
                "health": 2500,
                "max_health": 2500
            },
            "dota_goodguys_tower1_mid": {
                "health": 1800,
                "max_health": 1800
            },
            "dota_goodguys_tower2_mid": {
                "health": 2500,
                "max_health": 2500
            },
            "dota_goodguys_tower3_mid": {
                "health": 2500,
                "max_health": 2500
            },
            "dota_goodguys_tower1_bot": {
                "health": 1800,
                "max_health": 1800
            },
            "dota_goodguys_tower2_bot": {
                "health": 2500,
                "max_health": 2500
            },
            "dota_goodguys_tower3_bot": {
                "health": 2500,
                "max_health": 2500
            },
            "dota_goodguys_tower4_top": {
                "health": 2600,
                "max_health": 2600
            },
            "dota_goodguys_tower4_bot": {
                "health": 2600,
                "max_health": 2600
            },
            "good_rax_melee_top": {
                "health": 2200,
                "max_health": 2200
            },
            "good_rax_range_top": {
                "health": 1300,
                "max_health": 1300
            },
            "good_rax_melee_mid": {
                "health": 2200,
                "max_health": 2200
            },
            "good_rax_range_mid": {
                "health": 1300,
                "max_health": 1300
            },
            "good_rax_melee_bot": {
                "health": 2200,
                "max_health": 2200
            },
            "good_rax_range_bot": {
                "health": 1300,
                "max_health": 1300
            },
            "dota_goodguys_fort": {
                "health": 4500,
                "max_health": 4500
            }
        }
    },
    "provider": {
        "name": "Dota 2",
        "appid": 570,
        "version": 47,
        "timestamp": 1662795267
    },
    "map": {
        "name": "start",
        "matchid": "0",
        "game_time": 19,
        "clock_time": -70,
        "daytime": false,
        "nightstalker_night": false,
        "radiant_score": 0,
        "dire_score": 0,
        "game_state": "DOTA_GAMERULES_STATE_PRE_GAME",
        "paused": false,
        "win_team": "none",
        "customgamename": "",
        "ward_purchase_cooldown": 116
    },
    "player": {
        "steamid": "76561198171187153",
        "name": "maki",
        "activity": "playing",
        "kills": 0,
        "deaths": 0,
        "assists": 0,
        "last_hits": 0,
        "denies": 0,
        "kill_streak": 0,
        "commands_issued": 6,
        "kill_list": {},
        "team_name": "radiant",
        "gold": 75,
        "gold_reliable": 0,
        "gold_unreliable": 75,
        "gold_from_hero_kills": 0,
        "gold_from_creep_kills": 0,
        "gold_from_income": 0,
        "gold_from_shared": 0,
        "gpm": 0,
        "xpm": 0
    },
    "hero": {
        "xpos": -7100,
        "ypos": -6150,
        "id": 47,
        "name": "npc_dota_hero_viper",
        "level": 1,
        "xp": 0,
        "alive": true,
        "respawn_seconds": 0,
        "buyback_cost": 253,
        "buyback_cooldown": 0,
        "health": 660,
        "max_health": 660,
        "health_percent": 100,
        "mana": 303,
        "max_mana": 303,
        "mana_percent": 100,
        "silenced": false,
        "stunned": false,
        "disarmed": false,
        "magicimmune": false,
        "hexed": false,
        "muted": false,
        "break": false,
        "aghanims_scepter": false,
        "aghanims_shard": false,
        "smoked": false,
        "has_debuff": false,
        "talent_1": false,
        "talent_2": false,
        "talent_3": false,
        "talent_4": false,
        "talent_5": false,
        "talent_6": false,
        "talent_7": false,
        "talent_8": false
    },
    "abilities": {
        "ability0": {
            "name": "viper_poison_attack",
            "level": 0,
            "can_cast": false,
            "passive": false,
            "ability_active": true,
            "cooldown": 0,
            "ultimate": false
        },
        "ability1": {
            "name": "viper_nethertoxin",
            "level": 0,
            "can_cast": false,
            "passive": false,
            "ability_active": true,
            "cooldown": 0,
            "ultimate": false
        },
        "ability2": {
            "name": "viper_corrosive_skin",
            "level": 0,
            "can_cast": false,
            "passive": true,
            "ability_active": true,
            "cooldown": 0,
            "ultimate": false
        },
        "ability3": {
            "name": "viper_viper_strike",
            "level": 0,
            "can_cast": false,
            "passive": false,
            "ability_active": true,
            "cooldown": 0,
            "ultimate": true
        }
    },
    "items": {
        "slot0": {
            "name": "item_tango",
            "purchaser": 0,
            "can_cast": true,
            "cooldown": 0,
            "passive": false,
            "charges": 3
        },
        "slot1": {
            "name": "item_slippers",
            "purchaser": 0,
            "passive": true
        },
        "slot2": {
            "name": "item_slippers",
            "purchaser": 0,
            "passive": true
        },
        "slot3": {
            "name": "item_circlet",
            "purchaser": 0,
            "passive": true
        },
        "slot4": {
            "name": "item_ward_observer",
            "purchaser": 0,
            "can_cast": true,
            "cooldown": 0,
            "passive": false,
            "charges": 2
        },
        "slot5": {
            "name": "empty"
        },
        "slot6": {
            "name": "empty"
        },
        "slot7": {
            "name": "empty"
        },
        "slot8": {
            "name": "empty"
        },
        "stash0": {
            "name": "empty"
        },
        "stash1": {
            "name": "empty"
        },
        "stash2": {
            "name": "empty"
        },
        "stash3": {
            "name": "empty"
        },
        "stash4": {
            "name": "empty"
        },
        "stash5": {
            "name": "empty"
        },
        "teleport0": {
            "name": "item_tpscroll",
            "purchaser": 0,
            "can_cast": false,
            "cooldown": 98,
            "passive": false,
            "charges": 1
        },
        "neutral0": {
            "name": "empty"
        }
    },
    "draft": {},
    "wearables": {
        "wearable0": 623,
        "wearable1": 611,
        "wearable2": 654,
        "wearable3": 8632,
        "wearable4": 519,
        "wearable5": 790,
        "wearable6": 791,
        "wearable7": 14912
    }
}

取得できる全情報がここに記載されています.

稼働動画

稼働動画を記載しておきます.

Dota2データサイエンスで強くなるかも説(6)~環境データ(タワー)の取得~稼働動画

プログラム一式

GitHub - HamaruKi0303/Dota2-GSI-Science
Contribute to HamaruKi0303/Dota2-GSI-Science development by creating an account on GitHub.

おわりに

前回までのロガーコードではDataFrameに加工したものを保存することで,解析する際の手間を省略していました.
しかし,タワーなどの情報が入れ子の辞書型になっていたため,このような感じで毎秒のjsonファイルを出力するように変更しました.

そのため,解析する際にデータを整形する手間が増えてしまいました.
次回は,jsonに対応した解析プログラムを作っていきます.

参考サイト

コメント

タイトルとURLをコピーしました