You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
For some reason, in this code, the modules sc2 is not found:
import sc2
from sc2 import maps
from sc2.bot_ai import BotAI
from sc2.data import Difficulty, Race
from sc2.ids.ability_id import AbilityId
from sc2.ids.buff_id import BuffId
from sc2.ids.unit_typeid import UnitTypeId
from sc2.main import run_game
from sc2.player import Bot, Computer
import random
import cv2
import math
import numpy as np
import sys
import pickle
import time
class IncrediBot(BotAI): # inhereits from BotAI (part of BurnySC2)
async def on_step(self, iteration: int): # on_step is a method that is called every step of the game.
no_action = True
while no_action:
try:
with open('state_rwd_action.pkl', 'rb') as f:
state_rwd_action = pickle.load(f)
if state_rwd_action['action'] is None:
#print("No action yet")
no_action = True
else:
#print("Action found")
no_action = False
except:
pass
await self.distribute_workers() # put idle workers back to work
action = state_rwd_action['action']
'''
0: expand (ie: move to next spot, or build to 16 (minerals)+3 assemblers+3)
1: build stargate (or up to one) (evenly)
2: build voidray (evenly)
3: send scout (evenly/random/closest to enemy?)
4: attack (known buildings, units, then enemy base, just go in logical order.)
5: voidray flee (back to base)
'''
# 0: expand (ie: move to next spot, or build to 16 (minerals)+3 assemblers+3)
if action == 0:
try:
found_something = False
if self.supply_left < 4:
# build pylons.
if self.already_pending(UnitTypeId.PYLON) == 0:
if self.can_afford(UnitTypeId.PYLON):
await self.build(UnitTypeId.PYLON, near=random.choice(self.townhalls))
found_something = True
if not found_something:
for nexus in self.townhalls:
# get worker count for this nexus:
worker_count = len(self.workers.closer_than(10, nexus))
if worker_count < 22: # 16+3+3
if nexus.is_idle and self.can_afford(UnitTypeId.PROBE):
nexus.train(UnitTypeId.PROBE)
found_something = True
# have we built enough assimilators?
# find vespene geysers
for geyser in self.vespene_geyser.closer_than(10, nexus):
# build assimilator if there isn't one already:
if not self.can_afford(UnitTypeId.ASSIMILATOR):
break
if not self.structures(UnitTypeId.ASSIMILATOR).closer_than(2.0, geyser).exists:
await self.build(UnitTypeId.ASSIMILATOR, geyser)
found_something = True
if not found_something:
if self.already_pending(UnitTypeId.NEXUS) == 0 and self.can_afford(UnitTypeId.NEXUS):
await self.expand_now()
except Exception as e:
print(e)
#1: build gateway
elif action == 1:
try:
for nexus in self.townhalls:
# is there is not a gateway close:
if not self.structures(UnitTypeId.GATEWAY).closer_than(10, nexus).exists:
# if we can afford it:
if self.can_afford(UnitTypeId.GATEWAY) and self.already_pending(UnitTypeId.GATEWAY) == 0:
# build forge
await self.build(UnitTypeId.GATEWAY, near=nexus)
except Exception as e:
print(e)
#build a forge
elif action == 2:
try:
for nexus in self.townhalls:
# is there is not a forge close:
if not self.structures(UnitTypeId.FORGE).closer_than(10, nexus).exists:
# if we can afford it:
if self.can_afford(UnitTypeId.FORGE) and self.already_pending(UnitTypeId.FORGE) == 0:
# build forge
await self.build(UnitTypeId.FORGE, near=nexus)
except Exception as e:
print(e)
#3: send scout
elif action == 3:
# are there any idle probes:
try:
self.last_sent
except:
self.last_sent = 0
# if self.last_sent doesnt exist yet:
if (iteration - self.last_sent) > 200:
try:
if self.units(UnitTypeId.PROBE).idle.exists:
# pick one of these randomly:
probe = random.choice(self.units(UnitTypeId.PROBE).idle)
else:
probe = random.choice(self.units(UnitTypeId.PROBE))
# send probe towards enemy base:
probe.attack(self.enemy_start_locations[0])
self.last_sent = iteration
except Exception as e:
pass
#4: attack (known buildings, units, then enemy base, just go in logical order.)
elif action == 4:
try:
# take all void rays and attack!
for voidray in self.units(UnitTypeId.VOIDRAY, UnitTypeId.COLOSSUS, UnitTypeId.ZEALOT, UnitTypeId.STALKER, UnitTypeId.DARKTEMPLAR, UnitTypeId.CARRIER, UnitTypeId.IMMORTAL, UnitTypeId.OBSERVER).idle:
# if we can attack:
if self.enemy_units.closer_than(10, voidray):
# attack!
voidray.attack(random.choice(self.enemy_units.closer_than(10, voidray)))
# if we can attack:
elif self.enemy_structures.closer_than(10, voidray):
# attack!
voidray.attack(random.choice(self.enemy_structures.closer_than(10, voidray)))
# any enemy units:
elif self.enemy_units:
# attack!
voidray.attack(random.choice(self.enemy_units))
# any enemy structures:
elif self.enemy_structures:
# attack!
voidray.attack(random.choice(self.enemy_structures))
# if we can attack:
elif self.enemy_start_locations:
# attack!
voidray.attack(self.enemy_start_locations[0])
except Exception as e:
print(e)
#5: voidray flee (back to base)
elif action == 5:
if self.units(UnitTypeId.VOIDRAY, UnitTypeId.COLOSSUS, UnitTypeId.ZEALOT, UnitTypeId.STALKER, UnitTypeId.DARKTEMPLAR, UnitTypeId.CARRIER, UnitTypeId.IMMORTAL, UnitTypeId.OBSERVER).amount > 0:
for vr in self.units(UnitTypeId.VOIDRAY, UnitTypeId.COLOSSUS, UnitTypeId.ZEALOT, UnitTypeId.STALKER, UnitTypeId.DARKTEMPLAR, UnitTypeId.CARRIER, UnitTypeId.IMMORTAL, UnitTypeId.OBSERVER):
vr.attack(self.start_location)
#build a build a pylon
elif action == 6:
try:
for nexus in self.townhalls:
# is there is not a forge close:
if not self.structures(UnitTypeId.PYLON).closer_than(5, nexus).exists:
# if we can afford it:
if self.can_afford(UnitTypeId.PYLON) and self.already_pending(UnitTypeId.PYLON) == 0:
# build forge
await self.build(UnitTypeId.PYLON, near=nexus)
except Exception as e:
print(e)
#1: build gateway
elif action == 7:
try:
for nexus in self.townhalls:
# is there is not a gateway close:
if self.structures(UnitTypeId.STARGATE).closer_than(20, nexus).exists:
if not self.structures(UnitTypeId.FLEETBEACON).closer_than(30, nexus).exists:
# if we can afford it:
if self.can_afford(UnitTypeId.FLEETBEACON) and self.already_pending(UnitTypeId.FLEETBEACON) == 0:
# build forge
await self.build(UnitTypeId.FLEETBEACON, near=nexus)
except Exception as e:
print(e) #1: build gateway
#photoncannon
elif action == 8:
try:
for nexus in self.townhalls:
# is there is not a gateway close:
if self.structures(UnitTypeId.PYLON).closer_than(10, nexus).exists:
# if we can afford it:
if self.can_afford(UnitTypeId.PHOTONCANNON) and self.already_pending(UnitTypeId.PHOTONCANNON) == 0:
# build forge
await self.build(UnitTypeId.PHOTONCANNON, near=nexus)
except Exception as e:
print(e)
#build a STARGATE
elif action == 9:
try:
for nexus in self.townhalls:
# is there is not a forge close:
if self.structures(UnitTypeId.NEXUS).closer_than(20, nexus).exists:
# if we can afford it:
if self.can_afford(UnitTypeId.STARGATE) and self.already_pending(UnitTypeId.STARGATE) == 0:
# build forge
await self.build(UnitTypeId.STARGATE, near=nexus)
except Exception as e:
print(e)
#build a ROBOTICSBAY
elif action == 10:
try:
for nexus in self.townhalls:
# is there is not a forge close:
if self.structures(UnitTypeId.NEXUS).closer_than(20, nexus).exists:
# if we can afford it:
if self.can_afford(UnitTypeId.ROBOTICSBAY) and self.already_pending(UnitTypeId.ROBOTICSBAY) == 0:
# build forge
await self.build(UnitTypeId.ROBOTICSBAY, near=nexus)
except Exception as e:
print(e)
#build a ROBOTICSBAY
elif action == 11:
try:
for nexus in self.townhalls:
# is there is not a forge close:
if self.structures(UnitTypeId.NEXUS).closer_than(20, nexus).exists:
# if we can afford it:
if self.can_afford(UnitTypeId.ROBOTICSFACILITY) and self.already_pending(UnitTypeId.ROBOTICSFACILITY) == 0:
# build forge
await self.build(UnitTypeId.ROBOTICSFACILITY, near=nexus)
except Exception as e:
print(e)
#build a TWILIGHTCOUNCIL
elif action == 12:
try:
for nexus in self.townhalls:
# is there is not a forge close:
if self.structures(UnitTypeId.NEXUS).closer_than(20, nexus).exists:
if self.structures(UnitTypeId.TWILIGHTCOUNCIL).closer_than(20, nexus).exists:
# if we can afford it:
if self.can_afford(UnitTypeId.TWILIGHTCOUNCIL) and self.already_pending(UnitTypeId.TWILIGHTCOUNCIL) == 0:
# build forge
await self.build(UnitTypeId.TWILIGHTCOUNCIL, near=nexus)
except Exception as e:
print(e)
#build a DARKSHRINE
elif action == 13:
try:
for nexus in self.townhalls:
# is there is not a forge close:
if self.structures(UnitTypeId.NEXUS).closer_than(20, nexus).exists:
if self.structures(UnitTypeId.DARKSHRINE).closer_than(20, nexus).exists:
# if we can afford it:
if self.can_afford(UnitTypeId.DARKSHRINE) and self.already_pending(UnitTypeId.DARKSHRINE) == 0:
# build forge
await self.build(UnitTypeId.DARKSHRINE, near=nexus)
except Exception as e:
print(e)
#2: build Zelot (random gatway)
elif action == 14:
try:
if self.can_afford(UnitTypeId.ZEALOT):
for sg in self.structures(UnitTypeId.GATEWAY).ready.idle:
if self.can_afford(UnitTypeId.ZEALOT):
sg.train(UnitTypeId.ZEALOT)
except Exception as e:
print(e)
#build a CYBERNETICSCORE
elif action == 15:
try:
for nexus in self.townhalls:
# is there is not a forge close:
if self.structures(UnitTypeId.NEXUS).closer_than(20, nexus).exists:
if self.structures(UnitTypeId.CYBERNETICSCORE).closer_than(20, nexus).exists:
# if we can afford it:
if self.can_afford(UnitTypeId.CYBERNETICSCORE) and self.already_pending(UnitTypeId.CYBERNETICSCORE) == 0:
# build forge
await self.build(UnitTypeId.CYBERNETICSCORE, near=nexus)
except Exception as e:
print(e)
#2: build Zelot (random stargate)
elif action == 16:
try:
if self.can_afford(UnitTypeId.ZEALOT):
for sg in self.structures(UnitTypeId.GATEWAY).ready.idle:
if self.can_afford(UnitTypeId.ZEALOT):
sg.train(UnitTypeId.ZEALOT)
except Exception as e:
print(e)
#2: build STALKER (random stargate)
elif action == 17:
try:
if self.can_afford(UnitTypeId.STALKER):
for sg in self.structures(UnitTypeId.GATEWAY).ready.idle:
if self.can_afford(UnitTypeId.STALKER):
sg.train(UnitTypeId.STALKER)
except Exception as e:
print(e)
#2: build DARKTEMPLAR (random stargate)
elif action == 18:
try:
if self.can_afford(UnitTypeId.DARKTEMPLAR):
for sg in self.structures(UnitTypeId.GATEWAY).ready.idle:
if self.can_afford(UnitTypeId.DARKTEMPLAR):
sg.train(UnitTypeId.DARKTEMPLAR)
except Exception as e:
print(e)
#2: build CARRIER (random stargate)
elif action == 19:
try:
if self.can_afford(UnitTypeId.CARRIER):
for sg in self.structures(UnitTypeId.STARGATE).ready.idle:
if self.can_afford(UnitTypeId.CARRIER):
sg.train(UnitTypeId.CARRIER)
except Exception as e:
print(e)
#2: build voidrray (random stargate)
elif action == 20:
try:
if self.can_afford(UnitTypeId.VOIDRAY):
for sg in self.structures(UnitTypeId.STARGATE).ready.idle:
if self.can_afford(UnitTypeId.VOIDRAY):
sg.train(UnitTypeId.VOIDRAY)
except Exception as e:
print(e)
#2: build IMMORTAL (random stargate)
elif action == 21:
try:
if self.can_afford(UnitTypeId.IMMORTAL):
for sg in self.structures(UnitTypeId.ROBOTICSBAY).ready.idle:
if self.can_afford(UnitTypeId.IMMORTAL):
sg.train(UnitTypeId.IMMORTAL)
except Exception as e:
print(e)
#2: build COLOSSUS (random stargate)
elif action == 22:
try:
if self.can_afford(UnitTypeId.COLOSSUS ):
for sg in self.structures(UnitTypeId.ROBOTICSBAY).ready.idle:
if self.can_afford(UnitTypeId.COLOSSUS):
sg.train(UnitTypeId.COLOSSUS)
except Exception as e:
print(e)
#2: build COLOSSUS (random stargate)
elif action == 23:
try:
if self.can_afford(UnitTypeId.OBSERVER ):
for sg in self.structures(UnitTypeId.ROBOTICSBAY).ready.idle:
if self.can_afford(UnitTypeId.OBSERVER):
sg.train(UnitTypeId.OBSERVER)
except Exception as e:
print(e)
map = np.zeros((self.game_info.map_size[0], self.game_info.map_size[1], 3), dtype=np.uint8)
# draw the minerals:
for mineral in self.mineral_field:
pos = mineral.position
c = [175, 255, 255]
fraction = mineral.mineral_contents / 1800
if mineral.is_visible:
#print(mineral.mineral_contents)
map[math.ceil(pos.y)][math.ceil(pos.x)] = [int(fraction*i) for i in c]
else:
map[math.ceil(pos.y)][math.ceil(pos.x)] = [20,75,50]
# draw the enemy start location:
for enemy_start_location in self.enemy_start_locations:
pos = enemy_start_location
c = [0, 0, 255]
map[math.ceil(pos.y)][math.ceil(pos.x)] = c
# draw the enemy units:
for enemy_unit in self.enemy_units:
pos = enemy_unit.position
c = [100, 0, 255]
# get unit health fraction:
fraction = enemy_unit.health / enemy_unit.health_max if enemy_unit.health_max > 0 else 0.0001
map[math.ceil(pos.y)][math.ceil(pos.x)] = [int(fraction*i) for i in c]
# draw the enemy structures:
for enemy_structure in self.enemy_structures:
pos = enemy_structure.position
c = [0, 100, 255]
# get structure health fraction:
fraction = enemy_structure.health / enemy_structure.health_max if enemy_structure.health_max > 0 else 0.0001
map[math.ceil(pos.y)][math.ceil(pos.x)] = [int(fraction*i) for i in c]
# draw our structures:
for our_structure in self.structures:
# if it's a nexus:
if our_structure.type_id == UnitTypeId.NEXUS:
pos = our_structure.position
c = [255, 255, 175]
# get structure health fraction:
fraction = our_structure.health / our_structure.health_max if our_structure.health_max > 0 else 0.0001
map[math.ceil(pos.y)][math.ceil(pos.x)] = [int(fraction*i) for i in c]
else:
pos = our_structure.position
c = [0, 255, 175]
# get structure health fraction:
fraction = our_structure.health / our_structure.health_max if our_structure.health_max > 0 else 0.0001
map[math.ceil(pos.y)][math.ceil(pos.x)] = [int(fraction*i) for i in c]
# draw the vespene geysers:
for vespene in self.vespene_geyser:
# draw these after buildings, since assimilators go over them.
# tried to denote some way that assimilator was on top, couldnt
# come up with anything. Tried by positions, but the positions arent identical. ie:
# vesp position: (50.5, 63.5)
# bldg positions: [(64.369873046875, 58.982421875), (52.85693359375, 51.593505859375),...]
pos = vespene.position
c = [255, 175, 255]
fraction = vespene.vespene_contents / 2250
if vespene.is_visible:
map[math.ceil(pos.y)][math.ceil(pos.x)] = [int(fraction*i) for i in c]
else:
map[math.ceil(pos.y)][math.ceil(pos.x)] = [50,20,75]
# draw our units:
for our_unit in self.units:
# if it is a voidray:
if our_unit.type_id == UnitTypeId.VOIDRAY or UnitTypeId.COLOSSUS or UnitTypeId.ZEALOT or UnitTypeId.STALKER or UnitTypeId.DARKTEMPLAR or UnitTypeId.CARRIER or UnitTypeId.IMMORTAL or UnitTypeId.OBSERVER:
pos = our_unit.position
c = [255, 75 , 75]
# get health:
fraction = our_unit.health / our_unit.health_max if our_unit.health_max > 0 else 0.0001
map[math.ceil(pos.y)][math.ceil(pos.x)] = [int(fraction*i) for i in c]
else:
pos = our_unit.position
c = [175, 255, 0]
# get health:
fraction = our_unit.health / our_unit.health_max if our_unit.health_max > 0 else 0.0001
map[math.ceil(pos.y)][math.ceil(pos.x)] = [int(fraction*i) for i in c]
# show map with opencv, resized to be larger:
# horizontal flip:
cv2.imshow('map',cv2.flip(cv2.resize(map, None, fx=4, fy=4, interpolation=cv2.INTER_NEAREST), 0))
cv2.waitKey(1)
if SAVE_REPLAY:
# save map image into "replays dir"
cv2.imwrite(f"replays/{int(time.time())}-{iteration}.png", map)
reward = 0
try:
attack_count = 0
# iterate through our void rays:
for voidray in self.units(UnitTypeId.VOIDRAY, UnitTypeId.COLOSSUS, UnitTypeId.ZEALOT, UnitTypeId.STALKER, UnitTypeId.DARKTEMPLAR, UnitTypeId.CARRIER, UnitTypeId.IMMORTAL, UnitTypeId.OBSERVER):
# if voidray is attacking and is in range of enemy unit:
if voidray.is_attacking and voidray.target_in_range:
if self.enemy_units.closer_than(8, voidray) or self.enemy_structures.closer_than(8, voidray):
# reward += 0.005 # original was 0.005, decent results, but let's 3x it.
reward += 0.015
attack_count += 1
except Exception as e:
print("reward",e)
reward = 0
if iteration % 100 == 0:
print(f"Iter: {iteration}. RWD: {reward}. VR: {self.units(UnitTypeId.VOIDRAY, UnitTypeId.COLOSSUS, UnitTypeId.ZEALOT, UnitTypeId.STALKER, UnitTypeId.DARKTEMPLAR, UnitTypeId.CARRIER, UnitTypeId.IMMORTAL, UnitTypeId.OBSERVER).amount}")
# write the file:
data = {"state": map, "reward": reward, "action": None, "done": False} # empty action waiting for the next one!
with open('state_rwd_action.pkl', 'wb') as f:
pickle.dump(data, f)
result = run_game( # run_game is a function that runs the game.
maps.get("AcropolisLE"), # the map we are playing on
[Bot(Race.Protoss, IncrediBot()), # runs our coded bot, protoss race, and we pass our bot object
Computer(Race.Zerg, Difficulty.Hard)], # runs a pre-made computer agent, zerg race, with a hard difficulty.
realtime=False, # When set to True, the agent is limited in how long each step can take to process.
)
from sc2 import maps
from sc2.bot_ai import BotAI
from sc2.data import Difficulty, Race
from sc2.ids.unit_typeid import UnitTypeId
from sc2.main import run_game
from sc2.player import Bot, Computer
class CannonRushBot(BotAI):
# pylint: disable=R0912
async def on_step(self, iteration):
if iteration == 0:
await self.chat_send("(probe)(pylon)(cannon)(cannon)(gg)")
if not self.townhalls:
# Attack with all workers if we don't have any nexuses left, attack-move on enemy spawn (doesn't work on 4 player map) so that probes auto attack on the way
for worker in self.workers:
worker.attack(self.enemy_start_locations[0])
return
nexus = self.townhalls.random
# Make probes until we have 16 total
if self.supply_workers < 16 and nexus.is_idle:
if self.can_afford(UnitTypeId.PROBE):
nexus.train(UnitTypeId.PROBE)
# If we have no pylon, build one near starting nexus
elif not self.structures(UnitTypeId.PYLON) and self.already_pending(UnitTypeId.PYLON) == 0:
if self.can_afford(UnitTypeId.PYLON):
await self.build(UnitTypeId.PYLON, near=nexus)
# If we have no forge, build one near the pylon that is closest to our starting nexus
elif not self.structures(UnitTypeId.FORGE):
pylon_ready = self.structures(UnitTypeId.PYLON).ready
if pylon_ready:
if self.can_afford(UnitTypeId.FORGE):
await self.build(UnitTypeId.FORGE, near=pylon_ready.closest_to(nexus))
# If we have less than 2 pylons, build one at the enemy base
elif self.structures(UnitTypeId.PYLON).amount < 2:
if self.can_afford(UnitTypeId.PYLON):
pos = self.enemy_start_locations[0].towards(self.game_info.map_center, random.randrange(8, 15))
await self.build(UnitTypeId.PYLON, near=pos)
# If we have no cannons but at least 2 completed pylons, automatically find a placement location and build them near enemy start location
elif not self.structures(UnitTypeId.PHOTONCANNON):
if self.structures(UnitTypeId.PYLON).ready.amount >= 2 and self.can_afford(UnitTypeId.PHOTONCANNON):
pylon = self.structures(UnitTypeId.PYLON).closer_than(20, self.enemy_start_locations[0]).random
await self.build(UnitTypeId.PHOTONCANNON, near=pylon)
# Decide if we should make pylon or cannons, then build them at random location near enemy spawn
elif self.can_afford(UnitTypeId.PYLON) and self.can_afford(UnitTypeId.PHOTONCANNON):
# Ensure "fair" decision
for _ in range(20):
pos = self.enemy_start_locations[0].random_on_distance(random.randrange(5, 12))
building = UnitTypeId.PHOTONCANNON if self.state.psionic_matrix.covers(pos) else UnitTypeId.PYLON
await self.build(building, near=pos)
For some reason, in this code, the modules sc2 is not found:
import sc2
from sc2 import maps
from sc2.bot_ai import BotAI
from sc2.data import Difficulty, Race
from sc2.ids.ability_id import AbilityId
from sc2.ids.buff_id import BuffId
from sc2.ids.unit_typeid import UnitTypeId
from sc2.main import run_game
from sc2.player import Bot, Computer
import random
import cv2
import math
import numpy as np
import sys
import pickle
import time
SAVE_REPLAY = True
total_steps = 10000
steps_for_pun = np.linspace(0, 1, total_steps)
step_punishment = ((np.exp(steps_for_pun**3)/10) - 0.1)*10
class IncrediBot(BotAI): # inhereits from BotAI (part of BurnySC2)
async def on_step(self, iteration: int): # on_step is a method that is called every step of the game.
no_action = True
while no_action:
try:
with open('state_rwd_action.pkl', 'rb') as f:
state_rwd_action = pickle.load(f)
result = run_game( # run_game is a function that runs the game.
maps.get("AcropolisLE"), # the map we are playing on
[Bot(Race.Protoss, IncrediBot()), # runs our coded bot, protoss race, and we pass our bot object
Computer(Race.Zerg, Difficulty.Hard)], # runs a pre-made computer agent, zerg race, with a hard difficulty.
realtime=False, # When set to True, the agent is limited in how long each step can take to process.
)
if str(result) == "Result.Victory":
rwd = 500
else:
rwd = -500
with open("results.txt","a") as f:
f.write(f"{result}\n")
map = np.zeros((224, 224, 3), dtype=np.uint8)
observation = map
data = {"state": map, "reward": rwd, "action": None, "done": True} # empty action waiting for the next one!
with open('state_rwd_action.pkl', 'wb') as f:
pickle.dump(data, f)
cv2.destroyAllWindows()
cv2.waitKey(1)
time.sleep(3)
sys.exit()
======================================================================
======================================================================
But in this code, he found the module sc2:
import random
from sc2 import maps
from sc2.bot_ai import BotAI
from sc2.data import Difficulty, Race
from sc2.ids.unit_typeid import UnitTypeId
from sc2.main import run_game
from sc2.player import Bot, Computer
class CannonRushBot(BotAI):
def main():
run_game(
maps.get("ThunderbirdLE"),
[Bot(Race.Protoss, CannonRushBot(), name="CheeseCannon"),
Computer(Race.Protoss, Difficulty.Medium)],
realtime=False,
)
if name == "main":
main()
Pls someone help me!!!!
OS:windows 11
python: 3.10.11
The text was updated successfully, but these errors were encountered: