BurnySc2 / python-sc2

A StarCraft II bot api client library for Python 3
MIT License
472 stars 156 forks source link

ModuleNotFoundError: No module named 'sc2' #188

Open Douilol opened 6 months ago

Douilol commented 6 months ago

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)

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

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

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

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

cjy513203427 commented 6 months ago

I used PyCharm I did't have this problem. I met this problem before with VSCode

Nickrader commented 4 months ago

Try the poetry install method, see if that works.