2. StarCraft II Python Bot: Building units

Everything is an unit.

Random Apex

Base Script

import sc2
from sc2 import run_game, maps, Race, Difficulty
from sc2.player import Bot, Computer
from sc2.ids.unit_typeid import *


class AlanBot(sc2.BotAI):
    async def on_step(self, iteration):
        await self.buildWorkers()
        await self.distribute_workers()
        await self.buildRefineries()

    async def buildWorkers(self):
        for commandcenter in self.units(UnitTypeId.COMMANDCENTER).ready.noqueue:
            if self.can_afford(UnitTypeId.SCV) and self.workers.amount < self.units(UnitTypeId.COMMANDCENTER).amount * 14 + 4:
                await self.do(commandcenter.train(UnitTypeId.SCV))

    async def buildRefineries(self):
        for commandcenter in self.units(UnitTypeId.COMMANDCENTER).ready:
            vespenes = self.state.vespene_geyser.closer_than(18.0, commandcenter)
            for vespene in vespenes:
                if not self.can_afford(UnitTypeId.REFINERY):
                    break
                worker = self.select_build_worker(vespene.position)
                if worker is None:
                    break
                if not self.units(UnitTypeId.REFINERY).closer_than(1.0, vespene).exists:
                    await self.do(worker.build(UnitTypeId.REFINERY, vespene))


run_game(maps.get("AbyssalReefLE"), [
    Bot(Race.Terran, AlanBot()),
    Computer(Race.Zerg, Difficulty.Hard)
], realtime=True)

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 it 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)

# End

Do you want me to release the [very old] code?

Previous

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Google photo

Estás comentando usando tu cuenta de Google. Salir /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s