個人環境是python3.6,sc2包0.11.1
機器學習包下載連接:pysc2
地圖下載連接maps
pysc2是DeepMind開發的星際爭霸Ⅱ學習環境。 它是封裝星際爭霸Ⅱ機器學習API,同時也提供Python加強學習環境。
以神族爲例編寫代碼,神族建築科技圖以下:
python
# -*- encoding: utf-8 -*- ''' @File : __init__.py.py @Modify Time @Author @Desciption ------------ ------- ----------- 2019/11/3 12:32 Jonas None ''' import sc2 from sc2 import run_game, maps, Race, Difficulty from sc2.player import Bot, Computer class SentdeBot(sc2.BotAI): async def on_step(self, iteration: int): await self.distribute_workers() run_game(maps.get("AcidPlantLE"), [ Bot(Race.Protoss, SentdeBot()), Computer(Race.Terran, Difficulty.Easy) ],realtime = True)
注意
game_data.py的assert self.id != 0
註釋掉
pixel_map.py的assert self.bits_per_pixel % 8 == 0, "Unsupported pixel density"
註釋掉
不然會報錯git
運行結果以下,農民開始採礦
github
能夠正常採礦併發
import sc2 from sc2 import run_game, maps, Race, Difficulty from sc2.player import Bot, Computer from sc2.constants import * class SentdeBot(sc2.BotAI): async def on_step(self, iteration: int): await self.distribute_workers() await self.build_workers() await self.build_pylons() # 建造農民 async def build_workers(self): # 星靈樞紐(NEXUS)無隊列建造,能夠提升晶體礦的利用率,不至於佔用資源 for nexus in self.units(UnitTypeId.NEXUS).ready.noqueue: # 是否有50晶體礦 if self.can_afford(UnitTypeId.PROBE): await self.do(nexus.train(UnitTypeId.PROBE)) ## 建造水晶 async def build_pylons(self): ## 供應人口和現有人口之差小於5且水晶不是正在建造 if self.supply_left<5 and not self.already_pending(UnitTypeId.PYLON): nexuses = self.units(UnitTypeId.NEXUS).ready if nexuses.exists: if self.can_afford(UnitTypeId.PYLON): await self.build(UnitTypeId.PYLON,near=nexuses.first) ## 啓動遊戲 run_game(maps.get("AcidPlantLE"), [ Bot(Race.Protoss, SentdeBot()), Computer(Race.Terran, Difficulty.Easy) ],realtime = True)
運行結果以下,基地造農民,農民造水晶
dom
代碼以下機器學習
import sc2 from sc2 import run_game, maps, Race, Difficulty from sc2.player import Bot, Computer from sc2.constants import * class SentdeBot(sc2.BotAI): async def on_step(self, iteration: int): await self.distribute_workers() await self.build_workers() await self.build_pylons() await self.build_assimilators() await self.expand() # 建造農民 async def build_workers(self): # 星靈樞紐(NEXUS)無隊列建造,能夠提升晶體礦的利用率,不至於佔用資源 for nexus in self.units(UnitTypeId.NEXUS).ready.noqueue: # 是否有50晶體礦 if self.can_afford(UnitTypeId.PROBE): await self.do(nexus.train(UnitTypeId.PROBE)) ## 建造水晶 async def build_pylons(self): ## 供應人口和現有人口之差小於5且建築不是正在建造 if self.supply_left < 5 and not self.already_pending(UnitTypeId.PYLON): nexuses = self.units(UnitTypeId.NEXUS).ready if nexuses.exists: if self.can_afford(UnitTypeId.PYLON): await self.build(UnitTypeId.PYLON, near=nexuses.first) ## 建造吸取廠 async def build_assimilators(self): for nexus in self.units(UnitTypeId.NEXUS).ready: # 在瓦斯泉上建造吸取廠 vaspenes = self.state.vespene_geyser.closer_than(15.0,nexus) for vaspene in vaspenes: if not self.can_afford(UnitTypeId.ASSIMILATOR): break worker = self.select_build_worker(vaspene.position) if worker is None: break if not self.units(UnitTypeId.ASSIMILATOR).closer_than(1.0,vaspene).exists: await self.do(worker.build(UnitTypeId.ASSIMILATOR,vaspene)) ## 開礦 async def expand(self): if self.units(UnitTypeId.NEXUS).amount<3 and self.can_afford(UnitTypeId.NEXUS): await self.expand_now() ## 啓動遊戲 run_game(maps.get("AcidPlantLE"), [ Bot(Race.Protoss, SentdeBot()), Computer(Race.Terran, Difficulty.Easy) ], realtime=False)
run_game的realtime設置成False,能夠在加速模式下運行遊戲。
運行效果以下:
能夠建造吸取廠和開礦async
import sc2 from sc2 import run_game, maps, Race, Difficulty from sc2.player import Bot, Computer from sc2.constants import * class SentdeBot(sc2.BotAI): async def on_step(self, iteration: int): await self.distribute_workers() await self.build_workers() await self.build_pylons() await self.build_assimilators() await self.expand() await self.offensive_force_buildings() await self.build_offensive_force() # 建造農民 async def build_workers(self): # 星靈樞紐(NEXUS)無隊列建造,能夠提升晶體礦的利用率,不至於佔用資源 for nexus in self.units(UnitTypeId.NEXUS).ready.noqueue: # 是否有50晶體礦 if self.can_afford(UnitTypeId.PROBE): await self.do(nexus.train(UnitTypeId.PROBE)) ## 建造水晶 async def build_pylons(self): ## 供應人口和現有人口之差小於5且建築不是正在建造 if self.supply_left < 5 and not self.already_pending(UnitTypeId.PYLON): nexuses = self.units(UnitTypeId.NEXUS).ready if nexuses.exists: if self.can_afford(UnitTypeId.PYLON): await self.build(UnitTypeId.PYLON, near=nexuses.first) ## 建造吸取廠 async def build_assimilators(self): for nexus in self.units(UnitTypeId.NEXUS).ready: # 在瓦斯泉上建造吸取廠 vaspenes = self.state.vespene_geyser.closer_than(15.0,nexus) for vaspene in vaspenes: if not self.can_afford(UnitTypeId.ASSIMILATOR): break worker = self.select_build_worker(vaspene.position) if worker is None: break if not self.units(UnitTypeId.ASSIMILATOR).closer_than(1.0,vaspene).exists: await self.do(worker.build(UnitTypeId.ASSIMILATOR,vaspene)) ## 開礦 async def expand(self): if self.units(UnitTypeId.NEXUS).amount<2 and self.can_afford(UnitTypeId.NEXUS): await self.expand_now() ## 建造進攻性建築 async def offensive_force_buildings(self): if self.units(UnitTypeId.PYLON).ready.exists: pylon = self.units(UnitTypeId.PYLON).ready.random if self.units(UnitTypeId.PYLON).ready.exists: # 根據神族建築科技圖,折躍門建造事後才能夠建造控制核心 if self.units(UnitTypeId.GATEWAY).ready.exists: if not self.units(UnitTypeId.CYBERNETICSCORE): if self.can_afford(UnitTypeId.CYBERNETICSCORE) and not self.already_pending(UnitTypeId.CYBERNETICSCORE): await self.build(UnitTypeId.CYBERNETICSCORE,near = pylon) # 不然建造折躍門 else: if self.can_afford(UnitTypeId.GATEWAY) and not self.already_pending(UnitTypeId.GATEWAY): await self.build(UnitTypeId.GATEWAY,near=pylon) # 造兵 async def build_offensive_force(self): # 無隊列化建造 for gw in self.units(UnitTypeId.GATEWAY).ready.noqueue: if self.can_afford(UnitTypeId.STALKER) and self.supply_left>0: await self.do(gw.train(UnitTypeId.STALKER)) ## 啓動遊戲 run_game(maps.get("AcidPlantLE"), [ Bot(Race.Protoss, SentdeBot()), Computer(Race.Terran, Difficulty.Easy) ], realtime=False)
運行結果以下:
能夠看到,咱們建造了折躍門和控制核心並訓練了追獵者學習
代碼以下ui
import sc2 from sc2 import run_game, maps, Race, Difficulty from sc2.player import Bot, Computer from sc2.constants import * import random class SentdeBot(sc2.BotAI): async def on_step(self, iteration: int): await self.distribute_workers() await self.build_workers() await self.build_pylons() await self.build_assimilators() await self.expand() await self.offensive_force_buildings() await self.build_offensive_force() await self.attack() # 建造農民 async def build_workers(self): # 星靈樞紐(NEXUS)無隊列建造,能夠提升晶體礦的利用率,不至於佔用資源 for nexus in self.units(UnitTypeId.NEXUS).ready.noqueue: # 是否有50晶體礦 if self.can_afford(UnitTypeId.PROBE): await self.do(nexus.train(UnitTypeId.PROBE)) ## 建造水晶 async def build_pylons(self): ## 供應人口和現有人口之差小於5且建築不是正在建造 if self.supply_left < 5 and not self.already_pending(UnitTypeId.PYLON): nexuses = self.units(UnitTypeId.NEXUS).ready if nexuses.exists: if self.can_afford(UnitTypeId.PYLON): await self.build(UnitTypeId.PYLON, near=nexuses.first) ## 建造吸取廠 async def build_assimilators(self): for nexus in self.units(UnitTypeId.NEXUS).ready: # 在瓦斯泉上建造吸取廠 vaspenes = self.state.vespene_geyser.closer_than(15.0,nexus) for vaspene in vaspenes: if not self.can_afford(UnitTypeId.ASSIMILATOR): break worker = self.select_build_worker(vaspene.position) if worker is None: break if not self.units(UnitTypeId.ASSIMILATOR).closer_than(1.0,vaspene).exists: await self.do(worker.build(UnitTypeId.ASSIMILATOR,vaspene)) ## 開礦 async def expand(self): if self.units(UnitTypeId.NEXUS).amount<3 and self.can_afford(UnitTypeId.NEXUS): await self.expand_now() ## 建造進攻性建築 async def offensive_force_buildings(self): if self.units(UnitTypeId.PYLON).ready.exists: pylon = self.units(UnitTypeId.PYLON).ready.random # 根據神族建築科技圖,折躍門建造事後才能夠建造控制核心 if self.units(UnitTypeId.GATEWAY).ready.exists and not self.units(UnitTypeId.CYBERNETICSCORE): if self.can_afford(UnitTypeId.CYBERNETICSCORE) and not self.already_pending(UnitTypeId.CYBERNETICSCORE): await self.build(UnitTypeId.CYBERNETICSCORE,near = pylon) # 不然建造折躍門 elif len(self.units(UnitTypeId.GATEWAY))<=3: if self.can_afford(UnitTypeId.GATEWAY) and not self.already_pending(UnitTypeId.GATEWAY): await self.build(UnitTypeId.GATEWAY,near=pylon) ## 造兵 async def build_offensive_force(self): # 無隊列化建造 for gw in self.units(UnitTypeId.GATEWAY).ready.noqueue: if self.can_afford(UnitTypeId.STALKER) and self.supply_left>0: await self.do(gw.train(UnitTypeId.STALKER)) ## 尋找目標 def find_target(self,state): if len(self.known_enemy_units)>0: # 隨機選取敵方單位 return random.choice(self.known_enemy_units) elif len(self.known_enemy_units)>0: # 隨機選取敵方建築 return random.choice(self.known_enemy_structures) else: # 返回敵方出生點位 return self.enemy_start_locations[0] ## 進攻 async def attack(self): # 追獵者數量超過15個開始進攻 if self.units(UnitTypeId.STALKER).amount>15: for s in self.units(UnitTypeId.STALKER).idle: await self.do(s.attack(self.find_target(self.state))) # 防衛模式:視野範圍內存在敵人,開始攻擊 if self.units(UnitTypeId.STALKER).amount>5: if len(self.known_enemy_units)>0: for s in self.units(UnitTypeId.STALKER).idle: await self.do(s.attack(random.choice(self.known_enemy_units))) ## 啓動遊戲 run_game(maps.get("AcidPlantLE"), [ Bot(Race.Protoss, SentdeBot()), Computer(Race.Terran, Difficulty.Medium) ], realtime=False)
運行結果以下
能夠看到,4個折躍門訓練追獵者並發動進攻。3d
咱們目前的代碼只能擊敗中等和簡單電腦,那麼如何擊敗困難電腦呢?
代碼以下
import sc2 from sc2 import run_game, maps, Race, Difficulty from sc2.player import Bot, Computer from sc2.constants import * import random class SentdeBot(sc2.BotAI): def __init__(self): # 通過計算,每分鐘大約165迭代次數 self.ITERATIONS_PER_MINUTE = 165 # 最大農民數量 self.MAX_WORKERS = 65 async def on_step(self, iteration: int): self.iteration = iteration await self.distribute_workers() await self.build_workers() await self.build_pylons() await self.build_assimilators() await self.expand() await self.offensive_force_buildings() await self.build_offensive_force() await self.attack() # 建造農民 async def build_workers(self): # 星靈樞鈕*16(一個基地配備16個農民)大於農民數量而且現有農民數量小於MAX_WORKERS if len(self.units(UnitTypeId.NEXUS))*16>len(self.units(UnitTypeId.PROBE)) and len(self.units(UnitTypeId.PROBE))<self.MAX_WORKERS: # 星靈樞紐(NEXUS)無隊列建造,能夠提升晶體礦的利用率,不至於佔用資源 for nexus in self.units(UnitTypeId.NEXUS).ready.noqueue: # 是否有50晶體礦建造農民 if self.can_afford(UnitTypeId.PROBE): await self.do(nexus.train(UnitTypeId.PROBE)) ## 建造水晶 async def build_pylons(self): ## 供應人口和現有人口之差小於5且建築不是正在建造 if self.supply_left < 5 and not self.already_pending(UnitTypeId.PYLON): nexuses = self.units(UnitTypeId.NEXUS).ready if nexuses.exists: if self.can_afford(UnitTypeId.PYLON): await self.build(UnitTypeId.PYLON, near=nexuses.first) ## 建造吸取廠 async def build_assimilators(self): for nexus in self.units(UnitTypeId.NEXUS).ready: # 在瓦斯泉上建造吸取廠 vaspenes = self.state.vespene_geyser.closer_than(15.0,nexus) for vaspene in vaspenes: if not self.can_afford(UnitTypeId.ASSIMILATOR): break worker = self.select_build_worker(vaspene.position) if worker is None: break if not self.units(UnitTypeId.ASSIMILATOR).closer_than(1.0,vaspene).exists: await self.do(worker.build(UnitTypeId.ASSIMILATOR,vaspene)) ## 開礦 async def expand(self): # (self.iteration / self.ITERATIONS_PER_MINUTE)是一個緩慢遞增的值,動態開礦 if self.units(UnitTypeId.NEXUS).amount<self.iteration / self.ITERATIONS_PER_MINUTE and self.can_afford(UnitTypeId.NEXUS): await self.expand_now() ## 建造進攻性建築 async def offensive_force_buildings(self): print(self.iteration / self.ITERATIONS_PER_MINUTE) if self.units(UnitTypeId.PYLON).ready.exists: pylon = self.units(UnitTypeId.PYLON).ready.random # 根據神族建築科技圖,折躍門建造事後才能夠建造控制核心 if self.units(UnitTypeId.GATEWAY).ready.exists and not self.units(UnitTypeId.CYBERNETICSCORE): if self.can_afford(UnitTypeId.CYBERNETICSCORE) and not self.already_pending(UnitTypeId.CYBERNETICSCORE): await self.build(UnitTypeId.CYBERNETICSCORE, near=pylon) # 不然建造折躍門 # (self.iteration / self.ITERATIONS_PER_MINUTE)/2 是一個緩慢遞增的值 elif len(self.units(UnitTypeId.GATEWAY)) < ((self.iteration / self.ITERATIONS_PER_MINUTE) / 2): if self.can_afford(UnitTypeId.GATEWAY) and not self.already_pending(UnitTypeId.GATEWAY): await self.build(UnitTypeId.GATEWAY, near=pylon) # 控制核心存在的狀況下建造星門 if self.units(UnitTypeId.CYBERNETICSCORE).ready.exists: if len(self.units(UnitTypeId.STARGATE)) < ((self.iteration / self.ITERATIONS_PER_MINUTE) / 2): if self.can_afford(UnitTypeId.STARGATE) and not self.already_pending(UnitTypeId.STARGATE): await self.build(UnitTypeId.STARGATE, near=pylon) ## 造兵 async def build_offensive_force(self): # 無隊列化建造 for gw in self.units(UnitTypeId.GATEWAY).ready.noqueue: if not self.units(UnitTypeId.STALKER).amount > self.units(UnitTypeId.VOIDRAY).amount: if self.can_afford(UnitTypeId.STALKER) and self.supply_left > 0: await self.do(gw.train(UnitTypeId.STALKER)) for sg in self.units(UnitTypeId.STARGATE).ready.noqueue: if self.can_afford(UnitTypeId.VOIDRAY) and self.supply_left > 0: await self.do(sg.train(UnitTypeId.VOIDRAY)) ## 尋找目標 def find_target(self,state): if len(self.known_enemy_units)>0: # 隨機選取敵方單位 return random.choice(self.known_enemy_units) elif len(self.known_enemy_units)>0: # 隨機選取敵方建築 return random.choice(self.known_enemy_structures) else: # 返回敵方出生點位 return self.enemy_start_locations[0] ## 進攻 async def attack(self): # {UNIT: [n to fight, n to defend]} aggressive_units = {UnitTypeId.STALKER: [15, 5], UnitTypeId.VOIDRAY: [8, 3]} for UNIT in aggressive_units: # 攻擊模式 if self.units(UNIT).amount > aggressive_units[UNIT][0] and self.units(UNIT).amount > aggressive_units[UNIT][ 1]: for s in self.units(UNIT).idle: await self.do(s.attack(self.find_target(self.state))) # 防衛模式 elif self.units(UNIT).amount > aggressive_units[UNIT][1]: if len(self.known_enemy_units) > 0: for s in self.units(UNIT).idle: await self.do(s.attack(random.choice(self.known_enemy_units))) ## 啓動遊戲 run_game(maps.get("AcidPlantLE"), [ Bot(Race.Protoss, SentdeBot()), Computer(Race.Terran, Difficulty.Hard) ], realtime=False)
運行結果以下
能夠看到,擊敗了困難人族電腦,可是電腦選擇了rush戰術,咱們寫得AI腳本會輸掉遊戲。顯然,這不是最佳方案。
「只有AI才能拯救個人勝率」,請看下文。
此次咱們只造一個折躍門,全力經過星門造虛空光輝艦
修改offensive_force_buildings(self)方法的判斷
elif len(self.units(GATEWAY)) < 1: if self.can_afford(GATEWAY) and not self.already_pending(GATEWAY): await self.build(GATEWAY, near=pylon)
註釋或者刪除build_offensive_force(self)的建造追獵者的代碼
## 造兵 async def build_offensive_force(self): # 無隊列化建造 # for gw in self.units(UnitTypeId.GATEWAY).ready.noqueue: # if not self.units(UnitTypeId.STALKER).amount > self.units(UnitTypeId.VOIDRAY).amount: # # if self.can_afford(UnitTypeId.STALKER) and self.supply_left > 0: # await self.do(gw.train(UnitTypeId.STALKER)) for sg in self.units(UnitTypeId.STARGATE).ready.noqueue: if self.can_afford(UnitTypeId.VOIDRAY) and self.supply_left > 0: await self.do(sg.train(UnitTypeId.VOIDRAY))
attack(self)中的aggressive_units註釋掉Stalker
導入numpy和cv2庫
game_data = np.zeros((self.game_info.map_size[1], self.game_info.map_size[0], 3), np.uint8)
創建以地圖Heigt爲行,Width爲列的三維矩陣
for nexus in self.units(NEXUS): nex_pos = nexus.position print(nex_pos) cv2.circle(game_data, (int(nex_pos[0]), int(nex_pos[1])), 10, (0, 255, 0), -1) # BGR
遍歷星靈樞紐,獲取下一個位置,畫圓,circle(承載圓的img, 圓心, 半徑, 顏色, thickness=-1表示填充)
接下來咱們要垂直翻轉三維矩陣,由於咱們創建的矩陣左上角是原點(0,0),縱座標向下延申,橫座標向右延申。翻轉以後就成了正常的座標系。
flipped = cv2.flip(game_data, 0)
圖像縮放,達到可視化最佳。
resized = cv2.resize(flipped, dsize=None, fx=2, fy=2) cv2.imshow('Intel', resized) cv2.waitKey(1)
至此,完整代碼以下
import sc2 from sc2 import run_game, maps, Race, Difficulty from sc2.player import Bot, Computer from sc2.constants import * import random import numpy as np import cv2 class SentdeBot(sc2.BotAI): def __init__(self): # 通過計算,每分鐘大約165迭代次數 self.ITERATIONS_PER_MINUTE = 165 # 最大農民數量 self.MAX_WORKERS = 65 async def on_step(self, iteration: int): self.iteration = iteration await self.distribute_workers() await self.build_workers() await self.build_pylons() await self.build_assimilators() await self.expand() await self.offensive_force_buildings() await self.build_offensive_force() await self.intel() await self.attack() async def intel(self): # 根據地圖創建的三維矩陣 game_data = np.zeros((self.game_info.map_size[1], self.game_info.map_size[0], 3), np.uint8) for nexus in self.units(UnitTypeId.NEXUS): nex_pos = nexus.position # circle(承載圓的img, 圓心, 半徑, 顏色, thickness=-1表示填充) # 記錄星靈樞紐的位置 cv2.circle(game_data, (int(nex_pos[0]), int(nex_pos[1])), 10, (0, 255, 0), -1) # 圖像翻轉垂直鏡像 flipped = cv2.flip(game_data, 0) # 圖像縮放 # cv2.resize(原圖像,輸出圖像的大小,width方向的縮放比例,height方向縮放的比例) resized = cv2.resize(flipped, dsize=None, fx=2, fy=2) cv2.imshow('Intel', resized) # cv2.waitKey(每Xms刷新圖像) cv2.waitKey(1) # 建造農民 async def build_workers(self): # 星靈樞鈕*16(一個基地配備16個農民)大於農民數量而且現有農民數量小於MAX_WORKERS if len(self.units(UnitTypeId.NEXUS)) * 16 > len(self.units(UnitTypeId.PROBE)) and len( self.units(UnitTypeId.PROBE)) < self.MAX_WORKERS: # 星靈樞紐(NEXUS)無隊列建造,能夠提升晶體礦的利用率,不至於佔用資源 for nexus in self.units(UnitTypeId.NEXUS).ready.noqueue: # 是否有50晶體礦建造農民 if self.can_afford(UnitTypeId.PROBE): await self.do(nexus.train(UnitTypeId.PROBE)) ## 建造水晶 async def build_pylons(self): ## 供應人口和現有人口之差小於5且建築不是正在建造 if self.supply_left < 5 and not self.already_pending(UnitTypeId.PYLON): nexuses = self.units(UnitTypeId.NEXUS).ready if nexuses.exists: if self.can_afford(UnitTypeId.PYLON): await self.build(UnitTypeId.PYLON, near=nexuses.first) ## 建造吸取廠 async def build_assimilators(self): for nexus in self.units(UnitTypeId.NEXUS).ready: # 在瓦斯泉上建造吸取廠 vaspenes = self.state.vespene_geyser.closer_than(15.0, nexus) for vaspene in vaspenes: if not self.can_afford(UnitTypeId.ASSIMILATOR): break worker = self.select_build_worker(vaspene.position) if worker is None: break if not self.units(UnitTypeId.ASSIMILATOR).closer_than(1.0, vaspene).exists: await self.do(worker.build(UnitTypeId.ASSIMILATOR, vaspene)) ## 開礦 async def expand(self): # (self.iteration / self.ITERATIONS_PER_MINUTE)是一個緩慢遞增的值,動態開礦 if self.units(UnitTypeId.NEXUS).amount < self.iteration / self.ITERATIONS_PER_MINUTE and self.can_afford( UnitTypeId.NEXUS): await self.expand_now() ## 建造進攻性建築 async def offensive_force_buildings(self): print(self.iteration / self.ITERATIONS_PER_MINUTE) if self.units(UnitTypeId.PYLON).ready.exists: pylon = self.units(UnitTypeId.PYLON).ready.random # 根據神族建築科技圖,折躍門建造事後才能夠建造控制核心 if self.units(UnitTypeId.GATEWAY).ready.exists and not self.units(UnitTypeId.CYBERNETICSCORE): if self.can_afford(UnitTypeId.CYBERNETICSCORE) and not self.already_pending(UnitTypeId.CYBERNETICSCORE): await self.build(UnitTypeId.CYBERNETICSCORE, near=pylon) # 不然建造折躍門 # (self.iteration / self.ITERATIONS_PER_MINUTE)/2 是一個緩慢遞增的值 # elif len(self.units(UnitTypeId.GATEWAY)) < ((self.iteration / self.ITERATIONS_PER_MINUTE) / 2): elif len(self.units(UnitTypeId.GATEWAY)) < 1: if self.can_afford(UnitTypeId.GATEWAY) and not self.already_pending(UnitTypeId.GATEWAY): await self.build(UnitTypeId.GATEWAY, near=pylon) # 控制核心存在的狀況下建造星門 if self.units(UnitTypeId.CYBERNETICSCORE).ready.exists: if len(self.units(UnitTypeId.STARGATE)) < ((self.iteration / self.ITERATIONS_PER_MINUTE) / 2): if self.can_afford(UnitTypeId.STARGATE) and not self.already_pending(UnitTypeId.STARGATE): await self.build(UnitTypeId.STARGATE, near=pylon) ## 造兵 async def build_offensive_force(self): # 無隊列化建造 for sg in self.units(UnitTypeId.STARGATE).ready.noqueue: if self.can_afford(UnitTypeId.VOIDRAY) and self.supply_left > 0: await self.do(sg.train(UnitTypeId.VOIDRAY)) ## 尋找目標 def find_target(self, state): if len(self.known_enemy_units) > 0: # 隨機選取敵方單位 return random.choice(self.known_enemy_units) elif len(self.known_enemy_units) > 0: # 隨機選取敵方建築 return random.choice(self.known_enemy_structures) else: # 返回敵方出生點位 return self.enemy_start_locations[0] ## 進攻 async def attack(self): # {UNIT: [n to fight, n to defend]} aggressive_units = {UnitTypeId.VOIDRAY: [8, 3]} for UNIT in aggressive_units: # 攻擊模式 if self.units(UNIT).amount > aggressive_units[UNIT][0] and self.units(UNIT).amount > aggressive_units[UNIT][1]: for s in self.units(UNIT).idle: await self.do(s.attack(self.find_target(self.state))) # 防衛模式 elif self.units(UNIT).amount > aggressive_units[UNIT][1]: if len(self.known_enemy_units) > 0: for s in self.units(UNIT).idle: await self.do(s.attack(random.choice(self.known_enemy_units))) ## 啓動遊戲 run_game(maps.get("AcidPlantLE"), [ Bot(Race.Protoss, SentdeBot()), Computer(Race.Terran, Difficulty.Hard) ], realtime=False)
運行結果以下
採集到了地圖位置。
在intel(self)裏建立一個字典draw_dict,UnitTypeId做爲key,半徑和顏色是value
draw_dict = { UnitTypeId.NEXUS: [15, (0, 255, 0)], UnitTypeId.PYLON: [3, (20, 235, 0)], UnitTypeId.PROBE: [1, (55, 200, 0)], UnitTypeId.ASSIMILATOR: [2, (55, 200, 0)], UnitTypeId.GATEWAY: [3, (200, 100, 0)], UnitTypeId.CYBERNETICSCORE: [3, (150, 150, 0)], UnitTypeId.STARGATE: [5, (255, 0, 0)], UnitTypeId.ROBOTICSFACILITY: [5, (215, 155, 0)], UnitTypeId.VOIDRAY: [3, (255, 100, 0)] }
迭代同上
for unit_type in draw_dict: for unit in self.units(unit_type).ready: pos = unit.position cv2.circle(game_data, (int(pos[0]), int(pos[1])), draw_dict[unit_type][0], draw_dict[unit_type][1], -1)
存儲三族的主基地名稱(星靈樞紐,指揮中心,孵化場),刻畫敵方建築。
# 主基地名稱 main_base_names = ["nexus", "supplydepot", "hatchery"] # 記錄敵方基地位置 for enemy_building in self.known_enemy_structures: pos = enemy_building.position if enemy_building.name.lower() not in main_base_names: cv2.circle(game_data, (int(pos[0]), int(pos[1])), 5, (200, 50, 212), -1) for enemy_building in self.known_enemy_structures: pos = enemy_building.position if enemy_building.name.lower() in main_base_names: cv2.circle(game_data, (int(pos[0]), int(pos[1])), 15, (0, 0, 255), -1)
刻畫敵方單位,若是是農民畫得小些,其餘單位則畫大些。
for enemy_unit in self.known_enemy_units: if not enemy_unit.is_structure: worker_names = ["probe", "scv", "drone"] # if that unit is a PROBE, SCV, or DRONE... it's a worker pos = enemy_unit.position if enemy_unit.name.lower() in worker_names: cv2.circle(game_data, (int(pos[0]), int(pos[1])), 1, (55, 0, 155), -1) else: cv2.circle(game_data, (int(pos[0]), int(pos[1])), 3, (50, 0, 215), -1)
在offensive_force_buildings(self)方法中添加建造機械臺
if self.units(CYBERNETICSCORE).ready.exists: if len(self.units(ROBOTICSFACILITY)) < 1: if self.can_afford(ROBOTICSFACILITY) and not self.already_pending(ROBOTICSFACILITY): await self.build(ROBOTICSFACILITY, near=pylon)
建立scout(),訓練Observer
async def scout(self): if len(self.units(OBSERVER)) > 0: scout = self.units(OBSERVER)[0] if scout.is_idle: enemy_location = self.enemy_start_locations[0] move_to = self.random_location_variance(enemy_location) print(move_to) await self.do(scout.move(move_to)) else: for rf in self.units(ROBOTICSFACILITY).ready.noqueue: if self.can_afford(OBSERVER) and self.supply_left > 0: await self.do(rf.train(OBSERVER))
生成隨機位置,很簡單。意思是橫座標累計遞增-0.2和0.2倍的橫座標,限制條件爲若是x超過橫座標,那麼就是橫座標最大值。
縱座標同理。
def random_location_variance(self, enemy_start_location): x = enemy_start_location[0] y = enemy_start_location[1] x += ((random.randrange(-20, 20))/100) * enemy_start_location[0] y += ((random.randrange(-20, 20))/100) * enemy_start_location[1] if x < 0: x = 0 if y < 0: y = 0 if x > self.game_info.map_size[0]: x = self.game_info.map_size[0] if y > self.game_info.map_size[1]: y = self.game_info.map_size[1] go_to = position.Point2(position.Pointlike((x,y))) return go_to
完整代碼以下
# -*- encoding: utf-8 -*- ''' @File : demo.py @Modify Time @Author @Desciption ------------ ------- ----------- 2019/11/3 12:32 Jonas None ''' import sc2 from sc2 import run_game, maps, Race, Difficulty, position from sc2.player import Bot, Computer from sc2.constants import * import random import numpy as np import cv2 class SentdeBot(sc2.BotAI): def __init__(self): # 通過計算,每分鐘大約165迭代次數 self.ITERATIONS_PER_MINUTE = 165 # 最大農民數量 self.MAX_WORKERS = 50 async def on_step(self, iteration: int): self.iteration = iteration await self.scout() await self.distribute_workers() await self.build_workers() await self.build_pylons() await self.build_assimilators() await self.expand() await self.offensive_force_buildings() await self.build_offensive_force() await self.intel() await self.attack() ## 偵察 async def scout(self): if len(self.units(UnitTypeId.OBSERVER)) > 0: scout = self.units(UnitTypeId.OBSERVER)[0] if scout.is_idle: enemy_location = self.enemy_start_locations[0] move_to = self.random_location_variance(enemy_location) print(move_to) await self.do(scout.move(move_to)) else: for rf in self.units(UnitTypeId.ROBOTICSFACILITY).ready.noqueue: if self.can_afford(UnitTypeId.OBSERVER) and self.supply_left > 0: await self.do(rf.train(UnitTypeId.OBSERVER)) async def intel(self): game_data = np.zeros((self.game_info.map_size[1], self.game_info.map_size[0], 3), np.uint8) # UnitTypeId做爲key,半徑和顏色是value draw_dict = { UnitTypeId.NEXUS: [15, (0, 255, 0)], UnitTypeId.PYLON: [3, (20, 235, 0)], UnitTypeId.PROBE: [1, (55, 200, 0)], UnitTypeId.ASSIMILATOR: [2, (55, 200, 0)], UnitTypeId.GATEWAY: [3, (200, 100, 0)], UnitTypeId.CYBERNETICSCORE: [3, (150, 150, 0)], UnitTypeId.STARGATE: [5, (255, 0, 0)], UnitTypeId.ROBOTICSFACILITY: [5, (215, 155, 0)], UnitTypeId.VOIDRAY: [3, (255, 100, 0)], # OBSERVER: [3, (255, 255, 255)], } for unit_type in draw_dict: for unit in self.units(unit_type).ready: pos = unit.position cv2.circle(game_data, (int(pos[0]), int(pos[1])), draw_dict[unit_type][0], draw_dict[unit_type][1], -1) # 主基地名稱 main_base_names = ["nexus", "supplydepot", "hatchery"] # 記錄敵方基地位置 for enemy_building in self.known_enemy_structures: pos = enemy_building.position # 不是主基地建築,畫小一些 if enemy_building.name.lower() not in main_base_names: cv2.circle(game_data, (int(pos[0]), int(pos[1])), 5, (200, 50, 212), -1) for enemy_building in self.known_enemy_structures: pos = enemy_building.position if enemy_building.name.lower() in main_base_names: cv2.circle(game_data, (int(pos[0]), int(pos[1])), 15, (0, 0, 255), -1) for enemy_unit in self.known_enemy_units: if not enemy_unit.is_structure: worker_names = ["probe", "scv", "drone"] # if that unit is a PROBE, SCV, or DRONE... it's a worker pos = enemy_unit.position if enemy_unit.name.lower() in worker_names: cv2.circle(game_data, (int(pos[0]), int(pos[1])), 1, (55, 0, 155), -1) else: cv2.circle(game_data, (int(pos[0]), int(pos[1])), 3, (50, 0, 215), -1) for obs in self.units(UnitTypeId.OBSERVER).ready: pos = obs.position cv2.circle(game_data, (int(pos[0]), int(pos[1])), 1, (255, 255, 255), -1) # flip horizontally to make our final fix in visual representation: flipped = cv2.flip(game_data, 0) resized = cv2.resize(flipped, dsize=None, fx=2, fy=2) cv2.imshow('Intel', resized) cv2.waitKey(1) def random_location_variance(self, enemy_start_location): x = enemy_start_location[0] y = enemy_start_location[1] x += ((random.randrange(-20, 20)) / 100) * enemy_start_location[0] y += ((random.randrange(-20, 20)) / 100) * enemy_start_location[1] if x < 0: x = 0 if y < 0: y = 0 if x > self.game_info.map_size[0]: x = self.game_info.map_size[0] if y > self.game_info.map_size[1]: y = self.game_info.map_size[1] go_to = position.Point2(position.Pointlike((x, y))) return go_to # 建造農民 async def build_workers(self): # 星靈樞鈕*16(一個基地配備16個農民)大於農民數量而且現有農民數量小於MAX_WORKERS if len(self.units(UnitTypeId.NEXUS)) * 16 > len(self.units(UnitTypeId.PROBE)) and len( self.units(UnitTypeId.PROBE)) < self.MAX_WORKERS: # 星靈樞紐(NEXUS)無隊列建造,能夠提升晶體礦的利用率,不至於佔用資源 for nexus in self.units(UnitTypeId.NEXUS).ready.noqueue: # 是否有50晶體礦建造農民 if self.can_afford(UnitTypeId.PROBE): await self.do(nexus.train(UnitTypeId.PROBE)) ## 建造水晶 async def build_pylons(self): ## 供應人口和現有人口之差小於5且建築不是正在建造 if self.supply_left < 5 and not self.already_pending(UnitTypeId.PYLON): nexuses = self.units(UnitTypeId.NEXUS).ready if nexuses.exists: if self.can_afford(UnitTypeId.PYLON): await self.build(UnitTypeId.PYLON, near=nexuses.first) ## 建造吸取廠 async def build_assimilators(self): for nexus in self.units(UnitTypeId.NEXUS).ready: # 在瓦斯泉上建造吸取廠 vaspenes = self.state.vespene_geyser.closer_than(15.0, nexus) for vaspene in vaspenes: if not self.can_afford(UnitTypeId.ASSIMILATOR): break worker = self.select_build_worker(vaspene.position) if worker is None: break if not self.units(UnitTypeId.ASSIMILATOR).closer_than(1.0, vaspene).exists: await self.do(worker.build(UnitTypeId.ASSIMILATOR, vaspene)) ## 開礦 async def expand(self): # (self.iteration / self.ITERATIONS_PER_MINUTE)是一個緩慢遞增的值,動態開礦 if self.units(UnitTypeId.NEXUS).amount < self.iteration / self.ITERATIONS_PER_MINUTE and self.can_afford( UnitTypeId.NEXUS): await self.expand_now() ## 建造進攻性建築 async def offensive_force_buildings(self): print(self.iteration / self.ITERATIONS_PER_MINUTE) if self.units(UnitTypeId.PYLON).ready.exists: pylon = self.units(UnitTypeId.PYLON).ready.random # 根據神族建築科技圖,折躍門建造事後才能夠建造控制核心 if self.units(UnitTypeId.GATEWAY).ready.exists and not self.units(UnitTypeId.CYBERNETICSCORE): if self.can_afford(UnitTypeId.CYBERNETICSCORE) and not self.already_pending(UnitTypeId.CYBERNETICSCORE): await self.build(UnitTypeId.CYBERNETICSCORE, near=pylon) # 不然建造折躍門 # (self.iteration / self.ITERATIONS_PER_MINUTE)/2 是一個緩慢遞增的值 # elif len(self.units(UnitTypeId.GATEWAY)) < ((self.iteration / self.ITERATIONS_PER_MINUTE) / 2): elif len(self.units(UnitTypeId.GATEWAY)) < 1: if self.can_afford(UnitTypeId.GATEWAY) and not self.already_pending(UnitTypeId.GATEWAY): await self.build(UnitTypeId.GATEWAY, near=pylon) # 控制核心存在的狀況下建造機械臺 if self.units(UnitTypeId.CYBERNETICSCORE).ready.exists: if len(self.units(UnitTypeId.ROBOTICSFACILITY)) < 1: if self.can_afford(UnitTypeId.ROBOTICSFACILITY) and not self.already_pending( UnitTypeId.ROBOTICSFACILITY): await self.build(UnitTypeId.ROBOTICSFACILITY, near=pylon) # 控制核心存在的狀況下建造星門 if self.units(UnitTypeId.CYBERNETICSCORE).ready.exists: if len(self.units(UnitTypeId.STARGATE)) < ((self.iteration / self.ITERATIONS_PER_MINUTE) / 2): if self.can_afford(UnitTypeId.STARGATE) and not self.already_pending(UnitTypeId.STARGATE): await self.build(UnitTypeId.STARGATE, near=pylon) ## 造兵 async def build_offensive_force(self): # 無隊列化建造 # for gw in self.units(UnitTypeId.GATEWAY).ready.noqueue: # if not self.units(UnitTypeId.STALKER).amount > self.units(UnitTypeId.VOIDRAY).amount: # # if self.can_afford(UnitTypeId.STALKER) and self.supply_left > 0: # await self.do(gw.train(UnitTypeId.STALKER)) for sg in self.units(UnitTypeId.STARGATE).ready.noqueue: if self.can_afford(UnitTypeId.VOIDRAY) and self.supply_left > 0: await self.do(sg.train(UnitTypeId.VOIDRAY)) ## 尋找目標 def find_target(self, state): if len(self.known_enemy_units) > 0: # 隨機選取敵方單位 return random.choice(self.known_enemy_units) elif len(self.known_enemy_units) > 0: # 隨機選取敵方建築 return random.choice(self.known_enemy_structures) else: # 返回敵方出生點位 return self.enemy_start_locations[0] ## 進攻 async def attack(self): # {UNIT: [n to fight, n to defend]} aggressive_units = {UnitTypeId.VOIDRAY: [8, 3]} for UNIT in aggressive_units: # 攻擊模式 if self.units(UNIT).amount > aggressive_units[UNIT][0] and self.units(UNIT).amount > aggressive_units[UNIT][ 1]: for s in self.units(UNIT).idle: await self.do(s.attack(self.find_target(self.state))) # 防衛模式 elif self.units(UNIT).amount > aggressive_units[UNIT][1]: if len(self.known_enemy_units) > 0: for s in self.units(UNIT).idle: await self.do(s.attack(random.choice(self.known_enemy_units))) ## 啓動遊戲 run_game(maps.get("AcidPlantLE"), [ Bot(Race.Protoss, SentdeBot()), Computer(Race.Terran, Difficulty.Hard) ], realtime=False)
運行結果以下,紅色和粉紅色是敵方單位。