It has been a while since my last tutorial, but I have been holding out for PySC2 2.0, which was released only a few days ago. The latest version of the learning environment brings with it some great enhancements, but there are also a few backwards incompatible changes.

In this tutorial you will build a basic Zerg bot, one that will be able to produce Zerglings and send them across the map the attack the enemy. I have assumed you have basic Python programming knowledge and have been able to install PySC2 2.0.

Let’s get started.

1. Creating the Basic Agent

First, let’s import the essential modules:

Now we can create our agent:

The step method is the core part of our agent, it’s where all of our decision making takes place. At the end of every step you must return an action, in this case the action is to do nothing. We will add some more actions soon.

If you followed my previous tutorials you will notice that the format for the action has changed, previously the last line would have been:

I think the new format is a lot simpler and easier to follow, Timo has done a great job here.

The complete code for this step is here.

2. Add the Run Code

Now we set up the environment so we can run it. In my original tutorial we ran the agent on the command line and passed in many parameters, but here we will add some code to make it so the agent can be run without these extra parameters.

I have chosen to use the Abyssal Reef map here, it’s a bit more fun than the Simple 64 map from my previous tutorials.

Here we specify that the first player is our agent, and that the agent’s race is Zerg. You can choose another race simply by using sc2_env.Race.protoss , sc2_env.Race.terran or even sc2_env.Race.random if you are brave.

Next we specify that the second player is a bot, meaning that it uses the game’s internal AI, the bot’s race is random, and the difficulty level is very easy. The possible difficulty levels are very_easy , easy , medium , medium_hard , hard , harder , very_hard , cheat_vision , cheat_money and cheat_insane . Please note that medium_hard is actually “hard” in the game, hard is “harder”, harder is “very hard” and very_hard is “elite”.

In this space you could alternatively specify another agent, allowing two agents to play against each other!

Here we specify the screen and minimap resolutions, these are the defaults as they were in PySC2 1.x. These resolutions essentially determine how many “pixels” of data are in each feature layer, those layers include things like the terrain height, visibility, and unit ownership. Your bot can use these features to make decisions.

One of the new features in PySC2 2.0 is the addition of the RGB layer, essentially the rendered game that a human would see, and if you want to use that layer you would need to specify the dimensions here as rgb_dimensions , however we won’t cover that in this tutorial.

This parameter determines how many “game steps” pass before your bot will choose an action to take. By default this is set to 8, which is approximately 300 APM on “normal” game speed, we will set it to 160 to reduce the APM to 150. The extra APM is not necessary at this stage, and the game will finish more quickly if you take less actions.

Here we set the fixed length of each game, the default in PySC2 1.x was around 30 minutes at normal speed. In PySC2 2.0 you can set this value to 0 allows the game to run as long as necessary.

This last parameter is optional, however it will be handy for you to see the visualisation as it contains details about all of the observation layers available to your bot. By seeing these layers rendered on the screen you can understand them better.

The rest of the code is simply about looping, feeding the step details into the agent, receiving an action, and repeating until the game ends or until it is terminated.

Now you can run your agent:

python zerg_agent.py

You should see your agent harvesting minerals until it is ultimately overrun by the enemy.

The complete code for this step is here.

3. Select a Drone

Before the Zerg agent can produce any Zerglings, it will need a Spawning Pool. In order to build a Spawning Pool we need to select a Drone, so let’s do that.

One of the cool new features of PySC2 2.0 is the addition of feature units. I know this feature is cool because I made it. It allows you to get information about units on the screen that you cannot observe or that might be more difficult through the screen feature layer. We will use feature units to select a drone.

One of the other cool new features is the addition of a unit list, which allows you to retrieve unit types using a unit’s name. Previously we encoded our own unit types with something like _TERRAN_SCV = 45 but now we can use something like units.Terran.SCV . I also made this feature :)

Let’s add the unit list to the module import:

We’ll also need the random module for a few things.

Next we need to enable feature units:

Now, inside the step() method let’s use the feature units feature to get a list of all Drones on the screen:

That’s pretty cool, right?! Thanks to the new dot notation everything looks a lot cleaner, thanks Timo! Previously you might have done something like:

Now, let’s select a Drone:

The select_all_type parameter here acts like a CTRL+click, so all Drones on the screen will be selected. As you can see, the Drone’s x and y coordinates are accessible as properties. There are many more properties you can access, such as health , shields , energy , build_progress , and importantly ideal_harvesters and assigned_harvesters for bases and vespene.

You can run the agent now if you like, to make sure your Drones are selected.

The complete code for this step is here.

4. Build a Spawning Pool

Above the previous code, let’s make sure we have a Drone selected. Let’s add a small utility method to our class:

This code checks both the single and multi-selections to see if the first selected unit is the correct type. At the top of step() we can use this method with:

Next we want to make sure we can build a Spawning Pool. If we don’t have enough minerals this may not be possible and will result in a crash if we don’t check first:

Then we can select a random point on the screen, hopefully on some creep:

At the end we feed the random coordinates into the action.

Give it a run and see how it goes. With any luck you will have an abundance of Spawning Pools. How do we stop all of our Drones from becoming spawning pools?

Let’s add another utility method for selecting the units with a given unit type:

This is the same code you had above, but the unit_type can be swapped out as needed. Let’s use the method to replace the previous code:

Then we can use the method to get the spawning pools:

That’s better, now we’ll only build a spawning pool if we don’t already have one.

The complete code for this step is here.

5. Build Zerglings

Almost there! Now we have a Spawning Pool we can finally build some Zerglings. Let’s start by selecting all Larva on the screen:

Then we can create some Zerglings. Place this code just above the previous block:

Give it a test, you should get a coupled of Zerglings.

This is great, but we can’t build enough Zerglings to be effective, our supply is limited. We need Overlords.

The complete code for this step is here.

6. Spawn More Overlords

When we have Larva selected, we can spawn an Overlord if we have no free supply:

There’s one last thing I don’t like, let’s add another method:

We can now replace the action checks:

and:

and:

If you test your bot now you will find it produces a lot of Zerglings, it’s time to attack!

The complete code for this step is here.

7. Attack

Before we can attack, we need to know where we are and where the enemy is. For the purpose of this tutorial we will assume that the spawn locations are always at top-left and bottom-right. So we need to find out where we are, and attack the other.

Let’s create an __init__() method to initialise a variable:

That at the start of the step() method, add the following:

The obs.first() code checks if it’s the first step of the game. Then we get the centre x and y coordinates of our units on the minimap. There is a feature layer for this that exposes all units on the minimap based on who they belong to. I will explain this in more detail in a future tutorial.

Now we have our attack location, let’s attack. First we select our army:

Now before that, we can attack:

Test it out. Damn, our Zerglings get chewed up! Let’s wait until we have more Zerglings before we attack:

Yes that’s more like it!

The complete code for this step is here.

If you like this tutorial, please support me on Patreon. Also please join me on Discord, or follow me on Twitch, Medium, GitHub, Twitter and YouTube.