Bout between the top 2 AI players

Postmortem of “Coders Strike Back”

Learning the basics of AI programming one competition at a time.

Justin LeFebvre
4 min readMar 7, 2016

--

Every couple of months, I like to participate in AI contests hosted by codingame.com. These come in a wide variety of types including various optimization problems and multiplayer AI bot competitions. This time around was the latter. I would like to share my experience with you.

This week was a Star Wars themed racing game. The objective was to program an AI bot who controlled two racers who simultaneously navigated the race while sabotaging the racers of the opposing bot. This lead to some interesting interactions, especially with bots closer to the top of the leaderboard.

The game was turn based, where each turn consisted of your program reading world state information from stdin, and outputting a line of 3 integers per racer representing the x and y coordinates you wished the bot to travel to and a thrust value which was an integer from 0 to 200. This loop would be repeated until one of the bots on either team crossed all of the checkpoints in order for a designated number of laps, or if one of the bots on either team didn’t cross a checkpoint within 100 turns. There was also another instruction which allowed the pod to put up a shield, increasing its mass when a collision happened and preventing the bot from being knocked as far off course. The downside of this is that when you activated the shield, that bot would be unable to move again for 3 turns.

I decided that I was going to use Rust as my submission language of choice. I’ve been enjoying learning the language while programming my other side project (which I’ll elaborate on in a future post) and felt comfortable enough to move forward with it rather than my “go to” language of Python. Choosing a different language while trying to complete a bot for a coding competition has bitten me in the past but I actually didn’t feel that it hindered my productivity significantly this go around. Though, I’m not sure if that’s a testament to my skills as a programmer or Rust as a language. (It’s probably the latter, let’s be honest).

My plan of attack for tackling this problem began with first getting the input and output sorted out. I started with parsing code which created a set of structs to hold the data parsed from stdin.

One of the main goals I had was to have all of the data well structured before attempting to program any of the behaviors for the bots. The next step was to figure out how to have the racers navigate to each of the waypoints in an optimized way and finally the last step was implement blocking and/or aggressive behaviors to give my racers an edge.

What went right?

One of the things I’m especially proud of this week is the code I wrote to have the bot output a thrust instruction to attempt to match the velocity of the racer to a specified desired velocity. I figured that being able to base the AI around velocity and having a function to translate that desired velocity into a thrust output instruction would be beneficial and it definitely paid off. Here it is in all it’s glory. I’m pretty happy with the result.

Since the contest didn’t provide a mechanism to use any libraries, I also had to write that Vector2d implementation which was very much inspired by nalgebra.

As you can see, I didn’t quite get around to finishing up implementing generic versions of all of the methods of the struct but Rust actually helps significantly in this case by allowing you to specify the implementation of methods based on a specific type (in this case, Vector2d<f64>).

What went wrong?

One of the things that didn’t go so well, unfortunately, was the actual implementation of the AI itself. My AI was pretty naive with respect to the actual logic it performed to get my racers around the checkpoints. Basically, I implemented an “Arrive” behavior based on this implementation with a static “slowing radius” of 5000 pixels. This proved to be good enough at the time, but I would have preferred to have figured out how to dynamically change the radius based on how quickly the racer was traveling and/or actually plot out an ideal path for the racers to follow so that they didn’t bleed off as much speed while taking turns.

Another thing I didn’t love about my implementation was that I spent a good deal of time on trying to figure out and implement avoidance behavior that I could never quite get to work properly. My theory is that I never had the racers look quite far enough to be able to correct their trajectory before actually colliding with an obstacle. That is definitely something I’ll look into in the future.

What would I do differently?

One thing I will remember to do in the future is focus more on breaking down the problem into reasonable chunks. I spent a good portion of my free time this week trying to solve both the ideal path through the checkpoints AND converting that path to individual thrust instructions instead of realizing that all that mattered in the short term was figuring out a reasonable enough pathing algorithm with respect to velocity and then having the function to convert that velocity to an instruction. Had I figured out that (now) clear abstraction earlier in the week, I would have had a lot more time to play with different behaviors by the end of the week.

Just like everyone else who entered the contest, my overall goal was, of course, to win the competition. Sadly, I have to say, I did not win, but I’m pretty proud of my 285/2,530 finish while finishing 2nd for the Rust language submissions and can’t wait for the next one.

--

--

Justin LeFebvre

Software Engineer, Bujinkan Budo Taijutsu practitioner, Gamer