The engine actually does provide information about where units have scored. Take a look at the “breach” information given from the engine. Similarly you can get information about “destroyed”, “created”, etc. Simply look at the JSON string given from the game engine when communicating.
Ahh thank you! This is exactly what I was looking for. I hadn’t been inspecting the raw string from the engine. I was (perhaps naively) assuming that all useful information was parsed out into the game_state but apparently not.
Now for the fun of trying to decipher what exactly all of the various numbers mean
Perhaps this is a stupid question, and I AM a pretty bad coder when it comes to python, but how did you manage to get the string from algocore?
If you look at the big if else block in algocore you can see how the string gets parsed in on_turn. You can add a similar on_action method that will take in a state string and do something with it. The default thing that on_turn does is pass it directly to game_state to create an object, but you can do whatever you want with it, though reading through how game_state parses the string is a pretty good place to start.
Thanks for replying! I’m still a bit confused.
So, would it be the
state = json.loads(game_state_string) in algocore?
I get the fact that an object, Gamestate gets created every turn, by the game_state = gamelib.GameState(self.config, turn_state) thing. So, the string is already passed into strategy, or do I have to find a way to pass it in from algocore?
Also, on_action would be the phase like restore, deploy, etc…?
And we’re just modifying the algocore, not the strategy itself?
I hope I am not wasting your time by asking dumb questions.
I’m going to break this into 2 main sections:
- How does the engine talk to your python code
- The purpose of the GameState and AlgoStratgy classes
How does the Engine Talk:
All information passed from the engine to python is done through JSON format (the replays are also saved in this format). There are two places with json to be aware of. The first is a config file (and stored in
self.config). This is mainly a reference containing useful information like different units range, health, etc.
The second one contains all the information passed every single frame (not turn). It passes information about what happened in the game. This can be done anywhere in your code, but it is put into the GameState class because it gets input (and sends data) from stdin/stdout (
print()) and should only be sent once (try calling
game_state_string = get_command() a second time in the same place in algocore.py and it will never exit - exit with Ctrl->C Powershell and Cmd->C terminal). This is implemented in the
util.py class (go check it out) and you’ll see that’s how the two languages communicate. Prints to stdout to send commands and reads from the stdin to get commands.
However, commands can only be sent as strings (not really strings, but are interpreted as such) and so in order to send data between different languages (Java - the engine and python - your code), JSON is used. JSON is a standard format, not a language. If you are not familiar with it I highly recommend looking it up.
Thus, the command you have noted
state = json.loads(game_state_string) takes the string it got from the engine (in JSON format) and turns it into a python dictionary. If you aren’t familiar with dictionaries, then you can learn about them here. This is where you will find data what happened in the previous turn. Try printing out
state and they will look almost exactly the same (some formatting differences), except they are not.
game_state_string is a string, and
state is a python dictionary. You will be able to get information from the dictionary. This segues into the next part.
The Purpose of the Gamestate and AlgoStrategy
The GameState is a class meant to take some of the information from this dictionary and put it into an object you can use in your program. If you look at the constructor (
__init__) you will notice it does
__parse_state() on a serialized string. This serialized string is the string received from the engine that is in JSON format. Thus, the first line in this function is
state = json.loads(state_line).
state is now a dictionary containing relevant information from the engine and the rest of this function is about setting up the data the GameState is going to store (which is obtained from the
state dict). This is how the terms like “on_action”, etc are used. They are the keys to get the values from the dictionary (and for the starter-kit some is stored in the GameState object).
Regarding your question about modifying the algocore:
AlgoStrategy inherits from AlgoCore (see
class AlgoStrategy(gamelib.AlgoCore)). You can think of AlgoStrategy as a more specific version of AlgoCore. Inheritance means it (AlgoStrategy) uses all of the functions/data of its parent (AlgoCore) but can also add others. An example of this is when AlgoStrategy is created (
__init__) and it calls the funtion
super().__init__(). This means when an AlgoStrategy object is created, it also calls the construtor of it’s parent (AlgoCore).
Thus, if you change AlgoCore it will affect AlgoStrategy, but if you change AlgoStrategy it will NOT change AlgoCore. This is object-oriented programming inheritance and it is fundamental for Java and C++ programming. When you call AlgoStrategy you are effectively creating an object that has functions and data from BOTH. This is done so that you can freely edit AlgoStrategy without worrying about worrying about the communication with the engine or how to start/end the game (there are many many other applications, but these are a couple relevant ones).
Hope this was helpful and not way too long , I got a bit carried away…
Thank you for taking your time to explain!
I will have to do a little bit of reading on dictionaries later, and I will try to implement it.
One more thing to ask:
Do you have to test it in the terminal website, is it mandatory? Or is there another way to test AIs?
That depends on what you are testing. For the most part, the online replay viewer is the best way to watch games, and test strategies. If you are asking if there is a version of that locally, there is not. Alternatively, you can output an ASCII version of the map in your PowerShell or Terminal using the
print_map() function located at the bottom of the navigate.py file. You can edit this function to display whatever data from the map you want.
For example, an output from mine is this (I’ve modified the output so it looks a little different but the concept is the same):
If you only care about seeing who won games, then you can copy and paste a small python script I wrote (or wait, it should be added to the github repo in a little while). You would only need the
get_winners.py (the second script in the post)
I’m having troubling trying to get the information out of the state library.
Based on the info above I assume that I can somehow get it while only editing the code in my algo_strategy.py file.
I’m unsure where to go from here, any help would be great. Specifically, I’m looking to retrieve the ‘breach’ information.
Thank you in advance
There are two important parts to retrieving the
breach information (and similar information, such
- Breach data is only reported with results of the action phase, which are not stored by default
- Breach data is not parsed by
game_state, so it will have to be modified to actually retrieve the information.
Retrieving the results of the Action phase
The default AlgoCore does not report action phase data, so you will need to change that. If you look at the block of code from lines 55 to 80 in the
algocore.py file, you can see that this is the section that handles turns. If it is the build / deploy phase,
self.on_turn(game_state_string), from AlgoStrategy is called, which parses the data. However, if it is the action phase, nothing happens. To change this, I created a function in AlgoStrategy called
parse_action_phase, which takes the
game_state_string as an arguement (similar to
on_turn in this way), and called it in the correct
if block in AlgoCore. I then used that function to parse and store information
Retrieving the information from the
Now that data from the action phase has been retrieved, it now has to be parsed so that it is readable for your code. I started by parsing information the same way as in the
on_turn function, by creating a
game_state. To get breach information from the
game_state.py), first find the
__parse_state function (technically, you can do this anywhere, but considering that you are parsing a state, this seems to be the correct place to do so). At any point after the line
state = json.loads(state_line), using
state["events"]["breach"] will give you the information on breaches.
To be honest, I don’t really know what all of the information reported about
breach means. However, these are my best guesses:
- The 1st piece of information, the 2-number array, is the location of the breach
- The 2nd piece of information (counting the array as 1) is the damage dealt by the unit
- The 3rd is the type of information (3 is Ping, 4 is EMP, 5 is Scrambler).
- I don’t really know about the 4th, the string of a number (ex.
"730"), but it might have to do with the unique identity of a unit?
- The 5th piece of information, the number that, as far as I know, is always either a 1 or 2 (but I am not sure about this), is still a mystery to me. Maybe player identity?
I hope that this was helpful, despite how long and badly phrased it was. If it doesn’t make sense, please ask for clarification.
A discussion about what all of the numbers mean has happened in this post:
One thing that I didn’t mention (because I didn’t know) is that
GameUnit doesn’t like
REMOVE units. If you create a
GameState based on a
game_state_string from the action phase, and a
REMOVE unit is present, your algo will crash. This is because certain qualities (such as speed, damage, range, etc.) are not defined for the
REMOVE unit. If a
GameUnit of a
REMOVE unit is created (
GameUnits are created when a
GameState is created), you will receive an error. To fix this, you will have to edit
unit.py. In the function
__serialize_self, add 2 lines after the line
from .game_state import FIREWALL_TYPES, UNIT_TYPE_TO_INDEX, ENCRYPTOR, REMOVE
(Or really any time before any undefined qualities are requested). The lines to add are:
if self.unit_type == REMOVE:
Thanks to the same thread that I referenced earlier (Parsing Replay Files) for mentioning that this was an issue
Another issue that I have found is that this sometimes crashes despite this fix. The error is (ignoring the giant line of functions leading up to the error):
File "/tmp/algo16180919916539904124/gamelib/game_state.py", line 136, in __create_parsed_units self.game_map[x,y].pending_removal = True
IndexError: list index out of range
To fix it, around the line
if unit_type == REMOVE:
self.game_map[x,y].pending_removal = True
Change it to:
if unit_type == REMOVE:
self.game_map[x,y].pending_removal = True
gamelib.debug_write("Error! Program tried to die while parsing REMOVE unit")
Side note: This is a very hacky fix, so if somebody knows how to fix it properly, feel free to say so. I don’t really know why this is happening.
This was incredibly helpful.
Thank you for taking the time to explain this in such depth!
If I have any questions regarding this I’ll let you know. Thank you again.
To be honest, I don’t really know what all of the information reported about breach means. However, these are my best guesses:
The 1st piece of information, the 2-number array, is the location of the breach
The 2nd piece of information (counting the array as 1) is the damage dealt by the unit
The 3rd is the type of information (3 is Ping, 4 is EMP, 5 is Scrambler).
I don’t really know about the 4th, the string of a number (ex. “730”), but it might have to do with the unique identity of a unit?
The 5th piece of information, the number that, as far as I know, is always either a 1 or 2 (but I am not sure about this), is still a mystery to me. Maybe player identity?
After some testing on my own, I found out that the information above is pretty accurate. The 4th piece of information string number is the “name” of that unit, and you can use it to track down the same unit in other action_phase strings. The 5th piece of information is a 1 for own units, and a 2 for enemy units. This is a little different from how it is in the code (0 for own and 1 for enemy).
I am confused on where you get the information about “breach”?
can someone tell me how I can call that information and what I need to import or something?
Do you have to parse for every single frame?
As far as I know, you would, because the information is only sent to you through
game_state_string once. That being said, if all that you are interested in is the breach information, you don’t have to save the information for every single frame, only for the frames in which there is a breach.
Also, how would I know if it works?
Do you mean whether your method of retrieving the information works? I would recommend using
game_state.debug_write to output the information that you found, and then running your algo online to check to ensure that the information is correct.
Ok. I’ll try that. Thank’s for your reply! I hope it turns out ok.
Yeah so I read the entire thing, and I still have no idea what to do. Can someone explain to me exactly what to do in terms of someone who really didn’t start coding at all a month ago? I’m learning so much from doing this! I would really like to know how to do this…
So, I’ve made a function that gets the gamestate, but I… don’t really know how to get it out of the function.
Thanks in advance