Every couple of weeks, there is a contest hosted by the folks at codingame.com. This time around it was an optimization competition sponsored by Warner Bro.s for their upcoming movie “The Accountant”

The Accountant – Codingame Contest

If you just want to look at my statistics for this contest, you can just scroll to the bottom but, then you would miss seeing all the things I did wrong 🙂

Preamble

For the contest, you played as Christian Wolf and your aim was to secure data by killing all the enemies. Like all other codingame contest, the game was turn based, where each turn consisted of you reading the world state information from stdin and outputting your action to stdout. This would be repeated till either all the data-points were captured by enemies or all the enemies were killed or you ere killed. You also had a maximum of 100 ms to submit your action before you would timeout and die. If you got killed, you got a score of 0.
There were a bunch of test cases against which you could test your solution. After submitting, the solution was tested against a different set of validators. After each submission, you would get a score and your best score would be tracked for the leaderboard.
There are multiple languages that you can use to submit your solution but for obvious reasons C++ was what I went for.

What Went Right

I had already participated in a previous contest at codingame which gave me a base to start from. For the postmortem of the last contest click here.

I already had a setup which allowed me to combine multiple files into a single cpp file( which codingame requires) and also a chrome plugin that allowed me to work in Visual Studio as opposed to the web IDE which made for a faster workflow.

I spent the first day setting up the framework for the game. This included classes to encapsulate the GameState, Enemies etc. This allowed me to quickly tweak my algorithms and throwaway pieces of code that I felt were not working.

I spent the first few days trying a MCTS approach but that didn’t work. More on that in the What Went Wrong section. Once, I realized that I needed to do something different, I went back to the basics and coded a simple decision tree with no look forward. The 1st draft of this immediately raised my score to just under 20000 from about 4000. At the same time, my rank came under 300.

I managed to create a robust avoidance system. Getting a high score was dependent on staying alive and this was extremely crucial when trying to improve the algorithm. This also enabled me to try out different things without being afraid that I would get a lower score because I got killed.

I went for an iterative approach after my failed attempt at MCTS. Once I had the avoidance system in, I tried to kill the closest enemy. After that, it was trying to kill the enemy with the least number of shots. The impact was that for the most part with each submit, the score and hence my rank improved.

Continuous refactoring enabled me to keep my code readable and isolated the areas that I had to work on.

Using source control to keep track of the AI. I cannot even stress how helpful it was to go back a version or two, because the new logic was performing even worse than the one a few iterations before.

What Went Wrong

The contest started before the Hypersonic contest had ended. After the fatigue from the last contest, I did not start coding until a few days after the contest has begun.

Being in New Zealand for the contest from a timezone perspective sucks. When I went to bed, I was ranked 192. But, since there were still a few hours left, by the time the contest ended I had been pushed out off the top 200 which was the goal I had set for myself for this contest. I was also top ranked in New Zealand and India but someone managed to beat my score in the last few hours. Not that it mattered in this contest, but you also miss the first few hours of the contest because of the timezone.

I started off with MCTS but my understanding of the algorithm is still very limited. The fact that there are very few tutorials for this approach makes it much harder to understand and implement. I spent the first few days just coming up with a base which I could use for a MCTS approach. But, in the end I had to throw away all of that code. At least, I still have it for the next contest because of version control.

While implementing MCTS I went for a uniform (random) decision. In retrospect, I should have used something smarter.

Because of the fatigue from the last contest and work, I was just not able to improve my algorithm further than the decision tree.

The validators had no information associated with them. You had no idea why a validator failed or why your score went down, even though it had gone up in the IDE. This made tweaking your algorithm feel like a blind art.

It was impossible to make small tweaks and submit the solution to see how it fared because of a limitation which blocked you from submitting too many times continuously.

For a long time, I was unable to figure out whether I should be using ceiling, floor, round or truncate to round the various values and get the same values as were being provided.

What would I do differently

Understanding why my MCTS approach wasn’t working would be helpful as most of the top solutions in the contests seem to be using this approach. I am still not sure if it was the low number of moves that I was generating or the scoring function that was the issue. I am guessing its a combination of both.

Prioritizing saving data points over saving bullets would have given me a much higher score as each data point was worth 100 points.

Caching the targeted enemy.

I already had a simulation which I should have used to optimize my code rather than relying on the submission.

After getting the decision tree to work, I would have like to incorporate it into the MCTS and see if that helped.