Charlie Luo

30 Apr 2020

Battlecode 2020

Battlecode is an excellent annual AI competition held in January. I compete under a personal team called vvvvv, occasionally with a couple friends.

The competition itself is always about writing a bot to fight a 1v1 RTS-like game. However, a consistent theme is that bots are compartmentalized, so unlike an RTS, you couldn’t control every bot with global knowledge – rather, bots must make decisions with very limited information. Despite this notable difference, many mechanics and units can be compared to Starcraft, and throughout this writeup, I’ll be making these comparisons constantly. The competition is split up into several tournaments, with scrims being run between each tournament.



2020’s competition was called Soup. It was a completely different competition, more focused on outlasting the opponent and a departure from the RTS-styled units:

There were three notable twists:

  1. Every tile had a certain elevation, and units could only move and build on adjacent tiles with similar elevations
  2. Pollution, produced by neutral units (cows) and by refining soup, would slow down units performing actions in the polluted area
  3. Water level would gradually rise over the game - any tiles nearby a water tile would gradually be covered in water, destroying whatever unit occupied the tile


This time around, I paid close attention to every ranked scrim my bot got into, as well as all the meta strats on the Discord. And, since I learned my lesson from last year’s competition, I spent most of the Sprint tournament time building navigation, resource gathering, and communication code. Soon after, I modularized my code quite a bit and immediately started with the state based system I liked to use. And, for Sprint, I implemented a turtle strategy that was popular - simply surrounding the HQ with 8 landscapers and piling dirt as high as possible around the HQ. Unfortunately, this strategy was poorly implemented and not optimized, resulting in an early knockout from the Sprint bracket.

Out of the Sprint tournament, I noticed some important strategies being developed. Top bots often used a lattice strategy, building out a waffle-like pattern of elevation with landscapers and building huge numbers of vaporators in the holes. Vaporators could then be used to generally out-econ the opponent; building huge numbers of drones to crunch them (more on this later) or simply landscapers to directly tear down whatever turtle the opponent was building. When I saw this strategy, I believed this was absolutely the winning strategy – there were some insane rush bots, but map variance often caused rushes to fail, and any other strategy would just be outpaced by the amount of passive soup a lattice could generate. So, post-sprint, I wanted to build a lattice bot, but first, I decided to optimize and perfect my turtle.


To optimize the turtle bot, I focused on optimizing landscaper code and logic. Apart from bugfixes and maintaining an even wall, the most important piece of landscaper turtling code was the poor landscaper logic. On some maps, resources were extremely limited or water flooded the map extremely quickly, leading to situations where the landscapers surrounding my HQ would miss a corner and get flooded. The HQ ran some code that checked whether landscapers were still coming to build the wall – if water was rising too quickly or no more landscapers were coming, the HQ would do a broadcast indicating the landscapers surrounding the HQ should start building the wall immediately. This logic necessitated a communications overhaul, which prompted me to redo some of my miner resource finding code, which eventually lead to me working until the last hour to jam everything into the bot. Though the poor landscaper code is a relatively simple idea, the implementation had a huge number of edge cases that introduced some critical bugs that took a lot of time.

However, this poor landscaper code, along with some weird aggressive landscaper edge case logic and basic drone code took my bot extremely and surprisingly far in the seeding round, placing 8th. Most lattice bots weren’t refined enough to beat my bot at this tournament, especially with the additional consistency of the poor landscapers, and again, map variance often gave me enough time to build a wall and defensive drones before I would die. And, I was also rather lucky – matches tended to be coinflippy, and somehow, I won a ton of coinflips.

If anything, this tournament showed me that it’s better to test and try every unit and strategy before settling on whatever I thought was best. As mentioned before, I wrote some weird edge case code for landscapers and drones. Whenever landscapers got stuck, as a last resort, they would start tunneling straight for the other side of the map and try to kill the enemy HQ. Whenever drones spawned, they would sweep the map, picking up enemy units and dumping them in water, while broadcasting any water they see for other drones. This code was an unexpected lifesaver: when rush bots were nonoptimal, my drones would grab landscapers and stop the rush, and when turtle bots didn’t have drones, my aggressive landscapers would stick to their wall and slow their building.

Qualifiers, High School, and Reflection

Unfortunately, this performance did not hold into the qualifying tournament. I was occupied with schoolwork and Science Olympiad at the time, resulting in very little development time. This was the time period I wish I would have spent rewriting the turtle bot into a lattice – several other teams refined their lattices and began destroying my turtle bot in scrims. The only major change I made was to make the turtle greedier, so I could beat greedier bots: instead of just 8 surrounding landscapers, I added 12 more around each corner. However, I felt that this change actually made things worse, as I spent a lot more resources just building those new landscapers, which resulted in fewer drones and aggressive landscapers that could take advantage of the opponent’s unoptimized code. In addition, I ran into a resource thresholding issue – I couldn’t figure out the logic for when units should be built. To solve this, I think a lot more trial and error would be required, with many more scrims.

Overall, this resulted in vvvvv getting knocked out extremely quickly in qualifiers, though in the high school tournament I placed top 8. I wish I could have poured all my time into Battlecode at the time, especially after getting such a high seed, but unfortunately, I had other priorities. I do think I was much better than last year though; I kept notes and todo files, refactored my code so it made sense, and generally modularized code so much more nicely than I had before. I still wish I could have written a good lattice – I think I should have committed to it sooner, or just spent more time on Battlecode if I wanted to do better. Overall though, Battlecode 2020 was an incredibly fun experience with surprisingly interesting and innovative game specs.

Code for 2020 can be found here