Parsing Replay Files

I think you are right about the ordering and the id. When I wrote those notes I had verified that filters, encryptors, and destructors are certainly the first 3.

1 Like

I just now verified the number codes above are correct, including 6=Remove. It is treated as a unit spawning at a location and is even given an id.

I also noticed I missed a final value for the spawn segment. It seems to always be equal to 1 no matter what unit is being created.

Example: “spawn”:[[[2,13],6,“10”,1]]
2,13 - is the location
6 - is the Remove unit code
“10” - is an id (The id of the “Remove” unit, not the unit being removed)
1 - is the unknown value. I have yet to see it set to anything but 1.

2 Likes

I believe the last digit is the player number. I think its 1 for player 1 and 2 for player 2.

3 Likes

Well, I feel silly. Confirmed the last number is either 1 for player1 or 2 for player2.

I’ve been testing against a player 2 algo that passes every turn without taking any actions, so I didn’t see what happened when player 2 spawned things. Good call @RuberCuber.

1 Like

Worth noting that if you try and create a unit from the “spawn” data it will fail with:

my-bot:     self.speed = type_config["speed"]
my-bot: KeyError: 'speed'

if there is any Unit with the ‘RM’ property (code 6) since the remove action has no speed associated (makes sense, since you are getting rid of it).

You have to check for this if creating units from “speed”.

2 Likes

Does anyone have any additional information about the “move” option:

When I print it out it’s all pretty clear, except for the third item:

[[16, 2], [16, 3], [0, 0], 5, '182', 1]

I always get [0,0] so I’m wondering if anyone knows what it is measuring. The only thing I can think of is damage taken/received or maybe the position of the position it is targeting, except I don’t see the value change when close to another unit, but maybe I’m just missing it.

1 Like

Tested and confirmed the “selfDestruct” segment, broken down below.

Note that in accordance with the rules, an information unit that travels less than 5 spaces before running out of move options does not self destruct (does no damage) but does get removed from play. I’ve confirmed that such units that run out of moves do not appear in the “selfDestruct” segment, only in the “death” segment. An IU that travels at least 5 spaces before getting stuck appears in both the “selfDestruct” and “death” segments.

“selfDestruct”:[[[25,13], # x,y position of the self-destructed unit
[[24,14], # x,y position of an affected unit
[25,14], # x,y position of a second affected unit
[26,14]], # x,y position of a third affected unit
15.0, # damage dealt ( = starting stability of the self-destructing unit)
3, # Type code of unit that self destructed (3 = Ping, 4=EMP, 5=Scrambler)
“155”, # id of self destructing unit
1]], # player number (1 or 2) who owned the self-destructing unit

3 Likes

Tested and confirmed the “breach” segment. One entry in this segment appears for every Information Unit that makes it to the opponent’s opposite edge and scores, dealing health damage. IU’s that appear in the “breach” segment also appear in the “death” segment of the same frame as they are removed from the board.

“breach”:[[[13,27], # x,y position where IU hit opponents edge
1.0, # Amount of health damage done to opponent (always 1.0)
3, # Type code of unit that hit (3 = Ping, 4 = EMP, 5 = Scrambler)
“7”, # id of unit that scored
1]], # Owner of the scoring unit (1 = Player1, 2 = Player2)

5 Likes

Tested and confirmed the “damage” segment. One entry appears for every instance of damage dealt to any unit. The same unit can appear multiple times, once for each source of damage that turn. Damage can come from enemy units attacking or self-destructing, each of which have corresponding entries in the “attack” and “selfDestruct” segments.

“damage”:[[[27,14], # x,y position of unit which took damage
1.0, # amount of damage done (depends on attacking unit)
0, # Type code of unit which took damage (0 = Filter, 1 = Encryptor, 2 = Decryptor, 3 = Ping, 4 = EMP, 5 = Scrambler)
“4”, # id of unit which took damage
2]], # Owner of unit which took damage (1 = Player1, 2 = Player2)

4 Likes

Tested and confirmed the “shield” segment. One entry appears for every instance of a shield being applied to an Information Unit. The same IU can appear in the segment multiple times, each time affected by a different encryptor. The same encryptor can appear in the segment multiple times, each time shielding a different IU. Affected IU’s entries in the “p1Units” segment reflect the shielded amount in the same frame. Note that the decay on shields does not seem to be indicated in any special way and can only be seen as its effect on IU stability values.

“shield”:[[[1,13], # x,y position of shield-giving unit
[2,11], # x,y position of shield-receiving unit
10.0, # amount of shield given
1, # Probably type code of shield-giving unit (Always 1 = Encryptor)
“54”, # id of the shield-giving unit
“117”, # id of the shield-receiving unit
1]], # Owner of the units (1 = Player1, 2 = Player2)

3 Likes

Tested and confirmed the “move” segment. The 3rd value of each move seems to always be [0,0], still not sure what that represents. Note that newly spawned units do not move in the same frame they spawned in but their spawn frame counts towards their next move (A ping spawned on turn 0 will first move on turn 1).

“move”:[[[2,11], # x,y starting position of unit
[2,12], # x,y ending position of unit
[0,0], # ???
3, # Type code of unit (3 = Ping, 4 = EMP, 5 = Scrambler)
“117”, # id of unit
1]], # Owner of the unit (1 = Player1, 2 = Player2)

4 Likes

Tested and confirmed the “death” segment. Units removed from play for any reason show in this segment (attacked to death, successfully breached opposite edge, self-destructed, removed by owner).

“death”:[[[13,27], # x,y position of unit
3, # Type code of unit (0 = Filter, 1 = Encryptor, 2 = Destructor, 3 = Ping, 4 = EMP, 5 = Scrambler)
“7”, # id of unit
1, # Owner of unit (1 = Player1, 2 = Player2)
false]], # Unit was destroyed by a “Remove” command (true/false)

4 Likes

Last one, since “melee” seems to be completely unused.

Tested and confirmed the “attack” segment.

“attack”:[[[0,13], # x,y position of attacking unit
[0,14], # x,y position of target unit
1.0, # damage of attack
3, # Type code of attacking unit (2 = Destructor, 3 = Ping, 4 = EMP, 5 = Scrambler)
“117”, # id of attacking unit
“60”, # id of target unit
1]], # Owner of attacking unit (1 = Player1, 2 = Player2)

5 Likes

On the last line of the replay file there should also be a field keyed by “endStats”. This contains a few summary statistics, which is quite convenient because I was planning on calculating these things myself. Here’s a dump of my org-mode notes on this section:

**** endStats
This appears only in the last frame and has some summary information.
#+BEGIN_SRC json
{
“endStats”: {
“duration”: 6945,
“winner”: 1,
“player1”: {
“stationary_resource_spent”: 277.0,
“dynamic_resource_spoiled”: 70.0,
“crashed”: false,
“name”: “T035”,
“dynamic_resource_destroyed”: 298.0,
“dynamic_resource_spent”: 330.0,
“stationary_resource_left_on_board”: 112.0,
“points_scored”: 32.0,
“total_computation_time”: 441
},
“frames”: 2323,
“player2”: {
“stationary_resource_spent”: 258.0,
“dynamic_resource_spoiled”: 70.0,
“crashed”: false,
“name”: “T036”,
“dynamic_resource_destroyed”: 324.0,
“dynamic_resource_spent”: 330.0,
“stationary_resource_left_on_board”: 37.0,
“points_scored”: 6.0,
“total_computation_time”: 458
},
“turns”: 54
}
}
#+END_SRC

- _duration_: (int) Duration of the game in milliseconds?
- _winner_: (int) player id of the winner (1 or 2)
- _frames_: (int) number of frames elapsed
- _turns_: (int) number of turns elapsed
- _player#_: (json) More summary info described below

***** player#
- stationary_resources_spent: (float) Number of cores spent
- dynamic_resources_spoiled: (float) Number of bits decayed
- crashed: (bool) Whether or not the algo crashed
- name: (str) name of the player’s algorithm
- dynamic_resource_destroyed: (float) Amount of bits worth of information lost.
- dynamic_resource_spent: (float) number of bits spent
- stationary_resources_left_on_board: (float) cores worth of
units remaining
- points_scored: (float) damage dealt to the opponent
- total_computation_time: (int) compute time in millis.

2 Likes

Im giving Jumpster the community helper badge for doing all this great data mining

5 Likes

so I just wanted to put all of Jumpster’s stuff and RJTK’s stuff into one example string, I know I got something wrong so someone tell me what: Here is what I have so far

turn_0 = {
    "p2Units":[[],[],[],[],[],[],[]], # [filters, encryptors, Destructors, ping?, EMP?, Scrambler? Remove?] (I do not get the sublists someone explain that.)
    "turnInfo":[0,0,-1], # [state_type, turn_number, frame_number]
    "p1Stats":[30.0,25.0,5.0,0], # [health, cores, bits, time_of_last_turn]
    "p1Units":[[],[],[],[],[],[],[]], # [filters, encryptors, Destructors, ping?, EMP?, Scrambler? Remove?] (I do not get the sublists someone explain that.)
    "p2Stats":[30.0,25.0,5.0,0], # [health, cores, bits, time_of_last_turn]
    "events": {
        "selfDestruct":[[[25,13], [[24,14], [25,14], [26,14]], 15.0, 3, "155", 1]], # [[x, y](of unit), [x, y](of affected unit1), [x, y](of affected unit2), [x, y](of affected unit3), damage dealt, type of unit, id, player number]
        "breach":[[[13,27], 1.0, 3, "7", 1]], # [[x, y], damage, type of unit, id, player number]
        "damage":[[[27,14], 1.0, 0, "4", 2]], # [[x, y], damage, type of unit, id, player number]
        "shield":[[[1,13], [2,11], 10.0, 1, "54", "117", 1]], # [[x, y](of Encrypter), [x, y](of reciving unit), amount of shield, type of unit (1), id(shield giving), id(shield receiving), player number]
        "move":[[[2,11], [2,12], [0,0], 3, "117", 1]], # [[x, y](starting), [x, y](ending), ???, type of unit, id, player number]
        "spawn":[[[2,13],6,"10",1]], # [[x, y], Remove unit code, id, player number]
        "death":[[[13,27], 3, "7", 1, false]], # [[x, y], type of unit, id, player number, if it was using "Remove"]
        "attack":[[[0,13], [0,14], 1.0, 3, "117", "60", 1]], # [[x, y](attacking unit), [x, y](targeted unit), damage, type of unit (attacking), id (attacking), id (targeted), player number]
        "melee":[] # unused
        }
}

To get a better view you might want to copy paste into a new python file

5 Likes

Quick question, does 2 always refer to enemy and 1 always refer to myself in the last element of “damage”:[[[27,14], 1.0, 0, “4”, 2]]? If not, how can we manipulate to make sure I always get the information on my side or the enemy’s side?

Thank you!

1 Like

so this is odd, in the game_state_string 1 is you and 2 is the opponent, but in using functions in other places like get_resource or something, 0 is you and 1 is the enemy.

1 Like

The replay file format hasn’t been touched in a long while, we will probably standardize it and clean it up at some point

1 Like

Just to add it here for reference,
3rd parameter of [‘events’][‘move’] seems to be target edge, but is currently always loged as [0, 0]

events[ ..., "move":[ [ [2,11],  [2,12],  [0,0], 3, "1", 1], ...]....]
events[ ..., "move":[ [ [27,13], [27,14], [0,0], 3, "2", 1], ...]....]
events[ ..., "move":[ [ [24,17], [24,16], [0,0], 3, "3", 2], ...]....]
events[ ..., "move":[ [ [0,14],  [0,13],  [0,0], 3, "4", 2], ...]....]