Welcome to a tutorial series where we will be creating Python bots and AI for the game: StarCraft II. Around mid 2017, DeepMind and Blizzard (the creators of StarCraft II) announced a partnership and API for interacting with the strategy game.
Prior to this point, I had never actually played StarCraft II myself, but I have played many similar games like Command and Conquer and Age of Empires. The point of the games are to put you against other players, or computers, where you start with a "base," which allows you to build basic units that collect resources. From here, you can build more buildings that unlock new units, like combat units, and then you can do things like purchase/research upgrades for units or for even better units. Eventually, your objective is to amass an army to take out your opponent(s). In StarCraft II, there are 3 "races:" Terran, Protoss, and Zerg. The Protoss are more technology/robotics-based, so that's the race that I am going to at least start out playing with. It only seems fitting since we're coding an AI here.
For the Protoss, the following diagram breaks down all of the buildings and units that you can create:
The source for this image is Protoss Units, and is a fairly useful resource if you have no idea what you are doing in the actual game, like me.
As you can see, this is a tree-diagram, where one thing leads often to another. So, to build a "High Templar," you first must have a Nexus (you start with this, it's like your "command center"). From here, you build a Gateway, then a Cybernetics Core, then a Twilight Council, THEN Templar Archives.
Buildings, workers, and fighters all cost you resources and, arguably the most important element: Time. Even just considering your chosen path to the types of buildings, upgrades, and units, things are already getting fairly complex, but then we also engage in battles on a large variety of maps with different topologies. Some maps are more open, while others have far fewer paths you could take across it, you need to determine which areas to safely expand your base to...etc.
Finally, the enemy player is an obviously large factor too. Are they rushing you? Are they leaving you alone and amassing an army? What race are they? Are they researching upgrades? How much expansion have they done? ...and a whole lot more. Despite looking like a fairly simple game on the exterior, it's actually an extremely complex and competitive game.
I wont spend too much time explaining the game from here, since I am not an expert. All I did to pick up the game enough was just code basic bots, run them in real-time, and then make various changes in real-time to see what worked next well, then I would code that in, and repeat. Eventually, you can run games as fast as possible, and iterate quite fast.
First, you will need to get StarCraft II. The game is now free, so you just need to sign up for an account, grab the client, and install the game from here: Download Clients.
Next, I am going to be using python-sc2. There's also
pysc2, which is DeepMind's Python wrapper. I am going with
python-sc2 for now, since I think it's an easier one to pick up, but
pysc2 is more suitable most likely for deep-learning bots, since there are quite a few handy things pre-built for us there. Anyway, make sure to do
pip install sc2
As of the release of Python 3.7, certain things have broken, namely websockets. If you're having trouble, try to use Python 3.6 instead!
Now you are going to need maps!
Head to the Map Packs section of the Blizzard s2client and download some maps. I downloaded all of them, but you don't need to necessarily get them all. Once you have some maps in archives, extract them to a
Maps directory from within your
StarCraft II directory. Each set of maps should be in a *subdirectory* of the
Maps directory. For example, the file structure should be something like:
StarCraft II -Maps --Ladder2017Season1 ---AbyssalReefLE.SC2Map
If you installed StarCraft II to a custom directory/drive, then you will need to go into your 3rd party packages in Python, go to
sc2/paths.py, and change the
basedir value to match yours. Once you have the maps and the game, you're ready to rumble! Let's go over a basic example!
To begin, let's make some imports that we will be using:
import sc2 from sc2 import run_game, maps, Race, Difficulty from sc2.player import Bot, Computer
Everything here is just from the python-sc2 package to make our lives easier, including things like running games, picking races, and setting up our opponent.
Next, we need to create our bot's class. I am going to call my bot
SentdeBot, but you can call your bot whatever you like! Our bot will inherit from the
sc2.BotAI class. You can learn more about this class by heading to
sc2/bot_ai.py. Here, you can see there are quite a few methods here. Things like
known_enemy_units look fairly useful! Our bot's class will begin as:
So in the game, we obviously want to amass an army and annihilate our enemies. In order to do that, however, we first need resources. In StarCraft, that's minerals and gas. For minerals, we just need to mine them with workers. We start with 12 workers. For the
Protoss race that we are, our "workers" are called Probes. Our first order of business should be to spread these workers evenly across your jobs. For example, you would want no more than 3 workers per patch of minerals, as having any more will not yield minerals any quicker. Luckily for us, the
sc2.BotAI parent class has a method called
distribute_workers that does just this for us! So, at every step, we'd like to make sure our workers are indeed distributed:
class SentdeBot(sc2.BotAI): async def on_step(self, iteration): # what to do every step await self.distribute_workers() # in sc2/bot_ai.py
async keyword is used to make these asynchronous methods, so we don't need to wait on each one to complete to be doing other things. Don't let the
async keyword trip you up. You can learn more about Asyncio here if you like.
Let's assume that all we wanted to do was distribute our workers in games. In that case, we're done! To run the game, we will use
run_game. This function first takes the map you want to run, then a list of the players. After this, you can specify whether or not you want the game to be played in real-time (basically, regular speed or super fast).
run_game(maps.get("AbyssalReefLE"), [ Bot(Race.Protoss, SentdeBot()), Computer(Race.Terran, Difficulty.Easy) ], realtime=True)
So we're going to be playing on the
AbyssalReefLE map. The players will be a
Bot, which is the
Protoss race, and the bot class is
SentdeBot. The next and final player is the
Computer, which is of the
Terran race and easy difficulty.
Run this, and the game should eventually come up and your bot will be off to work!
Full code up to this point:
import sc2 from sc2 import run_game, maps, Race, Difficulty from sc2.player import Bot, Computer class SentdeBot(sc2.BotAI): async def on_step(self, iteration): # what to do every step await self.distribute_workers() # in sc2/bot_ai.py run_game(maps.get("AbyssalReefLE"), [ Bot(Race.Protoss, SentdeBot()), Computer(Race.Terran, Difficulty.Easy) ], realtime=True)
I hate to break it to you, but this isn't going to be winning you any games! But hey, you have a bot! In the next tutorial, we're going to work on adding more functionality to our bot so we can eventually start winning some games.