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
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
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.