<jupyter_start><jupyter_text>上位チームの戦略を知ることは、シミュレーションコンペではとても重要です。過去のシミュレーションコンペ（Halite & Football）では、上位チームの対戦ログを学習したチームが金メダルを取得しています。このnotebookでは、最上位チーム(nagiss)の対戦ログをダウンロードします。Knowing the top team's strategy is very important in Simulation competition. In past simulation competitions (Halite & Football), the team that learned from logs of the top teams got the gold medal.In this notebook, we download the match logs of the top team(nagiss).https://www.kaggle.com/masatomatsui/santa-episode-scraper  https://www.kaggle.com/lebroschar/1000-greedy-decision-tree-model<jupyter_code>import pandas as pd
import numpy as np
import os
import requests
import json
import datetime
import time
BUFFER = 1
MIN_SCORE = 1200 #ignore games with teams below this ranking

base_url = "https://www.kaggle.com/requests/EpisodeService/"
get_url = base_url + "GetEpisodeReplay"
list_url = base_url + "ListEpisodes"
# inital team list

r = requests.post(list_url, json = {"submissionId":  18703109}) # ID is sample value
rj = r.json()

teams_df = pd.DataFrame(rj['result']['teams'])
teams_df.sort_values('publicLeaderboardRank', inplace = True)
teams_df.head(10)<jupyter_output><empty_output><jupyter_text>Get Topteam(nagiss) Episode<jupyter_code>def get_filename(ep_id):
    return 'games/{}.json'.format(ep_id)

def saveEpisode(sub_id, epid, rj):
    # request
    re = requests.post(get_url, json = {"EpisodeId": int(epid)})
        
    # save replay
    with open(get_filename(epid), 'w') as f:
        f.write(re.json()['result']['replay'])

    # save episode info
#     with open('{}_{}_info.json'.format(sub_id, epid), 'w') as f:
#         json.dump([r for r in rj['result']['episodes'] if r['id']==epid][0], f)
def saveEpisodesForSubmission(sub):
    start_time = datetime.datetime.now()
    r = BUFFER;
    result = requests.post(list_url, json = {"submissionId":  int(sub)})
    team_json = result.json()
    team_df = pd.DataFrame(team_json['result']['episodes'])
    team_df.head()
    print('{} games for {}'.format(len(team_df), sub))

    for i in range(len(team_df)):
        agents = team_df.agents.iloc[i]
        if agents[0]['initialScore'] is None or agents[0]['initialScore'] is None:
            continue
        
        if agents[0]['initialScore'] < MIN_SCORE or agents[0]['initialScore'] < MIN_SCORE:
            continue
        
        epid = team_df.id.iloc[i]
        
        filename = get_filename(epid)
        if os.path.exists(filename):
            continue
    
        saveEpisode(sub, epid, team_json); r+=1;
        try:
            size = os.path.getsize(filename) / 1e6
            print('Saved Episode #{} @ {:.1f}MB'.format(i, size), end='\r')
        except:
            print('file {}.json did not seem to save'.format(epid))    
        if r > (datetime.datetime.now() - start_time).seconds:
            time.sleep( r - (datetime.datetime.now() - start_time).seconds)
    
    
    print('\r')
    time.sleep(BUFFER)
    
from collections import defaultdict

def get_top_opponents(sub_id, top=10):

    result = requests.post(list_url, json = {"submissionId":  sub_id})
    team_json = result.json()
    team_df = pd.DataFrame(team_json['result']['episodes'])
    team_df.head()

    agents = defaultdict(float)

    for game in range(len(team_df)):
        for agent in team_df.agents.iloc[game]:
            agents[agent['submissionId']] = max(agents[agent['submissionId']], agent['updatedScore'])

    agents = list(agents.items())
    agents.sort(key=lambda agent: -agent[1])
    return [a[0] for a in agents[:top]]
for sub_id in get_top_opponents(19120581, 10):
    saveEpisodesForSubmission(sub_id)

for sub_id in teams_df['publicLeaderboardSubmissionId'].head(10):
    saveEpisodesForSubmission(sub_id)<jupyter_output><empty_output><jupyter_text>Parsing Game Files<jupyter_code>import json

def get_game_data(filename):
    with open(filename) as f:
        data = json.load(f)

    game_id = data['info']['EpisodeId']
    agent_names = data['info']['TeamNames']
    print("Parsing game", game_id, agent_names)
    
    df = pd.DataFrame(columns=[])

    for agent in [0, 1]:
        d = {'game_id':[], 'agent':[], 'step':[], 'action':[], 'total':[]}
    
        # skip first step because it has no data in it
        for step in data['steps'][1:]:
            d['game_id'].append(game_id)
            d['agent'].append(agent_names[agent])
            d['step'].append(step[0]['observation']['step'])
            d['action'].append(step[agent]['action'])
            d['total'].append(step[agent]['reward'])
    
        df_a = pd.DataFrame(data=d, columns=['game_id', 'agent', 'step', 'action', 'total'])
    
        df_a['reward'] = df_a.total - df_a.total.shift(1, fill_value=0)
        df_a['prev_reward'] = df_a.reward.shift(1, fill_value=0)
        df_a['prev_action'] = df_a.action.shift(1, fill_value=0)
    
        df = df.append(df_a, ignore_index=True)

    for agent in agent_names:
        # convert to list because indixes don't match
        df.loc[df.agent == agent, 'prev_opp_action'] = df[df.agent != agent].action.shift(1, fill_value=0).tolist()

    df = df.astype({'prev_opp_action':'int32'})
    return df

# parses a game file into training data for a ML model
def get_training_data(filename):
    with open(filename) as f:
        data = json.load(f)

    game_id = data['info']['EpisodeId']
    agent_names = data['info']['TeamNames']
    num_machines = data['configuration']['banditCount']
    print("Parsing game", game_id, agent_names)
    
    d = {'game_id': [],
         'step': [], 
         'machine': [],
         'agent': [],
         'n_pulls': [],
         'n_success': [],
         'n_opp_pulls': [],
         'streak': [],
         'opp_streak': [],
         'win_streak': [],
         'threshold': []}
    
    agent_states = {'reward_0': 0, 'last_reward_0': 0, 'reward_1': 0, 'last_reward_1': 0}
    machine_states = [
        {'pulls_0': 0, 
         'success_0': 0, 
         'streak_0': 0, 
         'win_streak_0': 0,
         'pulls_1': 0,
         'success_1': 0,
         'streak_1': 0, 
         'win_streak_1': 0 } for i in range(num_machines)
    ]
    
    # add initial thresholds
    for m in range(num_machines):
        d['game_id'].append(game_id)
        d['step'].append(0)
        d['machine'].append(m)
        d['agent'].append(0)
        d['n_pulls'].append(0)
        d['n_success'].append(0)
        d['n_opp_pulls'].append(0)
        d['streak'].append(0)
        d['opp_streak'].append(0)
        d['win_streak'].append(0)
        d['threshold'].append(data['steps'][0][0]['observation']['thresholds'][m]/100)

    for step in data['steps'][1:]:
        if step[0]['status'] =='INVALID' or step[1]['status'] == 'INVALID':
            # just quit parsing if one of the agents screwed something up
            break
        
        # update agent states
        for agent in [0, 1]:
            agent_states[f"last_reward_{agent}"] = step[agent]['reward']- agent_states[f"reward_{agent}"]
            agent_states[f"reward_{agent}"] = step[agent]['reward']
            
        # update machine states
        for agent in [0, 1]:
            action = step[agent]['action']
            machine_states[action][f"pulls_{agent}"] += 1
            machine_states[action][f"success_{agent}"] += agent_states[f"last_reward_{agent}"]
            
            # update streaks
            for m in range(num_machines):
                if m == step[agent]['action']:
                    machine_states[m][f"streak_{agent}"] += 1
                else:
                    machine_states[m][f"streak_{agent}"] = 0
            
            # update win streaks
            if agent_states[f"last_reward_{agent}"]:
                machine_states[action][f"win_streak_{agent}"] += 1
            else:
                machine_states[action][f"win_streak_{agent}"] = 0
        
        # update training data for each selected machine
        for agent in [0, 1]:
            for machine in step[0]['observation']['lastActions']:
                opp = 1 if agent == 0 else 0
                d['game_id'].append(game_id)
                d['step'].append(step[0]['observation']['step'])
                d['machine'].append(machine)
                d['agent'].append(agent)
                d['n_pulls'].append(machine_states[machine][f"pulls_{agent}"])
                d['n_success'].append(machine_states[machine][f"success_{agent}"])
                d['n_opp_pulls'].append(machine_states[machine][f"pulls_{opp}"])
                d['streak'].append(machine_states[machine][f"streak_{agent}"])
                d['opp_streak'].append(machine_states[machine][f"streak_{opp}"])
                d['win_streak'].append(machine_states[machine][f"win_streak_{agent}"])
                d['threshold'].append(step[0]['observation']['thresholds'][machine]/100)

    df = pd.DataFrame(data = d)
    return df
    
train_data = pd.read_parquet('training_data/game_id_data.parquet')
existing_games = train_data.game_id.unique()
print(f"Found {len(existing_games)} games already in dataset")

# load training game files into one data frame
for dirname, _, filenames in os.walk('games'):
    print(f"Found {len(filenames)} game files")
    filenames = [f for f in filenames if int(f.split('.')[0]) not in existing_games]
    new_data = pd.concat([get_training_data(os.path.join(dirname, f)) for f in filenames], ignore_index=True)

train_data = train_data.append(new_data)
print(f"\nFound {train_data.shape[0]} training rows")
train_data.to_parquet('training_data/data.parquet')
train_data.head(1000000).to_parquet('training_data/data_1_000_000.parquet')
train_data.head(10000000).to_parquet('training_data/data_10_000_000.parquet')
train_data.head(30000000).to_parquet('training_data/data_30_000_000.parquet')
train_data.head()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.15149974580579562, 'fraction_numerical': 0.016878495170310117, 'mean_word_length': 3.0494030465212023, 'pattern_counts': {'":': 4, '<': 15, '<?xml version=': 0, '>': 14, 'https://': 3, 'lorem ipsum': 0, 'www.': 3, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 14, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '592233', 'license': 'MIT', 'n_tokens_mistral': 3451, 'n_tokens_neox': 3299, 'n_words': 623, 'path': 'santa-episode-scraper.ipynb', 'repo_name': 'TheoKanning/multi-armed-bandit', 'size': '9835'}