In my previous tutorials I briefly covered the process of selecting any unit of a particular type, such as an SCV, however I have received a few questions regarding how to select a specific unit and I have also received a few questions asking about the “magic” formula I used to determine how many of a specific unit are on screen. Since these are related I thought I might explain them in more detail here.

All of the code in this tutorial should be placed within the step() method of your agent. If you are unsure what this means, please check out my Building a Basic PySC2 Agent tutorial first.

1. Simple Single Unit Selection

The process I used in my previous tutorials was fairly simple, to start with you retrieve a list of x and y coordinates for a particular unit type. For example we can get the x and y coordinates for your command centre with the following:

What does that actually give you?

Great, lots of numbers. What do they actually represent?

By default the screen feature layer resolution is 84x84 pixels, each item of cc_x and cc_y can be combined to give you the coordinates of every pixel that the unit consumes. For example cc_x[0] is 45 and cc_y[0] is 31, so the first pixel is located at (45, 31), cc_x[1] is 46 and cc_y[1] is 31, so the second pixel is at (46, 31), and so on. In the image below you can see the location of the command centre according to these coordinates:

The screen feature coordinates of the command centre

With a single unit like the command centre, it is very easy to determine the centre of the unit, simply take the average of the x coordinates and the average of the y coordinates:

The result will be that x is 48 and y is 40. This is the middle of our command centre.

2. Random Unit Selection From Multiple

When there is only one unit of a particular type on the screen it’s easy, but what about when there are multiple units of a type? Let’s try the technique above with the vespene geysers:

This produces the following values:

These coordinates produce the following:

The screen feature coordinates of the vespene geysers

Cool, now let’s average them out and see what happens:

The x value will be 43 (42.5 rounded up) and y will be 39 (38.5 rounded up). Given a screen size of 84x84 that would put the location approximately in the middle on the screen. Unfortunately that means this technique would select nothing. Bummer!

Not to worry, there is a solution! If we don’t care which vespene geyser we select, we can simply choose a point at random. First we need to import the random module at the top of the file:

Then add this to your step() method:

First we choose a random index i and then use that index to collect the vespene_x and vespene_y values at that index. We can then use the x and y coordinates to select the unit.

The only problem with this technique is sometimes units can overlap each other, for example if an SCV is standing next to the vespene geyser you might accidentally choose it instead of the geyser (or vice versa). For this reason you should check to make sure that your selected unit is the one you intended to select.

To make sure you have selected a vespene geyser, you can do this:

This code takes the first unit in the single_select observation (index 0), and then the first entry in that unit (index 0) which happens to be the unit type. If it’s the vespene geyser we know we have done well, otherwise we might have to choose another random point.

3. Counting Units

Let’s make a quick jump to counting units as this leads us to being able to find the centre of specific units.

Many people asked me about the “magical” calculation I used in my previous tutorials to determine how many depots and barracks are on the screen.

If you know that you will only ever have one of a particular unit, for example the command centre, then the calculation is easy:

This just checks if there are any y coordinates returned. If so, you have a command centre, if not then your base is gone and you’ll be dead soon. RIP.

For more than one unit things get a little bit trickier, but only a little bit. Let’s take the x and y coordinates from the vespene geysers in the previous step:

We (the humans) know that there are two vespene geysers on the screen. Since there are 194 values in vespene_y , we can assume that each vespene geyser covers 97 pixels.

Please note: If you are using a screen resolution other than the default 84x84 then the number of pixels each unit covers will be different.

So now we can count the number of vespene geysers on the screen by dividing the number of values in vespene_y by 97. First let’s make sure to import the math module at the top of the file:

Then add this inside the step() method:

We round the value up since sometimes units can overlap slightly, shaving a few values off the total. Rounding up will generally give us the correct value, however units that overlap significantly like SCVs have a high chance of an inaccurate count, fortunately we have the worker count for that.

4. Selecting Specific Units

Now that we know how many vespene geysers are on the screen we can actually find the centre of each geyser fairly accurately using K-means clustering.

First of all, install the scikit-learn module and include the K-means clustering component at the top of our file:

Next we get the x and y values as per our previous examples:

Now let’s count the number of geysers:

Then we can combine the x and y values into pairs:

Now we can apply the K-means clustering method to determine the centre of each unit:

From there you can get the centre of each unit as follows:

Keep in mind the index of each geyser’s centre could change since the K-means algorithm initialises randomly. For example two consecutive calls of can produce the following:

Do not use kmeans.cluster_centers_[0] thinking that it will always refer to the geyser at (60, 21).

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.