Boris Fleysher

Building New Worlds, Together

Follow me on GitHub

Overview

In Fall 2018, I took CMPUT 350: Advanced Games Programming which focuses on advanced AI in games and C++. In this class, our project for the last half of the course was to build a StarCraft 2 bot from scratch and compete against the other teams. We formed into teams of 3-4 and went from there.

I had the pleasure of building our bot SegFault with:

I personally am relatively familiar with StarCraft, having played it for over a decade and lead the discussion on game strategy. We didn’t have a lot of time to make this bot, nor did we have any experience with making AI before this. As well, we all had other classes and responsibilities, meaning we had to pick a simple, robust strategy to work with if we wanted to do well.

We settled on a modification of a strategy I personally used when I played the game myself, 4-rax. Building an AI to be adaptable is tricky, since we’re using hand-coded rules. As such, we wanted to build a wall to prevent early rushes from destroying us. We were given the map, so we hard-coded the location of the wall to save time, built 4 barracks, researched stim-packs, combat shields and +1 attack, and then rushed out with our army and workers, hoping to overwhelm the opposing bot before they could achieve whatever they were trying for. We also implemented some micro-management to focus fire on enemy units, stutter-step and use stimpack to try and get the most of our ranged units.

Our bot did fairly well as most other bots played towards the late game, trying to expand and play from there. We overall finished 2nd out of 10 bots which our team was surprised with but proud of.

Building The Wall

In the following gif, we illustrate building the wall. We knew where the locations where for building the wall since we were given the map and could just hardcode it for the four different spawns, which we did for simplicity. This is a very common strategy for the faction we played, Terran. The strength of this faction is the use of ranged units that are weak in melee. By building a wall, we can remove the weakness in a defense situation. Which was something we were worried about, that someone would try to rush us and we would be horribly overrun. No bots attempted an incredibly quick attack, so this strategy was ultimately unnecessary. Likewise, we spent time programming SegFault to build an engineering bay and a missile tower to detect cloaked units incase someone tried rushing those. This also did not happen, though I still believe rushing cloaked units would do very well against amateur bots like ourselves.

While our wall worked, a future and more generalizable approach would be to use floodfill. The map is divided into buildable squares and non-buildable squares. All maps (as far as I know) place a ramp between your base and the only entrance. There have been maps with multiple ramps to enter the main base in the past, but the other ramps were typically blocked at the start of the game with destructable rocks. Given this fact, we can use flood fill to identify the seperate “regions” of the game and more specifically, identify which region of the game belongs to our base starting from our spawn point. We can then identify where the ramp is by comparing where we can path vs where we can build that is connected to our base region. By finding all the adjacent squares to the ramp, we should then be able to calculate how to build our two 2x2s and single 3x3 buildings.

Building the Wall

Stutterstep micro

In this following gif, you can really see SegFault microing all of the marines away from the enemy zealots simultaneously. The implementation was fairly naive - detect the closest hostile unit to a given marine, establish the direciton by subtracting one position from the other, and then move in that direction on a very short timer proportional to the marine’s attack speed. If we were out of range, we would issue an attack order on that enemy unit instead, which would let the StarCraft 2 engine handle moving back towards the target. A weakness of this “move away” approach is that when cornered, if there’s any geometry, there will be either an invalid command issued, or the marine will start to run past the hostile unit. Standing still is almost always worse, so running past the hostile unit, even for an attack or two is still better because we have changed the direction vector between the two units, and now should have new ground to run against. Lastely, we did not add stutter-stepping to the marauders, as we wanted them to serve as tanks to absorb the damage since they have a higher health pool and higher natural armor. In hindsight, I think we should have added stutter-stepping at a fraction, so they would still move away, but not as much (naturally putting them in the front). Lastely, a scanner sweep goes off in this gif. We did this as an emergency procedure to deal with cloaked units and to remove any high-ground advantage whenever our units take damage. It’s super basic, but works as a crude panic button and prevents a few silly situations, such as an army running up a ramp and dying, losing high ground vision and the rest of the army continues to trickle up the ramp ineffectively. Sutterstep micro

Priority Queue

The other big thing we worked on was the build order approach. In real time strategy games like StarCraft 2, you cannot build certain structures until you meet their prerequisites, typically other buildings. You cannot build a Tech Lab unless you have a Barracks. You cannot build a Barracks unless you have at least one Supply Depot. If your building gets destroyed, you need to rebuild it to be able to build anything that requires it. As well, you can only support a limited amount of “supply” of units before you need to build a special building, the Supply Depot to increase your maximum supply. If you reach maximum supply, you cannot produce any more units, neither worker or army units. It is imperative to build supply depots, preferably just before you hit your supply limit to avoid having to wait for one to be built to continue producing units. This makes supply depots incredibly important to build. Sometimes. If you fight with your army and lose a lot of supply, you won’t need to build supply depots for a long time until you reproduce your army. We needed a way to dynamically make sure we were always building something relevant and didn’t get “stuck” trying to build something illegally. Hence, the priority queue.

Whenever a building finished, we would check to see what buildings we had and add a new building to the queue that matched our build order strategy. For example, if we had two supply depots and a barracks, we would add 1 barracks and an engineering bay to the queue. We don’t want to add everything to the queue simultaenously, as we try to build whatever is at the top of the queue as soon as we have the resources to do so. Once the barracks and engineering bay finish, we can add 2 more barracks, as we begin to ramp up unit production. At any point, if our supply gets within a few points of the max supply limit, we queue up a supply depot with maximum priority. Over the course of a game, your output in terms of supply increasing changes and you’re able to output more supply per unit of time. We can increase the supply delta boundary when we need to build another depot based on how much supply we already have. I attempted to do it percent based but didn’t find any satisfactory results in our time frame, so this is certainly an area that needs more work.

Further Work

Besides the various improvements to add to our bot discussed in the previous steps, our bot is very limited as a rush bot. It has no concept of a longer game, nor does it try to adapt to the enemy in any meaningful way. It’s highly exploitable with no adaptability. I think we are also putting too many resources into unnecessary technologies, such as getting +1 attack which requires the engineering bay and takes a lot of time for little benefit in a rush strategy. Another area that I would love to see more effort put into is the scouting and knowing what the enemy is doing. I think refining the attack timing with the possibility of being a little bit flexible if the enemy does some very specific things could lead to a better more robust bot overall.

I have chosen to keep the Git Repository of the bot private at this time (to discourage other students from simply copying our work). If there is a desire to see it or discuss it, please contact me.