2. StarCraft II Python Bot: Building units

 You need to check different things. When you want to create one Barrack you need:

And when you want to create a Marine you need:

  • Enough minerals
  • At least one Barrack
  • A limit of units to create

This is why we create two different functions: buildStructure() and buildOffensiveUnit().

- buildStructure()

In this function we are going to receive two parameters: the structure that we want to build, and the amount of structures that we want of it.

A good place to start building some Supply Depots and Barracks is around your first Command Center. So we are going to save the position in a variable:
commandCenter = self.units(UnitTypeId.COMMANDCENTER).ready.random
nearCC = await self.find_placement(UnitTypeId.SUPPLYDEPOT, commandcenter.position, placement_step=2)
But for the Barracks is a little more special: we need to check if there are already Supply Depots (see the Terran Tech Tree), if we have reached the limit of Barracks that we want, and if we can afford to built it.

The complete function is the following:
async def buildStructure(self, structureName, amount):
if self.units(UnitTypeId.COMMANDCENTER).ready.exists:
commandCenter = self.units(UnitTypeId.COMMANDCENTER).ready.random
workers = self.workers.gathering
nearCC = await self.find_placement(UnitTypeId.SUPPLYDEPOT, commandCenter.position, placement_step=2)
if structureName == 'supplydepot':
if self.supply_left < 6 and self.can_afford(UnitTypeId.SUPPLYDEPOT):
if workers:
w = workers.furthest_to(workers.center)
if nearCC:
await self.do(w.build(UnitTypeId.SUPPLYDEPOT, nearCC))
if structureName == 'barracks':
if self.units.of_type([UnitTypeId.SUPPLYDEPOT, UnitTypeId.SUPPLYDEPOTLOWERED, UnitTypeId.SUPPLYDEPOTDROP]).ready.exists
and self.units(UnitTypeId.BARRACKS).amount + self.already_pending(UnitTypeId.BARRACKS) < amount
and self.can_afford(UnitTypeId.BARRACKS):
if workers:
w = workers.furthest_to(workers.center)
if nearCC:
await self.do(w.build(UnitTypeId.BARRACKS, nearCC))
The nearCC variable store a location that match a free place for the Supply Depots size. You can add a second location that find a place for the size of the Barracks later.

- buildOffensiveUnit()

The offensive unit is the way of the program to spawn our Terrans. We pass it a name, where to build it, and how many of them.
async def buildOffensiveUnit(self, unitName, structureName, maxAmount):
structure = self.unitSelector(structureName)
unit = self.unitSelector(unitName)
if self.units(structure).ready.exists:
for struct in self.units(structure).ready.noqueue:
if self.can_afford(unit) and self.supply_left > 0 and self.units(unit).amount < maxAmount:
await self.do(struct.train(unit))

- Call in all in on_step()

For the bot to do things you need to put all your functions in the on_step() function. That way the program takes control on the synchs, and will launch everything written in the function. Maybe a good thing would be making a FSM machine, but this case is very simple.

You can give it a bit of logic for some amounts of units.
async def on_step(self, iteration):
await self.buildWorkers()
await self.distribute_workers()
await self.buildRefineries()
await self.buildStructure('supplydepot', 2000)
await self.buildStructure('barracks', 4)
await self.buildOffensiveUnit('marine', 'barracks', 27)
if self.units(UnitTypeId.MARINE).amount > 17:
await self.move('marine', True)
else:
await self.move('marine', False)