Last active
September 3, 2018 23:54
-
-
Save miguelgondu/5feec8dd3f19f5bb0a9f35b6f554945b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Introduction " | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"I want to construct a database for macro learning in StarCraft 2. One way to study replays and mine information from them is using `pysc2`, an API developed by DeepMind that is specially designed for Reinforced Learning. Nahren and Justesen [developed a tool for mining replays using this API](https://github.com/njustesen/pysc2-replay), but unfortunately the DeepMind's API can only play replays that match the binary version of the game, and StarCraft 2 is being patched regularly, thus many \"old\" replays render unparsable. The other way to do it is using [`sc2reader`](https://github.com/ggtracker/sc2reader), an API developed by 26 contributors including David Joerg, Graylin Kim and Kevin Leung.\n", | |
"\n", | |
"In this blogpost I'll start talking about `sc2reader` and the information that can be mined using it. We will focus for now on the events regarding units and postpone the discussion about *spatial* and *command* related events for later posts." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# The replay object " | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"After installing `sc2reader` using `pip install sc2reader`, we can import the API and a replay like this: \n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 1, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"import sc2reader\n", | |
"\n", | |
"replay = sc2reader.load_replay('Replays/Dreamcatcher LE (12).SC2Replay')" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"If you're using Windows, you can find your own replays buried deep at `Documents\\StarCraft II\\Accounts\\...`, if you want replays from professional players you can look at [Spawning Tool](https://lotv.spawningtool.com/replays/?pro_only=on), a website which uses `sc2reader`. Copy some of them in your working directory.\n", | |
"\n", | |
"This replay object stores all the information about the file, including for example the participants and *rules* of the game at `replay.attributes`, the players at `replay.players` and the winner at `replay.winner`. Remember that all these methods and attributes can be listed using `dir(replay)`.\n", | |
"\n", | |
"We will focus on `replay.events`." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Events in a replay " | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"`replay.events` is a list storing all the events of the game. Among these are UnitBornEvents, UnitInitEvents, UnitDoneEvents, UnitDeadEvents, CameraEvents and many, many more. You could for example figure out all event types in your replay running" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 2, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"event_names = set([event.name for event in replay.events])" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"that is, all events have an attribute called `name`, which stores the type of the event. Let's separate events with regard to their types (or names):" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 3, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"events_of_type = {name: [] for name in event_names}\n", | |
"for event in replay.events:\n", | |
" events_of_type[event.name].append(event)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"`events_of_type` is a python dictionary in which the keys are event names in strings (e.g. `\"UnitBornEvent\"`), and the keys are lists of all the events of said type, so `events_of_type[\"CameraEvent\"]` returns the list of all CameraEvents.\n", | |
"\n", | |
"Let's go more in depth in some of these events:" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## UnitBornEvents " | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Say one of the players is a Terran and created a Marine, then a UnitBornEvent was issued, storing the location, time, unit type and unit controller, among other things. UnitBornEvents are generated, according to the documentation, *every time a unit is created in a finished state*. This doesn't include then warpgate units and buildings (except perhaps the initial ones).\n", | |
"\n", | |
"The most important attributes for our purposes are:\n", | |
"- `frame` and `second`, which store the frame in which the unit was created.\n", | |
"- `unit`, `unit_controller` which store the actual unit (e.g. Zergling) and the player which created it respectively. You can quickly access which player created that unit with `control_pid` which is either a 1 or a 2 (i.e. either player 1 or 2).\n", | |
"- `x` and `y`, which store the location in which this event took place.\n", | |
"\n", | |
"For example, try printing all these events using a for loop:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 12, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"unit_born_events = events_of_type[\"UnitBornEvent\"]" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 13, | |
"metadata": { | |
"scrolled": true | |
}, | |
"outputs": [], | |
"source": [ | |
"for ube in unit_born_events:\n", | |
" '''\n", | |
" print('{} created {} at second {}'.format(ube.unit_controller,\n", | |
" ube.unit,\n", | |
" ube.second))\n", | |
" '''" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"If you do so, you'll notice that the first events have None as the unit controller. These are precisely the events that track the creation of the map: mineral patches and Vespene gas geysers. " | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## UnitInitEvents " | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"If you start building a Supply Depot, a UnitInitEvents is issued holding about the same information of UnitBornEvents. It traks time, location and type of unit created. This event is helpful when tracking actual buildings.\n", | |
"\n", | |
"Try printing these events with" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 14, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"unit_init_events = events_of_type[\"UnitInitEvent\"]\n", | |
"\n", | |
"for uie in unit_init_events:\n", | |
" '''\n", | |
" print('{} started creating {} at second {}'.format(uie.unit_controller,\n", | |
" uie.unit,\n", | |
" uie.second))\n", | |
" '''\n", | |
" " | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 15, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"unit_done_events = events_of_type[\"UnitDoneEvent\"]\n", | |
"\n", | |
"for ude in unit_done_events:\n", | |
" '''\n", | |
" print(\"{} finished\".format(ude.unit))\n", | |
" '''" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Once these units finish, a UnitDoneEvent is created. One must be careful, because this UnitDoneEvent doesn't track the unit controller. Thankfully, every unit has a unique ID, so one could maintain a list of the units that were being created and then just test for pertenence.\n" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## UnitDiedEvent " | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Once a unit dies, a `UnitDeadEvent` is generated. It stores the unit that died, the killer player and killing unit, and also location and time. " | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 16, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"unit_died_events = events_of_type[\"UnitDiedEvent\"]\n", | |
"\n", | |
"for udiede in unit_died_events:\n", | |
" '''\n", | |
" print(\"{} was killed by {} using {} at ({}, {})\".format(udiede.unit,\n", | |
" udiede.killer,\n", | |
" udiede.killing_unit,\n", | |
" udiede.x,\n", | |
" udiede.y))\n", | |
" '''" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Writing a worker counter " | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Let's handle these events and create a worker counter. To do so, note that we only need to consider UnitBornEvents and UnitDiedEvents, because workers always enter the game in a finished state. We will write a function that takes a replay, a second and a player id and returns the amount of workers said player had up to that second. To do so, the function will maintain a list holding the workers that have been created and that haven't died yet." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 17, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def worker_counter(replay, second, player_id):\n", | |
" workers = []\n", | |
" for event in replay.events:\n", | |
" if event.name == \"UnitBornEvent\" and event.control_pid == player_id:\n", | |
" if event.unit.is_worker:\n", | |
" workers.append(event.unit)\n", | |
" \n", | |
" if event.name == \"UnitDiedEvent\":\n", | |
" if event.unit in workers:\n", | |
" workers.remove(event.unit)\n", | |
" \n", | |
" if event.second > second:\n", | |
" break\n", | |
" \n", | |
" return len(workers)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"And, why not, let's use this function to plot a graph of seconds vs. amount of workers:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 18, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"import matplotlib.pyplot as plt" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 19, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"image/png": "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\n", | |
"text/plain": [ | |
"<Figure size 432x288 with 1 Axes>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"length_of_game = replay.frames // 24\n", | |
"workers_1 = [worker_counter(replay, k, 1) for k in range(length_of_game)]\n", | |
"workers_2 = [worker_counter(replay, k, 2) for k in range(length_of_game)]\n", | |
"\n", | |
"plt.figure()\n", | |
"plt.plot(workers_1, label=replay.players[0])\n", | |
"plt.plot(workers_2, label=replay.players[1])\n", | |
"plt.legend(loc=2)\n", | |
"plt.show()" | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 3", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.6.5" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment