データサイエンスでDota2強くなるかも説(8)~ウォークマップを可視化してみた~

Dota2データサイエンス

はじめに

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

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

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

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

file

前回までのあらすじ

環境構築編

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

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

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

データ解析~ハード編~

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

そこで,原点に立ち返って,環境データを考慮して解析をしてみようと思いました.まずは環境データを含むデータの取得機構を検討&実装し*6,簡易的に描画してみました*7

今回の概要

今回は,記録したJsonファイルからウォークマップを作成していきます.

準備

GSI環境

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

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

Dota2 解析プログラム

Import

import glob
import pandas as pd
import json

import seaborn as sns
from matplotlib import pyplot as plt
import matplotlib as mpl

from tqdm.notebook import tqdm

import matplotlib.pyplot as plt
import numpy as np
from PIL import Image

ロガーの設定

from logging import getLogger, Formatter, StreamHandler, FileHandler, DEBUG, INFO

def get_module_logger(module):
    logger = getLogger(module)
    logger = _set_handler(logger, StreamHandler(), True)
    logger.setLevel(DEBUG)
    logger.propagate = False
    return logger

def _set_handler(logger, handler, verbose):
    if verbose:
        handler.setLevel(DEBUG)
    else:
        handler.setLevel(INFO)
    handler.setFormatter(Formatter('%(asctime)s %(name)s:%(lineno)s %(funcName)s [%(levelname)s]: %(message)s'))
    logger.addHandler(handler)
    return logger

logger = get_module_logger("Dota2Analysis")
logger.info('message')
    2022-09-11 10:57:07,796 Dota2Analysis:21 <cell line: 21> [INFO]: message
    2022-09-11 10:57:07,796 Dota2Analysis:21 <cell line: 21> [INFO]: message

seabornのテーマの設定

#sns.palplot(sns.color_palette("RdBu_r", 24))
#sns.set_palette("RdBu_r")
#sns.set(style='darkgrid')
#sns.set_style('whitegrid')
mpl.style.use('fivethirtyeight')

パラメータ設定

log_dir         = "../logs"
datasets_dir    = "../data"

ログフォルダ一覧の取得

data_file_list = glob.glob(log_dir + "/*")
data_file_list

ログファイル一覧の取得

json_file_list = {}
for data_path in data_file_list:
    data_name = data_path.split("\\")[-1]
    json_file_list[data_name] = glob.glob(data_path + "/*json")

辞書から参照する関数

def get_json_value(json_data, *args):
    if(args[0] in json_data):
        if(args[1] in json_data[args[0]]):
            if(len(args)==2):
                target_data = json_data[args[0]][args[1]]
                return target_data

            if(args[2] in json_data[args[0]][args[1]]):
                if(len(args)==3):
                    target_data = json_data[args[0]][args[1]][args[2]]
                    return target_data

                if(args[3] in json_data[args[0]][args[1]][args[2]]):
                    if(len(args)==4):
                        target_data = json_data[args[0]][args[1]][args[2]][args[3]]
                        return target_data

    return -1

Jsonファイルの読み込み関数


def get_json_data(json_file_list, *args):
    # print(">>>> args >>>>>>>>>")
    print(args)

    df_data_list = []

    df_data_all = pd.DataFrame({}, columns=["clock_time"])
    # # df_data_all.columns = ["clock_time"]

    for data_name in tqdm(json_file_list.keys()):
        file_list = json_file_list[data_name]

        target_name = "_".join(args) + "-" + data_name

        clock_time_list = []
        target_data_list = []

        for file_path in tqdm(file_list):

            with open(file_path) as f:
                json_data = json.load(f)

                clock_time = get_json_value(json_data, "map", "clock_time")
                # print(clock_time)
                clock_time_list.append(clock_time)

                target_data = get_json_value(json_data, *args)
                # print(target_data)
                target_data_list.append(target_data)

        df_data = pd.DataFrame([clock_time_list, target_data_list]).T
        df_data.columns = ["clock_time", target_name]

        # print(df_data)

        df_data_list.append(df_data)

    # df_data_merge = pd.concat(df_data_list, axis=1)
    for i in range(len(df_data_list)):
        df_data_all = pd.merge(df_data_all, df_data_list[i], on='clock_time', how='outer')

    # print(df_data_all)
    # print("---------------------")
    # print(df_data_all.columns)

    return df_data_all

データセット生成

# get_json_data(json_file_list, "hero", "health")

json2df = get_json_data(json_file_list, "hero", "level")

for i in range(4):
    json2df = pd.merge(json2df , get_json_data(json_file_list, "buildings", "radiant","dota_goodguys_tower{}_top".format(i+1), "health"), on='clock_time', how='outer')
    json2df = pd.merge(json2df , get_json_data(json_file_list, "buildings", "radiant","dota_goodguys_tower{}_bot".format(i+1), "health"), on='clock_time', how='outer')

for i in range(3):
    json2df = pd.merge(json2df , get_json_data(json_file_list, "buildings", "radiant","dota_goodguys_tower{}_mid".format(i+1), "health"), on='clock_time', how='outer')

logger.info('complete buildings')

json2df = pd.merge(json2df , get_json_data(json_file_list, "hero", "health"), on='clock_time', how='outer')
json2df = pd.merge(json2df , get_json_data(json_file_list, "hero", "xpos"), on='clock_time', how='outer')
json2df = pd.merge(json2df , get_json_data(json_file_list, "hero", "ypos"), on='clock_time', how='outer')
logger.info('complete hero')

json2df = pd.merge(json2df , get_json_data(json_file_list, "player", "gold"), on='clock_time', how='outer')
logger.info('complete player')

json2df.to_csv(datasets_dir + "/extract_data.csv")
    ('hero', 'level')

    0it [00:00, ?it/s]

    ('buildings', 'radiant', 'dota_goodguys_tower1_top', 'health')

    0it [00:00, ?it/s]

    ('buildings', 'radiant', 'dota_goodguys_tower1_bot', 'health')

    0it [00:00, ?it/s]

    ('buildings', 'radiant', 'dota_goodguys_tower2_top', 'health')

    0it [00:00, ?it/s]

    ('buildings', 'radiant', 'dota_goodguys_tower2_bot', 'health')

    0it [00:00, ?it/s]

    ('buildings', 'radiant', 'dota_goodguys_tower3_top', 'health')

    0it [00:00, ?it/s]

    ('buildings', 'radiant', 'dota_goodguys_tower3_bot', 'health')

    0it [00:00, ?it/s]

    ('buildings', 'radiant', 'dota_goodguys_tower4_top', 'health')

    0it [00:00, ?it/s]

    ('buildings', 'radiant', 'dota_goodguys_tower4_bot', 'health')

    0it [00:00, ?it/s]

    ('buildings', 'radiant', 'dota_goodguys_tower1_mid', 'health')

    0it [00:00, ?it/s]

    ('buildings', 'radiant', 'dota_goodguys_tower2_mid', 'health')

    0it [00:00, ?it/s]

    ('buildings', 'radiant', 'dota_goodguys_tower3_mid', 'health')

    0it [00:00, ?it/s]

    2022-09-11 10:57:08,951 Dota2Analysis:14 <cell line: 14> [INFO]: complete buildings
    2022-09-11 10:57:08,951 Dota2Analysis:14 <cell line: 14> [INFO]: complete buildings

    ('hero', 'health')

    0it [00:00, ?it/s]

    ('hero', 'xpos')

    0it [00:00, ?it/s]

    ('hero', 'ypos')

    0it [00:00, ?it/s]

    2022-09-11 10:57:09,018 Dota2Analysis:20 <cell line: 20> [INFO]: complete hero
    2022-09-11 10:57:09,018 Dota2Analysis:20 <cell line: 20> [INFO]: complete hero

    ('player', 'gold')

    0it [00:00, ?it/s]

    2022-09-11 10:57:09,041 Dota2Analysis:23 <cell line: 23> [INFO]: complete player
    2022-09-11 10:57:09,041 Dota2Analysis:23 <cell line: 23> [INFO]: complete player

データセットの読み込み

extract_data = pd.read_csv(datasets_dir + "/extract_data.csv", index_col=0)
extract_data.head(5)
clock_time hero_level-20220909_232301_W hero_level-20220910_001319_W hero_level-20220910_011144_W buildings_radiant_dota_goodguys_tower1_top_health-20220909_232301_W buildings_radiant_dota_goodguys_tower1_top_health-20220910_001319_W buildings_radiant_dota_goodguys_tower1_top_health-20220910_011144_W buildings_radiant_dota_goodguys_tower1_bot_health-20220909_232301_W buildings_radiant_dota_goodguys_tower1_bot_health-20220910_001319_W buildings_radiant_dota_goodguys_tower1_bot_health-20220910_011144_W ... hero_health-20220910_011144_W hero_xpos-20220909_232301_W hero_xpos-20220910_001319_W hero_xpos-20220910_011144_W hero_ypos-20220909_232301_W hero_ypos-20220910_001319_W hero_ypos-20220910_011144_W player_gold-20220909_232301_W player_gold-20220910_001319_W player_gold-20220910_011144_W
0 -74 -1 -1.0 -1.0 1800 1800.0 1800.0 1800 1800.0 1800.0 ... -1.0 -1 -1.0 -1.0 -1 -1.0 -1.0 10 10.0 10.0
1 -73 -1 -1.0 -1.0 1800 1800.0 1800.0 1800 1800.0 1800.0 ... -1.0 -1 -1.0 -1.0 -1 -1.0 -1.0 10 10.0 10.0
2 -72 1 1.0 1.0 1800 1800.0 1800.0 1800 1800.0 1800.0 ... 660.0 -6750 -6700.0 -6700.0 -6550 -6700.0 -6700.0 10 10.0 10.0
3 -71 1 1.0 1.0 1800 1800.0 1800.0 1800 1800.0 1800.0 ... 660.0 -6750 -6700.0 -6700.0 -6550 -6700.0 -6700.0 10 10.0 10.0
4 -70 1 1.0 1.0 1800 1800.0 1800.0 1800 1800.0 1800.0 ... 660.0 -6750 -6700.0 -6599.0 -6550 -6700.0 -6580.0 10 10.0 10.0

5 rows × 49 columns

レベルの描画

plot_data = extract_data.loc[:, extract_data.columns.str.match('^hero_level-.*')]
plt.figure(figsize=(20,8))
sns.lineplot(data=plot_data, lw=2)

移動軌跡の描画

# plot_data = extract_data.loc[:, extract_data.columns.str.match('^hero_xpos-.*')]

plt.figure(figsize=(30,30))
# sns.jointplot(x=x, y=y, kind="kde", color="#D91887")
# sns.jointplot(data=extract_data, x="hero_xpos-20220910_011144_W", y="hero_ypos-20220910_011144_W", hue="species")
sns.jointplot(data=extract_data, x="hero_xpos-20220910_011144_W", y="hero_ypos-20220910_011144_W")
plt.savefig('Trajectory.png', dpi = 300)

plt.figure(figsize=(30,30))
sns.jointplot(x=extract_data["hero_xpos-20220910_011144_W"].values, y=extract_data["hero_ypos-20220910_011144_W"].values, kind="hex", color="#D91887")
plt.savefig('Trajectory_hex.png', dpi = 300)

移動の軌跡を見たところ,ちゃんとmidレーンの防衛に努めていることが分かります.
また,中央に張り付いていることから,戦線もある程度は維持できていると思われます.

ウォークマップの描画

fig = plt.figure(figsize=(15, 15))
fig.patch.set_facecolor('white')
ax = fig.add_subplot(1, 1, 1)

x = extract_data["hero_xpos-20220910_011144_W"].values/3+2000
y = extract_data["hero_ypos-20220910_011144_W"].values/3*-1+2000

# ax.scatter(x, y, alpha=0.5, s=10, c="yellow")
ax.scatter(x, y, alpha=0.5, s=60, c=extract_data.index.values, cmap='seismic')
#plt.colorbar()

im = Image.open("Dota2_map_7.27.png")
ax.imshow(im, alpha=0.6)
#plt.show()
plt.savefig('walk_map_time.png', dpi = 300)

こちらのグラフは時間に関して色付けされています.

  • 濃青:前半
  • 白:中盤
  • 濃赤:終盤

これより,前半は中央で戦闘を行っており,中盤では敵を追撃するような行動が見られます.終盤ではmidレーンを往復するような戦っていることが分かります.

プログラム一式

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

おわりに

次回は,タワーデータに関しても解析していきたいと思います.

参考サイト

コメント

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