Full Code of wuhuikai/MSC for AI

master 238fa4640325 cached
48 files
32.7 MB
8.6M tokens
118 symbols
1 requests
Copy disabled (too large) Download .txt
Showing preview only (34,292K chars total). Download the full file to get everything.
Repository: wuhuikai/MSC
Branch: master
Commit: 238fa4640325
Files: 48
Total size: 32.7 MB

Directory structure:
gitextract_wn7uutsx/

├── .gitignore
├── Baselines/
│   ├── BuildOrderPrediction/
│   │   ├── test.py
│   │   ├── train.py
│   │   └── train_spatial.py
│   └── GlobalStateEvaluation/
│       ├── requirements.txt
│       ├── test.py
│       ├── train.py
│       └── train_spatial.py
├── README.md
├── _config.yml
├── data_loader/
│   └── BatchEnv.py
├── extract_features/
│   ├── SpatialFeatures.py
│   ├── compute_stat.sh
│   ├── extract_features.sh
│   ├── game_state.py
│   ├── global_feature_vector.py
│   ├── replay_stat.py
│   ├── spatial_feature_tensor.py
│   └── split.py
├── instructions/
│   ├── EasyWay.md
│   └── HardWay.md
├── parse_replay/
│   ├── extract_actions.py
│   ├── parse_replay.py
│   ├── parse_replay.sh
│   ├── replay2global_features.py
│   └── sample_actions.py
├── preprocess/
│   ├── parse_replay_info.py
│   ├── preprocess.py
│   └── preprocess.sh
├── requirements.txt
└── train_val_test/
    ├── Protoss_vs_Protoss/
    │   ├── test.json
    │   ├── train.json
    │   └── val.json
    ├── Protoss_vs_Terran/
    │   ├── test.json
    │   ├── train.json
    │   └── val.json
    ├── Protoss_vs_Zerg/
    │   ├── test.json
    │   ├── train.json
    │   └── val.json
    ├── Terran_vs_Terran/
    │   ├── test.json
    │   ├── train.json
    │   └── val.json
    ├── Terran_vs_Zerg/
    │   ├── test.json
    │   ├── train.json
    │   └── val.json
    └── Zerg_vs_Zerg/
        ├── test.json
        ├── train.json
        └── val.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*.tar.gz

# PyCharm
.idea

# Training Result
checkpoints

# Dataset
replays_infos
high_quality_replays
parsed_replays

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# dotenv
.env

# virtualenv
.venv
venv/
ENV/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/


================================================
FILE: Baselines/BuildOrderPrediction/test.py
================================================
import os
import visdom
import pickle
import argparse

import numpy as np

LAMBDA = 0.99

def calc_action_acc(action_pre, action_gt):
    return np.mean(action_pre == action_gt)

def calc_weighted_action_acc(action_pre, action_gt, weight):
    return np.sum((action_pre == action_gt) * np.abs(weight)) / np.sum(np.abs(weight))

def show_test_result(name, phrase, result, steps=10, title=''):
    action_pres, action_gts = result

    ################################## Calc Acc #########################################
    weights = [(action_gt[0]*2-1)*LAMBDA**np.arange(len(action_gt)-1, -1, -1) for action_gt in action_gts]

    action_pres_np = np.hstack(action_pres)
    action_gts_np = np.hstack(action_gts)
    weights_np = np.hstack(weights)

    action_acc = calc_action_acc(action_pres_np, action_gts_np)
    weighted_action_acc = calc_weighted_action_acc(action_pres_np, action_gts_np, weights_np)
    print('\tAction Accuracy: {}%\tWeighted Action Accuracy: {}%'.format(action_acc*100, weighted_action_acc * 100))
    ################################### Plot ###################################################
    vis = visdom.Visdom(env=name + '[{}]'.format(phrase))

    action_pre_result, action_gt_result = [[] for _ in range(steps)], [[] for _ in range(steps)]
    weight_result = [[] for _ in range(steps)]
    for action_pre, action_gt, weight in zip(action_pres, action_gts, weights):
        if len(action_pre) < steps:
            continue

        step = len(action_pre) // steps
        for s in range(steps):
            action_pre_result[s].append(action_pre[s * step:(s + 1) * step])
            action_gt_result[s].append(action_gt[s * step:(s + 1) * step])
            weight_result[s].append(weight[s * step:(s + 1) * step])

    legend = ['Action', 'Weighted Action']
    X = np.repeat(np.arange(steps), len(legend), axis=0).reshape(steps, -1)
    Y = np.zeros((steps, len(legend)))
    for idx, (action_pres, action_gts, weights) in enumerate(
            zip(action_pre_result, action_gt_result, weight_result)):

        action_pres_np = np.hstack(action_pres)
        action_gts_np = np.hstack(action_gts)
        weights_np = np.hstack(weights)

        Y[idx, 0] = calc_action_acc(action_pres_np, action_gts_np)
        Y[idx, 1] = calc_weighted_action_acc(action_pres_np, action_gts_np, weights_np)

    vis.line(X=X, Y=Y,
             opts=dict(title='Acc[{}]'.format(title), legend=legend), win=title)

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='A2C : Starcraft II')
    parser.add_argument('--name', type=str, default='StarCraft II:TvT[BuildOrder]',
                        help='Experiment name. All outputs will be stored in checkpoints/[name]/')
    parser.add_argument('--phrase', type=str, default='test',
                        help='val|test (default: test)')
    args = parser.parse_args()

    test_path = os.path.join('checkpoints', args.name, args.phrase)
    test_results = sorted([int(os.path.basename(result).split('.')[0].split('_')[-1])
                        for result in os.listdir(test_path)])

    for idx, name_id in enumerate(test_results):
        result_path = os.path.join(test_path, 'model_iter_{}.pth'.format(name_id))
        print('Processing {}/{} ...'.format(idx+1, len(test_results)))
        print('\t'+result_path)

        with open(result_path, 'rb') as f:
            result = pickle.load(f)

        show_test_result(args.name, args.phrase, result, title=idx)

================================================
FILE: Baselines/BuildOrderPrediction/train.py
================================================
from __future__ import print_function

import os
import json
import time
import pickle
import argparse

import visdom
import numpy as np

import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F

from torch.autograd import Variable

from Baselines.GlobalStateEvaluation.test import show_test_result

from data_loader.BatchEnv import BatchGlobalFeatureEnv

class BuildOrderGRU(torch.nn.Module):
    def __init__(self, num_inputs, num_outputs):
        super(BuildOrderGRU, self).__init__()
        self.linear1 = nn.Linear(num_inputs, 1024)
        self.linear2 = nn.Linear(1024, 2048)

        self.rnn1 = nn.GRUCell(input_size=2048, hidden_size=2048)
        self.rnn2 = nn.GRUCell(input_size=2048, hidden_size=512)

        self.actor_linear = nn.Linear(512, num_outputs)

        self.h1, self.h2 = None, None

    def forward(self, states, require_init):
        batch = states.size(1)
        if self.h1 is None or self.h2 is None:
            self.h1 = Variable(states.data.new().resize_((batch, 2048)).zero_())
            self.h2 = Variable(states.data.new().resize_((batch, 512)).zero_())
        elif True in require_init:
            h1, h2 = self.h1.data, self.h2.data
            for idx, init in enumerate(require_init):
                if init:
                    h1[idx].zero_()
                    h2[idx].zero_()
            self.h1, self.h2 = Variable(h1), Variable(h2)
        else:
            pass

        values = []
        for idx, state in enumerate(states):
            x = F.relu(self.linear1(state))
            x = F.relu(self.linear2(x))
            self.h1 = self.rnn1(x, self.h1)
            self.h2 = self.rnn2(self.h1, self.h2)

            values.append(self.actor_linear(self.h2))

        return values

    def detach(self):
        if self.h1 is not None:
            self.h1.detach_()
        if self.h2 is not None:
            self.h2.detach_()

def train(model, env, args):
    #################################### PLOT ###################################################
    STEPS = 10
    LAMBDA = 0.99
    vis = visdom.Visdom(env=args.name+'[{}]'.format(args.phrase))
    pre_per_replay = [[] for _ in range(args.n_replays)]
    gt_per_replay = [[] for _ in range(args.n_replays)]
    acc = None
    win = vis.line(X=np.zeros(1), Y=np.zeros(1))
    loss_win = vis.line(X=np.zeros(1), Y=np.zeros(1))

    #################################### TRAIN ######################################################
    torch.manual_seed(args.seed)
    torch.cuda.manual_seed(args.seed)

    gpu_id = args.gpu_id
    with torch.cuda.device(gpu_id):
        model = model.cuda() if gpu_id >= 0 else model
    model.train()
    optimizer = optim.Adam(model.parameters(), lr=args.lr)

    epoch = 0
    save = args.save_intervel
    env_return = env.step(reward=False, action=True)
    if env_return is not None:
        (states, actions_gt), require_init = env_return
    with torch.cuda.device(gpu_id):
        states = torch.from_numpy(states).float()
        actions_gt = torch.from_numpy(actions_gt).long().squeeze()
        weight = torch.ones((env.n_actions,))
        weight[-1] = 0.05
        if gpu_id >= 0:
            states = states.cuda()
            actions_gt = actions_gt.cuda()
            weight = weight.cuda()

    while True:
        actions = model(Variable(states), require_init)

        action_loss = 0
        for action, action_gt in zip(actions, actions_gt):
            action_loss = action_loss + F.cross_entropy(action, Variable(action_gt), weight=weight)
        action_loss = action_loss / len(actions)

        model.zero_grad()
        action_loss.backward()
        optimizer.step()
        model.detach()

        if env.epoch > epoch:
            epoch = env.epoch
            for p in optimizer.param_groups:
                p['lr'] *= 0.1

        ############################ PLOT ##########################################
        vis.updateTrace(X=np.asarray([env.step_count()]),
                        Y=np.asarray(action_loss.data.cpu().numpy()),
                        win=loss_win,
                        name='action')

        actions_np = np.swapaxes(np.asarray([np.argmax(action.data.cpu().numpy(), axis=1) for action in actions]), 0, 1)
        actions_gt_np = np.swapaxes(actions_gt.cpu().numpy(), 0, 1)

        for idx, (action, action_gt, init) in enumerate(zip(actions_np, actions_gt_np, require_init)):
            if init and len(pre_per_replay[idx]) > 0:
                pre_per_replay[idx] = np.asarray(pre_per_replay[idx], dtype=np.uint8)
                gt_per_replay[idx] = np.asarray(gt_per_replay[idx], dtype=np.uint8)

                step = len(pre_per_replay[idx]) // STEPS
                if step > 0:
                    acc_tmp = []
                    for s in range(STEPS):
                        action_pre = pre_per_replay[idx][s*step:(s+1)*step]
                        action_gt = gt_per_replay[idx][s*step:(s+1)*step]
                        acc_tmp.append(np.mean(action_pre == action_gt))

                    acc_tmp = np.asarray(acc_tmp)
                    if acc is None:
                        acc = acc_tmp
                    else:
                        acc = LAMBDA * acc + (1-LAMBDA) * acc_tmp

                    if acc is None:
                        continue
                    for s in range(STEPS):
                        vis.updateTrace(X=np.asarray([env.step_count()]),
                                        Y=np.asarray([acc[s]]),
                                        win=win,
                                        name='{}[{}%~{}%]'.format('action', s*10, (s+1)*10))
                    vis.updateTrace(X=np.asarray([env.step_count()]),
                                    Y=np.asarray([np.mean(acc)]),
                                    win=win,
                                    name='action[TOTAL]')

                pre_per_replay[idx] = []
                gt_per_replay[idx] = []

            pre_per_replay[idx].append(action[-1])
            gt_per_replay[idx].append(action_gt[-1])

        ####################### NEXT BATCH ###################################
        env_return = env.step(reward=False, action=True)
        if env_return is not None:
            (raw_states, raw_actions_gt), require_init = env_return
            states = states.copy_(torch.from_numpy(raw_states).float())
            actions_gt = actions_gt.copy_(torch.from_numpy(raw_actions_gt).long().squeeze())

        if env.step_count() > save or env_return is None:
            save = env.step_count()+args.save_intervel
            torch.save(model.state_dict(),
                       os.path.join(args.model_path, 'model_iter_{}.pth'.format(env.step_count())))
            torch.save(model.state_dict(), os.path.join(args.model_path, 'model_latest.pth'))
        if env_return is None:
            env.close()
            break

def test(model, env, args):
    ######################### SAVE RESULT ############################
    action_pre_per_replay = [[]]
    action_gt_per_replay = [[]]

    ######################### TEST ###################################
    torch.manual_seed(args.seed)
    torch.cuda.manual_seed(args.seed)

    gpu_id = args.gpu_id
    with torch.cuda.device(gpu_id):
        model = model.cuda() if gpu_id >= 0 else model
    model.eval()

    env_return = env.step(reward=False, action=True)
    if env_return is not  None:
        (states, actions_gt), require_init = env_return
    with torch.cuda.device(gpu_id):
        states = torch.from_numpy(states).float()
        actions_gt = torch.from_numpy(actions_gt).long().squeeze()
        if gpu_id >= 0:
            states = states.cuda()
            actions_gt = actions_gt.cuda()

    while True:
        actions = model(Variable(states), require_init)
        ############################ PLOT ##########################################
        actions_np = np.squeeze(np.vstack([np.argmax(action.data.cpu().numpy(), axis=1) for action in actions]))
        actions_gt_np = np.squeeze(actions_gt.cpu().numpy())

        if require_init[-1] and len(action_gt_per_replay[-1]) > 0:
            action_pre_per_replay[-1] = np.ravel(np.hstack(action_pre_per_replay[-1]))
            action_gt_per_replay[-1] = np.ravel(np.hstack(action_gt_per_replay[-1]))

            action_pre_per_replay.append([])
            action_gt_per_replay.append([])

        action_pre_per_replay[-1].append(actions_np)
        action_gt_per_replay[-1].append(actions_gt_np)
        ########################### NEXT BATCH #############################################
        env_return = env.step(reward=False, action=True)
        if env_return is not None:
            (raw_states, raw_actions), require_init = env_return
            states = states.copy_(torch.from_numpy(raw_states).float())
            actions_gt = actions_gt.copy_(torch.from_numpy(raw_actions).long().squeeze())
        else:
            action_pre_per_replay[-1] = np.ravel(np.hstack(action_pre_per_replay[-1]))
            action_gt_per_replay[-1] = np.ravel(np.hstack(action_gt_per_replay[-1]))

            env.close()
            break

    return action_pre_per_replay, action_gt_per_replay

def next_path(model_folder, paths):
    models = {int(os.path.basename(model).split('.')[0].split('_')[-1])
                for model in os.listdir(model_folder) if 'latest' not in model}
    models_not_process = models - paths
    if len(models_not_process) == 0:
        return None
    models_not_process = sorted(models_not_process)
    paths.add(models_not_process[0])

    return os.path.join(model_folder, 'model_iter_{}.pth'.format(models_not_process[0]))

def main():
    # Training settings
    parser = argparse.ArgumentParser(description='Global State Evaluation : StarCraft II')
    parser.add_argument('--name', type=str, default='StarCraft II:TvT[BuildOrder]',
                        help='Experiment name. All outputs will be stored in checkpoints/[name]/')
    parser.add_argument('--replays_path', default='train_val_test/Terran_vs_Terran',
                        help='Path for training, validation and test set (default: train_val_test/Terran_vs_Terran)')
    parser.add_argument('--race', default='Terran', help='Which race? (default: Terran)')
    parser.add_argument('--enemy_race', default='Terran', help='Which the enemy race? (default: Terran)')
    parser.add_argument('--phrase', type=str, default='train',
                        help='train|val|test (default: train)')
    parser.add_argument('--gpu_id', default=0, type=int, help='Which GPU to use [-1 indicate CPU] (default: 0)')

    parser.add_argument('--lr', type=float, default=0.001, help='Learning rate (default: 0.001)')
    parser.add_argument('--seed', type=int, default=1, help='Random seed (default: 1)')

    parser.add_argument('--n_steps', type=int, default=20, help='# of forward steps (default: 20)')
    parser.add_argument('--n_replays', type=int, default=256, help='# of replays (default: 256)')
    parser.add_argument('--n_epoch', type=int, default=10, help='# of epoches (default: 10)')

    parser.add_argument('--save_intervel', type=int, default=1000000,
                        help='Frequency of model saving (default: 1000000)')
    args = parser.parse_args()

    args.save_path = os.path.join('checkpoints', args.name)
    args.model_path = os.path.join(args.save_path, 'snapshots')

    print('------------ Options -------------')
    for k, v in sorted(vars(args).items()):
        print('{}: {}'.format(k, v))
    print('-------------- End ----------------')

    if args.phrase == 'train':
        if not os.path.isdir(args.save_path):
            os.makedirs(args.save_path)
        if not os.path.isdir(args.model_path):
            os.makedirs(args.model_path)
        with open(os.path.join(args.save_path, 'config'), 'w') as f:
            f.write(json.dumps(vars(args)))

        env = BatchGlobalFeatureEnv()
        env.init(os.path.join(args.replays_path, '{}.json'.format(args.phrase)),
                    './', args.race, args.enemy_race, n_steps=args.n_steps, seed=args.seed,
                        n_replays=args.n_replays, epochs=args.n_epoch)
        model = BuildOrderGRU(env.n_features, env.n_actions)
        train(model, env, args)
    elif 'val' in args.phrase or 'test' in args.phrase:
        test_result_path = os.path.join(args.save_path, args.phrase)
        if not os.path.isdir(test_result_path):
            os.makedirs(test_result_path)

        dataset_path = 'test.json' if 'test' in args.phrase else 'val.json'
        paths = set()
        while True:
            path = next_path(args.model_path, paths)
            if path is not None:
                print('[{}]Testing {} ...'.format(len(paths), path))

                env = BatchGlobalFeatureEnv()
                env.init(os.path.join(args.replays_path, dataset_path),
                            './', args.race, args.enemy_race, n_steps=args.n_steps,
                                            seed=args.seed, n_replays=1, epochs=1)
                model = BuildOrderGRU(env.n_features, env.n_actions)
                model.load_state_dict(torch.load(path))
                result = test(model, env, args)
                with open(os.path.join(test_result_path, os.path.basename(path)), 'wb') as f:
                    pickle.dump(result, f)
                show_test_result(args.name, args.phrase, result, title=len(paths)-1)
            else:
                time.sleep(60)

if __name__ == '__main__':
    main()

================================================
FILE: Baselines/BuildOrderPrediction/train_spatial.py
================================================
from __future__ import print_function

import os
import json
import time
import pickle
import argparse

import visdom
import numpy as np

import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F

from torch.autograd import Variable

from Baselines.GlobalStateEvaluation.test import show_test_result

from data_loader.BatchEnv import BatchSpatialEnv

class BuildOrderGRU(torch.nn.Module):
    def __init__(self, n_channels, n_features, n_actions):
        super(BuildOrderGRU, self).__init__()

        self.conv1 = nn.Conv2d(n_channels, 16, 8, stride=4)
        self.conv2 = nn.Conv2d(16, 32, 4, stride=2)

        self.linear_g = nn.Linear(n_features, 128)

        self.linear = nn.Linear(1280, 512)
        self.rnn = nn.GRUCell(input_size=512, hidden_size=128)

        self.actor_linear = nn.Linear(128, n_actions)

        self.h = None

    def forward(self, states_S, states_G, require_init):
        batch = states_S.size(1)
        if self.h is None:
            self.h = Variable(states_S.data.new().resize_((batch, 128)).zero_())
        elif True in require_init:
            h= self.h.data
            for idx, init in enumerate(require_init):
                if init:
                    h[idx].zero_()
            self.h = Variable(h)
        else:
            pass

        values = []
        for idx, (state_S, state_G) in enumerate(zip(states_S, states_G)):
            x_s = F.relu(self.conv1(state_S))
            x_s = F.relu(self.conv2(x_s))
            x_s = x_s.view(-1, 1152)

            x_g = F.relu(self.linear_g(state_G))

            x = torch.cat((x_s, x_g), 1)
            x = F.relu(self.linear(x))

            self.h = self.rnn(x, self.h)

            values.append(self.actor_linear(self.h))

        return values

    def detach(self):
        if self.h is not None:
            self.h.detach_()

def train(model, env, args):
    #################################### PLOT ###################################################
    STEPS = 10
    LAMBDA = 0.99
    vis = visdom.Visdom(env=args.name+'[{}]'.format(args.phrase))
    pre_per_replay = [[] for _ in range(args.n_replays)]
    gt_per_replay = [[] for _ in range(args.n_replays)]
    acc = None
    win = vis.line(X=np.zeros(1), Y=np.zeros(1))
    loss_win = vis.line(X=np.zeros(1), Y=np.zeros(1))

    #################################### TRAIN ######################################################
    torch.manual_seed(args.seed)
    torch.cuda.manual_seed(args.seed)

    gpu_id = args.gpu_id
    with torch.cuda.device(gpu_id):
        model = model.cuda() if gpu_id >= 0 else model
    model.train()
    optimizer = optim.Adam(model.parameters(), lr=args.lr)

    epoch = 0
    save = args.save_intervel
    env_return = env.step(reward=False, action=True)
    if env_return is not None:
        (states_S, states_G, actions_gt), require_init = env_return
    with torch.cuda.device(gpu_id):
        states_S = torch.from_numpy(states_S).float()
        states_G = torch.from_numpy(states_G).float()
        actions_gt = torch.from_numpy(actions_gt).long().squeeze()
        weight = torch.ones((env.n_actions,))
        weight[-1] = 0.05
        if gpu_id >= 0:
            states_S = states_S.cuda()
            states_G = states_G.cuda()
            actions_gt = actions_gt.cuda()
            weight = weight.cuda()

    while True:
        actions = model(Variable(states_S), Variable(states_G), require_init)
        action_loss = 0
        for action, action_gt in zip(actions, actions_gt):
            action_loss = action_loss + F.cross_entropy(action, Variable(action_gt), weight=weight)
        action_loss = action_loss / len(actions_gt)

        model.zero_grad()
        action_loss.backward()
        optimizer.step()
        model.detach()

        if env.epoch > epoch:
            epoch = env.epoch
            for p in optimizer.param_groups:
                p['lr'] *= 0.5

        ############################ PLOT ##########################################
        vis.updateTrace(X=np.asarray([env.step_count()]),
                        Y=np.asarray(action_loss.data.cpu().numpy()),
                        win=loss_win,
                        name='action')

        actions_np = np.swapaxes(np.asarray([np.argmax(action.data.cpu().numpy(), axis=1) for action in actions]), 0, 1)
        actions_gt_np = np.swapaxes(actions_gt.cpu().numpy(), 0, 1)

        for idx, (action, action_gt, init) in enumerate(zip(actions_np, actions_gt_np, require_init)):
            if init and len(pre_per_replay[idx]) > 0:
                pre_per_replay[idx] = np.asarray(pre_per_replay[idx], dtype=np.uint8)
                gt_per_replay[idx] = np.asarray(gt_per_replay[idx], dtype=np.uint8)

                step = len(pre_per_replay[idx]) // STEPS
                if step > 0:
                    acc_tmp = []
                    for s in range(STEPS):
                        action_pre = pre_per_replay[idx][s*step:(s+1)*step]
                        action_gt = gt_per_replay[idx][s*step:(s+1)*step]
                        acc_tmp.append(np.mean(action_pre == action_gt))

                    acc_tmp = np.asarray(acc_tmp)
                    if acc is None:
                        acc = acc_tmp
                    else:
                        acc = LAMBDA * acc + (1-LAMBDA) * acc_tmp

                    if acc is None:
                        continue
                    for s in range(STEPS):
                        vis.updateTrace(X=np.asarray([env.step_count()]),
                                        Y=np.asarray([acc[s]]),
                                        win=win,
                                        name='{}[{}%~{}%]'.format('action', s*10, (s+1)*10))
                    vis.updateTrace(X=np.asarray([env.step_count()]),
                                    Y=np.asarray([np.mean(acc)]),
                                    win=win,
                                    name='action[TOTAL]')

                pre_per_replay[idx] = []
                gt_per_replay[idx] = []

            pre_per_replay[idx].append(action[-1])
            gt_per_replay[idx].append(action_gt[-1])

        ####################### NEXT BATCH ###################################
        env_return = env.step(reward=False, action=True)
        if env_return is not None:
            (raw_states_S, raw_states_G, raw_rewards), require_init = env_return
            states_S = states_S.copy_(torch.from_numpy(raw_states_S).float())
            states_G = states_G.copy_(torch.from_numpy(raw_states_G).float())
            actions_gt = actions_gt.copy_(torch.from_numpy(raw_rewards).long().squeeze())

        if env.step_count() > save or env_return is None:
            save = env.step_count()+args.save_intervel
            torch.save(model.state_dict(),
                       os.path.join(args.model_path, 'model_iter_{}.pth'.format(env.step_count())))
            torch.save(model.state_dict(), os.path.join(args.model_path, 'model_latest.pth'))
        if env_return is None:
            env.close()
            break

def test(model, env, args):
    ######################### SAVE RESULT ############################
    action_pre_per_replay = [[]]
    action_gt_per_replay = [[]]

    ######################### TEST ###################################
    torch.manual_seed(args.seed)
    torch.cuda.manual_seed(args.seed)

    gpu_id = args.gpu_id
    with torch.cuda.device(gpu_id):
        model = model.cuda() if gpu_id >= 0 else model
    model.eval()

    env_return = env.step(reward=False, action=True)
    if env_return is not  None:
        (states_S, states_G, actions_gt), require_init = env_return
    with torch.cuda.device(gpu_id):
        states_S = torch.from_numpy(states_S).float()
        states_G = torch.from_numpy(states_G).float()
        actions_gt = torch.from_numpy(actions_gt).float()
        if gpu_id >= 0:
            states_S = states_S.cuda()
            states_G = states_G.cuda()
            actions_gt = actions_gt.cuda()

    while True:
        actions = model(Variable(states_S), Variable(states_G), require_init)
        ############################ PLOT ##########################################
        actions_np = np.squeeze(np.vstack([np.argmax(action.data.cpu().numpy(), axis=1) for action in actions]))
        actions_gt_np = np.squeeze(actions_gt.cpu().numpy())

        if require_init[-1] and len(action_gt_per_replay[-1]) > 0:
            action_pre_per_replay[-1] = np.ravel(np.hstack(action_pre_per_replay[-1]))
            action_gt_per_replay[-1] = np.ravel(np.hstack(action_gt_per_replay[-1]))

            action_pre_per_replay.append([])
            action_gt_per_replay.append([])

        action_pre_per_replay[-1].append(actions_np)
        action_gt_per_replay[-1].append(actions_gt_np)
        ########################### NEXT BATCH #############################################
        env_return = env.step(reward=False, action=True)
        if env_return is not None:
            (raw_states_S, raw_states_G, raw_actions), require_init = env_return
            states_S = states_S.copy_(torch.from_numpy(raw_states_S).float())
            states_G = states_G.copy_(torch.from_numpy(raw_states_G).float())
            actions_gt = actions_gt.copy_(torch.from_numpy(raw_actions).float())
        else:
            action_pre_per_replay[-1] = np.ravel(np.hstack(action_pre_per_replay[-1]))
            action_gt_per_replay[-1] = np.ravel(np.hstack(action_gt_per_replay[-1]))

            env.close()
            break

    return action_pre_per_replay, action_gt_per_replay

def next_path(model_folder, paths):
    models = {int(os.path.basename(model).split('.')[0].split('_')[-1])
                for model in os.listdir(model_folder) if 'latest' not in model}
    models_not_process = models - paths
    if len(models_not_process) == 0:
        return None
    models_not_process = sorted(models_not_process)
    paths.add(models_not_process[0])

    return os.path.join(model_folder, 'model_iter_{}.pth'.format(models_not_process[0]))

def main():
    # Training settings
    parser = argparse.ArgumentParser(description='Global State Evaluation : StarCraft II')
    parser.add_argument('--name', type=str, default='StarCraft II:TvT[BuildOrder:Spatial]',
                        help='Experiment name. All outputs will be stored in checkpoints/[name]/')
    parser.add_argument('--replays_path', default='train_val_test/Terran_vs_Terran',
                        help='Path for training, validation and test set (default: train_val_test/Terran_vs_Terran)')
    parser.add_argument('--race', default='Terran', help='Which race? (default: Terran)')
    parser.add_argument('--enemy_race', default='Terran', help='Which the enemy race? (default: Terran)')
    parser.add_argument('--phrase', type=str, default='train',
                        help='train|val|test (default: train)')
    parser.add_argument('--gpu_id', default=0, type=int, help='Which GPU to use [-1 indicate CPU] (default: 0)')

    parser.add_argument('--lr', type=float, default=0.001, help='Learning rate (default: 0.001)')
    parser.add_argument('--seed', type=int, default=1, help='Random seed (default: 1)')

    parser.add_argument('--n_steps', type=int, default=20, help='# of forward steps (default: 20)')
    parser.add_argument('--n_replays', type=int, default=32, help='# of replays (default: 32)')
    parser.add_argument('--n_epoch', type=int, default=10, help='# of epoches (default: 10)')

    parser.add_argument('--save_intervel', type=int, default=1000000,
                        help='Frequency of model saving (default: 1000000)')
    args = parser.parse_args()

    args.save_path = os.path.join('checkpoints', args.name)
    args.model_path = os.path.join(args.save_path, 'snapshots')

    print('------------ Options -------------')
    for k, v in sorted(vars(args).items()):
        print('{}: {}'.format(k, v))
    print('-------------- End ----------------')

    if args.phrase == 'train':
        if not os.path.isdir(args.save_path):
            os.makedirs(args.save_path)
        if not os.path.isdir(args.model_path):
            os.makedirs(args.model_path)
        with open(os.path.join(args.save_path, 'config'), 'w') as f:
            f.write(json.dumps(vars(args)))

        env = BatchSpatialEnv()
        env.init(os.path.join(args.replays_path, '{}.json'.format(args.phrase)),
                    './', args.race, args.enemy_race, n_steps=args.n_steps, seed=args.seed,
                        n_replays=args.n_replays, epochs=args.n_epoch)
        model = BuildOrderGRU(env.n_channels, env.n_features, env.n_actions)
        train(model, env, args)
    elif 'val' in args.phrase or 'test' in args.phrase:
        test_result_path = os.path.join(args.save_path, args.phrase)
        if not os.path.isdir(test_result_path):
            os.makedirs(test_result_path)

        dataset_path = 'test.json' if 'test' in args.phrase else 'val.json'
        paths = set()
        while True:
            path = next_path(args.model_path, paths)
            if path is not None:
                print('[{}]Testing {} ...'.format(len(paths), path))

                env = BatchSpatialEnv()
                env.init(os.path.join(args.replays_path, dataset_path),
                            './', args.race, args.enemy_race, n_steps=args.n_steps,
                                            seed=args.seed, n_replays=1, epochs=1)
                model = BuildOrderGRU(env.n_channels, env.n_features, env.n_actions)
                model.load_state_dict(torch.load(path))
                result = test(model, env, args)
                with open(os.path.join(test_result_path, os.path.basename(path)), 'wb') as f:
                    pickle.dump(result, f)
                show_test_result(args.name, args.phrase, result, title=len(paths)-1)
            else:
                time.sleep(60)

if __name__ == '__main__':
    main()


================================================
FILE: Baselines/GlobalStateEvaluation/requirements.txt
================================================
numpy == 1.13.0

visdom == 0.1.4

torch == 0.1.12.post2

================================================
FILE: Baselines/GlobalStateEvaluation/test.py
================================================
import os
import visdom
import pickle
import argparse

import numpy as np

LAMBDA = 0.99

def calc_value_acc(value_pre, value_gt):
    return np.mean(value_pre == value_gt)

def calc_weighted_value_acc(value_pre, value_gt, weight):
    return np.sum((value_pre == value_gt) * np.abs(weight)) / np.sum(np.abs(weight))

def show_test_result(name, phrase, result, steps=10, title=''):
    value_pres, value_gts = result

    ################################## Calc Acc #########################################
    weights = [(value_gt[0]*2-1)*LAMBDA**np.arange(len(value_gt)-1, -1, -1) for value_gt in value_gts]

    value_pres_np = np.hstack(value_pres)
    value_gts_np = np.hstack(value_gts)
    weights_np = np.hstack(weights)

    value_acc = calc_value_acc(value_pres_np, value_gts_np)
    weighted_value_acc = calc_weighted_value_acc(value_pres_np, value_gts_np, weights_np)
    print('\tValue Accuracy: {}%\tWeighted Value Accuracy: {}%'.format(value_acc*100, weighted_value_acc * 100))
    ################################### Plot ###################################################
    vis = visdom.Visdom(env=name + '[{}]'.format(phrase))

    value_pre_result, value_gt_result = [[] for _ in range(steps)], [[] for _ in range(steps)]
    weight_result = [[] for _ in range(steps)]
    for value_pre, value_gt, weight in zip(value_pres, value_gts, weights):
        if len(value_pre) < steps:
            continue

        step = len(value_pre) // steps
        for s in range(steps):
            value_pre_result[s].append(value_pre[s * step:(s + 1) * step])
            value_gt_result[s].append(value_gt[s * step:(s + 1) * step])
            weight_result[s].append(weight[s * step:(s + 1) * step])

    legend = ['Value', 'Weighted Value']
    X = np.repeat(np.arange(steps), len(legend), axis=0).reshape(steps, -1)
    Y = np.zeros((steps, len(legend)))
    for idx, (value_pres, value_gts, weights) in enumerate(
            zip(value_pre_result, value_gt_result, weight_result)):

        value_pres_np = np.hstack(value_pres)
        value_gts_np = np.hstack(value_gts)
        weights_np = np.hstack(weights)

        Y[idx, 0] = calc_value_acc(value_pres_np, value_gts_np)
        Y[idx, 1] = calc_weighted_value_acc(value_pres_np, value_gts_np, weights_np)

    vis.line(X=X, Y=Y,
             opts=dict(title='Acc[{}]'.format(title), legend=legend), win=title)

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='A2C : Starcraft II')
    parser.add_argument('--name', type=str, default='StarCraft II:TvT',
                        help='Experiment name. All outputs will be stored in checkpoints/[name]/')
    parser.add_argument('--phrase', type=str, default='test',
                        help='val|test (default: test)')
    args = parser.parse_args()

    test_path = os.path.join('checkpoints', args.name, args.phrase)
    test_results = sorted([int(os.path.basename(result).split('.')[0].split('_')[-1])
                        for result in os.listdir(test_path)])

    for idx, name_id in enumerate(test_results):
        result_path = os.path.join(test_path, 'model_iter_{}.pth'.format(name_id))
        print('Processing {}/{} ...'.format(idx+1, len(test_results)))
        print('\t'+result_path)

        with open(result_path, 'rb') as f:
            result = pickle.load(f)

        show_test_result(args.name, args.phrase, result, title=idx)

================================================
FILE: Baselines/GlobalStateEvaluation/train.py
================================================
from __future__ import print_function

import os
import json
import time
import pickle
import argparse

import visdom
import numpy as np

import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F

from torch.autograd import Variable

from Baselines.GlobalStateEvaluation.test import show_test_result

from data_loader.BatchEnv import BatchGlobalFeatureEnv

class StateEvaluationGRU(torch.nn.Module):
    def __init__(self, num_inputs):
        super(StateEvaluationGRU, self).__init__()
        self.linear1 = nn.Linear(num_inputs, 1024)
        self.linear2 = nn.Linear(1024, 2048)

        self.rnn1 = nn.GRUCell(input_size=2048, hidden_size=2048)
        self.rnn2 = nn.GRUCell(input_size=2048, hidden_size=512)

        self.critic_linear = nn.Linear(512, 1)

        self.h1, self.h2 = None, None

    def forward(self, states, require_init):
        batch = states.size(1)
        if self.h1 is None or self.h2 is None:
            self.h1 = Variable(states.data.new().resize_((batch, 2048)).zero_())
            self.h2 = Variable(states.data.new().resize_((batch, 512)).zero_())
        elif True in require_init:
            h1, h2 = self.h1.data, self.h2.data
            for idx, init in enumerate(require_init):
                if init:
                    h1[idx].zero_()
                    h2[idx].zero_()
            self.h1, self.h2 = Variable(h1), Variable(h2)
        else:
            pass

        values = []
        for idx, state in enumerate(states):
            x = F.relu(self.linear1(state))
            x = F.relu(self.linear2(x))
            self.h1 = self.rnn1(x, self.h1)
            self.h2 = self.rnn2(self.h1, self.h2)

            values.append(F.sigmoid(self.critic_linear(self.h2)))

        return values

    def detach(self):
        if self.h1 is not None:
            self.h1.detach_()
        if self.h2 is not None:
            self.h2.detach_()

def train(model, env, args):
    #################################### PLOT ###################################################
    STEPS = 10
    LAMBDA = 0.99
    vis = visdom.Visdom(env=args.name+'[{}]'.format(args.phrase))
    pre_per_replay = [[] for _ in range(args.n_replays)]
    gt_per_replay = [[] for _ in range(args.n_replays)]
    acc = None
    win = vis.line(X=np.zeros(1), Y=np.zeros(1))
    loss_win = vis.line(X=np.zeros(1), Y=np.zeros(1))

    #################################### TRAIN ######################################################
    torch.manual_seed(args.seed)
    torch.cuda.manual_seed(args.seed)

    gpu_id = args.gpu_id
    with torch.cuda.device(gpu_id):
        model = model.cuda() if gpu_id >= 0 else model
    model.train()
    optimizer = optim.Adam(model.parameters(), lr=args.lr)

    epoch = 0
    save = args.save_intervel
    env_return = env.step()
    if env_return is not None:
        (states, rewards), require_init = env_return
    with torch.cuda.device(gpu_id):
        states = torch.from_numpy(states).float()
        rewards = torch.from_numpy(rewards).float()
        if gpu_id >= 0:
            states = states.cuda()
            rewards = rewards.cuda()

    while True:
        values = model(Variable(states), require_init)

        value_loss = 0
        for value, reward in zip(values, rewards):
            value_loss = value_loss + F.binary_cross_entropy(value, Variable(reward))

        model.zero_grad()
        value_loss.backward()
        optimizer.step()
        model.detach()

        if env.epoch > epoch:
            epoch = env.epoch
            for p in optimizer.param_groups:
                p['lr'] *= 0.1

        ############################ PLOT ##########################################
        vis.updateTrace(X=np.asarray([env.step_count()]),
                        Y=np.asarray(value_loss.data.cpu().numpy()),
                        win=loss_win,
                        name='value')

        values_np = np.swapaxes(np.asarray([value.data.cpu().numpy() for value in values]), 0, 1)
        rewards_np = np.swapaxes(rewards.cpu().numpy(), 0, 1)

        for idx, (value, reward, init) in enumerate(zip(values_np, rewards_np, require_init)):
            if init and len(pre_per_replay[idx]) > 0:
                pre_per_replay[idx] = np.asarray(pre_per_replay[idx], dtype=np.uint8)
                gt_per_replay[idx] = np.asarray(gt_per_replay[idx], dtype=np.uint8)

                step = len(pre_per_replay[idx]) // STEPS
                if step > 0:
                    acc_tmp = []
                    for s in range(STEPS):
                        value_pre = pre_per_replay[idx][s*step:(s+1)*step]
                        value_gt = gt_per_replay[idx][s*step:(s+1)*step]
                        acc_tmp.append(np.mean(value_pre == value_gt))

                    acc_tmp = np.asarray(acc_tmp)
                    if acc is None:
                        acc = acc_tmp
                    else:
                        acc = LAMBDA * acc + (1-LAMBDA) * acc_tmp

                    if acc is None:
                        continue
                    for s in range(STEPS):
                        vis.updateTrace(X=np.asarray([env.step_count()]),
                                        Y=np.asarray([acc[s]]),
                                        win=win,
                                        name='{}[{}%~{}%]'.format('value', s*10, (s+1)*10))
                    vis.updateTrace(X=np.asarray([env.step_count()]),
                                    Y=np.asarray([np.mean(acc)]),
                                    win=win,
                                    name='value[TOTAL]')

                pre_per_replay[idx] = []
                gt_per_replay[idx] = []

            pre_per_replay[idx].append(int(value[-1] >= 0.5))
            gt_per_replay[idx].append(int(reward[-1]))

        ####################### NEXT BATCH ###################################
        env_return = env.step()
        if env_return is not None:
            (raw_states, raw_rewards), require_init = env_return
            states = states.copy_(torch.from_numpy(raw_states).float())
            rewards = rewards.copy_(torch.from_numpy(raw_rewards).float())

        if env.step_count() > save or env_return is None:
            save = env.step_count()+args.save_intervel
            torch.save(model.state_dict(),
                       os.path.join(args.model_path, 'model_iter_{}.pth'.format(env.step_count())))
            torch.save(model.state_dict(), os.path.join(args.model_path, 'model_latest.pth'))
        if env_return is None:
            env.close()
            break

def test(model, env, args):
    ######################### SAVE RESULT ############################
    value_pre_per_replay = [[]]
    value_gt_per_replay = [[]]

    ######################### TEST ###################################
    torch.manual_seed(args.seed)
    torch.cuda.manual_seed(args.seed)

    gpu_id = args.gpu_id
    with torch.cuda.device(gpu_id):
        model = model.cuda() if gpu_id >= 0 else model
    model.eval()

    env_return = env.step()
    if env_return is not  None:
        (states, rewards), require_init = env_return
    with torch.cuda.device(gpu_id):
        states = torch.from_numpy(states).float()
        rewards = torch.from_numpy(rewards).float()
        if gpu_id >= 0:
            states = states.cuda()
            rewards = rewards.cuda()

    while True:
        values = model(Variable(states), require_init)
        ############################ PLOT ##########################################
        values_np = np.squeeze(np.vstack([value.data.cpu().numpy() for value in values]))
        rewards_np = np.squeeze(rewards.cpu().numpy())

        if require_init[-1] and len(value_gt_per_replay[-1]) > 0:
            value_pre_per_replay[-1] = np.ravel(np.hstack(value_pre_per_replay[-1]))
            value_gt_per_replay[-1] = np.ravel(np.hstack(value_gt_per_replay[-1]))

            value_pre_per_replay.append([])
            value_gt_per_replay.append([])

        value_pre_per_replay[-1].append(values_np>=0.5)
        value_gt_per_replay[-1].append(rewards_np)

        ########################### NEXT BATCH #############################################
        env_return = env.step()
        if env_return is not None:
            (raw_states, raw_rewards), require_init = env_return
            states = states.copy_(torch.from_numpy(raw_states).float())
            rewards = rewards.copy_(torch.from_numpy(raw_rewards).float())
        else:
            value_pre_per_replay[-1] = np.ravel(np.hstack(value_pre_per_replay[-1]))
            value_gt_per_replay[-1] = np.ravel(np.hstack(value_gt_per_replay[-1]))

            env.close()
            break

    return value_pre_per_replay, value_gt_per_replay

def next_path(model_folder, paths):
    models = {int(os.path.basename(model).split('.')[0].split('_')[-1])
                for model in os.listdir(model_folder) if 'latest' not in model}
    models_not_process = models - paths
    if len(models_not_process) == 0:
        return None
    models_not_process = sorted(models_not_process)
    paths.add(models_not_process[0])

    return os.path.join(model_folder, 'model_iter_{}.pth'.format(models_not_process[0]))

def main():
    # Training settings
    parser = argparse.ArgumentParser(description='Global State Evaluation : StarCraft II')
    parser.add_argument('--name', type=str, default='StarCraft II:TvT',
                        help='Experiment name. All outputs will be stored in checkpoints/[name]/')
    parser.add_argument('--replays_path', default='train_val_test/Terran_vs_Terran',
                        help='Path for training, validation and test set (default: train_val_test/Terran_vs_Terran)')
    parser.add_argument('--race', default='Terran', help='Which race? (default: Terran)')
    parser.add_argument('--enemy_race', default='Terran', help='Which the enemy race? (default: Terran)')
    parser.add_argument('--phrase', type=str, default='train',
                        help='train|val|test (default: train)')
    parser.add_argument('--gpu_id', default=0, type=int, help='Which GPU to use [-1 indicate CPU] (default: 0)')

    parser.add_argument('--lr', type=float, default=0.001, help='Learning rate (default: 0.001)')
    parser.add_argument('--seed', type=int, default=1, help='Random seed (default: 1)')

    parser.add_argument('--n_steps', type=int, default=20, help='# of forward steps (default: 20)')
    parser.add_argument('--n_replays', type=int, default=256, help='# of replays (default: 256)')
    parser.add_argument('--n_epoch', type=int, default=10, help='# of epoches (default: 10)')

    parser.add_argument('--save_intervel', type=int, default=1000000,
                        help='Frequency of model saving (default: 1000000)')
    args = parser.parse_args()

    args.save_path = os.path.join('checkpoints', args.name)
    args.model_path = os.path.join(args.save_path, 'snapshots')

    print('------------ Options -------------')
    for k, v in sorted(vars(args).items()):
        print('{}: {}'.format(k, v))
    print('-------------- End ----------------')

    if args.phrase == 'train':
        if not os.path.isdir(args.save_path):
            os.makedirs(args.save_path)
        if not os.path.isdir(args.model_path):
            os.makedirs(args.model_path)
        with open(os.path.join(args.save_path, 'config'), 'w') as f:
            f.write(json.dumps(vars(args)))

        env = BatchGlobalFeatureEnv()
        env.init(os.path.join(args.replays_path, '{}.json'.format(args.phrase)),
                    './', args.race, args.enemy_race, n_steps=args.n_steps, seed=args.seed,
                        n_replays=args.n_replays, epochs=args.n_epoch)
        model = StateEvaluationGRU(env.n_features)
        train(model, env, args)
    elif 'val' in args.phrase or 'test' in args.phrase:
        test_result_path = os.path.join(args.save_path, args.phrase)
        if not os.path.isdir(test_result_path):
            os.makedirs(test_result_path)

        dataset_path = 'test.json' if 'test' in args.phrase else 'val.json'
        paths = set()
        while True:
            path = next_path(args.model_path, paths)
            if path is not None:
                print('[{}]Testing {} ...'.format(len(paths), path))

                env = BatchGlobalFeatureEnv()
                env.init(os.path.join(args.replays_path, dataset_path),
                            './', args.race, args.enemy_race, n_steps=args.n_steps,
                                            seed=args.seed, n_replays=1, epochs=1)
                model = StateEvaluationGRU(env.n_features)
                model.load_state_dict(torch.load(path))
                result = test(model, env, args)
                with open(os.path.join(test_result_path, os.path.basename(path)), 'wb') as f:
                    pickle.dump(result, f)
                show_test_result(args.name, args.phrase, result, title=len(paths)-1)
            else:
                time.sleep(60)

if __name__ == '__main__':
    main()

================================================
FILE: Baselines/GlobalStateEvaluation/train_spatial.py
================================================
from __future__ import print_function

import os
import json
import time
import pickle
import argparse

import visdom
import numpy as np

import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F

from torch.autograd import Variable

from Baselines.GlobalStateEvaluation.test import show_test_result

from data_loader.BatchEnv import BatchSpatialEnv

class StateEvaluationGRU(torch.nn.Module):
    def __init__(self, n_channels, n_features):
        super(StateEvaluationGRU, self).__init__()

        self.conv1 = nn.Conv2d(n_channels, 16, 8, stride=4)
        self.conv2 = nn.Conv2d(16, 32, 4, stride=2)

        self.linear_g = nn.Linear(n_features, 128)

        self.linear = nn.Linear(1280, 512)
        self.rnn = nn.GRUCell(input_size=512, hidden_size=128)

        self.critic_linear = nn.Linear(128, 1)

        self.h = None

    def forward(self, states_S, states_G, require_init):
        batch = states_S.size(1)
        if self.h is None:
            self.h = Variable(states_S.data.new().resize_((batch, 128)).zero_())
        elif True in require_init:
            h= self.h.data
            for idx, init in enumerate(require_init):
                if init:
                    h[idx].zero_()
            self.h = Variable(h)
        else:
            pass

        values = []
        for idx, (state_S, state_G) in enumerate(zip(states_S, states_G)):
            x_s = F.relu(self.conv1(state_S))
            x_s = F.relu(self.conv2(x_s))
            x_s = x_s.view(-1, 1152)

            x_g = F.relu(self.linear_g(state_G))

            x = torch.cat((x_s, x_g), 1)
            x = F.relu(self.linear(x))

            self.h = self.rnn(x, self.h)

            values.append(F.sigmoid(self.critic_linear(self.h)))

        return values

    def detach(self):
        if self.h is not None:
            self.h.detach_()

def train(model, env, args):
    #################################### PLOT ###################################################
    STEPS = 10
    LAMBDA = 0.99
    vis = visdom.Visdom(env=args.name+'[{}]'.format(args.phrase))
    pre_per_replay = [[] for _ in range(args.n_replays)]
    gt_per_replay = [[] for _ in range(args.n_replays)]
    acc = None
    win = vis.line(X=np.zeros(1), Y=np.zeros(1))
    loss_win = vis.line(X=np.zeros(1), Y=np.zeros(1))

    #################################### TRAIN ######################################################
    torch.manual_seed(args.seed)
    torch.cuda.manual_seed(args.seed)

    gpu_id = args.gpu_id
    with torch.cuda.device(gpu_id):
        model = model.cuda() if gpu_id >= 0 else model
    model.train()
    optimizer = optim.Adam(model.parameters(), lr=args.lr)

    epoch = 0
    save = args.save_intervel
    env_return = env.step()
    if env_return is not None:
        (states_S, states_G, rewards), require_init = env_return
    with torch.cuda.device(gpu_id):
        states_S = torch.from_numpy(states_S).float()
        states_G = torch.from_numpy(states_G).float()
        rewards = torch.from_numpy(rewards).float()
        if gpu_id >= 0:
            states_S = states_S.cuda()
            states_G = states_G.cuda()
            rewards = rewards.cuda()

    while True:
        values = model(Variable(states_S), Variable(states_G), require_init)

        value_loss = 0
        for value, reward in zip(values, rewards):
            value_loss = value_loss + F.binary_cross_entropy(value, Variable(reward))

        model.zero_grad()
        value_loss.backward()
        optimizer.step()
        model.detach()

        if env.epoch > epoch:
            epoch = env.epoch
            for p in optimizer.param_groups:
                p['lr'] *= 0.5

        ############################ PLOT ##########################################
        vis.updateTrace(X=np.asarray([env.step_count()]),
                        Y=np.asarray(value_loss.data.cpu().numpy()),
                        win=loss_win,
                        name='value')

        values_np = np.swapaxes(np.asarray([value.data.cpu().numpy() for value in values]), 0, 1)
        rewards_np = np.swapaxes(rewards.cpu().numpy(), 0, 1)

        for idx, (value, reward, init) in enumerate(zip(values_np, rewards_np, require_init)):
            if init and len(pre_per_replay[idx]) > 0:
                pre_per_replay[idx] = np.asarray(pre_per_replay[idx], dtype=np.uint8)
                gt_per_replay[idx] = np.asarray(gt_per_replay[idx], dtype=np.uint8)

                step = len(pre_per_replay[idx]) // STEPS
                if step > 0:
                    acc_tmp = []
                    for s in range(STEPS):
                        value_pre = pre_per_replay[idx][s*step:(s+1)*step]
                        value_gt = gt_per_replay[idx][s*step:(s+1)*step]
                        acc_tmp.append(np.mean(value_pre == value_gt))

                    acc_tmp = np.asarray(acc_tmp)
                    if acc is None:
                        acc = acc_tmp
                    else:
                        acc = LAMBDA * acc + (1-LAMBDA) * acc_tmp

                    if acc is None:
                        continue
                    for s in range(STEPS):
                        vis.updateTrace(X=np.asarray([env.step_count()]),
                                        Y=np.asarray([acc[s]]),
                                        win=win,
                                        name='{}[{}%~{}%]'.format('value', s*10, (s+1)*10))
                    vis.updateTrace(X=np.asarray([env.step_count()]),
                                    Y=np.asarray([np.mean(acc)]),
                                    win=win,
                                    name='value[TOTAL]')

                pre_per_replay[idx] = []
                gt_per_replay[idx] = []

            pre_per_replay[idx].append(int(value[-1] >= 0.5))
            gt_per_replay[idx].append(int(reward[-1]))

        ####################### NEXT BATCH ###################################
        env_return = env.step()
        if env_return is not None:
            (raw_states_S, raw_states_G, raw_rewards), require_init = env_return
            states_S = states_S.copy_(torch.from_numpy(raw_states_S).float())
            states_G = states_G.copy_(torch.from_numpy(raw_states_G).float())
            rewards = rewards.copy_(torch.from_numpy(raw_rewards).float())

        if env.step_count() > save or env_return is None:
            save = env.step_count()+args.save_intervel
            torch.save(model.state_dict(),
                       os.path.join(args.model_path, 'model_iter_{}.pth'.format(env.step_count())))
            torch.save(model.state_dict(), os.path.join(args.model_path, 'model_latest.pth'))
        if env_return is None:
            env.close()
            break

def test(model, env, args):
    ######################### SAVE RESULT ############################
    value_pre_per_replay = [[]]
    value_gt_per_replay = [[]]

    ######################### TEST ###################################
    torch.manual_seed(args.seed)
    torch.cuda.manual_seed(args.seed)

    gpu_id = args.gpu_id
    with torch.cuda.device(gpu_id):
        model = model.cuda() if gpu_id >= 0 else model
    model.eval()

    env_return = env.step()
    if env_return is not  None:
        (states_S, states_G, rewards), require_init = env_return
    with torch.cuda.device(gpu_id):
        states_S = torch.from_numpy(states_S).float()
        states_G = torch.from_numpy(states_G).float()
        rewards = torch.from_numpy(rewards).float()
        if gpu_id >= 0:
            states_S = states_S.cuda()
            states_G = states_G.cuda()
            rewards = rewards.cuda()

    while True:
        values = model(Variable(states_S), Variable(states_G), require_init)
        ############################ PLOT ##########################################
        values_np = np.squeeze(np.vstack([value.data.cpu().numpy() for value in values]))
        rewards_np = np.squeeze(rewards.cpu().numpy())

        if require_init[-1] and len(value_gt_per_replay[-1]) > 0:
            value_pre_per_replay[-1] = np.ravel(np.hstack(value_pre_per_replay[-1]))
            value_gt_per_replay[-1] = np.ravel(np.hstack(value_gt_per_replay[-1]))

            value_pre_per_replay.append([])
            value_gt_per_replay.append([])

        value_pre_per_replay[-1].append(values_np>=0.5)
        value_gt_per_replay[-1].append(rewards_np)

        ########################### NEXT BATCH #############################################
        env_return = env.step()
        if env_return is not None:
            (raw_states_S, raw_states_G, raw_rewards), require_init = env_return
            states_S = states_S.copy_(torch.from_numpy(raw_states_S).float())
            states_G = states_G.copy_(torch.from_numpy(raw_states_G).float())
            rewards = rewards.copy_(torch.from_numpy(raw_rewards).float())
        else:
            value_pre_per_replay[-1] = np.ravel(np.hstack(value_pre_per_replay[-1]))
            value_gt_per_replay[-1] = np.ravel(np.hstack(value_gt_per_replay[-1]))

            env.close()
            break

    return value_pre_per_replay, value_gt_per_replay

def next_path(model_folder, paths):
    models = {int(os.path.basename(model).split('.')[0].split('_')[-1])
                for model in os.listdir(model_folder) if 'latest' not in model}
    models_not_process = models - paths
    if len(models_not_process) == 0:
        return None
    models_not_process = sorted(models_not_process)
    paths.add(models_not_process[0])

    return os.path.join(model_folder, 'model_iter_{}.pth'.format(models_not_process[0]))

def main():
    # Training settings
    parser = argparse.ArgumentParser(description='Global State Evaluation : StarCraft II')
    parser.add_argument('--name', type=str, default='StarCraft II:TvT',
                        help='Experiment name. All outputs will be stored in checkpoints/[name]/')
    parser.add_argument('--replays_path', default='train_val_test/Terran_vs_Terran',
                        help='Path for training, validation and test set (default: train_val_test/Terran_vs_Terran)')
    parser.add_argument('--race', default='Terran', help='Which race? (default: Terran)')
    parser.add_argument('--enemy_race', default='Terran', help='Which the enemy race? (default: Terran)')
    parser.add_argument('--phrase', type=str, default='train',
                        help='train|val|test (default: train)')
    parser.add_argument('--gpu_id', default=0, type=int, help='Which GPU to use [-1 indicate CPU] (default: 0)')

    parser.add_argument('--lr', type=float, default=0.001, help='Learning rate (default: 0.001)')
    parser.add_argument('--seed', type=int, default=1, help='Random seed (default: 1)')

    parser.add_argument('--n_steps', type=int, default=20, help='# of forward steps (default: 20)')
    parser.add_argument('--n_replays', type=int, default=32, help='# of replays (default: 32)')
    parser.add_argument('--n_epoch', type=int, default=10, help='# of epoches (default: 10)')

    parser.add_argument('--save_intervel', type=int, default=1000000,
                        help='Frequency of model saving (default: 1000000)')
    args = parser.parse_args()

    args.save_path = os.path.join('checkpoints', args.name)
    args.model_path = os.path.join(args.save_path, 'snapshots')

    print('------------ Options -------------')
    for k, v in sorted(vars(args).items()):
        print('{}: {}'.format(k, v))
    print('-------------- End ----------------')

    if args.phrase == 'train':
        if not os.path.isdir(args.save_path):
            os.makedirs(args.save_path)
        if not os.path.isdir(args.model_path):
            os.makedirs(args.model_path)
        with open(os.path.join(args.save_path, 'config'), 'w') as f:
            f.write(json.dumps(vars(args)))

        env = BatchSpatialEnv()
        env.init(os.path.join(args.replays_path, '{}.json'.format(args.phrase)),
                    './', args.race, args.enemy_race, n_steps=args.n_steps, seed=args.seed,
                        n_replays=args.n_replays, epochs=args.n_epoch)
        model = StateEvaluationGRU(env.n_channels, env.n_features)
        train(model, env, args)
    elif 'val' in args.phrase or 'test' in args.phrase:
        test_result_path = os.path.join(args.save_path, args.phrase)
        if not os.path.isdir(test_result_path):
            os.makedirs(test_result_path)

        dataset_path = 'test.json' if 'test' in args.phrase else 'val.json'
        paths = set()
        while True:
            path = next_path(args.model_path, paths)
            if path is not None:
                print('[{}]Testing {} ...'.format(len(paths), path))

                env = BatchSpatialEnv()
                env.init(os.path.join(args.replays_path, dataset_path),
                            './', args.race, args.enemy_race, n_steps=args.n_steps,
                                            seed=args.seed, n_replays=1, epochs=1)
                model = StateEvaluationGRU(env.n_channels, env.n_features)
                model.load_state_dict(torch.load(path))
                result = test(model, env, args)
                with open(os.path.join(test_result_path, os.path.basename(path)), 'wb') as f:
                    pickle.dump(result, f)
                show_test_result(args.name, args.phrase, result, title=len(paths)-1)
            else:
                time.sleep(60)

if __name__ == '__main__':
    main()


================================================
FILE: README.md
================================================
# MSC
[MSC: A Dataset for Macro-Management in StarCraft II.](https://arxiv.org/pdf/1710.03131.pdf)
```
@article{wu2017msc,
  title={MSC: A Dataset for Macro-Management in StarCraft II},
  author={Wu, Huikai and Zhang, Junge and Huang, Kaiqi},
  journal={arXiv preprint arXiv:1710.03131},
  year={2017}
}
```
## Download
- **Global** features are available [HERE](https://drive.google.com/file/d/0Bybnpq8dvwudNUVOX1FCWnZoSGM/view?usp=sharing&resourcekey=0-GJ3Loydd4k2QaONnVd3B5Q).
- **[TRAIN|VAL|TEST]** split is available [HERE](train_val_test).
- **[Stat]** is available [HERE](parsed_replays/Stat). The stat files with postfix **_human.json** are human-readable.
- **Spatial** features are **NOT** avaiable since I do not have any download server. Please follow the [instructions](#step-by-step-instructions) to generate the spatial features by yourself.

## Baselines
### Global State Evaluation
| Method | TvT:T | TvZ:T | TvZ:Z | TvP:T | TvP:P | ZvZ:Z | ZvP:Z | ZvP:P | PvP:P |
| - | - | - | - | - | - | - | - | - | - |
| Baseline[Global] | 61.09 | 58.89 | 60.61 | 57.21 | 60.95 | 59.91 | 59.95 | 59.35 | 51.36 |
| Baseline[Spatial] | 50.85 | 52.35 | 59.82 | 54.90 | 59.15 | 54.65 | 55.02 | 58.78 | 57.76 |
### Build Order Prediction
| Method | TvT:T | TvZ:T | TvZ:Z | TvP:T | TvP:P | ZvZ:Z | ZvP:Z | ZvP:P | PvP:P |
| - | - | - | - | - | - | - | - | - | - |
| Baseline[Global] | 74.12 | 73.01 | 73.89 | 70.29 | 79.28 | 76.07 | 72.02 | 78.08 | 76.28 |
| Baseline[Spatial] | 73.07 | 73.71 | 75.92 | 64.15 | 75.09 | 74.88 | 72.32 | 76.12 | 74.22 |
## Dataset: Global Feature Vector
Each replay is a **(T, M)** matrix **F**, where **F[t, :]** is the feature vector for time step **t**.

Each **row** of **F** is a **M**-dimensional vector, with **M** varying as **[RACE] v.s. [RACE]**.

The **M**-dimensional vector is orgnized as follows:
1. **[0]:** reward, i.e. final result of the game. **0**: DEFEAT, **1:** WIN.
2. **[1]:** ground truth action, ranging from **[0, #ACTION]**.
3. **[2-15):** cumulative score **[NOT NORMALIZED]**, which is defined in [Here](https://github.com/wuhuikai/MSC/blob/ebb1a722206e594e1c3a1da7cf21df8c514e5040/parse_replay/replay2global_features.py#L52).
4. **[15-M):** observation feature vector, which is normalized into **[0, 1]**.
    1. **[15]:** frame id.
    2. **[16-27):** player info, including various [resources](https://github.com/wuhuikai/MSC/blob/ebb1a722206e594e1c3a1da7cf21df8c514e5040/parse_replay/replay2global_features.py#L68) and **n_power_source**.
    3. **[27-#1):** alerts, **boolean**.
    4. **[#1-#2):** upgrades, **boolean**.
    5. **[#2-#3):** research count.
    6. **[#3-#4):** friendly units info, which is defined in [Here](https://github.com/wuhuikai/MSC/blob/ebb1a722206e594e1c3a1da7cf21df8c514e5040/extract_features/game_state.py#L110).
    7. **[#4-M):** enemy units info, where **M = #4 + #[ENEMY RACE]**.
         
        | V.S. | TvT:T | TvZ:T | TvZ:Z | TvP:T | TvP:P | ZvZ:Z | ZvP:Z | ZvP:P | PvP:P |
        | - | - | - | - | - | - | - | - | - | - |
        | M | 753 | 1131 | 1121 | 663 | 653 | 1499 | 1031 | 1031 | 563 |

        | RACE | #1 | #2 | #3 | #4 | #ACTION | #RACE |
        | - | - | - | - | - | - | - |
        | Terran | 29 | 60 | 81 | 417 | 75 | 336|
        | Protoss | 29 | 55 | 71 | 317 | 61 | 246 |
        | Zerg | 29 | 55 | 71 | 785 | 74 | 714 |
Code for loading **F**:
```python
import numpy as np
from scipy import sparse
F = np.asarray(sparse.load_npz(PATH).todense())
```
## Dataset: Spatial Feature Tensor
Each replay contains a **(T, 13, 64, 64)** tensor **S** and a **(T, 26)** matrix **G**.

The specifics for **S[t, :, :, :]** is as follows:
1. **S[t, 0:8, :, :]:** screen features, roughly normalized into **[0-1]**, which is defined in [Here](https://github.com/wuhuikai/MSC/blob/ebb1a722206e594e1c3a1da7cf21df8c514e5040/extract_features/SpatialFeatures.py#L45).
2. **S[t, 8:13, :, :]:** minimap features, roughly normalized into **[0-1]**, which is defined in [Here](https://github.com/wuhuikai/MSC/blob/ebb1a722206e594e1c3a1da7cf21df8c514e5040/extract_features/SpatialFeatures.py#L58).

**WARNING**[Cheat Layer]: The last layer **S[t, 12, :, :]** refers to **unit_type**, which could only be obtained in replays.

Code for loading **S**:
```python
import numpy as np
from scipy import sparse
S = np.asarray(sparse.load_npz(PATH).todense()).reshape([-1, 13, 64, 64])
```
The specifics for **G[t, :]** is as follows:
1. **[0-11):** frame id + player info, normalized into **[0, 1]**, which is defined [Here](https://github.com/wuhuikai/MSC/blob/ebb1a722206e594e1c3a1da7cf21df8c514e5040/extract_features/SpatialFeatures.py#L97).
2. **[11-24):** cumulative score **[NOT NORMALIZED]**, which is defined in [Here](https://github.com/wuhuikai/MSC/blob/ebb1a722206e594e1c3a1da7cf21df8c514e5040/extract_features/SpatialFeatures.py#L111).
3. **[24]:** reward, i.e. final result of the game. **0:** DEFEAT, **1**: WIN
4. **[25]:** ground truth action, ranging from **[0, #ACTION]**.

Code for loading **G**:
```python
import numpy as np
from scipy import sparse
G = np.asarray(sparse.load_npz(PATH).todense())
```
## Build the Dataset Yourself Step by Step
### Install [SC2LE](https://github.com/Blizzard/s2client-proto)
1. Download and **unzip** (Password: iagreetotheeula) StarCraft II Linux Packages [3.16.1](http://blzdistsc2-a.akamaihd.net/Linux/SC2.3.16.1.zip) into **$STAR_CRAFT$**.
2. Download and **unzip** (Password: iagreetotheeula) Replay Packs ([3.16.1 - Pack 1](http://blzdistsc2-a.akamaihd.net/ReplayPacks/3.16.1-Pack_1-fix.zip), [3.16.1 - Pack 2](http://blzdistsc2-a.akamaihd.net/ReplayPacks/3.16.1-Pack_2.zip) [Currently not used]) into **$STAR_CRAFT$**.

After step 1 and step 2, the folder structure is as follows:
```
$STAR_CRAFT$
    ├── Battle.net
    ├── Libs
    ├── Maps
    ├── Replays
    ├── SC2Data
    └── Versions
```
- **NOTE:**
    1. **$STAR_CRAFT$/Replays** contains all ***.SC2Replay** files from **3.16.1 - Pack 1** and **3.16.1 - Pack 2** [Currently not used]
    2. **$STAR_CRAFT$/Battle.net** contains all contents from the folder **Battle.net** in **3.16.1 - Pack 1** and **3.16.1 - Pack 2** [Currently not used]
### Step-by-Step Instructions
#### [The Easy Way](instructions/EasyWay.md)
#### [The Hard Way [Step-by-Step in Details]](instructions/HardWay.md)
### Requirements
```
future == 0.16.0

numpy == 1.13.0
scipy == 0.19.0

python_gflags == 3.1.1

tqdm == 4.14.0

protobuf == 3.4.0
pystream_protobuf == 1.4.4

PySC2 == 1.0
s2clientprotocol == 1.1
```


================================================
FILE: _config.yml
================================================
theme: jekyll-theme-hacker

================================================
FILE: data_loader/BatchEnv.py
================================================
import os
import json
from collections import namedtuple

import numpy as np
from scipy import sparse

from tqdm import tqdm

class BatchEnv(object):
    def __init__(self):
        pass

    def init(self, path, root, race, enemy_race, step_mul=8, n_replays=4, n_steps=5, epochs=10, seed=None):
        np.random.seed(seed)

        with open(path) as f:
            replays = json.load(f)

        self.replays = self.__generate_replay_list__(replays, root, race)

        self.race = race
        self.enemy_race = enemy_race

        self.step_mul = step_mul
        self.n_replays = n_replays
        self.n_steps = n_steps

        self.epochs = epochs
        self.epoch = -1
        self.steps = 0

        self.replay_idx = -1
        self.replay_list = [None for _ in range(self.n_replays)]

        ## Display Progress Bar
        self.epoch_pbar = tqdm(total=self.epochs, desc='Epoch')
        self.replay_pbar = None

        self.__post_init__()

    def __generate_replay_list__(self, replays, race):
        raise NotImplementedError

    def __init_epoch__(self):
        self.epoch += 1
        if self.epoch > 0:
            self.epoch_pbar.update(1)
        if self.epoch == self.epochs:
            return False

        np.random.shuffle(self.replays)
        ## Display Progress Bar
        if self.replay_pbar is not None:
            self.replay_pbar.close()
        self.replay_pbar = tqdm(total=len(self.replays), desc='  Replays')
        return True

    def __reset__(self):
        self.replay_idx += 1
        if self.replay_idx % len(self.replays) == 0:
            has_more = self.__init_epoch__()
            if not has_more:
                return None

        path = self.replays[self.replay_idx%len(self.replays)]

        return self.__load_replay__(path)

    def __load_replay__(self, path):
        raise NotImplementedError

    def step(self, **kwargs):
        require_init = [False for _ in range(self.n_replays)]
        for i in range(self.n_replays):
            if self.replay_list[i] is None or self.replay_list[i]['done']:
                if self.replay_list[i] is not None:
                    keys = set(self.replay_list[i].keys())
                    for k in keys:
                        del self.replay_list[i][k]
                self.replay_list[i] = self.__reset__()
                require_init[i] = True
            if self.replay_list[i] is None:
                return None

        result = []
        for step in range(self.n_steps):
            result_per_step = []
            for i in range(self.n_replays):
                replay_dict = self.replay_list[i]

                features = self.__one_step__(replay_dict, replay_dict['done'])

                result_per_step.append(features)

            result.append(result_per_step)

        return self.__post_process__(result, **kwargs), require_init

    def __one_step__(self, replay_dict, done):
        raise NotImplementedError

    def __post_process__(self, result, **kwargs):
        raise NotImplementedError

    def step_count(self):
        return self.steps

    def close(self):
        if self.epoch_pbar is not None:
            self.epoch_pbar.close()
        if self.replay_pbar is not None:
            self.replay_pbar.close()

class BatchGlobalFeatureEnv(BatchEnv):
    n_features_dic = {'Terran':  {'Terran': 738,  'Protoss': 648,  'Zerg': 1116},
                      'Protoss': {'Terran': 638,  'Protoss': 548,  'Zerg': 1016},
                      'Zerg':    {'Terran': 1106, 'Protoss': 1016, 'Zerg': 1484}}
    n_actions_dic = {'Terran': 75, 'Protoss': 61, 'Zerg': 74}

    def __post_init__(self):
        self.n_features = self.n_features_dic[self.race][self.enemy_race]
        self.n_actions = self.n_actions_dic[self.race]

    def __generate_replay_list__(self, replays, root, race):
        result = []
        for path_dict in replays:
            for player_path in path_dict[race]:
                result.append(os.path.join(root, player_path['global_path']))

        return result

    def __load_replay__(self, path):
        replay_dict = {}
        replay_dict['ptr'] = 0
        replay_dict['done'] = False
        replay_dict['states'] = np.asarray(sparse.load_npz(path).todense())

        return replay_dict

    def __one_step__(self, replay_dict, done):
        states = replay_dict['states']
        feature_shape = states.shape[1:]
        if done:
            return np.zeros(feature_shape)

        self.steps += 1
        state = states[replay_dict['ptr']]
        replay_dict['ptr'] += 1
        if replay_dict['ptr'] == states.shape[0]:
            self.replay_pbar.update(1)
            replay_dict['done'] = True

        return state

    def __post_process__(self, result, reward=True, action=False, score=False):
        result = np.asarray(result)

        result_return = [result[:, :, 15:]]
        if reward:
            result_return.append(result[:, :, 0:1])
        if action:
            result_return.append(result[:, :, 1:2])
        if score:
            result_return.append(result[:, :, 2:15])

        return result_return

class BatchSpatialEnv(BatchEnv):
    n_channels = 5
    n_features = 11
    n_actions_dic = {'Terran': 75, 'Protoss': 61, 'Zerg': 74}
    Feature = namedtuple('Feature', ['S', 'G'])

    def __post_init__(self):
        self.n_actions = self.n_actions_dic[self.race]

    def __generate_replay_list__(self, replays, root, race):
        result = []
        for path_dict in replays:
            for player_path in path_dict[race]:
                result.append([os.path.join(root, player_path['spatial_path_S']),
                               os.path.join(root, player_path['spatial_path_G'])])
        return result

    def __load_replay__(self, path):
        replay_dict = {}
        replay_dict['ptr'] = 0
        replay_dict['done'] = False
        replay_dict['states_S'] = np.asarray(sparse.load_npz(path[0]).todense()).reshape([-1, 13, 64, 64])
        replay_dict['states_G'] = np.asarray(sparse.load_npz(path[1]).todense())

        return replay_dict

    def __one_step__(self, replay_dict, done):
        states_S = replay_dict['states_S']
        states_G = replay_dict['states_G']
        feature_shape_S = states_S.shape[1:]
        feature_shape_G = states_G.shape[1:]
        if done:
            return self.Feature(np.zeros(feature_shape_S), np.zeros(feature_shape_G))

        self.steps += 1
        state_S = states_S[replay_dict['ptr']]
        state_G = states_G[replay_dict['ptr']]
        replay_dict['ptr'] += 1
        if replay_dict['ptr'] == states_S.shape[0]:
            self.replay_pbar.update(1)
            replay_dict['done'] = True

        return self.Feature(state_S, state_G)

    def __post_process__(self, result, reward=True, action=False, score=False):
        result = self.Feature(*zip(*[self.Feature(*zip(*result_per_step)) for result_per_step in result]))

        S = np.asarray(result.S)
        G = np.asarray(result.G)

        result_return = [S[:, :, 8:13, :, :], G[:,:, :11]]
        if reward:
            result_return.append(G[:, :, 24:25])
        if action:
            result_return.append(G[:, :, 25:26])
        if score:
            result_return.append(G[:, :, 11:24])

        return result_return

if __name__ == '__main__':
    env = BatchSpatialEnv()
    env.init('../train_val_test/Terran_vs_Terran/train.json', '../', 'Terran', 'Terran')
    while True:
        r = env.step()
        if r is None:
            break


================================================
FILE: extract_features/SpatialFeatures.py
================================================
from pysc2.lib.features import *
from pysc2.lib import stopwatch

sw = stopwatch.sw

class ScreenFeatures(collections.namedtuple("ScreenFeatures", ["height_map", "visibility_map",
                    "creep", "power", "player_relative", "unit_type", "unit_density", "unit_density_aa"])):
  """The set of screen feature layers."""
  __slots__ = ()

  def __new__(cls, **kwargs):
    feats = {}
    for name, (scale, type_, palette, clip) in six.iteritems(kwargs):
      feats[name] = Feature(
          index=ScreenFeatures._fields.index(name),
          name=name,
          layer_set="renders",
          full_name="screen " + name,
          scale=scale,
          type=type_,
          palette=palette(scale) if callable(palette) else palette,
          clip=clip)
    return super(ScreenFeatures, cls).__new__(cls, **feats)


class MinimapFeatures(collections.namedtuple("MinimapFeatures", [
    "height_map", "visibility_map", "creep", "player_relative", "unit_type"])):
  """The set of minimap feature layers."""
  __slots__ = ()

  def __new__(cls, **kwargs):
    feats = {}
    for name, (scale, type_, palette) in six.iteritems(kwargs):
      feats[name] = Feature(
          index=MinimapFeatures._fields.index(name),
          name=name,
          layer_set="minimap_renders",
          full_name="minimap " + name,
          scale=scale,
          type=type_,
          palette=palette(scale) if callable(palette) else palette,
          clip=False)
    return super(MinimapFeatures, cls).__new__(cls, **feats)

SCREEN_FEATURES = ScreenFeatures(
    height_map=(256, FeatureType.SCALAR, colors.winter, False),
    visibility_map=(4, FeatureType.CATEGORICAL,
                    colors.VISIBILITY_PALETTE, False),
    creep=(2, FeatureType.CATEGORICAL, colors.CREEP_PALETTE, False),
    power=(2, FeatureType.CATEGORICAL, colors.POWER_PALETTE, False),
    player_relative=(5, FeatureType.CATEGORICAL,
                     colors.PLAYER_RELATIVE_PALETTE, False),
    unit_type=(1850, FeatureType.CATEGORICAL, colors.unit_type, False),
    unit_density=(16, FeatureType.SCALAR, colors.hot, False),
    unit_density_aa=(256, FeatureType.SCALAR, colors.hot, False),
)

MINIMAP_FEATURES = MinimapFeatures(
    height_map=(256, FeatureType.SCALAR, colors.winter),
    visibility_map=(4, FeatureType.CATEGORICAL, colors.VISIBILITY_PALETTE),
    creep=(2, FeatureType.CATEGORICAL, colors.CREEP_PALETTE),
    player_relative=(5, FeatureType.CATEGORICAL,
                     colors.PLAYER_RELATIVE_PALETTE),
    unit_type=(1850, FeatureType.CATEGORICAL, colors.unit_type)
)

class SpatialFeatures(Features):
    def observation_spec(self):
        """The observation spec for the SC2 environment.
        Returns:
          The dict of observation names to their tensor shapes. Shapes with a 0 can
          vary in length, for example the number of valid actions depends on which
          units you have selected.
        """
        return {
            "screen": (len(SCREEN_FEATURES),
                       self._screen_size_px.y,
                       self._screen_size_px.x),
            "minimap": (len(MINIMAP_FEATURES),
                        self._minimap_size_px.y,
                        self._minimap_size_px.x),
            "player": (11,),
            "score": (13,)
        }

    @sw.decorate
    def transform_obs(self, obs):
        """Render some SC2 observations into something an agent can handle."""
        out = {}

        with sw("feature_layers"):
            out["screen"] = np.stack(
                f.unpack(obs)/f.scale for f in SCREEN_FEATURES).astype(np.float32, copy=False)
            out["minimap"] = np.stack(
                f.unpack(obs)/f.scale for f in MINIMAP_FEATURES).astype(np.float32, copy=False)

        out["player"] = np.array([
            obs.game_loop - 1,
            obs.player_common.minerals,
            obs.player_common.vespene,
            obs.player_common.food_used,
            obs.player_common.food_cap,
            obs.player_common.food_army,
            obs.player_common.food_workers,
            obs.player_common.idle_worker_count,
            obs.player_common.army_count,
            obs.player_common.warp_gate_count,
            obs.player_common.larva_count,
        ], dtype=np.int32)

        out["score"] = np.array([
            obs.score.score,
            obs.score.score_details.idle_production_time,
            obs.score.score_details.idle_worker_time,
            obs.score.score_details.total_value_units,
            obs.score.score_details.total_value_structures,
            obs.score.score_details.killed_value_units,
            obs.score.score_details.killed_value_structures,
            obs.score.score_details.collected_minerals,
            obs.score.score_details.collected_vespene,
            obs.score.score_details.collection_rate_minerals,
            obs.score.score_details.collection_rate_vespene,
            obs.score.score_details.spent_minerals,
            obs.score.score_details.spent_vespene,
        ], dtype=np.int32)

        return out

================================================
FILE: extract_features/compute_stat.sh
================================================
python replay_stat.py --race $1

================================================
FILE: extract_features/extract_features.sh
================================================
python global_feature_vector.py --hq_replay_set $1 &&
python spatial_feature_tensor.py --hq_replay_set $1 &&
python split.py --hq_replay_set $1

================================================
FILE: extract_features/game_state.py
================================================
import os
import json
import pprint
import numpy as np

def load_stat(path):
    def dict_key_to_int(obj):
        def str2int(s):
            try:
                return int(s)
            except:
                return s

        if not isinstance(obj, dict):
            return str2int(obj)

        return {str2int(k): dict_key_to_int(v) for k, v in obj.items()}

    with open(path) as f:
        stat = json.load(f)
    stat = dict_key_to_int(stat)
    stat['action_id'][-1] = len(stat['action_id'])

    return stat

class GameState(object):
    ## Starcraft Stat
    eps = 1e-9

    stat_path, stat = None, None
    enemy_stat_path, enemy_stat = None, None

    max_vars = ['frame_id', 'minerals', 'vespene', 'food_cap',
                    'food_used', 'food_army', 'food_workers', 'idle_worker_count',
                        'army_count', 'warp_gate_count', 'larva_count', 'n_power_source']
    max_keys = ['frame_id', 'minerals', 'vespene', 'food_cap',
                    'food_cap', 'food_cap', 'food_cap', 'idle_worker_count',
                        'army_count', 'warp_gate_count', 'larva_count', 'n_power_source']

    int_vars = max_vars

    def __init__(self, stat_path, enemy_stat_path):
        if self.stat_path is None or not os.path.samefile(self.stat_path, stat_path):
            self.stat_path = stat_path
            self.stat = load_stat(stat_path)
        if self.enemy_stat_path is None or not os.path.samefile(self.enemy_stat_path, enemy_stat_path):
            self.enemy_stat_path = enemy_stat_path
            self.enemy_stat = load_stat(enemy_stat_path)

        for k in self.int_vars:
            setattr(self, k, -1)
        # Reward
        self.reward = -1
        self.score = None
        # Alerts
        self.alert = set()
        # Upgrades
        self.upgrades = set()
        # Actions
        self.action = -1
        self.research = {}
        # Units
        self.friendly_units = {}
        self.enemy_units = {}

    def update(self, state):
        for k in self.int_vars:
            setattr(self, k, state[k])
        # Reward
        self.reward = 2 - state['reward']
        self.score = state['score_cumulative']
        # Alert
        self.alert = set(state['alert'])
        # Upgrades
        self.upgrades = set(state['upgrades'])
        # Actions
        self.__set_action__(-1 if state['action'] is None else state['action'][0])
        ## Units
        # Friendly units
        self.friendly_units = self.__set_units__(state['friendly_units'])
        # Enemy units
        self.enemy_units    = self.__set_units__(state['enemy_units'])

    def get_action(self):
        return self.stat['action_id'][self.action]

    def __set_units__(self, units):
        results = {}
        for unit_type_id, unit in units.items():
            unit_type_id = int(unit_type_id)
            if unit_type_id not in results:
                results[unit_type_id] = {'built': [], 'building': []}
            for unit_instance in unit['units']:
                if unit_instance['build_progress'] >= 1:
                    results[unit_type_id]['built'].append(unit_instance)
                else:
                    results[unit_type_id]['building'].append(unit_instance)
        return results

    def __set_action__(self, action):
        self.action = action
        if action == -1:
            return
        if self.stat['action_name'][action].startswith('Research'):
            if action not in self.research:
                self.research[action] = 0
            self.research[action] += 1

    def __units2vec__(self, units, stat):
        name2id = {'total_num': 0, 'finished_num': 1, 'building_num': 2,
                   'max_building_progress': 3, 'min_building_progress': 4,
                   'avg_building_progress': 5}
        units_stat = stat['units_type']

        result = np.zeros(len(units_stat)*len(name2id))
        for k, unit in units.items():
            if k not in units_stat:
                continue
            start = units_stat[k] * len(name2id)
            result[start + name2id['total_num']] = len(unit['built'])+len(unit['building'])
            if result[start + name2id['total_num']] == 0:
                continue

            result[start + name2id['finished_num']] = len(unit['built'])
            result[start + name2id['building_num']] = len(unit['building'])

            if result[start + name2id['building_num']] > 0:
                ## Init min value
                result[start+name2id['min_building_progress']] = 1.0

                for u in unit['building']:
                    result[start + name2id['max_building_progress']] = \
                        max(result[start + name2id['max_building_progress']], u['build_progress'])
                    result[start + name2id['min_building_progress']] = \
                        min(result[start + name2id['min_building_progress']], u['build_progress'])
                    result[start + name2id['avg_building_progress']] += u['build_progress']

                result[start+name2id['avg_building_progress']] /= result[start+name2id['building_num']]

            for name in {'total_num', 'finished_num', 'building_num'}:
                result[start+name2id[name]] /= stat['max_unit_num']

        return result

    def __set_to_array__(self, set_var, key2id):
        result = np.zeros(len(key2id))
        for key in set_var:
            result[key2id[key]] = 1
        return result

    def __dict_to_array__(self, dict_var, key2id, scale=1):
        result = np.zeros(len(key2id))
        for key, value in dict_var.items():
            result[key2id[key]] = value/scale
        return result

    def to_vector(self):
        result = []
        # Frame_id; Resources
        max_result = []
        for k, v in zip(self.max_keys, self.max_vars):
            max_result.append(self.__dict__[v]/(self.stat['max_'+k]+self.eps))
        result.append(max_result)
        # Alerts
        result.append(self.__set_to_array__(self.alert, self.stat['alert']))
        # Upgrades
        result.append(self.__set_to_array__(self.upgrades, self.stat['upgrades']))
        # Research
        result.append(self.__dict_to_array__(self.research, self.stat['research_id'],
                                             self.stat['max_research_num']))
        ## Units
        result.append(self.__units2vec__(self.friendly_units, self.stat))
        result.append(self.__units2vec__(self.enemy_units, self.enemy_stat))

        return np.hstack(result)

    def __str__(self):
        return pprint.pformat(self.__dict__)


================================================
FILE: extract_features/global_feature_vector.py
================================================
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os
import json
from absl import flags

import numpy as np
from scipy import sparse

from tqdm import tqdm

from google.protobuf.json_format import Parse

from s2clientprotocol import sc2api_pb2 as sc_pb

from game_state import GameState

FLAGS = flags.FLAGS
flags.DEFINE_string(name='hq_replay_set', default='../high_quality_replays/Terran_vs_Terran.json',
                    help='File storing replays list')
flags.DEFINE_string(name='parsed_replay_path', default='../parsed_replays',
                    help='Path storing parsed replays')

def parse_replay(replay_player_path, reward, race, enemy_race):
    with open(os.path.join(FLAGS.parsed_replay_path, 'GlobalFeatures', replay_player_path)) as f:
        states = json.load(f)

    states_np = []
    game_state = GameState(os.path.join(FLAGS.parsed_replay_path, 'Stat', '{}.json'.format(race)),
                           os.path.join(FLAGS.parsed_replay_path, 'Stat', '{}.json'.format(enemy_race)))
    for state in states:
        game_state.update(state)
        states_np.append(np.hstack([game_state.reward, game_state.get_action(),
                                    game_state.score, game_state.to_vector()]))
    states_np = np.asarray(states_np)

    sparse.save_npz(os.path.join(FLAGS.parsed_replay_path, 'GlobalFeatureVector',
                                 replay_player_path), sparse.csc_matrix(states_np))

def main():
    with open(FLAGS.hq_replay_set) as f:
        replay_list = sorted(json.load(f))

    race_vs_race = os.path.basename(FLAGS.hq_replay_set).split('.')[0]
    global_feature_vec_path = os.path.join(FLAGS.parsed_replay_path, 'GlobalFeatureVector', race_vs_race)
    races = set(race_vs_race.split('_vs_'))
    for race in races:
        path = os.path.join(global_feature_vec_path, race)
        if not os.path.isdir(path):
            os.makedirs(path)

    pbar = tqdm(total=len(replay_list), desc='#Replay')
    for replay_path, replay_info_path in replay_list:
        with open(replay_info_path) as f:
            info = json.load(f)
        info = Parse(info['info'], sc_pb.ResponseReplayInfo())

        replay_name = os.path.basename(replay_path)
        for player_info in info.player_info:
            race = sc_pb.Race.Name(player_info.player_info.race_actual)
            player_id = player_info.player_info.player_id
            reward = player_info.player_result.result

            replay_player_path = os.path.join(race_vs_race, race, '{}@{}'.format(player_id, replay_name))
            parse_replay(replay_player_path, reward, race, race if len(races) == 1 else list(races - {race})[0])

        pbar.update()

if __name__ == '__main__':
    main()


================================================
FILE: extract_features/replay_stat.py
================================================
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import glob
import pprint
import numpy as np

import os
import json
from absl import flags

from tqdm import tqdm

FLAGS = flags.FLAGS
flags.DEFINE_string(name='hq_replay_path', default='../high_quality_replays',
                    help='Path for replays lists')
flags.DEFINE_string(name='parsed_replay_path', default='../parsed_replays',
                    help='Path storing parsed replays')
flags.DEFINE_string(name='race', default='Terran',
                     help='Race name')
# MAX stat
max_keys = {'frame_id', 'minerals', 'vespene', 'food_cap',
                'idle_worker_count', 'army_count', 'warp_gate_count',
                    'larva_count', 'n_power_source'}
# SET stat
set_keys = {'alert', 'upgrades'}

def update(replay_path, stat):
    with open(replay_path) as f:
        states = json.load(f)

    research_count = {}
    for state in states:
        # MAX stat
        for key in max_keys:
            stat['max_' + key] = max(state[key], stat['max_'+key])
        # SET stat
        for key in set_keys:
            stat[key].update(set(state[key]))
        # max_score_cumulative
        stat['max_score_cumulative'] = max(stat['max_score_cumulative'], state['score_cumulative'][0])
        ## Units stat
        units = state['friendly_units']
        for unit_type, unit in units.items():
            stat['units_type'].add(unit_type)
            stat['units_name'][unit_type] = unit['name']
            stat['max_unit_num'] = max(stat['max_unit_num'], len(unit['units']))
        ## Actions
        if state['action'] is None:
            continue

        id, name = state['action']
        stat['action_id'].add(id)
        stat['action_name'][id] = name

        if name.startswith('Research_'):
            stat['research_id'].add(id)
            if id not in research_count:
                research_count[id] = 0
            research_count[id] += 1
    if len(research_count) > 0:
        stat['max_research_num'] = max(stat['max_research_num'], max(research_count.values()))

def post_process(stat):
    for key in set_keys | {'action_id', 'research_id', 'units_type'}:
        values = np.asarray(list(stat[key]))
        idx = np.argsort(values)
        values = values[idx]
        stat[key] = {k: v for k, v in zip(values, idx)}

    # Turn all keys into str
    def dict_key_to_str(obj):
        if not isinstance(obj, dict):
            return str(obj)
        return {str(k):dict_key_to_str(v) for k, v in obj.items()}

    return dict_key_to_str(stat)

def main():
    replay_lists = sorted(glob.glob(os.path.join(FLAGS.hq_replay_path, '*{}*.json'.format(FLAGS.race))))
    save_path = os.path.join(FLAGS.parsed_replay_path, 'Stat')
    if not os.path.isdir(save_path):
        os.makedirs(save_path)

    ## Init Stat Dict
    stat = {}
    # MAX stat
    for key in max_keys:
        stat['max_'+key] = 0
    # SET stat
    for key in set_keys:
        stat[key] = set()
    # score_cumulative
    stat['max_score_cumulative'] = 0
    ## Units stat
    stat['units_type'] = set()
    stat['units_name'] = {}
    stat['max_unit_num'] = 0
    ## Actions
    stat['action_id'] = set()
    stat['action_name'] = {}
    stat['research_id'] = set()
    stat['max_research_num'] = 0

    set_bar = tqdm(total=len(replay_lists), desc='#SET')
    for replay_list_path in replay_lists:
        race_vs_race = os.path.basename(replay_list_path).split('.')[0]
        replays = glob.glob(os.path.join(FLAGS.parsed_replay_path, 'GlobalFeatures',
                                         race_vs_race, FLAGS.race, '*.SC2Replay'))

        pbar = tqdm(total=len(replays), desc='\t#Replay')
        for replay in replays:
            update(replay, stat)
            pbar.update()

        set_bar.update()

    stat = post_process(stat)

    with open(os.path.join(save_path, '{}_human.json'.format(FLAGS.race)), 'w') as f:
        f.write(pprint.pformat(stat))
    with open(os.path.join(save_path, '{}.json'.format(FLAGS.race)), 'w') as f:
        json.dump(stat, f)

if __name__ == '__main__':
    main()


================================================
FILE: extract_features/spatial_feature_tensor.py
================================================
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os
import json
import stream
from absl import flags
from multiprocessing import Pool

import numpy as np
from scipy import sparse

from tqdm import tqdm

from google.protobuf.json_format import Parse

from pysc2.lib import FUNCTIONS
from s2clientprotocol import sc2api_pb2 as sc_pb

from game_state import load_stat
from SpatialFeatures import SpatialFeatures

FLAGS = flags.FLAGS
flags.DEFINE_string(name='hq_replay_set', default='../high_quality_replays/Terran_vs_Terran.json',
                    help='File storing replays list')
flags.DEFINE_string(name='parsed_replay_path', default='../parsed_replays',
                    help='Path storing parsed replays')
flags.DEFINE_integer(name='step_mul', default=8,
                     help='step size')
flags.DEFINE_integer(name='n_workers', default=16,
                     help='#processes')

def parse_replay(replay_player_path, sampled_action_path, reward, race, enemy_race, stat):
    with open(os.path.join(FLAGS.parsed_replay_path, 'GlobalInfos', replay_player_path)) as f:
        global_info = json.load(f)
    feat = SpatialFeatures(Parse(global_info['game_info'], sc_pb.ResponseGameInfo()))

    states = [obs for obs in stream.parse(os.path.join(FLAGS.parsed_replay_path,
                    'SampledObservations', replay_player_path), sc_pb.ResponseObservation)]

    # Sampled Actions
    with open(sampled_action_path) as f:
        sampled_action = json.load(f)
    sampled_action_id = [id // FLAGS.step_mul + 1 for id in sampled_action]
    # Actions
    with open(os.path.join(FLAGS.parsed_replay_path, 'Actions', replay_player_path)) as f:
        actions = json.load(f)
    actions = [None if len(actions[idx]) == 0 else Parse(actions[idx][0], sc_pb.Action())
               for idx in sampled_action_id]

    assert len(states) == len(actions)

    spatial_states_np, global_states_np = [], []
    for state, action in zip(states, actions):
        action_id = -1
        if action is not None:
            try:
                func_id = feat.reverse_action(action).function
                func_name = FUNCTIONS[func_id].name
                if func_name.split('_')[0] in {'Build', 'Train', 'Research', 'Morph', 'Cancel', 'Halt', 'Stop'}:
                    action_id = func_id
            except:
                pass

        obs = feat.transform_obs(state.observation)
        spatial_states_np.append(np.concatenate([obs['screen'], obs['minimap']], axis=0))

        global_states_np.append(np.hstack([obs['player']/(stat['max']+1e-5), obs['score'], [reward],
                                           [stat['action_id'][action_id]]]))

    spatial_states_np = np.asarray(spatial_states_np)
    global_states_np = np.asarray(global_states_np)

    spatial_states_np = spatial_states_np.reshape([len(states), -1])
    sparse.save_npz(os.path.join(FLAGS.parsed_replay_path, 'SpatialFeatureTensor',
                                 replay_player_path+'@S'), sparse.csc_matrix(spatial_states_np))
    sparse.save_npz(os.path.join(FLAGS.parsed_replay_path, 'SpatialFeatureTensor',
                                 replay_player_path+'@G'), sparse.csc_matrix(global_states_np))

class Parser(object):
    def __init__(self, race_vs_race, races, stats):
        self.race_vs_race = race_vs_race
        self.races = races
        self.stats = stats

    def __call__(self, line):
        replay_path, replay_info_path = line
        with open(replay_info_path) as f:
            info = json.load(f)
        info = Parse(info['info'], sc_pb.ResponseReplayInfo())

        replay_name = os.path.basename(replay_path)
        sampled_action_path = os.path.join(FLAGS.parsed_replay_path, 'SampledActions', self.race_vs_race, replay_name)
        for player_info in info.player_info:
            race = sc_pb.Race.Name(player_info.player_info.race_actual)
            player_id = player_info.player_info.player_id
            reward = 2 - player_info.player_result.result

            replay_player_path = os.path.join(self.race_vs_race, race, '{}@{}'.format(player_id, replay_name))
            parse_replay(replay_player_path, sampled_action_path, reward, race,
                                race if len(self.races) == 1 else list(self.races - {race})[0], self.stats[race])

max_keys = ['frame_id', 'minerals', 'vespene', 'food_cap',
                    'food_cap', 'food_cap', 'food_cap', 'idle_worker_count',
                        'army_count', 'warp_gate_count', 'larva_count']

def main():
    with open(FLAGS.hq_replay_set) as f:
        replay_list = sorted(json.load(f))

    race_vs_race = os.path.basename(FLAGS.hq_replay_set).split('.')[0]
    global_feature_vec_path = os.path.join(FLAGS.parsed_replay_path, 'SpatialFeatureTensor', race_vs_race)
    races = set(race_vs_race.split('_vs_'))
    stats = {}
    for race in races:
        path = os.path.join(global_feature_vec_path, race)
        if not os.path.isdir(path):
            os.makedirs(path)

        stat = load_stat(os.path.join(FLAGS.parsed_replay_path, 'Stat', '{}.json'.format(race)))
        stats[race] = {'max': np.asarray([stat['max_'+k] for k in max_keys]),
                       'action_id': stat['action_id']}

    pbar = tqdm(total=len(replay_list), desc='#Replay')
    with Pool(FLAGS.n_workers) as p:
        for _ in p.imap(Parser(race_vs_race, races, stats), replay_list):
            pbar.update()

if __name__ == '__main__':
    main()


================================================
FILE: extract_features/split.py
================================================
import os
import json
import numpy as np
from absl import flags

from google.protobuf.json_format import Parse
from s2clientprotocol import sc2api_pb2 as sc_pb

FLAGS = flags.FLAGS
flags.DEFINE_string(name='hq_replay_set', default='../high_quality_replays/Terran_vs_Terran.json',
                    help='File storing replays list')
flags.DEFINE_string(name='root', default='..',
                    help='Root for parsed replays')
flags.DEFINE_string(name='parsed_replay_path', default='parsed_replays',
                    help='Path for parsed replays')
flags.DEFINE_string(name='save_path', default='../train_val_test',
                    help='Path for saving results')
flags.DEFINE_string(name='ratio', default='7:1:2',
                    help='train:val:test')
flags.DEFINE_integer(name='seed', default=1,
                    help='random seed')

def save(replays, prefix, folder):
    print('{}/{}: {}'.format(folder, prefix, len(replays)))
    with open(os.path.join(folder, prefix+'.json'), 'w') as f:
        json.dump(replays, f)

def main():
    np.random.seed(FLAGS.seed)
    ratio = np.asarray([float(i) for i in FLAGS.ratio.split(':')])
    ratio /= np.sum(ratio)

    if not os.path.isdir(FLAGS.save_path):
        os.makedirs(FLAGS.save_path)

    with open(FLAGS.hq_replay_set) as f:
        replays_list = json.load(f)

    result = []
    race_vs_race = os.path.basename(FLAGS.hq_replay_set).split('.')[0]
    for replay_path, info_path in replays_list:
        replay_path_dict = {}
        replay_name = os.path.basename(replay_path)

        ## Parsed Replay
        for race in set(race_vs_race.split('_vs_')):
            replay_path_dict[race] = []

        with open(info_path) as f:
            info = json.load(f)
        proto = Parse(info['info'], sc_pb.ResponseReplayInfo())
        for p in proto.player_info:
            player_id = p.player_info.player_id
            race = sc_pb.Race.Name(p.player_info.race_actual)

            parsed_replays_info = {}
            ## Global Feature
            global_path = os.path.join(FLAGS.parsed_replay_path, 'GlobalFeatureVector', race_vs_race, race,
                                            '{}@{}.npz'.format(player_id, replay_name))

            if os.path.isfile(os.path.join(FLAGS.root, global_path)):
                parsed_replays_info['global_path'] = global_path
            ## Spatial Feature
            spatial_path_S = os.path.join(FLAGS.parsed_replay_path, 'SpatialFeatureTensor', race_vs_race, race,
                                            '{}@{}@S.npz'.format(player_id, replay_name))
            if os.path.isfile(os.path.join(FLAGS.root, spatial_path_S)):
                parsed_replays_info['spatial_path_S'] = spatial_path_S

            spatial_path_G = os.path.join(FLAGS.parsed_replay_path, 'SpatialFeatureTensor', race_vs_race, race,
                                          '{}@{}@G.npz'.format(player_id, replay_name))
            if os.path.isfile(os.path.join(FLAGS.root, spatial_path_G)):
                parsed_replays_info['spatial_path_G'] = spatial_path_G

            replay_path_dict[race].append(parsed_replays_info)

        result.append(replay_path_dict)

    FLAGS.save_path = os.path.join(FLAGS.save_path, race_vs_race)
    if not os.path.isdir(FLAGS.save_path):
        os.makedirs(FLAGS.save_path)

    train_end = int(len(result)*ratio[0])
    val_end = int(len(result)*(ratio[0]+ratio[1]))

    np.random.shuffle(result)
    save(result[:train_end], 'train', FLAGS.save_path)
    save(result[train_end:val_end], 'val', FLAGS.save_path)
    save(result[val_end:], 'test', FLAGS.save_path)

if __name__ == '__main__':
    main()

================================================
FILE: instructions/EasyWay.md
================================================
# Step-by-Step: The Easy Way
## Preprocess
```sh
cd preprocess
bash preprocess.sh
```
## Parse Replay
```sh
cd parse_replay
bash parse_replay.sh $HQ_REPLAY_LIST$ [N_PROCESSES]
```
For example:
```sh
cd parse_replay
bash parse_replay.sh ../high_quality_replays/Protoss_vs_Terran.json 32
```
## Build Dataset
```sh
cd extract_features
```
### Compute Stat
```sh
bash compute_stat.sh [RACE]
```
For example:
```sh
bash compute_stat.sh Terran
```
### Extract Features
```sh
bash extract_features.sh $HQ_REPLAY_LIST$
```
For example:
```sh
bash extract_features.sh ../high_quality_replays/Protoss_vs_Terran.json
```

================================================
FILE: instructions/HardWay.md
================================================
# Step-by-Step: Details
## Preprocessing Replays
```sh
cd preprocess
```
### Parse Replay Info
```sh
python parse_replay_info.py
  --replays_paths $REPLAY_FOLDER_PATH_1$;$REPLAY_FOLDER_PATH_2$;...;$REPLAY_FOLDER_PATH_N$
  --save_path $SAVE_PATH$
  --n_instance [N_PROCESSES]
  --batch_size [BATCH_SIZE]
```
- **Code for reading processed files:**
    ```python
    import json
    from google.protobuf.json_format import Parse
    from s2clientprotocol import sc2api_pb2 as sc_pb

    with open(REPLAY_INFO_PATH) as f:
        info = json.load(f)
    REPLAY_PATH = info['path']
    REPLAY_INFO_PROTO = Parse(info['info'], sc_pb.ResponseReplayInfo())
    ```
    **ResponseReplayInfo** is defined [Here](https://github.com/Blizzard/s2client-proto/blob/4028f80aac30120f541e0e103efd63e921f1b7d5/s2clientprotocol/sc2api.proto#L398).
### Filter Replays
```sh
python preprocess.py
  --infos_path $REPLAY_INFO_PATH$
  --save_path $SAVE_PATH$
  --min_duration [MIN_DURATION]
  --max_duration [MAX_DURATION]
  --min_apm [MIN_APM]
  --min_mmr [MIN_MMR]
```
- **Format of processed files [JSON]:**
    ```python
    [[REPLAY_PATH_1, REPLAY_INFO_PATH_1],
     [REPLAY_PATH_2, REPLAY_INFO_PATH_2],
     ...,
     [REPLAY_PATH_N, REPLAY_INFO_PATH_N]]
    ```
## Parsing Replays
```sh
cd parse_replay
```
### Extract Actions
```sh
python extract_actions.py
  --hq_replay_set $PREFILTERED_REPLAY_LIST$
  --save_path $SAVE_PATH$
  --n_instance [N_PROCESSES]
  --batch_size [BATCH_SIZE]
  --step_mul [STEP_SIZE]
  --width [WORLD_WIDTH]
  --map_size [MAP_SIZE]
```
- **Code for reading processed files:**
    ```python
    import json
    from google.protobuf.json_format import Parse
    from s2clientprotocol import sc2api_pb2 as sc_pb

    with open(ACTION_PATH) as f:
        actions = json.load(f)

    for actions_per_frame in actions:
        for action_str in actions_per_frame:
            action = Parse(action_str, sc_pb.Action())
    ```
    **Action** is defined [Here](https://github.com/Blizzard/s2client-proto/blob/4028f80aac30120f541e0e103efd63e921f1b7d5/s2clientprotocol/sc2api.proto#L553).
### Sample Actions
```sh
python sample_actions.py
  --hq_replay_set $PREFILTERED_REPLAY_LIST$
  --parsed_replays $PARSED_REPLAYS$
  --infos_path $REPLAY_INFOS$
  --step_mul [STEP_SIZE]
  --skip [SKIP_FRAMES]
```
- **Format of processed files [JSON]:**
    ```python
    [FRAME_ID_1, FRAME_ID_2, ..., FRAME_ID_N]
    ```
### Extract Sampled Observations
```sh
python parse_replay.py
  --hq_replay_set $PREFILTERED_REPLAY_LIST$
  --save_path $SAVE_PATH$
  --n_instance [N_PROCESSES]
  --batch_size [BATCH_SIZE]
  --width [WORLD_WIDTH]
  --map_size [MAP_SIZE]
```
- **Code for reading GlobalInfos files:**
    ```python
    import json
    from google.protobuf.json_format import Parse
    from s2clientprotocol import sc2api_pb2 as sc_pb

    with open(GLOBAL_INFO_PATH) as f:
        global_info = json.load(f)
    GAME_INFO = Parse(global_info['game_info'], sc_pb.ResponseGameInfo())
    DATA_RAW  = Parse(global_info['data_raw'], sc_pb.ResponseData())
    ```
    **ResponseGameInfo** is defined [Here](https://github.com/Blizzard/s2client-proto/blob/4028f80aac30120f541e0e103efd63e921f1b7d5/s2clientprotocol/sc2api.proto#L315) while **ResponseData** is defined [Here](https://github.com/Blizzard/s2client-proto/blob/4028f80aac30120f541e0e103efd63e921f1b7d5/s2clientprotocol/sc2api.proto#L367).
- **Code for reading SampledObservations files**
    ```python
    import stream
    from s2clientprotocol import sc2api_pb2 as sc_pb

    OBS =  [obs for obs in stream.parse(SAMPLED_OBSERVATION_PATH), sc_pb.ResponseObservation)]
    ```
    **ResponseObsevation** is defined [Here](https://github.com/Blizzard/s2client-proto/blob/4028f80aac30120f541e0e103efd63e921f1b7d5/s2clientprotocol/sc2api.proto#L329).
### Extract Global Features
```sh
python replay2global_features.py
  --hq_replay_set $PREFILTERED_REPLAY_LIST$
  --parsed_replay_path: $PARSED_REPLAYS$
  --step_mul [STEP_SIZE]
```
- **Format of processed files [JSON]:**
    ```python
    [state_1, state_2, ..., state_N]
    state_t = {...} [READ THE CODE or PRINT]
    ```
## Build Dataset
```sh
cd extract_features
```
### Compute Stat
```sh
python replay_stat.py
    --hq_replay_path $PREFILTERED_REPLAY_FOLDER$
    --parsed_replay_path $PARSED_REPLAYS$
    --race [RACE]
```
The stat files with postfix **_human.json** is human-readable.
### Extract Features
- Global Feature Vector
    ```sh
    python global_feature_vector.py
        --hq_replay_set $PREFILTERED_REPLAY_LIST$
        --parsed_replay_path: $PARSED_REPLAYS$
    ```
- Spatial Feature Tensor
    ```sh
    python spatial_feature_tensor.py
        --hq_replay_set $PREFILTERED_REPLAY_LIST$
        --parsed_replay_path: $PARSED_REPLAYS$
        --step_mul [STEP_SIZE]
        --n_workers [#PROCESSES]
    ```
### Split Training, Validation and Test sets
```sh
python split.py
  --hq_replay_set $PREFILTERED_REPLAY_LIST$
  --root $ROOT_PARSED_REPLAYS$
  --parsed_replay_path $PARSED_REPLAYS$
  --save_path $SAVE_PATH$
  --ratio [TRAIN:VAL:TEST]
  --seed [RANDOM_SEED]
```
- **Format of processed files [JSON]:**
    ```python
    [{RACE_1: [{"global__path": GLOBAL_FEATURE_PATH,
                "spatial_path_S": SPATIAL_FEATURE_PATH_S,
                "spatial_path_G": SPATIAL_FEATURE_PATH_G}, ...],
      RACE_2: [{...}, ...]}, {...}, ...]
    ```
- **NOTE:** The pre-split training, validation and test sets are available in [**Here**](https://github.com/wuhuikai/MSC/tree/master/train_val_test).

================================================
FILE: parse_replay/extract_actions.py
================================================
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os
import sys
import json
import time
import signal
import threading
import queue as Queue
import multiprocessing
from absl import flags
from future.builtins import range

from google.protobuf.json_format import MessageToJson

from pysc2 import run_configs
from pysc2.lib import point
from s2clientprotocol import sc2api_pb2 as sc_pb

FLAGS = flags.FLAGS
flags.DEFINE_string(name='hq_replay_set', default='../high_quality_replays/Terran_vs_Terran.json',
                    help='File storing replays list')
flags.DEFINE_string(name='save_path', default='../parsed_replays',
                    help='Path for saving results')

flags.DEFINE_integer(name='n_instance', default=16,
                     help='# of processes to run')
flags.DEFINE_integer(name='step_mul', default=8,
                     help='step size')
flags.DEFINE_integer(name='batch_size', default=10,
                     help='# of replays to process in one iter')
flags.DEFINE_integer(name='width', default=24,
                     help='World width')
flags.DEFINE_integer(name='map_size', default=64,
                     help='Map size')

FLAGS(sys.argv)
size = point.Point(FLAGS.map_size, FLAGS.map_size)
interface = sc_pb.InterfaceOptions(raw=True, score=False,
                                   feature_layer=sc_pb.SpatialCameraSetup(width=FLAGS.width))
size.assign_to(interface.feature_layer.resolution)
size.assign_to(interface.feature_layer.minimap_resolution)

class ReplayProcessor(multiprocessing.Process):
    """A Process that pulls replays and processes them."""
    def __init__(self, run_config, replay_queue, counter, total_num):
        super(ReplayProcessor, self).__init__()
        self.run_config = run_config
        self.replay_queue = replay_queue
        self.counter = counter
        self.total_num = total_num

    def run(self):
        signal.signal(signal.SIGTERM, lambda a, b: sys.exit())  # Exit quietly.
        while True:
            with self.run_config.start() as controller:
                for _ in range(FLAGS.batch_size):
                    try:
                        replay_path = self.replay_queue.get()
                    except Queue.Empty:
                        return

                    try:
                        with self.counter.get_lock():
                            self.counter.value += 1
                            print('Processing {}/{} ...'.format(self.counter.value, self.total_num))

                        replay_data = self.run_config.replay_data(replay_path)
                        info = controller.replay_info(replay_data)
                        map_data = None
                        if info.local_map_path:
                            map_data = self.run_config.map_data(info.local_map_path)

                        for player_info in info.player_info:
                            race = sc_pb.Race.Name(player_info.player_info.race_actual)
                            player_id = player_info.player_info.player_id

                            if os.path.isfile(os.path.join(FLAGS.save_path, race,
                                                           '{}@{}'.format(player_id, os.path.basename(replay_path)))):
                                continue

                            self.process_replay(controller, replay_data, map_data, player_id, race, replay_path)
                    except Exception as e:
                        print(e)
                        break
                    finally:
                        self.replay_queue.task_done()

    def process_replay(self, controller, replay_data, map_data, player_id, race, replay_path):
        controller.start_replay(sc_pb.RequestStartReplay(
            replay_data=replay_data,
            map_data=map_data,
            options=interface,
            observed_player_id=player_id))

        save_folder = os.path.join(FLAGS.save_path, race)
        actions = []
        controller.step()
        while True:
            obs = controller.observe()
            actions.append([MessageToJson(a) for a in obs.actions])

            if obs.player_result:
                with open(os.path.join(save_folder, '{}@{}'.format(
                        player_id, os.path.basename(replay_path))), 'w') as f:
                    json.dump(actions, f)
                return
            controller.step(FLAGS.step_mul)

def replay_queue_filler(replay_queue, replay_list):
    """A thread that fills the replay_queue with replay filenames."""
    for replay_path in replay_list:
        replay_queue.put(replay_path)

def main():
    race_vs_race = os.path.basename(FLAGS.hq_replay_set).split('.')[0]
    FLAGS.save_path = os.path.join(FLAGS.save_path, 'Actions', race_vs_race)

    for race in set(race_vs_race.split('_vs_')):
        path = os.path.join(FLAGS.save_path, race)
        if not os.path.isdir(path):
            os.makedirs(path)

    run_config = run_configs.get()
    try:
        with open(FLAGS.hq_replay_set) as f:
            replay_list = json.load(f)
        replay_list = sorted([p for p, _ in replay_list])

        replay_queue = multiprocessing.JoinableQueue(FLAGS.n_instance * 10)
        replay_queue_thread = threading.Thread(target=replay_queue_filler,
                                           args=(replay_queue, replay_list))
        replay_queue_thread.daemon = True
        replay_queue_thread.start()

        counter = multiprocessing.Value('i', 0)
        for i in range(FLAGS.n_instance):
            p = ReplayProcessor(run_config, replay_queue, counter, len(replay_list))
            p.daemon = True
            p.start()
            time.sleep(1)   # Stagger startups, otherwise they seem to conflict somehow

        replay_queue.join() # Wait for the queue to empty.
    except KeyboardInterrupt:
        print("Caught KeyboardInterrupt, exiting.")

if __name__ == '__main__':
    main()


================================================
FILE: parse_replay/parse_replay.py
================================================
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os
import sys
import json
import time
import signal
import threading
import queue as Queue
import multiprocessing
from absl import flags
from future.builtins import range

from google.protobuf.json_format import MessageToJson

from pysc2 import run_configs
from pysc2.lib import point
from s2clientprotocol import sc2api_pb2 as sc_pb

import stream

FLAGS = flags.FLAGS
flags.DEFINE_string(name='hq_replay_set', default='../high_quality_replays/Terran_vs_Terran.json',
                    help='File storing replays list')
flags.DEFINE_string(name='save_path', default='../parsed_replays',
                    help='Path for saving results')

flags.DEFINE_integer(name='n_instance', default=16,
                     help='# of processes to run')
flags.DEFINE_integer(name='batch_size', default=10,
                     help='# of replays to process in one iter')

flags.DEFINE_integer(name='width', default=24,
                     help='World width')
flags.DEFINE_integer(name='map_size', default=64,
                     help='Map size')

FLAGS(sys.argv)
size = point.Point(FLAGS.map_size, FLAGS.map_size)
interface = sc_pb.InterfaceOptions(raw=True, score=True,
                feature_layer=sc_pb.SpatialCameraSetup(width=FLAGS.width))
size.assign_to(interface.feature_layer.resolution)
size.assign_to(interface.feature_layer.minimap_resolution)

class ReplayProcessor(multiprocessing.Process):
    """A Process that pulls replays and processes them."""
    def __init__(self, run_config, replay_queue, counter, total_num):
        super(ReplayProcessor, self).__init__()
        self.run_config = run_config
        self.replay_queue = replay_queue
        self.counter = counter
        self.total_num = total_num

    def run(self):
        signal.signal(signal.SIGTERM, lambda a, b: sys.exit())  # Exit quietly.
        while True:
            with self.run_config.start() as controller:
                for _ in range(FLAGS.batch_size):
                    try:
                        replay_path = self.replay_queue.get()
                    except Queue.Empty:
                        return
                    try:
                        with self.counter.get_lock():
                            self.counter.value += 1
                            print('Processing {}/{} ...'.format(self.counter.value, self.total_num))

                        sampled_action_path = os.path.join(FLAGS.save_path.replace(
                            'SampledObservations', 'SampledActions'), os.path.basename(replay_path))
                        if not os.path.isfile(sampled_action_path):
                            return

                        with open(sampled_action_path) as f:
                            actions = json.load(f)
                        actions.insert(0, 0)

                        replay_data = self.run_config.replay_data(replay_path)
                        info = controller.replay_info(replay_data)
                        map_data = None
                        if info.local_map_path:
                            map_data = self.run_config.map_data(info.local_map_path)

                        for player_info in info.player_info:
                            race = sc_pb.Race.Name(player_info.player_info.race_actual)
                            player_id = player_info.player_info.player_id

                            observation_path = os.path.join(FLAGS.save_path, race,
                                                            '{}@{}'.format(player_id, os.path.basename(replay_path)))
                            global_info_path = observation_path.replace('SampledObservations', 'GlobalInfos')

                            if os.path.isfile(observation_path) and os.path.isfile(global_info_path):
                                continue

                            ostream = stream.open(observation_path, 'wb', buffer_size=1000)
                            self.process_replay(controller, replay_data, map_data, player_id, actions,
                                                ostream, global_info_path)
                            ostream.close()
                    except Exception as e:
                        try:
                            ostream.close()
                            if os.path.isfile(observation_path):
                                os.remove(observation_path)

                            if os.path.isfile(global_info_path):
                                os.remove(global_info_path)
                        except:
                            pass

                        print(e)
                        break
                    finally:
                        self.replay_queue.task_done()

    def process_replay(self, controller, replay_data, map_data, player_id, actions, ostream, global_info_path):
        controller.start_replay(sc_pb.RequestStartReplay(
            replay_data=replay_data,
            map_data=map_data,
            options=interface,
            observed_player_id=player_id))

        global_info = {'game_info': controller.game_info(),
                       'data_raw': controller.data_raw()}
        with open(global_info_path, 'w') as f:
            json.dump({k:MessageToJson(v) for k, v in global_info.items()}, f)

        controller.step()
        for pre_id, id in zip(actions[:-1], actions[1:]):
            controller.step(id - pre_id)
            obs = controller.observe()
            ostream.write(obs)

def replay_queue_filler(replay_queue, replay_list):
    """A thread that fills the replay_queue with replay filenames."""
    for replay_path in replay_list:
        replay_queue.put(replay_path)

def main():
    race_vs_race = os.path.basename(FLAGS.hq_replay_set).split('.')[0]
    FLAGS.save_path = os.path.join(FLAGS.save_path, 'SampledObservations', race_vs_race)

    for race in set(race_vs_race.split('_vs_')):
        path = os.path.join(FLAGS.save_path, race)
        if not os.path.isdir(path):
            os.makedirs(path)

        path = path.replace('SampledObservations', 'GlobalInfos')
        if not os.path.isdir(path):
            os.makedirs(path)

    run_config = run_configs.get()
    try:
        with open(FLAGS.hq_replay_set) as f:
            replay_list = json.load(f)
        replay_list = sorted([p for p, _ in replay_list])

        replay_queue = multiprocessing.JoinableQueue(FLAGS.n_instance * 10)
        replay_queue_thread = threading.Thread(target=replay_queue_filler,
                                           args=(replay_queue, replay_list))
        replay_queue_thread.daemon = True
        replay_queue_thread.start()

        counter = multiprocessing.Value('i', 0)
        for i in range(FLAGS.n_instance):
            p = ReplayProcessor(run_config, replay_queue, counter, len(replay_list))
            p.daemon = True
            p.start()
            time.sleep(1)   # Stagger startups, otherwise they seem to conflict somehow

        replay_queue.join() # Wait for the queue to empty.
    except KeyboardInterrupt:
        print("Caught KeyboardInterrupt, exiting.")

if __name__ == '__main__':
    main()


================================================
FILE: parse_replay/parse_replay.sh
================================================
python extract_actions.py --hq_replay_set $1 --n_instance $2 &&
python sample_actions.py --hq_replay_set $1 &&
python parse_replay.py --hq_replay_set $1 --n_instance $2 &&
python replay2global_features.py --hq_replay_set $1

================================================
FILE: parse_replay/replay2global_features.py
================================================
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os
import json
import stream
from absl import flags

from tqdm import tqdm

from google.protobuf.json_format import Parse

from pysc2.lib import features
from pysc2.lib import FUNCTIONS
from pysc2.lib import static_data
from s2clientprotocol import sc2api_pb2 as sc_pb

FLAGS = flags.FLAGS
flags.DEFINE_string(name='hq_replay_set', default='../high_quality_replays/Terran_vs_Terran.json',
                    help='File storing replays list')
flags.DEFINE_string(name='parsed_replay_path', default='../parsed_replays',
                    help='Path storing parsed replays')
flags.DEFINE_integer(name='step_mul', default=8,
                     help='step size')

def process_replay(sampled_action, actions, observations, feat, units_info, reward):
    states = []

    for frame_id, action, obs in zip(sampled_action, actions, observations):
        state = {}
        # actions
        state['action'] = None
        if action is not None:
            try:
                func_id = feat.reverse_action(action).function
                func_name = FUNCTIONS[func_id].name
                if func_name.split('_')[0] in {'Build', 'Train', 'Research', 'Morph', 'Cancel', 'Halt', 'Stop'}:
                    state['action'] = (func_id, func_name)
            except ValueError:
                pass

        observation = obs.observation
        #####################################################
        # frame_id
        assert frame_id == observation.game_loop-1
        state['frame_id'] = frame_id
        # reward
        state['reward'] = reward

        state['score_cumulative'] = [
            observation.score.score,
            observation.score.score_details.idle_production_time,
            observation.score.score_details.idle_worker_time,
            observation.score.score_details.total_value_units,
            observation.score.score_details.total_value_structures,
            observation.score.score_details.killed_value_units,
            observation.score.score_details.killed_value_structures,
            observation.score.score_details.collected_minerals,
            observation.score.score_details.collected_vespene,
            observation.score.score_details.collection_rate_minerals,
            observation.score.score_details.collection_rate_vespene,
            observation.score.score_details.spent_minerals,
            observation.score.score_details.spent_vespene,
        ]
        # resources
        resources = observation.player_common
        state['minerals'] = resources.minerals
        state['vespene'] = resources.vespene
        state['food_cap'] = resources.food_cap
        state['food_used'] = resources.food_used
        state['food_army'] = resources.food_army
        state['food_workers'] = resources.food_workers
        state['idle_worker_count'] = resources.idle_worker_count
        state['army_count'] = resources.army_count
        state['warp_gate_count'] = resources.warp_gate_count
        state['larva_count'] = resources.larva_count
        #####################################################
        # alert
        state['alert'] = list(observation.alerts)

        #####################################################
        ### raw data
        raw_data = observation.raw_data
        ## player
        player = raw_data.player
        # upgrades
        state['upgrades'] = list(player.upgrade_ids)
        # power
        state['n_power_source'] = len(player.power_sources)
        #####################################################
        ## units
        state['friendly_units'] = {}
        state['enemy_units'] = {}
        for unit in raw_data.units:
            if unit.display_type == 3:
                continue
            if unit.alliance != 1 and unit.alliance != 4:
                continue
            # Friendly or Enemy
            units = state['friendly_units'] if unit.alliance == 1 else state['enemy_units']
            # Already have this unit_type ?
            unit_type = unit.unit_type
            if unit_type not in units:
                units[unit_type] = {'units': [], 'name': units_info[unit_type]}
            # Basic info
            unit_info = {'tag': unit.tag,
                         'build_progress': unit.build_progress}

            units[unit_type]['units'].append(unit_info)

        states.append(state)

    return states

def parse_replay(replay_player_path, sampled_action_path, reward):
    if os.path.isfile(os.path.join(FLAGS.parsed_replay_path, 'GlobalFeatures', replay_player_path)):
        return

    # Global Info
    with open(os.path.join(FLAGS.parsed_replay_path, 'GlobalInfos', replay_player_path)) as f:
        global_info = json.load(f)
    units_info = static_data.StaticData(Parse(global_info['data_raw'], sc_pb.ResponseData())).units
    feat = features.Features(Parse(global_info['game_info'], sc_pb.ResponseGameInfo()))

    # Sampled Actions
    with open(sampled_action_path) as f:
        sampled_action = json.load(f)
    sampled_action_id = [id // FLAGS.step_mul + 1 for id in sampled_action]

    # Actions
    with open(os.path.join(FLAGS.parsed_replay_path, 'Actions', replay_player_path)) as f:
        actions = json.load(f)
    actions = [None if len(actions[idx]) == 0 else Parse(actions[idx][0], sc_pb.Action())
                for idx in sampled_action_id]

    # Observations
    observations =  [obs for obs in stream.parse(os.path.join(FLAGS.parsed_replay_path,
                            'SampledObservations', replay_player_path), sc_pb.ResponseObservation)]

    assert len(sampled_action) == len(sampled_action_id) == len(actions) == len(observations)

    states = process_replay(sampled_action, actions, observations, feat, units_info, reward)

    with open(os.path.join(FLAGS.parsed_replay_path, 'GlobalFeatures', replay_player_path), 'w') as f:
        json.dump(states, f)

def main():
    with open(FLAGS.hq_replay_set) as f:
        replay_list = sorted(json.load(f))

    race_vs_race = os.path.basename(FLAGS.hq_replay_set).split('.')[0]
    global_feature_path = os.path.join(FLAGS.parsed_replay_path, 'GlobalFeatures', race_vs_race)
    for race in set(race_vs_race.split('_vs_')):
        path = os.path.join(global_feature_path, race)
        if not os.path.isdir(path):
            os.makedirs(path)

    pbar = tqdm(total=len(replay_list), desc='#Replay')
    for replay_path, replay_info_path in replay_list:
        with open(replay_info_path) as f:
            info = json.load(f)
        info = Parse(info['info'], sc_pb.ResponseReplayInfo())

        replay_name = os.path.basename(replay_path)
        sampled_action_path = os.path.join(FLAGS.parsed_replay_path, 'SampledActions', race_vs_race, replay_name)
        for player_info in info.player_info:
            race = sc_pb.Race.Name(player_info.player_info.race_actual)
            player_id = player_info.player_info.player_id
            reward = player_info.player_result.result

            replay_player_path = os.path.join(race_vs_race, race, '{}@{}'.format(player_id, replay_name))
            parse_replay(replay_player_path, sampled_action_path, reward)

        pbar.update()

if __name__ == '__main__':
    main()


================================================
FILE: parse_replay/sample_actions.py
================================================
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os
import json
from absl import flags
from tqdm import tqdm

from google.protobuf.json_format import Parse

from pysc2.lib import features
from pysc2.lib import  FUNCTIONS
from s2clientprotocol import sc2api_pb2 as sc_pb

FLAGS = flags.FLAGS
flags.DEFINE_string(name='hq_replay_set', default='../high_quality_replays/Terran_vs_Terran.json',
                    help='File storing replays list')
flags.DEFINE_string(name='parsed_replays', default='../parsed_replays',
                    help='Path for parsed actions')
flags.DEFINE_string(name='infos_path', default='../replays_infos',
                    help='Paths for infos of replays')
flags.DEFINE_integer(name='step_mul', default=8,
                     help='step size')
flags.DEFINE_integer(name='skip', default=96,
                     help='# of skipped frames')

def sample_action_from_player(action_path):
    feat = features.Features(screen_size_px=(1, 1), minimap_size_px=(1, 1))
    with open(action_path) as f:
        actions = json.load(f)

    frame_id = 0
    result_frames = []
    for action_strs in actions:
        action_name = None
        for action_str in action_strs:
            action = Parse(action_str, sc_pb.Action())
            try:
                func_id = feat.reverse_action(action).function
                func_name = FUNCTIONS[func_id].name
                if func_name.split('_')[0] in {'Build', 'Train', 'Research', 'Morph', 'Cancel', 'Halt', 'Stop'}:
                    action_name = func_name
                    break
            except:
                pass
        if frame_id > 0 and (action_name is not None or frame_id%FLAGS.skip == 0):
            result_frames.append(frame_id-FLAGS.step_mul)

        frame_id += FLAGS.step_mul

    return result_frames

def sample_action(replay_path, action_path, sampled_path):
    replay_info = os.path.join(FLAGS.infos_path, replay_path)
    if not os.path.isfile(replay_info):
        return
    with open(replay_info) as f:
        info = json.load(f)

    result = []
    proto = Parse(info['info'], sc_pb.ResponseReplayInfo())
    for p in proto.player_info:
        player_id = p.player_info.player_id
        race = sc_pb.Race.Name(p.player_info.race_actual)

        action_file = os.path.join(action_path, race, '{}@{}'.format(player_id, replay_path))
        if not os.path.isfile(action_file):
            return

        result.append(sample_action_from_player(action_file))

    assert len(result) == 2
    sampled_actions = sorted(set(result[0]) | set(result[1]))

    with open(os.path.join(sampled_path, replay_path), 'w') as f:
        json.dump(sampled_actions, f)

def main():
    with open(FLAGS.hq_replay_set) as f:
        replay_list = json.load(f)
    replay_list = sorted([p for p, _ in replay_list])

    race_vs_race = os.path.basename(FLAGS.hq_replay_set).split('.')[0]
    sampled_path = os.path.join(FLAGS.parsed_replays, 'SampledActions', race_vs_race)
    if not os.path.isdir(sampled_path):
        os.makedirs(sampled_path)
    action_path = os.path.join(FLAGS.parsed_replays, 'Actions', race_vs_race)

    pbar = tqdm(total=len(replay_list), desc='#Replay')
    for replay_path in replay_list:
        sample_action(os.path.basename(replay_path), action_path, sampled_path)
        pbar.update()

if __name__ == '__main__':
    main()


================================================
FILE: preprocess/parse_replay_info.py
================================================
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os
import sys
import time
import json
import signal
import threading
import queue as Queue
from absl import flags
import multiprocessing
from itertools import chain
from future.builtins import range
from google.protobuf.json_format import MessageToJson

from pysc2 import run_configs

FLAGS = flags.FLAGS
flags.DEFINE_string(name='replays_paths', default='./;',
                    help='Paths for replays, split by ;')
flags.DEFINE_string(name='save_path', default='../replays_infos',
                    help='Path for saving results')

flags.DEFINE_integer(name='n_instance', default=4,
                     help='# of processes to run')
flags.DEFINE_integer(name='batch_size', default=300,
                     help='# of replays to process in one iter')

class ReplayProcessor(multiprocessing.Process):
    """A Process that pulls replays and processes them."""
    def __init__(self, run_config, replay_queue, counter, total_num):
        super(ReplayProcessor, self).__init__()
        self.run_config = run_config
        self.replay_queue = replay_queue
        self.counter = counter
        self.total_num = total_num

    def run(self):
        signal.signal(signal.SIGTERM, lambda a, b: sys.exit())  # Exit quietly.
        while True:
            with self.run_config.start() as controller:
                for _ in range(FLAGS.batch_size):
                    try:
                        replay_path = self.replay_queue.get()
                    except Queue.Empty:
                        return
                    try:
                        replay_data = self.run_config.replay_data(replay_path)
                        info = controller.replay_info(replay_data)

                        info_json = MessageToJson(info)
                        with open(os.path.join(FLAGS.save_path, os.path.basename(replay_path)), 'w') as f:
                            json.dump({'info': info_json, 'path':replay_path}, f)
                        with self.counter.get_lock():
                            self.counter.value += 1
                            print('Processing {}/{} ...'.format(self.counter.value, self.total_num))
                    finally:
                        self.replay_queue.task_done()

def replay_queue_filler(replay_queue, replay_list):
    """A thread that fills the replay_queue with replay paths."""
    for replay_path in replay_list:
        replay_queue.put(replay_path)

def main():
    if not os.path.isdir(FLAGS.save_path):
        os.makedirs(FLAGS.save_path)

    run_config = run_configs.get()

    try:
        replay_list = sorted(chain(*[run_config.replay_paths(path)
                                        for path in FLAGS.replays_paths.split(';')
                                            if len(path.strip()) > 0]))
        replay_queue = multiprocessing.JoinableQueue(FLAGS.n_instance * 10)
        replay_queue_thread = threading.Thread(target=replay_queue_filler,
                                               args=(replay_queue, replay_list))
        replay_queue_thread.daemon = True
        replay_queue_thread.start()

        counter = multiprocessing.Value('i', 0)
        for i in range(FLAGS.n_instance):
            p = ReplayProcessor(run_config, replay_queue, counter, len(replay_list))
            p.daemon = True
            p.start()
            time.sleep(1)   # Stagger startups, otherwise they seem to conflict somehow

        replay_queue.join() # Wait for the queue to empty.
    except KeyboardInterrupt:
        print("Caught KeyboardInterrupt, exiting.")

if __name__ == '__main__':
    main()


================================================
FILE: preprocess/preprocess.py
================================================
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os
import json
import glob
from absl import flags
from tqdm import tqdm

from google.protobuf.json_format import Parse

from pysc2 import run_configs
from s2clientprotocol import sc2api_pb2 as sc_pb

FLAGS = flags.FLAGS
flags.DEFINE_string(name='infos_path', default='../replays_infos',
                    help='Paths for infos of replays')
flags.DEFINE_string(name='save_path', default='../high_quality_replays',
                    help='Path for saving results')

flags.DEFINE_integer(name='min_duration', default=10000,
                     help='Min duration')
flags.DEFINE_integer(name='max_duration', default=None,
                     help='Max duration')
flags.DEFINE_integer(name='min_apm', default=10,
                     help='Min APM')
flags.DEFINE_integer(name='min_mmr', default=1000,
                     help='Min MMR')

def valid_replay(info, ping):
    """Make sure the replay isn't corrupt, and is worth looking at."""
    if info.HasField("error"):
        return False
    if info.base_build != ping.base_build:
        return False
    if info.game_duration_loops < FLAGS.min_duration:
        return False
    if FLAGS.max_duration is not None and info.game_duration_loops > FLAGS.max_duration:
        return  False
    if len(info.player_info) != 2:
        return False

    for p in info.player_info:
        if p.player_apm < FLAGS.min_apm or p.player_mmr < FLAGS.min_mmr:
            # Low APM = player just standing around.
            # Low MMR = corrupt replay or player who is weak.
            return False
        if p.player_result.result not in {1, 2}:
            return False

    return True

def main():
    if not os.path.isdir(FLAGS.save_path):
        os.makedirs(FLAGS.save_path)
    replay_infos = glob.glob(os.path.join(FLAGS.infos_path, '*.SC2Replay'))

    run_config = run_configs.get()
    with run_config.start() as controller:
        ping = controller.ping()

    result = {}
    pbar = tqdm(total=len(replay_infos), desc='#Replay')
    for info_path in replay_infos:
        with open(info_path) as f:
            info = json.load(f)

        proto = Parse(info['info'], sc_pb.ResponseReplayInfo())
        if valid_replay(proto, ping):
            players_info = proto.player_info
            races = '_vs_'.join(sorted(sc_pb.Race.Name(player_info.player_info.race_actual)
                                       for player_info in players_info))
            if races not in result:
                result[races] = []
            result[races].append((info['path'], info_path))
        pbar.update()

    for k, v in result.items():
        with open(os.path.join(FLAGS.save_path, k+'.json'), 'w') as f:
            json.dump(v, f)

if __name__ == '__main__':
    main()


================================================
FILE: preprocess/preprocess.sh
================================================
python parse_replay_info.py --n_instance 32 && python preprocess.py


================================================
FILE: requirements.txt
================================================
future == 0.16.0

numpy == 1.13.0
scipy == 0.19.0

python_gflags == 3.1.1

tqdm == 4.14.0

protobuf == 3.4.0
pystream_protobuf == 1.4.4

PySC2 == 1.0
s2clientprotocol == 1.1

================================================
FILE: train_val_test/Protoss_vs_Protoss/test.json
================================================
[{"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@567732f6b9495759f6ee5cfd9748093a68f701b3e520a73929816f66a84266f2.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@567732f6b9495759f6ee5cfd9748093a68f701b3e520a73929816f66a84266f2.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@567732f6b9495759f6ee5cfd9748093a68f701b3e520a73929816f66a84266f2.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@567732f6b9495759f6ee5cfd9748093a68f701b3e520a73929816f66a84266f2.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@567732f6b9495759f6ee5cfd9748093a68f701b3e520a73929816f66a84266f2.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@567732f6b9495759f6ee5cfd9748093a68f701b3e520a73929816f66a84266f2.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@a6ad08a95f78fb7bde13e0961d98053584ce6cd6e1d5d21687c7198a225fc416.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@a6ad08a95f78fb7bde13e0961d98053584ce6cd6e1d5d21687c7198a225fc416.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@a6ad08a95f78fb7bde13e0961d98053584ce6cd6e1d5d21687c7198a225fc416.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@a6ad08a95f78fb7bde13e0961d98053584ce6cd6e1d5d21687c7198a225fc416.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@a6ad08a95f78fb7bde13e0961d98053584ce6cd6e1d5d21687c7198a225fc416.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@a6ad08a95f78fb7bde13e0961d98053584ce6cd6e1d5d21687c7198a225fc416.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@4b9484f2ea9718ed81b5ea12a489875c3110c87d4ed41c1fe72d34493e8a89b7.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@4b9484f2ea9718ed81b5ea12a489875c3110c87d4ed41c1fe72d34493e8a89b7.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@4b9484f2ea9718ed81b5ea12a489875c3110c87d4ed41c1fe72d34493e8a89b7.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@4b9484f2ea9718ed81b5ea12a489875c3110c87d4ed41c1fe72d34493e8a89b7.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@4b9484f2ea9718ed81b5ea12a489875c3110c87d4ed41c1fe72d34493e8a89b7.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@4b9484f2ea9718ed81b5ea12a489875c3110c87d4ed41c1fe72d34493e8a89b7.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@39e0cbf0ed3cfb77d08ab30e27ee7447ecc54baa85e8db4c1aaf35f6f1e19284.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@39e0cbf0ed3cfb77d08ab30e27ee7447ecc54baa85e8db4c1aaf35f6f1e19284.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@39e0cbf0ed3cfb77d08ab30e27ee7447ecc54baa85e8db4c1aaf35f6f1e19284.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@39e0cbf0ed3cfb77d08ab30e27ee7447ecc54baa85e8db4c1aaf35f6f1e19284.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@39e0cbf0ed3cfb77d08ab30e27ee7447ecc54baa85e8db4c1aaf35f6f1e19284.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@39e0cbf0ed3cfb77d08ab30e27ee7447ecc54baa85e8db4c1aaf35f6f1e19284.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@e267850865a92fc396648572fec520e74262f5db7bbec9aeee85912b9cb3f77a.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@e267850865a92fc396648572fec520e74262f5db7bbec9aeee85912b9cb3f77a.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@e267850865a92fc396648572fec520e74262f5db7bbec9aeee85912b9cb3f77a.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@e267850865a92fc396648572fec520e74262f5db7bbec9aeee85912b9cb3f77a.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@e267850865a92fc396648572fec520e74262f5db7bbec9aeee85912b9cb3f77a.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@e267850865a92fc396648572fec520e74262f5db7bbec9aeee85912b9cb3f77a.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@a20a6a8a4b48d54dbf2984b699ddad951735e14b310d3b4c6a101ecc19f43b55.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@a20a6a8a4b48d54dbf2984b699ddad951735e14b310d3b4c6a101ecc19f43b55.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@a20a6a8a4b48d54dbf2984b699ddad951735e14b310d3b4c6a101ecc19f43b55.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@a20a6a8a4b48d54dbf2984b699ddad951735e14b310d3b4c6a101ecc19f43b55.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@a20a6a8a4b48d54dbf2984b699ddad951735e14b310d3b4c6a101ecc19f43b55.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@a20a6a8a4b48d54dbf2984b699ddad951735e14b310d3b4c6a101ecc19f43b55.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@e3c9d371660a1b0b185d242efe2919028320ee0595a144dbc5e54d18da870b5b.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@e3c9d371660a1b0b185d242efe2919028320ee0595a144dbc5e54d18da870b5b.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@e3c9d371660a1b0b185d242efe2919028320ee0595a144dbc5e54d18da870b5b.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@e3c9d371660a1b0b185d242efe2919028320ee0595a144dbc5e54d18da870b5b.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@e3c9d371660a1b0b185d242efe2919028320ee0595a144dbc5e54d18da870b5b.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@e3c9d371660a1b0b185d242efe2919028320ee0595a144dbc5e54d18da870b5b.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@36cabd73cec8a2c5f6bb9f315af15a3ac3f14afd9e5b31a09fe8c008c30e241f.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@36cabd73cec8a2c5f6bb9f315af15a3ac3f14afd9e5b31a09fe8c008c30e241f.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@36cabd73cec8a2c5f6bb9f315af15a3ac3f14afd9e5b31a09fe8c008c30e241f.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@36cabd73cec8a2c5f6bb9f315af15a3ac3f14afd9e5b31a09fe8c008c30e241f.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@36cabd73cec8a2c5f6bb9f315af15a3ac3f14afd9e5b31a09fe8c008c30e241f.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@36cabd73cec8a2c5f6bb9f315af15a3ac3f14afd9e5b31a09fe8c008c30e241f.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@78684173704199429a203f8f3621c1770fc7e03163a21c7508c30f5fcc68f4e2.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@78684173704199429a203f8f3621c1770fc7e03163a21c7508c30f5fcc68f4e2.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@78684173704199429a203f8f3621c1770fc7e03163a21c7508c30f5fcc68f4e2.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@78684173704199429a203f8f3621c1770fc7e03163a21c7508c30f5fcc68f4e2.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@78684173704199429a203f8f3621c1770fc7e03163a21c7508c30f5fcc68f4e2.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@78684173704199429a203f8f3621c1770fc7e03163a21c7508c30f5fcc68f4e2.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@9e619d4e65697896444e6fe3f7603e4e166b13213309938c6eaebbb2a317a70d.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@9e619d4e65697896444e6fe3f7603e4e166b13213309938c6eaebbb2a317a70d.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@9e619d4e65697896444e6fe3f7603e4e166b13213309938c6eaebbb2a317a70d.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@9e619d4e65697896444e6fe3f7603e4e166b13213309938c6eaebbb2a317a70d.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@9e619d4e65697896444e6fe3f7603e4e166b13213309938c6eaebbb2a317a70d.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@9e619d4e65697896444e6fe3f7603e4e166b13213309938c6eaebbb2a317a70d.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@4e478e933d15de4761ac048c4404f4628618cb1b15ddef7dddf14f7158f9e9bf.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@4e478e933d15de4761ac048c4404f4628618cb1b15ddef7dddf14f7158f9e9bf.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@4e478e933d15de4761ac048c4404f4628618cb1b15ddef7dddf14f7158f9e9bf.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@4e478e933d15de4761ac048c4404f4628618cb1b15ddef7dddf14f7158f9e9bf.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@4e478e933d15de4761ac048c4404f4628618cb1b15ddef7dddf14f7158f9e9bf.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@4e478e933d15de4761ac048c4404f4628618cb1b15ddef7dddf14f7158f9e9bf.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@0a0176be2b2ba94ac270315996df863e559adbbb6d73e3a85f37b3aa0ede992a.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@0a0176be2b2ba94ac270315996df863e559adbbb6d73e3a85f37b3aa0ede992a.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@0a0176be2b2ba94ac270315996df863e559adbbb6d73e3a85f37b3aa0ede992a.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@0a0176be2b2ba94ac270315996df863e559adbbb6d73e3a85f37b3aa0ede992a.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@0a0176be2b2ba94ac270315996df863e559adbbb6d73e3a85f37b3aa0ede992a.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@0a0176be2b2ba94ac270315996df863e559adbbb6d73e3a85f37b3aa0ede992a.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@e7974fd487df64ffe69b57ce479cf2cd1d8adf838fa7498543d06dc06d4d48db.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@e7974fd487df64ffe69b57ce479cf2cd1d8adf838fa7498543d06dc06d4d48db.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@e7974fd487df64ffe69b57ce479cf2cd1d8adf838fa7498543d06dc06d4d48db.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@e7974fd487df64ffe69b57ce479cf2cd1d8adf838fa7498543d06dc06d4d48db.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@e7974fd487df64ffe69b57ce479cf2cd1d8adf838fa7498543d06dc06d4d48db.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@e7974fd487df64ffe69b57ce479cf2cd1d8adf838fa7498543d06dc06d4d48db.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@6730766f38183a1c1af3e08397d8480451aa02d6cc5bf2e8131c5362d3f6ec59.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@6730766f38183a1c1af3e08397d8480451aa02d6cc5bf2e8131c5362d3f6ec59.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@6730766f38183a1c1af3e08397d8480451aa02d6cc5bf2e8131c5362d3f6ec59.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@6730766f38183a1c1af3e08397d8480451aa02d6cc5bf2e8131c5362d3f6ec59.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@6730766f38183a1c1af3e08397d8480451aa02d6cc5bf2e8131c5362d3f6ec59.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@6730766f38183a1c1af3e08397d8480451aa02d6cc5bf2e8131c5362d3f6ec59.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@567c415e5d911a8bc25b63732155788fc69b0e229b6c2a3a2a8f5e01a2fff4a5.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@567c415e5d911a8bc25b63732155788fc69b0e229b6c2a3a2a8f5e01a2fff4a5.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@567c415e5d911a8bc25b63732155788fc69b0e229b6c2a3a2a8f5e01a2fff4a5.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@567c415e5d911a8bc25b63732155788fc69b0e229b6c2a3a2a8f5e01a2fff4a5.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@567c415e5d911a8bc25b63732155788fc69b0e229b6c2a3a2a8f5e01a2fff4a5.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@567c415e5d911a8bc25b63732155788fc69b0e229b6c2a3a2a8f5e01a2fff4a5.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@585c1ce8e5f5e2fd3de37a702de5e213a72457888698027b97ed322117abb407.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@585c1ce8e5f5e2fd3de37a702de5e213a72457888698027b97ed322117abb407.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@585c1ce8e5f5e2fd3de37a702de5e213a72457888698027b97ed322117abb407.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@585c1ce8e5f5e2fd3de37a702de5e213a72457888698027b97ed322117abb407.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@585c1ce8e5f5e2fd3de37a702de5e213a72457888698027b97ed322117abb407.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@585c1ce8e5f5e2fd3de37a702de5e213a72457888698027b97ed322117abb407.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@5998216b4fe4e774b4b689d1ae860b9f1a4880d55cbc8af01b8a70b4e552e2b5.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@5998216b4fe4e774b4b689d1ae860b9f1a4880d55cbc8af01b8a70b4e552e2b5.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@5998216b4fe4e774b4b689d1ae860b9f1a4880d55cbc8af01b8a70b4e552e2b5.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@5998216b4fe4e774b4b689d1ae860b9f1a4880d55cbc8af01b8a70b4e552e2b5.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@5998216b4fe4e774b4b689d1ae860b9f1a4880d55cbc8af01b8a70b4e552e2b5.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@5998216b4fe4e774b4b689d1ae860b9f1a4880d55cbc8af01b8a70b4e552e2b5.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@53134c897be8aa48fa1034a24ed2db88294885d580f2702996fcb4630c90ca49.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@53134c897be8aa48fa1034a24ed2db88294885d580f2702996fcb4630c90ca49.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@53134c897be8aa48fa1034a24ed2db88294885d580f2702996fcb4630c90ca49.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@53134c897be8aa48fa1034a24ed2db88294885d580f2702996fcb4630c90ca49.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@53134c897be8aa48fa1034a24ed2db88294885d580f2702996fcb4630c90ca49.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@53134c897be8aa48fa1034a24ed2db88294885d580f2702996fcb4630c90ca49.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@d023319f0827ceb2d3639bc43de9a8cac9b2fef21dd302a360b55723ed9c3fe1.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@d023319f0827ceb2d3639bc43de9a8cac9b2fef21dd302a360b55723ed9c3fe1.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@d023319f0827ceb2d3639bc43de9a8cac9b2fef21dd302a360b55723ed9c3fe1.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@d023319f0827ceb2d3639bc43de9a8cac9b2fef21dd302a360b55723ed9c3fe1.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@d023319f0827ceb2d3639bc43de9a8cac9b2fef21dd302a360b55723ed9c3fe1.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@d023319f0827ceb2d3639bc43de9a8cac9b2fef21dd302a360b55723ed9c3fe1.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@617bf452951e5edc1ee21820823233567e284ae01e037f4ba844044b0b6a4eb4.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@617bf452951e5edc1ee21820823233567e284ae01e037f4ba844044b0b6a4eb4.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@617bf452951e5edc1ee21820823233567e284ae01e037f4ba844044b0b6a4eb4.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@617bf452951e5edc1ee21820823233567e284ae01e037f4ba844044b0b6a4eb4.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@617bf452951e5edc1ee21820823233567e284ae01e037f4ba844044b0b6a4eb4.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@617bf452951e5edc1ee21820823233567e284ae01e037f4ba844044b0b6a4eb4.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@ba7ae6a5fc6dfe2b1b5c350f5a8b5bd4515325b332558ed3c85eaeb807cba2c0.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@ba7ae6a5fc6dfe2b1b5c350f5a8b5bd4515325b332558ed3c85eaeb807cba2c0.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@ba7ae6a5fc6dfe2b1b5c350f5a8b5bd4515325b332558ed3c85eaeb807cba2c0.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@ba7ae6a5fc6dfe2b1b5c350f5a8b5bd4515325b332558ed3c85eaeb807cba2c0.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@ba7ae6a5fc6dfe2b1b5c350f5a8b5bd4515325b332558ed3c85eaeb807cba2c0.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@ba7ae6a5fc6dfe2b1b5c350f5a8b5bd4515325b332558ed3c85eaeb807cba2c0.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@163b356ad5f46d1b385d8846ebac2c53ad1f9a0c63d759389632caff28413c40.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@163b356ad5f46d1b385d8846ebac2c53ad1f9a0c63d759389632caff28413c40.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@163b356ad5f46d1b385d8846ebac2c53ad1f9a0c63d759389632caff28413c40.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@163b356ad5f46d1b385d8846ebac2c53ad1f9a0c63d759389632caff28413c40.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@163b356ad5f46d1b385d8846ebac2c53ad1f9a0c63d759389632caff28413c40.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@163b356ad5f46d1b385d8846ebac2c53ad1f9a0c63d759389632caff28413c40.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@0041f867a19b27400acbfb497df38a7ebfb4eec679b3fd30f370fc24c087b096.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@0041f867a19b27400acbfb497df38a7ebfb4eec679b3fd30f370fc24c087b096.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@0041f867a19b27400acbfb497df38a7ebfb4eec679b3fd30f370fc24c087b096.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@0041f867a19b27400acbfb497df38a7ebfb4eec679b3fd30f370fc24c087b096.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@0041f867a19b27400acbfb497df38a7ebfb4eec679b3fd30f370fc24c087b096.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@0041f867a19b27400acbfb497df38a7ebfb4eec679b3fd30f370fc24c087b096.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@bd8bafa752fee83f91562256a927c2964a9257ee5888410abc610e31bb3cfa60.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@bd8bafa752fee83f91562256a927c2964a9257ee5888410abc610e31bb3cfa60.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@bd8bafa752fee83f91562256a927c2964a9257ee5888410abc610e31bb3cfa60.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@bd8bafa752fee83f91562256a927c2964a9257ee5888410abc610e31bb3cfa60.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@bd8bafa752fee83f91562256a927c2964a9257ee5888410abc610e31bb3cfa60.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@bd8bafa752fee83f91562256a927c2964a9257ee5888410abc610e31bb3cfa60.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@663ca1a62053c2bd0e735e85324f1e1363737b3b7b479f00400e1d27d784cf3a.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@663ca1a62053c2bd0e735e85324f1e1363737b3b7b479f00400e1d27d784cf3a.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@663ca1a62053c2bd0e735e85324f1e1363737b3b7b479f00400e1d27d784cf3a.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@663ca1a62053c2bd0e735e85324f1e1363737b3b7b479f00400e1d27d784cf3a.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@663ca1a62053c2bd0e735e85324f1e1363737b3b7b479f00400e1d27d784cf3a.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@663ca1a62053c2bd0e735e85324f1e1363737b3b7b479f00400e1d27d784cf3a.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@9446678a1eefeeba8029739b3b96c1ad5b6c126e2c2129718ba5d1eda383af29.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@9446678a1eefeeba8029739b3b96c1ad5b6c126e2c2129718ba5d1eda383af29.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@9446678a1eefeeba8029739b3b96c1ad5b6c126e2c2129718ba5d1eda383af29.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@9446678a1eefeeba8029739b3b96c1ad5b6c126e2c2129718ba5d1eda383af29.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@9446678a1eefeeba8029739b3b96c1ad5b6c126e2c2129718ba5d1eda383af29.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@9446678a1eefeeba8029739b3b96c1ad5b6c126e2c2129718ba5d1eda383af29.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@f72d2891fa16c832e4ea3acef7b65005413623180fe9f6ffc147db9c1c7f207e.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@f72d2891fa16c832e4ea3acef7b65005413623180fe9f6ffc147db9c1c7f207e.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@f72d2891fa16c832e4ea3acef7b65005413623180fe9f6ffc147db9c1c7f207e.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@f72d2891fa16c832e4ea3acef7b65005413623180fe9f6ffc147db9c1c7f207e.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@f72d2891fa16c832e4ea3acef7b65005413623180fe9f6ffc147db9c1c7f207e.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@f72d2891fa16c832e4ea3acef7b65005413623180fe9f6ffc147db9c1c7f207e.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@6beb9ba1765c9ace20b89d9e04f7e9b6d1d1f1324d5d0f2d55841d92a51aa949.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@6beb9ba1765c9ace20b89d9e04f7e9b6d1d1f1324d5d0f2d55841d92a51aa949.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@6beb9ba1765c9ace20b89d9e04f7e9b6d1d1f1324d5d0f2d55841d92a51aa949.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@6beb9ba1765c9ace20b89d9e04f7e9b6d1d1f1324d5d0f2d55841d92a51aa949.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@6beb9ba1765c9ace20b89d9e04f7e9b6d1d1f1324d5d0f2d55841d92a51aa949.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@6beb9ba1765c9ace20b89d9e04f7e9b6d1d1f1324d5d0f2d55841d92a51aa949.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@0a8b054952835571238c63a5fd5240a550512f2d012711b299d4d14f7a08fa7b.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@0a8b054952835571238c63a5fd5240a550512f2d012711b299d4d14f7a08fa7b.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@0a8b054952835571238c63a5fd5240a550512f2d012711b299d4d14f7a08fa7b.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@0a8b054952835571238c63a5fd5240a550512f2d012711b299d4d14f7a08fa7b.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@0a8b054952835571238c63a5fd5240a550512f2d012711b299d4d14f7a08fa7b.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@0a8b054952835571238c63a5fd5240a550512f2d012711b299d4d14f7a08fa7b.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@1b2176f8452f01212ecc658d9c6a5ccd378294236ba8afa2dab078fde2118207.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@1b2176f8452f01212ecc658d9c6a5ccd378294236ba8afa2dab078fde2118207.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@1b2176f8452f01212ecc658d9c6a5ccd378294236ba8afa2dab078fde2118207.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@1b2176f8452f01212ecc658d9c6a5ccd378294236ba8afa2dab078fde2118207.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@1b2176f8452f01212ecc658d9c6a5ccd378294236ba8afa2dab078fde2118207.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@1b2176f8452f01212ecc658d9c6a5ccd378294236ba8afa2dab078fde2118207.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@f0854dc605a89bf8c78a86f91cfa3189118d2740d87c6f3d538bae6125d9e334.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@f0854dc605a89bf8c78a86f91cfa3189118d2740d87c6f3d538bae6125d9e334.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@f0854dc605a89bf8c78a86f91cfa3189118d2740d87c6f3d538bae6125d9e334.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@f0854dc605a89bf8c78a86f91cfa3189118d2740d87c6f3d538bae6125d9e334.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@f0854dc605a89bf8c78a86f91cfa3189118d2740d87c6f3d538bae6125d9e334.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@f0854dc605a89bf8c78a86f91cfa3189118d2740d87c6f3d538bae6125d9e334.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@51f6dc35012a80e31d32f856c8e4027e190ea5056bb21b1581f796274ea7877c.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@51f6dc35012a80e31d32f856c8e4027e190ea5056bb21b1581f796274ea7877c.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@51f6dc35012a80e31d32f856c8e4027e190ea5056bb21b1581f796274ea7877c.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@51f6dc35012a80e31d32f856c8e4027e190ea5056bb21b1581f796274ea7877c.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@51f6dc35012a80e31d32f856c8e4027e190ea5056bb21b1581f796274ea7877c.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@51f6dc35012a80e31d32f856c8e4027e190ea5056bb21b1581f796274ea7877c.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@39c725f9446b35231d1773d5c85c06c321103fdc2f75843d142afccd6c833efb.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@39c725f9446b35231d1773d5c85c06c321103fdc2f75843d142afccd6c833efb.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@39c725f9446b35231d1773d5c85c06c321103fdc2f75843d142afccd6c833efb.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@39c725f9446b35231d1773d5c85c06c321103fdc2f75843d142afccd6c833efb.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@39c725f9446b35231d1773d5c85c06c321103fdc2f75843d142afccd6c833efb.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@39c725f9446b35231d1773d5c85c06c321103fdc2f75843d142afccd6c833efb.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@639211d3862f3bee00f118f567c91be4988d04a2501895674508cf5a847763d6.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@639211d3862f3bee00f118f567c91be4988d04a2501895674508cf5a847763d6.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@639211d3862f3bee00f118f567c91be4988d04a2501895674508cf5a847763d6.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@639211d3862f3bee00f118f567c91be4988d04a2501895674508cf5a847763d6.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@639211d3862f3bee00f118f567c91be4988d04a2501895674508cf5a847763d6.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@639211d3862f3bee00f118f567c91be4988d04a2501895674508cf5a847763d6.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@0486f166f90c0e183c1a70472ed20ed65d6c27995b380039123271b6a190fea7.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@0486f166f90c0e183c1a70472ed20ed65d6c27995b380039123271b6a190fea7.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@0486f166f90c0e183c1a70472ed20ed65d6c27995b380039123271b6a190fea7.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@0486f166f90c0e183c1a70472ed20ed65d6c27995b380039123271b6a190fea7.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@0486f166f90c0e183c1a70472ed20ed65d6c27995b380039123271b6a190fea7.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@0486f166f90c0e183c1a70472ed20ed65d6c27995b380039123271b6a190fea7.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@8dd7cb0e5f818bc9306464c508bd64591749f3170cd9ea2683d7b33f2ebebc90.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@8dd7cb0e5f818bc9306464c508bd64591749f3170cd9ea2683d7b33f2ebebc90.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@8dd7cb0e5f818bc9306464c508bd64591749f3170cd9ea2683d7b33f2ebebc90.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@8dd7cb0e5f818bc9306464c508bd64591749f3170cd9ea2683d7b33f2ebebc90.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@8dd7cb0e5f818bc9306464c508bd64591749f3170cd9ea2683d7b33f2ebebc90.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@8dd7cb0e5f818bc9306464c508bd64591749f3170cd9ea2683d7b33f2ebebc90.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@cc80a68190369e8a3cec775db2c4a26b969ea0c8b6cc59a623e81317dd7864f3.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@cc80a68190369e8a3cec775db2c4a26b969ea0c8b6cc59a623e81317dd7864f3.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@cc80a68190369e8a3cec775db2c4a26b969ea0c8b6cc59a623e81317dd7864f3.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@cc80a68190369e8a3cec775db2c4a26b969ea0c8b6cc59a623e81317dd7864f3.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@cc80a68190369e8a3cec775db2c4a26b969ea0c8b6cc59a623e81317dd7864f3.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@cc80a68190369e8a3cec775db2c4a26b969ea0c8b6cc59a623e81317dd7864f3.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@5dbb806000acedd52b221c0f903401b009164979c67a60cf1efc19b012faa3fb.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@5dbb806000acedd52b221c0f903401b009164979c67a60cf1efc19b012faa3fb.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@5dbb806000acedd52b221c0f903401b009164979c67a60cf1efc19b012faa3fb.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@5dbb806000acedd52b221c0f903401b009164979c67a60cf1efc19b012faa3fb.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@5dbb806000acedd52b221c0f903401b009164979c67a60cf1efc19b012faa3fb.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@5dbb806000acedd52b221c0f903401b009164979c67a60cf1efc19b012faa3fb.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@ae53c198902220fd4c30c8c4148bae8bb79a11390510a56c75e403e86c68fc66.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@ae53c198902220fd4c30c8c4148bae8bb79a11390510a56c75e403e86c68fc66.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@ae53c198902220fd4c30c8c4148bae8bb79a11390510a56c75e403e86c68fc66.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@ae53c198902220fd4c30c8c4148bae8bb79a11390510a56c75e403e86c68fc66.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@ae53c198902220fd4c30c8c4148bae8bb79a11390510a56c75e403e86c68fc66.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@ae53c198902220fd4c30c8c4148bae8bb79a11390510a56c75e403e86c68fc66.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@707f596df30532c8229d2a575cb2adac6388400cc1d5578e56f2093ec3f0efd1.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@707f596df30532c8229d2a575cb2adac6388400cc1d5578e56f2093ec3f0efd1.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@707f596df30532c8229d2a575cb2adac6388400cc1d5578e56f2093ec3f0efd1.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@707f596df30532c8229d2a575cb2adac6388400cc1d5578e56f2093ec3f0efd1.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@707f596df30532c8229d2a575cb2adac6388400cc1d5578e56f2093ec3f0efd1.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@707f596df30532c8229d2a575cb2adac6388400cc1d5578e56f2093ec3f0efd1.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@c77c3a5f3ae2a308b0bd0c8dc4839196db2d42268a52a98239e45fc78304b543.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@c77c3a5f3ae2a308b0bd0c8dc4839196db2d42268a52a98239e45fc78304b543.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@c77c3a5f3ae2a308b0bd0c8dc4839196db2d42268a52a98239e45fc78304b543.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@c77c3a5f3ae2a308b0bd0c8dc4839196db2d42268a52a98239e45fc78304b543.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@c77c3a5f3ae2a308b0bd0c8dc4839196db2d42268a52a98239e45fc78304b543.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@c77c3a5f3ae2a308b0bd0c8dc4839196db2d42268a52a98239e45fc78304b543.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@735ae9a1a09129f5adad85366b94af77eb4cdc1f68b13c56c9b3d7287d0ad080.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@735ae9a1a09129f5adad85366b94af77eb4cdc1f68b13c56c9b3d7287d0ad080.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@735ae9a1a09129f5adad85366b94af77eb4cdc1f68b13c56c9b3d7287d0ad080.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@735ae9a1a09129f5adad85366b94af77eb4cdc1f68b13c56c9b3d7287d0ad080.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@735ae9a1a09129f5adad85366b94af77eb4cdc1f68b13c56c9b3d7287d0ad080.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@735ae9a1a09129f5adad85366b94af77eb4cdc1f68b13c56c9b3d7287d0ad080.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@f378009df863516748ebba4a0cb3dfadd566c350d7ec9c8de088db5835844ac6.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@f378009df863516748ebba4a0cb3dfadd566c350d7ec9c8de088db5835844ac6.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@f378009df863516748ebba4a0cb3dfadd566c350d7ec9c8de088db5835844ac6.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@f378009df863516748ebba4a0cb3dfadd566c350d7ec9c8de088db5835844ac6.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@f378009df863516748ebba4a0cb3dfadd566c350d7ec9c8de088db5835844ac6.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@f378009df863516748ebba4a0cb3dfadd566c350d7ec9c8de088db5835844ac6.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@c63e89a56d2c90f4f8c2dcecc0f011cebc14889cac9f6026db48d721b1ecf26b.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@c63e89a56d2c90f4f8c2dcecc0f011cebc14889cac9f6026db48d721b1ecf26b.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@c63e89a56d2c90f4f8c2dcecc0f011cebc14889cac9f6026db48d721b1ecf26b.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@c63e89a56d2c90f4f8c2dcecc0f011cebc14889cac9f6026db48d721b1ecf26b.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@c63e89a56d2c90f4f8c2dcecc0f011cebc14889cac9f6026db48d721b1ecf26b.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@c63e89a56d2c90f4f8c2dcecc0f011cebc14889cac9f6026db48d721b1ecf26b.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@468ff93d8f45509f370806c4a194f3a9253b9f590c8e923eba9c28c975728101.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@468ff93d8f45509f370806c4a194f3a9253b9f590c8e923eba9c28c975728101.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@468ff93d8f45509f370806c4a194f3a9253b9f590c8e923eba9c28c975728101.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@468ff93d8f45509f370806c4a194f3a9253b9f590c8e923eba9c28c975728101.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@468ff93d8f45509f370806c4a194f3a9253b9f590c8e923eba9c28c975728101.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@468ff93d8f45509f370806c4a194f3a9253b9f590c8e923eba9c28c975728101.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@0d3ec14a0587209c5220878d33802b09cbfae0495fbc277ea0da8413f91dc5f4.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@0d3ec14a0587209c5220878d33802b09cbfae0495fbc277ea0da8413f91dc5f4.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@0d3ec14a0587209c5220878d33802b09cbfae0495fbc277ea0da8413f91dc5f4.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@0d3ec14a0587209c5220878d33802b09cbfae0495fbc277ea0da8413f91dc5f4.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@0d3ec14a0587209c5220878d33802b09cbfae0495fbc277ea0da8413f91dc5f4.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@0d3ec14a0587209c5220878d33802b09cbfae0495fbc277ea0da8413f91dc5f4.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@e6b56428af296bd9e790b033b678bb613af82ca046ca4940119b06bcfca019b0.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@e6b56428af296bd9e790b033b678bb613af82ca046ca4940119b06bcfca019b0.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@e6b56428af296bd9e790b033b678bb613af82ca046ca4940119b06bcfca019b0.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@e6b56428af296bd9e790b033b678bb613af82ca046ca4940119b06bcfca019b0.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@e6b56428af296bd9e790b033b678bb613af82ca046ca4940119b06bcfca019b0.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@e6b56428af296bd9e790b033b678bb613af82ca046ca4940119b06bcfca019b0.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@c9bf10c26adf1fc59a69c91102971676312a42af5b94f6e595d156ba65e147fd.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@c9bf10c26adf1fc59a69c91102971676312a42af5b94f6e595d156ba65e147fd.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@c9bf10c26adf1fc59a69c91102971676312a42af5b94f6e595d156ba65e147fd.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@c9bf10c26adf1fc59a69c91102971676312a42af5b94f6e595d156ba65e147fd.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@c9bf10c26adf1fc59a69c91102971676312a42af5b94f6e595d156ba65e147fd.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@c9bf10c26adf1fc59a69c91102971676312a42af5b94f6e595d156ba65e147fd.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@95023137d2bc9c6f52704d9fe0bed2fa3fa11a54b42b5dc304809be2222ade4d.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@95023137d2bc9c6f52704d9fe0bed2fa3fa11a54b42b5dc304809be2222ade4d.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@95023137d2bc9c6f52704d9fe0bed2fa3fa11a54b42b5dc304809be2222ade4d.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@95023137d2bc9c6f52704d9fe0bed2fa3fa11a54b42b5dc304809be2222ade4d.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@95023137d2bc9c6f52704d9fe0bed2fa3fa11a54b42b5dc304809be2222ade4d.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@95023137d2bc9c6f52704d9fe0bed2fa3fa11a54b42b5dc304809be2222ade4d.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@19d76f39a499376e417e3cf6326d2b088acdc4f2de69b73ffb26eebb260d757a.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@19d76f39a499376e417e3cf6326d2b088acdc4f2de69b73ffb26eebb260d757a.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@19d76f39a499376e417e3cf6326d2b088acdc4f2de69b73ffb26eebb260d757a.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@19d76f39a499376e417e3cf6326d2b088acdc4f2de69b73ffb26eebb260d757a.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@19d76f39a499376e417e3cf6326d2b088acdc4f2de69b73ffb26eebb260d757a.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@19d76f39a499376e417e3cf6326d2b088acdc4f2de69b73ffb26eebb260d757a.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@425855f521d60158567aa37fbf50e7ebd0ea919735a75ed06c6fb1ef502deb2c.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@425855f521d60158567aa37fbf50e7ebd0ea919735a75ed06c6fb1ef502deb2c.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@425855f521d60158567aa37fbf50e7ebd0ea919735a75ed06c6fb1ef502deb2c.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@425855f521d60158567aa37fbf50e7ebd0ea919735a75ed06c6fb1ef502deb2c.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@425855f521d60158567aa37fbf50e7ebd0ea919735a75ed06c6fb1ef502deb2c.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@425855f521d60158567aa37fbf50e7ebd0ea919735a75ed06c6fb1ef502deb2c.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@65f68ef71b12dfeb6b2a6a7f30273a009e3614c43dfc844d1c58e3e706ab038b.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@65f68ef71b12dfeb6b2a6a7f30273a009e3614c43dfc844d1c58e3e706ab038b.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@65f68ef71b12dfeb6b2a6a7f30273a009e3614c43dfc844d1c58e3e706ab038b.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@65f68ef71b12dfeb6b2a6a7f30273a009e3614c43dfc844d1c58e3e706ab038b.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@65f68ef71b12dfeb6b2a6a7f30273a009e3614c43dfc844d1c58e3e706ab038b.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@65f68ef71b12dfeb6b2a6a7f30273a009e3614c43dfc844d1c58e3e706ab038b.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@04f77f96981061ad5bb4d502af88a0bd483c7abac7c44b8dadfa82738b0c4c18.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@04f77f96981061ad5bb4d502af88a0bd483c7abac7c44b8dadfa82738b0c4c18.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@04f77f96981061ad5bb4d502af88a0bd483c7abac7c44b8dadfa82738b0c4c18.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@04f77f96981061ad5bb4d502af88a0bd483c7abac7c44b8dadfa82738b0c4c18.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@04f77f96981061ad5bb4d502af88a0bd483c7abac7c44b8dadfa82738b0c4c18.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@04f77f96981061ad5bb4d502af88a0bd483c7abac7c44b8dadfa82738b0c4c18.SC2Replay@G.npz"}]}, {"Protoss": [{"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@ce918d13433295738ded52d3cc6ac35beb91ceeef38593ad12dbad5cb139bc8a.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@ce918d13433295738ded52d3cc6ac35beb91ceeef38593ad12dbad5cb139bc8a.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/1@ce918d13433295738ded52d3cc6ac35beb91ceeef38593ad12dbad5cb139bc8a.SC2Replay@G.npz"}, {"global_path": "parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/2@ce918d13433295738ded52d3cc6ac35beb91ceeef38593ad12dbad5cb139bc8a.SC2Replay.npz", "spatial_path_S": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@ce918d13433295738ded52d3cc6ac35beb91ceeef38593ad12dbad5cb139bc8a.SC2Replay@S.npz", "spatial_path_G": "parsed_replays/SpatialFeatureTensor/Protoss_vs_Protoss/Protoss/2@ce918d13433295
Download .txt
gitextract_wn7uutsx/

├── .gitignore
├── Baselines/
│   ├── BuildOrderPrediction/
│   │   ├── test.py
│   │   ├── train.py
│   │   └── train_spatial.py
│   └── GlobalStateEvaluation/
│       ├── requirements.txt
│       ├── test.py
│       ├── train.py
│       └── train_spatial.py
├── README.md
├── _config.yml
├── data_loader/
│   └── BatchEnv.py
├── extract_features/
│   ├── SpatialFeatures.py
│   ├── compute_stat.sh
│   ├── extract_features.sh
│   ├── game_state.py
│   ├── global_feature_vector.py
│   ├── replay_stat.py
│   ├── spatial_feature_tensor.py
│   └── split.py
├── instructions/
│   ├── EasyWay.md
│   └── HardWay.md
├── parse_replay/
│   ├── extract_actions.py
│   ├── parse_replay.py
│   ├── parse_replay.sh
│   ├── replay2global_features.py
│   └── sample_actions.py
├── preprocess/
│   ├── parse_replay_info.py
│   ├── preprocess.py
│   └── preprocess.sh
├── requirements.txt
└── train_val_test/
    ├── Protoss_vs_Protoss/
    │   ├── test.json
    │   ├── train.json
    │   └── val.json
    ├── Protoss_vs_Terran/
    │   ├── test.json
    │   ├── train.json
    │   └── val.json
    ├── Protoss_vs_Zerg/
    │   ├── test.json
    │   ├── train.json
    │   └── val.json
    ├── Terran_vs_Terran/
    │   ├── test.json
    │   ├── train.json
    │   └── val.json
    ├── Terran_vs_Zerg/
    │   ├── test.json
    │   ├── train.json
    │   └── val.json
    └── Zerg_vs_Zerg/
        ├── test.json
        ├── train.json
        └── val.json
Download .txt
SYMBOL INDEX (118 symbols across 19 files)

FILE: Baselines/BuildOrderPrediction/test.py
  function calc_action_acc (line 10) | def calc_action_acc(action_pre, action_gt):
  function calc_weighted_action_acc (line 13) | def calc_weighted_action_acc(action_pre, action_gt, weight):
  function show_test_result (line 16) | def show_test_result(name, phrase, result, steps=10, title=''):

FILE: Baselines/BuildOrderPrediction/train.py
  class BuildOrderGRU (line 23) | class BuildOrderGRU(torch.nn.Module):
    method __init__ (line 24) | def __init__(self, num_inputs, num_outputs):
    method forward (line 36) | def forward(self, states, require_init):
    method detach (line 62) | def detach(self):
  function train (line 68) | def train(model, env, args):
  function test (line 184) | def test(model, env, args):
  function next_path (line 238) | def next_path(model_folder, paths):
  function main (line 249) | def main():

FILE: Baselines/BuildOrderPrediction/train_spatial.py
  class BuildOrderGRU (line 23) | class BuildOrderGRU(torch.nn.Module):
    method __init__ (line 24) | def __init__(self, n_channels, n_features, n_actions):
    method forward (line 39) | def forward(self, states_S, states_G, require_init):
    method detach (line 69) | def detach(self):
  function train (line 73) | def train(model, env, args):
  function test (line 191) | def test(model, env, args):
  function next_path (line 248) | def next_path(model_folder, paths):
  function main (line 259) | def main():

FILE: Baselines/GlobalStateEvaluation/test.py
  function calc_value_acc (line 10) | def calc_value_acc(value_pre, value_gt):
  function calc_weighted_value_acc (line 13) | def calc_weighted_value_acc(value_pre, value_gt, weight):
  function show_test_result (line 16) | def show_test_result(name, phrase, result, steps=10, title=''):

FILE: Baselines/GlobalStateEvaluation/train.py
  class StateEvaluationGRU (line 23) | class StateEvaluationGRU(torch.nn.Module):
    method __init__ (line 24) | def __init__(self, num_inputs):
    method forward (line 36) | def forward(self, states, require_init):
    method detach (line 62) | def detach(self):
  function train (line 68) | def train(model, env, args):
  function test (line 180) | def test(model, env, args):
  function next_path (line 235) | def next_path(model_folder, paths):
  function main (line 246) | def main():

FILE: Baselines/GlobalStateEvaluation/train_spatial.py
  class StateEvaluationGRU (line 23) | class StateEvaluationGRU(torch.nn.Module):
    method __init__ (line 24) | def __init__(self, n_channels, n_features):
    method forward (line 39) | def forward(self, states_S, states_G, require_init):
    method detach (line 69) | def detach(self):
  function train (line 73) | def train(model, env, args):
  function test (line 188) | def test(model, env, args):
  function next_path (line 246) | def next_path(model_folder, paths):
  function main (line 257) | def main():

FILE: data_loader/BatchEnv.py
  class BatchEnv (line 10) | class BatchEnv(object):
    method __init__ (line 11) | def __init__(self):
    method init (line 14) | def init(self, path, root, race, enemy_race, step_mul=8, n_replays=4, ...
    method __generate_replay_list__ (line 42) | def __generate_replay_list__(self, replays, race):
    method __init_epoch__ (line 45) | def __init_epoch__(self):
    method __reset__ (line 59) | def __reset__(self):
    method __load_replay__ (line 70) | def __load_replay__(self, path):
    method step (line 73) | def step(self, **kwargs):
    method __one_step__ (line 100) | def __one_step__(self, replay_dict, done):
    method __post_process__ (line 103) | def __post_process__(self, result, **kwargs):
    method step_count (line 106) | def step_count(self):
    method close (line 109) | def close(self):
  class BatchGlobalFeatureEnv (line 115) | class BatchGlobalFeatureEnv(BatchEnv):
    method __post_init__ (line 121) | def __post_init__(self):
    method __generate_replay_list__ (line 125) | def __generate_replay_list__(self, replays, root, race):
    method __load_replay__ (line 133) | def __load_replay__(self, path):
    method __one_step__ (line 141) | def __one_step__(self, replay_dict, done):
    method __post_process__ (line 156) | def __post_process__(self, result, reward=True, action=False, score=Fa...
  class BatchSpatialEnv (line 169) | class BatchSpatialEnv(BatchEnv):
    method __post_init__ (line 175) | def __post_init__(self):
    method __generate_replay_list__ (line 178) | def __generate_replay_list__(self, replays, root, race):
    method __load_replay__ (line 186) | def __load_replay__(self, path):
    method __one_step__ (line 195) | def __one_step__(self, replay_dict, done):
    method __post_process__ (line 213) | def __post_process__(self, result, reward=True, action=False, score=Fa...

FILE: extract_features/SpatialFeatures.py
  class ScreenFeatures (line 6) | class ScreenFeatures(collections.namedtuple("ScreenFeatures", ["height_m...
    method __new__ (line 11) | def __new__(cls, **kwargs):
  class MinimapFeatures (line 26) | class MinimapFeatures(collections.namedtuple("MinimapFeatures", [
    method __new__ (line 31) | def __new__(cls, **kwargs):
  class SpatialFeatures (line 67) | class SpatialFeatures(Features):
    method observation_spec (line 68) | def observation_spec(self):
    method transform_obs (line 87) | def transform_obs(self, obs):

FILE: extract_features/game_state.py
  function load_stat (line 6) | def load_stat(path):
  class GameState (line 26) | class GameState(object):
    method __init__ (line 42) | def __init__(self, stat_path, enemy_stat_path):
    method update (line 66) | def update(self, state):
    method get_action (line 84) | def get_action(self):
    method __set_units__ (line 87) | def __set_units__(self, units):
    method __set_action__ (line 100) | def __set_action__(self, action):
    method __units2vec__ (line 109) | def __units2vec__(self, units, stat):
    method __set_to_array__ (line 145) | def __set_to_array__(self, set_var, key2id):
    method __dict_to_array__ (line 151) | def __dict_to_array__(self, dict_var, key2id, scale=1):
    method to_vector (line 157) | def to_vector(self):
    method __str__ (line 177) | def __str__(self):

FILE: extract_features/global_feature_vector.py
  function parse_replay (line 26) | def parse_replay(replay_player_path, reward, race, enemy_race):
  function main (line 42) | def main():

FILE: extract_features/replay_stat.py
  function update (line 29) | def update(replay_path, stat):
  function post_process (line 65) | def post_process(stat):
  function main (line 80) | def main():

FILE: extract_features/spatial_feature_tensor.py
  function parse_replay (line 34) | def parse_replay(replay_player_path, sampled_action_path, reward, race, ...
  class Parser (line 81) | class Parser(object):
    method __init__ (line 82) | def __init__(self, race_vs_race, races, stats):
    method __call__ (line 87) | def __call__(self, line):
  function main (line 108) | def main():

FILE: extract_features/split.py
  function save (line 23) | def save(replays, prefix, folder):
  function main (line 28) | def main():

FILE: parse_replay/extract_actions.py
  class ReplayProcessor (line 46) | class ReplayProcessor(multiprocessing.Process):
    method __init__ (line 48) | def __init__(self, run_config, replay_queue, counter, total_num):
    method run (line 55) | def run(self):
    method process_replay (line 91) | def process_replay(self, controller, replay_data, map_data, player_id,...
  function replay_queue_filler (line 112) | def replay_queue_filler(replay_queue, replay_list):
  function main (line 117) | def main():

FILE: parse_replay/parse_replay.py
  class ReplayProcessor (line 47) | class ReplayProcessor(multiprocessing.Process):
    method __init__ (line 49) | def __init__(self, run_config, replay_queue, counter, total_num):
    method run (line 56) | def run(self):
    method process_replay (line 116) | def process_replay(self, controller, replay_data, map_data, player_id,...
  function replay_queue_filler (line 134) | def replay_queue_filler(replay_queue, replay_list):
  function main (line 139) | def main():

FILE: parse_replay/replay2global_features.py
  function process_replay (line 27) | def process_replay(sampled_action, actions, observations, feat, units_in...
  function parse_replay (line 116) | def parse_replay(replay_player_path, sampled_action_path, reward):
  function main (line 148) | def main():

FILE: parse_replay/sample_actions.py
  function sample_action_from_player (line 28) | def sample_action_from_player(action_path):
  function sample_action (line 54) | def sample_action(replay_path, action_path, sampled_path):
  function main (line 79) | def main():

FILE: preprocess/parse_replay_info.py
  class ReplayProcessor (line 31) | class ReplayProcessor(multiprocessing.Process):
    method __init__ (line 33) | def __init__(self, run_config, replay_queue, counter, total_num):
    method run (line 40) | def run(self):
  function replay_queue_filler (line 62) | def replay_queue_filler(replay_queue, replay_list):
  function main (line 67) | def main():

FILE: preprocess/preprocess.py
  function valid_replay (line 31) | def valid_replay(info, ping):
  function main (line 54) | def main():
Copy disabled (too large) Download .json
Condensed preview — 48 files, each showing path, character count, and a content snippet. Download the .json file for the full structured content (35,235K chars).
[
  {
    "path": ".gitignore",
    "chars": 1276,
    "preview": "*.tar.gz\n\n# PyCharm\n.idea\n\n# Training Result\ncheckpoints\n\n# Dataset\nreplays_infos\nhigh_quality_replays\nparsed_replays\n\n#"
  },
  {
    "path": "Baselines/BuildOrderPrediction/test.py",
    "chars": 3476,
    "preview": "import os\nimport visdom\nimport pickle\nimport argparse\n\nimport numpy as np\n\nLAMBDA = 0.99\n\ndef calc_action_acc(action_pre"
  },
  {
    "path": "Baselines/BuildOrderPrediction/train.py",
    "chars": 13547,
    "preview": "from __future__ import print_function\n\nimport os\nimport json\nimport time\nimport pickle\nimport argparse\n\nimport visdom\nim"
  },
  {
    "path": "Baselines/BuildOrderPrediction/train_spatial.py",
    "chars": 13968,
    "preview": "from __future__ import print_function\n\nimport os\nimport json\nimport time\nimport pickle\nimport argparse\n\nimport visdom\nim"
  },
  {
    "path": "Baselines/GlobalStateEvaluation/requirements.txt",
    "chars": 55,
    "preview": "numpy == 1.13.0\n\nvisdom == 0.1.4\n\ntorch == 0.1.12.post2"
  },
  {
    "path": "Baselines/GlobalStateEvaluation/test.py",
    "chars": 3404,
    "preview": "import os\nimport visdom\nimport pickle\nimport argparse\n\nimport numpy as np\n\nLAMBDA = 0.99\n\ndef calc_value_acc(value_pre, "
  },
  {
    "path": "Baselines/GlobalStateEvaluation/train.py",
    "chars": 13073,
    "preview": "from __future__ import print_function\n\nimport os\nimport json\nimport time\nimport pickle\nimport argparse\n\nimport visdom\nim"
  },
  {
    "path": "Baselines/GlobalStateEvaluation/train_spatial.py",
    "chars": 13512,
    "preview": "from __future__ import print_function\n\nimport os\nimport json\nimport time\nimport pickle\nimport argparse\n\nimport visdom\nim"
  },
  {
    "path": "README.md",
    "chars": 6500,
    "preview": "# MSC\n[MSC: A Dataset for Macro-Management in StarCraft II.](https://arxiv.org/pdf/1710.03131.pdf)\n```\n@article{wu2017ms"
  },
  {
    "path": "_config.yml",
    "chars": 26,
    "preview": "theme: jekyll-theme-hacker"
  },
  {
    "path": "data_loader/BatchEnv.py",
    "chars": 7509,
    "preview": "import os\nimport json\nfrom collections import namedtuple\n\nimport numpy as np\nfrom scipy import sparse\n\nfrom tqdm import "
  },
  {
    "path": "extract_features/SpatialFeatures.py",
    "chars": 5062,
    "preview": "from pysc2.lib.features import *\nfrom pysc2.lib import stopwatch\n\nsw = stopwatch.sw\n\nclass ScreenFeatures(collections.na"
  },
  {
    "path": "extract_features/compute_stat.sh",
    "chars": 31,
    "preview": "python replay_stat.py --race $1"
  },
  {
    "path": "extract_features/extract_features.sh",
    "chars": 143,
    "preview": "python global_feature_vector.py --hq_replay_set $1 &&\npython spatial_feature_tensor.py --hq_replay_set $1 &&\npython spli"
  },
  {
    "path": "extract_features/game_state.py",
    "chars": 6580,
    "preview": "import os\nimport json\nimport pprint\nimport numpy as np\n\ndef load_stat(path):\n    def dict_key_to_int(obj):\n        def s"
  },
  {
    "path": "extract_features/global_feature_vector.py",
    "chars": 2779,
    "preview": "from __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\n"
  },
  {
    "path": "extract_features/replay_stat.py",
    "chars": 4151,
    "preview": "from __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport glo"
  },
  {
    "path": "extract_features/spatial_feature_tensor.py",
    "chars": 5518,
    "preview": "from __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\n"
  },
  {
    "path": "extract_features/split.py",
    "chars": 3661,
    "preview": "import os\nimport json\nimport numpy as np\nfrom absl import flags\n\nfrom google.protobuf.json_format import Parse\nfrom s2cl"
  },
  {
    "path": "instructions/EasyWay.md",
    "chars": 610,
    "preview": "# Step-by-Step: The Easy Way\n## Preprocess\n```sh\ncd preprocess\nbash preprocess.sh\n```\n## Parse Replay\n```sh\ncd parse_rep"
  },
  {
    "path": "instructions/HardWay.md",
    "chars": 5515,
    "preview": "# Step-by-Step: Details\n## Preprocessing Replays\n```sh\ncd preprocess\n```\n### Parse Replay Info\n```sh\npython parse_replay"
  },
  {
    "path": "parse_replay/extract_actions.py",
    "chars": 5970,
    "preview": "from __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\n"
  },
  {
    "path": "parse_replay/parse_replay.py",
    "chars": 7207,
    "preview": "from __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\n"
  },
  {
    "path": "parse_replay/parse_replay.sh",
    "chars": 223,
    "preview": "python extract_actions.py --hq_replay_set $1 --n_instance $2 &&\npython sample_actions.py --hq_replay_set $1 &&\npython pa"
  },
  {
    "path": "parse_replay/replay2global_features.py",
    "chars": 7296,
    "preview": "from __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\n"
  },
  {
    "path": "parse_replay/sample_actions.py",
    "chars": 3432,
    "preview": "from __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\n"
  },
  {
    "path": "preprocess/parse_replay_info.py",
    "chars": 3696,
    "preview": "from __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\n"
  },
  {
    "path": "preprocess/preprocess.py",
    "chars": 2846,
    "preview": "from __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\n"
  },
  {
    "path": "preprocess/preprocess.sh",
    "chars": 68,
    "preview": "python parse_replay_info.py --n_instance 32 && python preprocess.py\n"
  },
  {
    "path": "requirements.txt",
    "chars": 173,
    "preview": "future == 0.16.0\n\nnumpy == 1.13.0\nscipy == 0.19.0\n\npython_gflags == 3.1.1\n\ntqdm == 4.14.0\n\nprotobuf == 3.4.0\npystream_pr"
  },
  {
    "path": "train_val_test/Protoss_vs_Protoss/test.json",
    "chars": 437906,
    "preview": "[{\"Protoss\": [{\"global_path\": \"parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@567732f6b9495759f6ee5cfd9"
  },
  {
    "path": "train_val_test/Protoss_vs_Protoss/train.json",
    "chars": 1529644,
    "preview": "[{\"Protoss\": [{\"global_path\": \"parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@ce20db88a1a42fc3d20639548"
  },
  {
    "path": "train_val_test/Protoss_vs_Protoss/val.json",
    "chars": 218953,
    "preview": "[{\"Protoss\": [{\"global_path\": \"parsed_replays/GlobalFeatureVector/Protoss_vs_Protoss/Protoss/1@128bdf3924378b3366232dd23"
  },
  {
    "path": "train_val_test/Protoss_vs_Terran/test.json",
    "chars": 1597948,
    "preview": "[{\"Protoss\": [{\"global_path\": \"parsed_replays/GlobalFeatureVector/Protoss_vs_Terran/Protoss/2@3c99beec3ab9c1e4e8d1e140dd"
  },
  {
    "path": "train_val_test/Protoss_vs_Terran/train.json",
    "chars": 5591300,
    "preview": "[{\"Protoss\": [{\"global_path\": \"parsed_replays/GlobalFeatureVector/Protoss_vs_Terran/Protoss/1@56042fd202c899bb2cc20243cc"
  },
  {
    "path": "train_val_test/Protoss_vs_Terran/val.json",
    "chars": 799480,
    "preview": "[{\"Protoss\": [{\"global_path\": \"parsed_replays/GlobalFeatureVector/Protoss_vs_Terran/Protoss/1@d2f41784e0d0e11504d544a47f"
  },
  {
    "path": "train_val_test/Protoss_vs_Zerg/test.json",
    "chars": 1291584,
    "preview": "[{\"Zerg\": [{\"global_path\": \"parsed_replays/GlobalFeatureVector/Protoss_vs_Zerg/Zerg/1@01fb58da89d3851a1e4744f43827092f63"
  },
  {
    "path": "train_val_test/Protoss_vs_Zerg/train.json",
    "chars": 4519552,
    "preview": "[{\"Zerg\": [{\"global_path\": \"parsed_replays/GlobalFeatureVector/Protoss_vs_Zerg/Zerg/2@341ff08c4945d1723eac14432f1ee82be8"
  },
  {
    "path": "train_val_test/Protoss_vs_Zerg/val.json",
    "chars": 645792,
    "preview": "[{\"Zerg\": [{\"global_path\": \"parsed_replays/GlobalFeatureVector/Protoss_vs_Zerg/Zerg/2@c6e4c53243f71c4c6cff768ef0bb2559a6"
  },
  {
    "path": "train_val_test/Terran_vs_Terran/test.json",
    "chars": 970200,
    "preview": "[{\"Terran\": [{\"global_path\": \"parsed_replays/GlobalFeatureVector/Terran_vs_Terran/Terran/1@85a6d8a8d4d2a3a1f7a216a35690b"
  },
  {
    "path": "train_val_test/Terran_vs_Terran/train.json",
    "chars": 3392730,
    "preview": "[{\"Terran\": [{\"global_path\": \"parsed_replays/GlobalFeatureVector/Terran_vs_Terran/Terran/1@ce570951ceb2cd334a6edc2786760"
  },
  {
    "path": "train_val_test/Terran_vs_Terran/val.json",
    "chars": 485100,
    "preview": "[{\"Terran\": [{\"global_path\": \"parsed_replays/GlobalFeatureVector/Terran_vs_Terran/Terran/1@8ab8d2356eda577969d99dbf83799"
  },
  {
    "path": "train_val_test/Terran_vs_Zerg/test.json",
    "chars": 1964000,
    "preview": "[{\"Zerg\": [{\"global_path\": \"parsed_replays/GlobalFeatureVector/Terran_vs_Zerg/Zerg/1@4818575eea117e4122a2393d00673dd77a3"
  },
  {
    "path": "train_val_test/Terran_vs_Zerg/train.json",
    "chars": 6871054,
    "preview": "[{\"Zerg\": [{\"global_path\": \"parsed_replays/GlobalFeatureVector/Terran_vs_Zerg/Zerg/2@d24922989bbe00e3bc2a4fabd8fbea54321"
  },
  {
    "path": "train_val_test/Terran_vs_Zerg/val.json",
    "chars": 981018,
    "preview": "[{\"Zerg\": [{\"global_path\": \"parsed_replays/GlobalFeatureVector/Terran_vs_Zerg/Zerg/1@5e9be42a0c8f31f7b234f102fd1fd8491c4"
  },
  {
    "path": "train_val_test/Zerg_vs_Zerg/test.json",
    "chars": 569296,
    "preview": "[{\"Zerg\": [{\"global_path\": \"parsed_replays/GlobalFeatureVector/Zerg_vs_Zerg/Zerg/1@ffc32a62def944bb1bae87eeda9a39fd3f6a1"
  },
  {
    "path": "train_val_test/Zerg_vs_Zerg/train.json",
    "chars": 1991584,
    "preview": "[{\"Zerg\": [{\"global_path\": \"parsed_replays/GlobalFeatureVector/Zerg_vs_Zerg/Zerg/1@f77112d63fc4ceaa8e8a79f9aafa9b928406a"
  },
  {
    "path": "train_val_test/Zerg_vs_Zerg/val.json",
    "chars": 284648,
    "preview": "[{\"Zerg\": [{\"global_path\": \"parsed_replays/GlobalFeatureVector/Zerg_vs_Zerg/Zerg/1@49c11a4968d0d73e21b838446c5b238ea64ba"
  }
]

About this extraction

This page contains the full source code of the wuhuikai/MSC GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 48 files (32.7 MB), approximately 8.6M tokens, and a symbol index with 118 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!