Предыдущая статья — Python AI в StarCraft II. Часть II: рабочие и пилоны.
В третьей части серии статей про использование AI в игре Starcraft II мы займёмся добычей другого очень важного ресурса — газа. А также произведем экспансию в другие регионы.
В конце концов наша область истощится. Не забывайте, что нам нужно всего трое рабочих на один участок с полезными ископаемыми. В итоге нам нужно расширяться. Однако к расширению не следует относиться слишком легкомысленно. При расширении вы ослабляете свою защиту. Тем не менее, когда мы истощим нашу область, нам придется задуматься о расширении. К счастью, это для нас несложная задача, поскольку некоторая логика расширения уже для нас реализована. Давайте добавим add await self.expand()
в наш метод on_step
:
async def on_step(self, iteration): # what to do every step await self.distribute_workers() # in sc2/bot_ai.py await self.build_workers() # workers bc obviously await self.build_pylons() # pylons are protoss supply buildings await self.expand()
Теперь наш метод expand
имеет следующий вид:
async def expand(self): if self.units(NEXUS).amount < 2 and self.can_afford(NEXUS): await self.expand_now()
В приведенном выше случае мы ограничиваем наши расширения только двумя локациями. Rод для метода expand_now
можно найти в файле sc2 / bot_ai.py.
Наш полный код на данный момент выглядит следующим образом:
import sc2 from sc2 import run_game, maps, Race, Difficulty from sc2.player import Bot, Computer from sc2.constants import NEXUS, PROBE, PYLON class SentdeBot(sc2.BotAI): async def on_step(self, iteration): # what to do every step await self.distribute_workers() # in sc2/bot_ai.py await self.build_workers() # workers bc obviously await self.build_pylons() # pylons are protoss supply buildings await self.expand() async def build_workers(self): # nexus = command center for nexus in self.units(NEXUS).ready.noqueue: # we want at least 20 workers, otherwise let's allocate 70% of our supply to workers. # later we should use some sort of regression algo maybe for this? if self.can_afford(PROBE): await self.do(nexus.train(PROBE)) async def build_pylons(self): if self.supply_left < 5 and not self.already_pending(PYLON): nexuses = self.units(NEXUS).ready if nexuses.exists: if self.can_afford(PYLON): await self.build(PYLON, near=nexuses.first) async def expand(self): if self.units(NEXUS).amount < 2 and self.can_afford(NEXUS): await self.expand_now() run_game(maps.get("AbyssalReefLE"), [ Bot(Race.Protoss, SentdeBot()), Computer(Race.Terran, Difficulty.Easy) ], realtime=False)
Вот теперь-то мы развернемся! У нас есть 2 зоны командного центра и быстрорастущая коллекция ресурсов. Далее нужно сосредоточиться на том, что нам требуется для создания нашей армии (о ней речь пойдет в следующем уроке).
[machinelearning_ad_block]Когда мы приблизимся к нашей цели собрать армию и атаковать нашего врага, мы обнаружим, что нам понадобится топливо (газ). Газ собирают из гейзеров Веспен, разбросанных по карте. Для расы протоссов здание, необходимое для сбора этого газа, называется ассимилятором (класс Assimilator). Нам нужно построить его прямо над гейзерами.
Чтобы произвести постройку, нам надо импортировать класс Assimilator. Поэтому давайте для начала добавим его в наш стандартный импорт:
from sc2.constants import NEXUS, PROBE, PYLON, ASSIMILATOR
Теперь давайте добавим await self.build_assimilator()
в наш метод on_step
, после чего он приобретет следующий вид:
class SentdeBot(sc2.BotAI): async def on_step(self, iteration): await self.distribute_workers() # in sc2/bot_ai.py await self.build_workers() # workers bc obviously await self.build_pylons() # pylons are protoss supply buildings await self.expand() # expand to a new resource area. await self.build_assimilator() # getting gas
А теперь нам нужно написать этот метод. Как вы, вероятно, догадываетесь, найти правильный гейзер и построить прямо на нем здание может оказаться серьезной проблемой для написания кода. К счастью для нас, большая часть этой работы была уже сделана.
Например, мы можем найти доступные гейзеры рядом с нашим Нексусом с помощью API. У нас есть по одному Нексусу на каждую ресурсную область — так мы и будем их находить.
Затем нам также нужно подобрать для этого одного из наших рабочих. Чтобы не терять много времени, нужно взять ближайшего. Это тоже уже сделано за нас.
Наконец, мы можем построить что-то, обратившись к рабочему юниту. Для этого нужно просто выполнить .build (THING, WHERE)
.
Приступим.
Сначала мы хотим перебрать все наши нексусы, а затем все васпены (другое название для месторождений газа в Starcraft II), находящиеся в радиусе ~ 25
единиц от нексуса:
async def build_assimilator(self): for nexus in self.units(NEXUS).ready: vaspenes = self.state.vespene_geyser.closer_than(25.0, nexus)
Отлично, мы нашли васпены. Теперь мы хотим строить. Как обычно, если у нас нет необходимых средств, строить мы не можем:
async def build_assimilator(self): for nexus in self.units(NEXUS).ready: vaspenes = self.state.vespene_geyser.closer_than(25.0, nexus) for vaspene in vaspenes: if not self.can_afford(ASSIMILATOR): break
В противном случае нам нужно выбрать доступного рабочего:
async def build_assimilator(self): for nexus in self.units(NEXUS).ready: vaspenes = self.state.vespene_geyser.closer_than(25.0, nexus) for vaspene in vaspenes: if not self.can_afford(ASSIMILATOR): break worker = self.select_build_worker(vaspene.position) if worker is None: break
Если у нас есть место, средства и рабочий, можно строить ассимилятор (если, конечно, поблизости уже нет построенного)!
if not self.units(ASSIMILATOR).closer_than(1.0, vaspene).exists: await self.do(worker.build(ASSIMILATOR, vaspene))
Весь метод выглядит вот так:
async def build_assimilator(self): for nexus in self.units(NEXUS).ready: vaspenes = self.state.vespene_geyser.closer_than(25.0, nexus) for vaspene in vaspenes: if not self.can_afford(ASSIMILATOR): break worker = self.select_build_worker(vaspene.position) if worker is None: break if not self.units(ASSIMILATOR).closer_than(1.0, vaspene).exists: await self.do(worker.build(ASSIMILATOR, vaspene))
А полный код теперь имеет вот такой вид:
import sc2 from sc2 import run_game, maps, Race, Difficulty from sc2.player import Bot, Computer from sc2.constants import NEXUS, PROBE, PYLON, ASSIMILATOR class SentdeBot(sc2.BotAI): async def on_step(self, iteration): await self.distribute_workers() # in sc2/bot_ai.py await self.build_workers() # workers bc obviously await self.build_pylons() # pylons are protoss supply buildings await self.expand() # expand to a new resource area. await self.build_assimilator() # getting gas async def build_workers(self): # nexus = command center for nexus in self.units(NEXUS).ready.noqueue: # we want at least 20 workers, otherwise let's allocate 70% of our supply to workers. # later we should use some sort of regression algo maybe for this? if self.can_afford(PROBE): await self.do(nexus.train(PROBE)) async def build_pylons(self): if self.supply_left < 5 and not self.already_pending(PYLON): nexuses = self.units(NEXUS).ready if nexuses.exists: if self.can_afford(PYLON): await self.build(PYLON, near=nexuses.first) async def expand(self): if self.units(NEXUS).amount < 2 and self.can_afford(NEXUS): await self.expand_now() async def build_assimilator(self): for nexus in self.units(NEXUS).ready: vaspenes = self.state.vespene_geyser.closer_than(25.0, nexus) for vaspene in vaspenes: if not self.can_afford(ASSIMILATOR): break worker = self.select_build_worker(vaspene.position) if worker is None: break if not self.units(ASSIMILATOR).closer_than(1.0, vaspene).exists: await self.do(worker.build(ASSIMILATOR, vaspene)) run_game(maps.get("AbyssalReefLE"), [ Bot(Race.Protoss, SentdeBot()), Computer(Race.Terran, Difficulty.Easy) ], realtime=False)
Запустив его, мы можем убедиться, что идет строительство ассимиляторов:
Потрясающе! Теперь давайте займемся созданием армии! Но уже в следующей статье.
Следующая статья — Python AI в StarCraft II. Часть IV: создание AI армии.