C1 _Termial Hackathon: a Starter (Python)

Github Code: https://github.com/luckystarufo/_Terminal_CorrelationOne

[# Update 01/02/2019: as requested by Correlation One, the repo doesn’t contain my original codes anymore…]

In this blog, I will share my Hackathon experience in this winter break. It’s related to an online game called _Terminal, which is rather new and still under some sort of tests. I was one of their ‘targeted players’, who later on made a phone call with them providing feedbacks.

[Note: In the beginning, I thought this could be a great project on reinforcement learning, eg. to apply the same technique as the training for alpha Go Zero, however, I later on realized that it may not be possible: the simulator is not fast enough to provide feedbacks & the action state is HUGE which offers difficulties on the Monte Carlo Tree Search.]

Since the Season 1 Global Tournament has now officially ended, I want to share a little piece of my experience to help those who want to enter this game later on land on it more smoothly. And you are also highly welcome to leave your comments & suggestions if you also played in this Season, especially those who ranked high & won the prizes! 🙂

What is C1 _Terminal?

C1 _Terminal in an online e-sports game held by Correlation One, where players code up their algorithms to perform their strategies to compete with each other. More information are provided on their web: https://terminal.c1games.com/home I find this game super fun in the beginning and incredibly challenging after you dive deep into it.

Poster on their Facebook Page

Overview of Game Rules

  • The game takes place on what we call ‘the playground’ or ‘the arena’, which is consisted of grids.
  • The top half of the arena is enemy’s side, the lower half of the arena is our side.
  • It is a Tower Defense Game. We deploy units to play. The units used to attack are called information units, the ones used to defend are called firewall units.
  • After the ‘deploy phase’, information units will move according to some path finding algorithms and the firewall units remain stationary.
  • There are 3 different types of information units and 3 types of firewall units, which have different stabilities (HP), ranges, attacks and costs.
  • To deploy units, we use our resources. Information units take BITs and firewall units take COREs.

Now, you can kinda of see the game’s big picture: it’s about how to efficiently use your resources to deploy the ‘right units’ at the ‘right locations’ to compete. There’re clearly flexibilities and trade-offs.

The official documentation of the rules can be found here: https://terminal.c1games.com/rules

You can play by hand to gain more intuitions first and then code up your strategies. (Note: play with the BOSSES is extremely helpful to help you understand some common strategies.)

Information of how to code it and upload can be quickly understood according to official the tutorials: https://terminal.c1games.com/tutorials

Sharing my Experience

OK, here comes the main part. Now, I shall assume you are already familiar with the uploading procedures, able to deploy units with codes and get some sense of common strategies.

If so, let me show you how the codes are organized, how to extract useful information that helps you strategize and the strategies I tried.

[Note: My best algo might be v1.3 in my personal opinion, it achieved an ELO of 1743 with a rank of #16 once but falled after other players improved their algo I suppose. You can find it in my GitHub repo.]

Code Structures & Interfaces

In the starter-algo/ folder, the essentials are the gamelib/ folder (which is the library), algo_strategy.py (your main script that code up the strategy) and run.sh (script contains bash commands that run your algo_strategy.py).

The code is highly object oriented. In fact, we are dealing with 4 main objects: GameState, GameMap, GameUnit and AlgoCore. You can find the implementation of them in the corresponding python files inside gamelib/ library.

GameState

The GameState object (if initiated) provides information on resources, health, turn number, etc. And it also defines some constants (eg. ARENA_SIZE). You can take a look at its full list of Attributes in game_state.py. The methods it provides are quite basic (attempt_spawn, attempt_remove, get_resource, etc.) except one in my opinion: find_path_to_edge, which is quite handy because it offers you the route of an information unit. (It can be helpful to decide how to defend or attack).

GameMap

The GameMap object is basically a ‘2D array’ of some sort which models the Arena (some positions are invalid because they are outside the range of the Arena). The ‘element’ of the array is basically the GameUnit object which I will talk about next.

GameUnit

The GameUnit object models the units in the game, both information units (PING, EMP, SCRAMBLER) and the stationary units (FILTER, ENCRYPTOR, DESTRUCTOR) plus one more unit type called REMOVE, which is the unit (whatever it is) that will be removed in the next round. One thing to emphasize: the GameUnit object doesn’t come with the ‘location’ info, because it is not yet deployed!

AlgoCore

The AlgoCore object is the part that ‘talks’ to the game engine, meaning it sends and receives messages to and from the engine. It is primarily implemented in gamelib/algocore.py, but YOU are also supposed to implement another class called AlgoStrategy inside algo_strategy.py which inherits this one. In other words, the game engine is talking to the class you are going to implement!

Logics

Here’s the global overview of how your code runs under the hood: In the beginning, your algo_strategy.py is executed (i.e. the main function below):

if __name__ == "__main__":
algo = AlgoStrategy()
algo.start()

As you can see, an AlgoStrategy object is created and its start() method is executed. Since you are not supposed to implement the algo.start() method in algo_strategy.py, it will search the method in its super class (i.e. the base class it inherits from), which will be the one lying in algocore.py.

What exactly does algo.start() do? It will first receive a string called game_state_string from the game engine through get_command(). This string is stored in a so called ‘json’ format and later on will be converted to a python dictionary through json.loads().

After that, the string is used to instantiate a GameState object by calling:

if stateType == 0:
self.on_turn(game_state_string)

and therefore executing:

game_state = gamelib.GameState(self.config, turn_state)

in the algo_strategy.py on_turn() function, we now have the access to the information of the game state that enable us to strategize our next step.

After you deploy all your next moves (with the help of the interfaces in other modules of course), game_state.submit_turn() will be called. This will send our strategies back to the game engine and get it deployed for real this time.

Here comes the HIDDEN CHALLENGE of this game, I don’t want any new-coming players miss this part because this will put one into server disadvantage: the information we got above is merely the ones in what we call the ‘deploy phase’, i.e. the outcome of  the battle from last round, which doesn’t contain any dynamics information. If you want to know what exactly have happened (eg. the path that enemy’s information units take, the cause of the damages of your stationary units, etc.), you need to examine the code inside start() more carefully:

def start(self):
debug_write(BANNER_TEXT)

while True:
game_state_string = get_command()
if "replaySave" in game_state_string:
parsed_config = json.loads(game_state_string)
self.on_game_start(parsed_config)
elif "turnInfo" in game_state_string:
state = json.loads(game_state_string)
stateType = int(state.get("turnInfo")[0])
if stateType == 0:
self.on_turn(game_state_string)
elif stateType == 1:
# frame information here !!!
elif stateType == 2:
debug_write("Got end state quitting bot.")
break
else:
debug_write("Got unexpected string with turnInfo: {}".format(game_state_string))
else:
debug_write("Got unexpected string : {}".format(game_state_string))

As you can see from above: we call self.on_turn(game_state_string) whenever stateType == 0. What does this stateType variable tell us? It turns out:

  1. stateType == 0 represents the deploy phase
  2. stateType == 1 represents the action phase (which contains the information of each frame!) 
  3. stateType == 2 means the game has ended.

In other words, you need to code up another function called, let’s say, on_action(), which should be similar to on_turn() (that parses the game_state_string using json.loads() to create a dictionary of information) and call it under the else if statement like this:

......
......

if stateType == 0:
self.on_turn(game_state_string)
elif stateType == 1:
self.on_action(game_state_string)
elif stateType == 2:
debug_write("Got end state quitting bot.")
break
else:
......
......

My Strategy & Code

You can check my code here. [Disclaimer: I tried to make things bug-free, well-commented and comply to PEP8 style, however, there’s no guarantees on that. I was on a road-trip throughout the competition so I didn’t push myself very hard. I released it only meant to provide a skeleton code for new players’ reference. And I will refine it later if I get time.]

V0 series:

This series is based on strategies that first proposed in BLACKBEARD, one of the official BOSSES, which I loved the most in the beginning because of its aggressive style and effective attacks.

Later on I may add more details here ……

V1 series:

This series of algo is based on one of the greatest strategies on the LEADER BOARD: Transistors. So, credit to @KKROEPS, who controlled the LEADER BOARD for quite amount of time.

Here’s the big picture of it:

  • I created a hierarchical defense system inside create_my_hierarchical_defenses(), which will deploy the defense ‘level by level’ if we have cores.
  • I split the half Arena (on both sides) into seven regions and I collected the information of each round to check which region of mine get attacked and which region of my enemy’s is weak.
  • Then, of course, I strengthened my weakness and attack the enemy’s weakness spot.
  • There are two types of attacks that I performed:
    • EMP + scrambler (if there are many stationary units in the region that I want to attack)
    • a troop PINGs (if it is of very low defense level)

Later on, I noticed that my algo is vulnerable if the enemy build up straight walls and use EMPs to attack my front rows (then my algo will use lots of resources to fix that which is a waste), so I try to fix it in a hard-code manner (mostly in v1.4 and v1.5).

Other useful stuffs (that I know):

Extract Info from Human Walking Data with Dynamic Mode Decomposition

This is my first post, I’ll do something very simple: ADVERTISE the methodology that our applied mathematicians use in data analysis. Unlike statisticians or computer scientists, we usually start from ‘dynamical system’ point of view. To give you a taste of what I mean, a method called dynamic mode decomposition (DMD) will be offered as an example.

Continue reading “Extract Info from Human Walking Data with Dynamic Mode Decomposition”