The Lessons I Learned From MIT’s Battlecode 2017 Competition (Post-Mortem)
I am not a good programmer. Unfortunately, my studies of Java began a mere week before BattleCode 2017 began, and as a result I would have been perfectly merry disappearing in round two, never to be seen again.
Against all odds, “test bot please ignore” defeated round after round of bots during the qualifying tournament to reach the top 32 teams out of over 450 participants in the final competition. Us. We. I. We were one win away from a paid flight to MIT to watch the final competition unfold in person, despite a pretty major failure in round five against “Oak’s Disciples” (where our bot stopped functioning comepletely for some inexplicable reason) that knocked us into the loser’s bracket. I’m beyond proud of myself, especially considering most of our primary competition were several years our senior.
We made a lot of mistakes that I have no excuse for, all of which I plan (or at least hope) to avoid during next year’s competition. With some fortune, you can learn from my errors and become a better Battlecoder. A day or three was lost several times during development, which is not a price you can afford to pay when you have 22 days to code an AI. First however, is some necessary context about this year’s brief and game specs.
BattleCode: Farming Simulator, Now With Guns
I’m going to explain in as little detail as possible. If you’re interested, the full brief can be found on the BattleCode website at http://www.battlecode.org
The game’s theme this year was aptly dubbed ‘bullet hell’ by the organisers. Unlike previous years where fighting has mostly been done with targeted lasers, this time we were provided with projectiles instead, allowing for creative dodging strategies. The game’s currency was unsurprising: bullets. Confusingly, units were built with bullets, but bullets were also fired by soldiers, scouts and tanks, meaning you couldn’t just send off your steel legion firing in a random direction as many of us dreamed of early on.
Another important facet was that bullets could also damage their own team, meaning it was important to make sure there was nothing in the way of your firing. Scouts in particular could move faster than their bullets, meaning it was important you didn’t fire and then shoot yourself next turn.
There were six units in this edition of BattleCode which were all well balanced (at least after the first week) and useful in their own way. These were:
Archons — The headquarters of your operation. Can move around slowly and build Gardeners. High HP, but once it’s gone, it’s gone, and you can no longer build your economy.
Gardeners — Despite sounding like a cruel joke in a battle scene, the addition of these worker units was one of the core twists this year. Gardeners could plant ‘bullet trees’ that would provide resources based on their health that round. Gardeners could ‘water’ these to increase their health. Thus, having a stable network of Gardeners and trees was imperative to success.
Scouts —Fast moving, low damage units who ignore terrain.
Soldiers — You guessed it. Standard fighting unit with a gun. Also has the option to fire a three-bullet cone (a ‘triad’) or a five-bullet cone (a ‘pentad’). To this day, I am lost as to what the Chinese mafia has to do with shooting three bullets instead of one.
Tank — The same as soldiers, except larger, slower and more expensive. To counteract their weaknesses, their bullets did increased damage and travelled faster. Perfect for dealing with pesky scouts who could easily dodge most other units. A mildly useless ability they posessed was damaging trees by attempting to move onto them. Any team with common sense would just build Lumberjacks instead to deal with neutral trees.
Lumberjacks — Around the map were a varying number of neutral trees. These had a fair amount of health and blocked movement for all units other than scouts. These could be shot down (terribly inefficient and expensive), run over with tanks (terribly inefficient and expensive), or be cut down by lumberjacks. Some maps were almost entirely filled with these trees, making lumberjacks a staple of most compositions, especially if the Archon could sense a lot of neutral trees around the spawn. Another important feature was that lumberjacks had a melee attack where they would damage every unit in a circle around it, regardless of team. This fed into our strategy later on, but I’ll come to that in a bit.
There are a lot more nuances I won’t mention for simplicity’s sake.
The Formation of Test Bot Please Ignore
In mid December, the developers of Battlecode posted an IAmA on Reddit which quickly hit the front page. As a direct result of this, the competition showing this year was roughly quadruple what it had been in previous years. After some time spent reading through the thread, I posted on the forums asking for a team, and within a few hours I had picked up a few more Redditors to complete the merry band.
After some waiting, the specs were announced, and we were off.
Mistake 1: Trying to Write a ‘Good’ Bot Before a Working one
In the first few days, the winning teams’ strategy was basically ‘Build gardeners, have them run off and plant some trees, repeat’. We were in the unfortunate mindset that once our super-good and super well-structured bot was ready, we’d easily stomp the competition. We started developing high level mechanics like multiple bot behaviours before we’d even managed to make our gardeners function.
It sounds ridiculous now that I put finger to keyboard, but it’s so easy to start running before you can walk. I beseech all teams reading this in preparation for BattleCode 2018 to actually make a working bot that wins matches before you do anything else. Contrary to what other post mortems tell you, sure as hell go and try to conquer the leaderboards in those first few days. What it means is that you have a functional bot significantly better than anyone else’s functional bot, which is not ever a bad place to be.
And so it was, as the sprint tournament approached, that we didn’t have a bot at all. Too much time was spent executing the former half of our name, which meant that it looked as if all our opponents were going to successfully complete the second half. I panicked, and made the stupid decision of giving up. A day or two before the tournament however, my teammate emerged with his brain child…
Mistake 1.5: Trying to Copy Other Strategies
This was honestly a massive waste of time on my part. A few days after scrimmages launched, a team called Waterloo Omega Ruby (WOR) had a slightly different strategy of planting trees.
The most popular way of planting trees. Gardeners found a location based on arbitrary conditions, and planted five trees in all directions but one around themselves. Mainly used because it was dead easy to implement as well as being decently effective.
Waterloo used a spread out forest of trees with a network of gardeners to maintain them. This was protected by a ring of soldiers around the perimeter. This strategy annihilated the scout heavy meta early on as attacked gardeners could simply run away to where a soldier was
I wasted a whole day trying to replicate this to underwhelming results. Meanwhile, the sprint tournament drew nearer and nearer. I should have been working on my soldier code. I should have been working to counter aggressive scouts. But I didn’t.
The Rush Bot is Born
I honestly cannot take any credit for this. I was feeling more than a bit lost, with not a lot of ideas and far too many bugs to work on. There was so much structure left to build that I couldn’t really start on the actual bot. A few days before the tournament, my teammate pushes a new branch which focuses on using scouts that hide in trees to kill the enemy gardeners. While we were one of the first teams to adopt this strategy, it became overwhelmingly the dominant meta in the sprint tournament. The problem? We started so much later that we carried this strategy out a lot worse than the competition. We quickly lost our first game and were knocked out of the running. By this point, two of our team had dropped out leaving just me and Milo.
Mistake 2: Thinking that Scouts Wouldn’t be Nerfed After Their Ridiculously Strong Showing
While other teams developed their soldiers to defend from scout attacks, Milo slavishly worked on his scout ai. By midway through the seeding tournament deadline, I have no doubt that we had one of the best scout ai around. We invariably won duels with other scouts in addition to occasionally trading with a gardener.
But we were too late. The scout meta was long gone.
By this point, soldier code was effective defending gardeners, and with scouts’ reduced damage, they could no longer take out gardeners before being nuked from orbit by patrolling soldiers. Again, Milo came to the rescue with his mass lumber bot. The strategy was dead simple. We had played with build orders dependent on how many trees surrounded the spawn, but it didn’t provide satisfactory results. built a single scout to take bullets from neutral trees (something not a lot of teams did, meaning our economy got a huge boost at the start) and then built a bunch of other units in a set order, which were mainly lumberjacks. The seeding tournament went alright and we were eliminated in round four after two wins and two losses.
Developing the Lumber Rush
We knew we could never out-code or out-algorithm students from prominent colleges, or even out-work teams of four high school students who would see each other every day to work on the bot. The plan was that if we used a strategy most teams had never seen before, their bot would react in a sub-optimal way. The orthodox meta of gardener nests protected by an expanding ring of soldiers was thrown to the wayside. In retrospect, lumberjacks were a bit underpowered, but for the most part that didn’t matter. One thing we noticed was that we died a lot to fast soldier rushes. We never truly managed to resolve this issue, nor the slew of others that plagued us (like our archon getting stuck in front of all our units) although to try and counteract this we introduced a sort of ‘gas cloud’ of lumberjacks, where they’d try to remain 2 units away from every other lumberjack. As we built more units, the cloud expanded until it encompassed the enemy base.
Mistake 3: Not Including Rush Code
We were ribbed by high ranking teams simply because their soldiers knew how to run backwards. That’s it. Running Backwards. Because lumberjacks moved at the same speed as soldiers, they could never close the gap on good soldier micro. Fortunately, this problem never came to haunt us, although if we’d made top 16 I have no doubt we’d be kicking ourselves. The match above was taken from our round 5 victory in the qualifying tournament, and the casters were just begging our bot to attack before our opponent could amass soldiers. We had over 110 lumberjacks by the time our cloud found their nest. If we were up against better soldier AI (the fools didn’t make their soldiers run backwards) then we would have lost the best of three for sure. Certainly a sobering experience.
Mistake 3: Not Creating Maps Specifically to Break our Bots
We might have even made top 16 had we tried a little bit harder to break our bots. What we didn’t know at that time was that the devs are known for making maps to test bots in the final tournament. And then disaster struck in round five. We were unfortunately matched against the team predicted to win the competition, Oak’s Disciples. But to aggravate matters, our bot decided to not work! The maps for that round were designed so that the archon couldn’t move. But due to the way our unit building code worked, we never tried to build in the free directions. So I sat there, frozen in shock, as my opponent’s blue legions marched towards my location.
Our Greatest Victory
Round 5, loser’s bracket B. At this point, it’s all or nothing. We are fortunately not matched against one of the highest seeded teams, so there was no tactical stomp inbound. We lost the first map because of the problem fighting good soldier code, won the second because it was open and the balloon just expanded and expanded until we destroyed them. And then the third map.
Victory. A blowout victory. Because the map was filled with trees, our gardener army just built and built into this massive blob you see in the video. It was a wonderful feeling sitting with my family as Test Bot forces engulfed <insert farming pun here>.
Battlecode has been the most fun programming I’ve had in a long time. It was in fact the first time I’ve ever applied programming to an actual problem that needed a real solution, and I absolutely recommend it to anyone who can code. Prizes are only available to students, but the competition is open to all.
I plan to take part in Battlecode 2018, and 2019, and hopefully every year up to 2023 which would be my last year as an undergrad.
2018 Word of Warning
The Battlecode organizers tried some new things this year. I’m going to avoid being too critical of them, since I think it’s definitely a lack of experience that fueled a lot of the scope creep. Additionally, prizes are now heavily weighted towards US participants, despite numbers of US and International teams in the finals being roughly equal in previous years. I’m gonna write about 2018 in brief here, primarily as a message to future developers to hopefully avoid the mistakes of this year’s competition.
I did Battlecode 2018 for only a short week. As much as I’m not trying to be harsh to what is ultimately a bunch of grad students trying their best to develop a cool programming competition, Battlecode 2018 was butchered. Almost nobody could get it running for the first four days, the opening meta was extremely broken, and because of all the delays there was much less time to fix it. This was compounded by the fact that the devs decided to only permit 4/16 international teams through to the finals, held in a separate tournament a few days before the US qualifiers, and disqualified non-US teams from the highschool bracket. I feel… disenfranchised to say the least. Scrimmage servers technically made it up a day before the sprint tournament, however bots that crashed would be awarded the win, leading to poor ranking quality. The sprint tournament was also plagued by issues, such as the devs insistence on watching each and every match in full against the protests of pretty much everyone, despite many bots not even being basically functional. I went out in round 0 as a result of RNG seedings putting me up against a top 8 team. After calling the sprint tournament ‘a joke’ in the Discord, I was promptly banned by the devs from it. I think that says all that needs to be said. What I said was certainly out of line, but so was wordlessly banning me from the Discord. To future participants, I say to think hard about whether or not to compete in Halite instead. Ultimately, I think the premise of Battlecode is far superior, but if the execution next year is the same as this year, I’m left with no choice but to abandon ship, Captain Jack Sparrow style.