async def on_building_construction_complete(self, bot: sc2.BotAI, unit: Unit): unit_id: UnitTypeId = unit.type_id if unit_id == ASSIMILATOR: diff = -unit.surplus_harvesters if diff < 0: print("ASSIMILATOR WHEN BUILT HAS 4 OR MORE ASSIGNED ALREADY") # add three workers to it local_minerals_tags = { mineral.tag for mineral in bot.mineral_field if mineral.distance_to(unit.position) <= 8 } n_closest_workers = bot.workers.filter( lambda worker: worker.order_target in local_minerals_tags or worker.is_carrying_minerals).n_closest_to_distance( unit.position, 20, diff) if n_closest_workers == None: # no workers close to the new built assimilator, do nothing pass for worker in n_closest_workers: bot.do(worker.gather(unit)) elif unit_id == NEXUS: # do stuff here also pass
def __init__(self, ai: sc2.BotAI, knowledge: 'Knowledge', x: int, y: int, x2: int, y2: int): self.ai = ai self.knowledge = knowledge self.cache: UnitCacheManager = self.knowledge.unit_cache self.bottom_left_x = x self.bottom_left_y = y self.top_right_x = x2 self.top_right_y = y2 self.center = Point2(((x + x2) / 2.0, (y + y2) / 2.0)) self._x, self._y = self.center.rounded self.zone: Optional['Zone'] = None self.heat: float = 0 self.stealth_heat: float = 0 self.last_enemy_power = ExtendedPower(knowledge.unit_values) d2 = 15 for zone in knowledge.expansion_zones: if zone.center_location.distance_to(self.center) < d2: if ai.get_terrain_height( zone.center_location) == ai.get_terrain_height( self.center): # if zone == self.knowledge.own_main_zone: # print("MAIN ZONE") self.zone = zone
async def chronoboost_building(bot: BotAI, nexus, building=None): if not building: building = bot.units(WARPGATE).ready.random_or( bot.units(GATEWAY).ready.random_or( bot.units(ROBOTICSFACILITY).ready.random_or( bot.townhalls.ready.random))) chronoboost = AbilityId.EFFECT_CHRONOBOOSTENERGYCOST await bot.do(nexus(chronoboost, building))
async def __build_supplydepot(self, bot: sc2.BotAI): # build supply depot if bot.supply_left < (2 if bot.units(UnitTypeId.BARRACKS).amount < 3 else 4): if bot.can_afford(UnitTypeId.SUPPLYDEPOT) and bot.already_pending( UnitTypeId.SUPPLYDEPOT) < 2: await bot.build(UnitTypeId.SUPPLYDEPOT, near=bot.cc.position.random_on_distance(5))
def assign_closest_gas(self, bot: sc2.BotAI, unit: Unit): gas_buildings = bot.gas_buildings.ready.sorted_by_distance_to( unit.position) for mining_place in gas_buildings: if mining_place.has_vespene and mining_place.surplus_harvesters < 0: bot.do(unit.gather(mining_place)) return True return False
async def __train_marines(self, bot: sc2.BotAI): # train marines if bot.units(UnitTypeId.MARINE).amount < 10 or bot.units( UnitTypeId.MARINE).amount < bot.units( UnitTypeId.REAPER).amount * 5: for rax in bot.units(UnitTypeId.BARRACKS).ready.noqueue: if not bot.can_afford(UnitTypeId.MARINE): break await bot.do(rax.train(UnitTypeId.MARINE))
async def __group_idle_units(self, bot: sc2.BotAI): if bot.time > 480: idle_units = bot.units(UnitTypeId.MARINE).idle | bot.units( UnitTypeId.REAPER).idle if not bot.cc: idle_units = idle_units | bot.workers if idle_units.amount > 20: self.attack_groups.add(ControlGroup(idle_units))
async def __collect_with_scv(self, bot: sc2.BotAI): # collect with SCV for scv in bot.units(UnitTypeId.SCV).idle: await bot.do(scv.gather(bot.state.mineral_field.closest_to(bot.cc)) ) for a in bot.units(UnitTypeId.REFINERY): if a.assigned_harvesters < a.ideal_harvesters: w = bot.workers.closer_than(20, a) if w.exists: await bot.do(w.random.gather(a))
async def upgrade_zergling_speed(bot: BotAI): spawning_pool = bot.units(UnitTypeId.SPAWNINGPOOL).ready if not spawning_pool.exists: return False spawning_pool = spawning_pool.first metabolic_boost = AbilityId.RESEARCH_ZERGLINGMETABOLICBOOST if bot.can_afford(metabolic_boost): available_abilities = await bot.get_available_abilities(spawning_pool) if metabolic_boost in available_abilities: bot_logger.log_action(bot, 'upgrading metabolic boost') await bot.do(spawning_pool(metabolic_boost)) return True return False
async def __build_refinery(self, bot: sc2.BotAI): if bot.units(UnitTypeId.REFINERY).amount < 2: if bot.can_afford(UnitTypeId.REFINERY): vgs = bot.state.vespene_geyser.closer_than(20.0, bot.cc) for vg in vgs: if bot.units(UnitTypeId.REFINERY).closer_than(1.0, vg).exists: break worker = bot.select_build_worker(vg.position) if worker is None: break await bot.do(worker.build(UnitTypeId.REFINERY, vg)) break
def assign_probe(self, bot: sc2.BotAI, unit: Unit, prio_gas: bool): """ Assigns the unit (PROBE) to either the closes gas or mineral patch depending on prio_gas """ if prio_gas: if not self.assign_closest_gas( bot, unit) and not self.assign_closest_mineral(bot, unit): closest_mineral = bot.mineral_field.closest_to(unit) bot.do(unit.gather(closest_mineral)) else: if not self.assign_closest_mineral( bot, unit) and not self.assign_closest_gas(bot, unit): closest_mineral = bot.mineral_field.closest_to(unit) bot.do(unit.gather(closest_mineral))
async def build_building_once(bot: BotAI, building: UnitTypeId, location: Union[Point2, Point3, Unit], max_distance: int = 20, unit: Optional[Unit] = None, random_alternative: bool = True, placement_step: int = 2): if not bot.units(building).ready.exists and not bot.already_pending( building) and bot.can_afford(building): bot_logger.log_action(bot, "building {} at {}".format(building, location)) return await bot.build(building, near=location, max_distance=max_distance, unit=unit, random_alternative=random_alternative, placement_step=placement_step)
def test_empty(): queue = ExpenseQueue(BotAI()) assert_(queue.is_empty()) # queue is initially empty queue.put(lambda item: None) assert_(not queue.is_empty()) # there is an action enqueued queue.get() assert_(queue.is_empty()) # and now it should be gone
def assign_closest_mineral(self, bot: sc2.BotAI, unit: Unit) -> bool: """ Assigns unit to the closest mineral field that is not utilized """ bases = bot.townhalls.ready.sorted_by_distance_to(unit.position) for mining_place in bases: if mining_place.surplus_harvesters < 0: local_minerals = { mineral for mineral \ in bot.mineral_field if mineral.distance_to(mining_place) <= 8 } target_mineral = max( local_minerals, key=lambda mineral: mineral.mineral_contents, default=None) bot.do(unit.gather(target_mineral)) return True return False
def draw_influence_in_game(self, bot: BotAI, grid: np.ndarray, lower_threshold: int, upper_threshold: int, color: Tuple[int, int, int], size: int) -> None: height: float = bot.get_terrain_z_height(bot.start_location) for x, y in zip(*np.where((grid > lower_threshold) & (grid < upper_threshold))): pos: Point3 = Point3((x, y, height)) if grid[x, y] == np.inf: val: int = 9999 else: val: int = int(grid[x, y]) bot.client.debug_text_world(str(val), pos, color, size)
def is_base_full(self, bot: sc2.BotAI, base: Unit) -> bool: """ Returns True if all 'numbers' are full over a nexus and its assimilators """ # find the assimilators close to this base assimilators = bot.structures(ASSIMILATOR).closer_than( 8, base.position).filter(lambda assimilator: assimilator.has_vespene) count = sum( map(lambda assimilator: assimilator.assigned_harvesters, assimilators)) count += base.assigned_harvesters return count >= (base.ideal_harvesters + 3 * assimilators.amount)
async def micro_zealots(bot: BotAI): zealots = bot.units(ZEALOT).ready actions = [] for zealot in zealots: if bot.known_enemy_units.exists: actions.append( zealot.attack(bot.known_enemy_units.closest_to( zealot.position))) else: target = bot.known_enemy_structures.random_or( bot.known_enemy_units.random_or(bot.enemy_start_locations[0])) actions.append(zealot.attack(target)) await bot.do_actions(actions)
async def depots_required(bot: sc2.BotAI, ) -> int: """ Returns the number of supply depots needed based on bot's production structures """ growth_speed = 0 townhall_count = bot.structures({ UnitTypeId.COMMANDCENTER, UnitTypeId.PLANETARYFORTRESS, UnitTypeId.ORBITALCOMMAND }).ready.amount rax_count = bot.structures(UnitTypeId.BARRACKS).ready.amount rax_count += bot.structures(UnitTypeId.BARRACKSREACTOR).ready.amount factory_count = bot.structures(UnitTypeId.FACTORY).ready.amount factory_count += bot.structures(UnitTypeId.FACTORYREACTOR).ready.amount starport_count = bot.structures(UnitTypeId.STARPORT).ready.amount starport_count += bot.structures(UnitTypeId.STARPORTREACTOR).ready.amount # Probes/scv take 12 seconds to build # https://liquipedia.net/starcraft2/Nexus_(Legacy_of_the_Void) growth_speed += townhall_count / 12.0 # https://liquipedia.net/starcraft2/Barracks_(Legacy_of_the_Void) # fastest usage is marauder supply with 2 supply and train 21 seconds growth_speed += rax_count * 2 / 21.0 # https://liquipedia.net/starcraft2/Factory_(Legacy_of_the_Void) # fastest usage is helliom with 2 supply and build time of 21 seconds growth_speed += factory_count * 2 / 21.0 # https://liquipedia.net/starcraft2/Starport_(Legacy_of_the_Void) # We'll use viking timing here growth_speed += starport_count * 2 / 30.0 growth_speed *= 1.2 # Just a little bit of margin of error build_time = 21 # depot build time # build_time += min(self.ai.time / 60, 5) # probe walk time predicted_supply = min(200, bot.supply_used + build_time * growth_speed) current_depots = bot.structures({ UnitTypeId.SUPPLYDEPOT, UnitTypeId.SUPPLYDEPOTLOWERED, UnitTypeId.SUPPLYDEPOTDROP }).ready.amount if bot.supply_cap == 200: return current_depots return ceil((predicted_supply - bot.supply_cap) / 8) + current_depots
async def micro_sentries(bot: BotAI): sentries = bot.units(SENTRY).ready actions = [] for sentry in sentries: if bot.known_enemy_units.exists: closest_known_enemy = bot.known_enemy_units.closest_to( sentry.position) if sentry.target_in_range(closest_known_enemy): guardian_shield = AbilityId.GUARDIANSHIELD_GUARDIANSHIELD can_cast = await bot.can_cast(sentry, guardian_shield) if can_cast: actions.append(sentry(guardian_shield)) actions.append(sentry.attack(closest_known_enemy)) else: target = bot.known_enemy_structures.random_or( bot.known_enemy_units.random_or(bot.enemy_start_locations[0])) actions.append(sentry.attack(target)) await bot.do_actions(actions)
async def micro_stalkers(bot: BotAI): stalkers = bot.units(STALKER).ready actions = [] for stalker in stalkers: enemy_threats_close = bot.known_enemy_units.filter( lambda x: x.can_attack_ground).closer_than( 15, stalker) # threats that can attack if enemy_threats_close.exists: closest_enemy = enemy_threats_close.sorted_by_distance_to( stalker).first if stalker.weapon_cooldown == 0: actions.append(stalker.attack(closest_enemy)) elif not bot.known_enemy_units.filter( lambda x: x.can_attack_ground).closer_than(3, stalker).exists: actions.append(stalker.move(closest_enemy.position)) else: target = bot.known_enemy_structures.random_or( bot.known_enemy_units.random_or(bot.enemy_start_locations[0])) actions.append(stalker.attack(target)) await bot.do_actions(actions)
async def micro_immortals(bot: BotAI): immortals = bot.units(IMMORTAL).ready actions = [] for immortal in immortals: enemy_threats_close = bot.known_enemy_units.filter( lambda x: x.can_attack_ground).closer_than( 15, immortal) # threats that can attack if enemy_threats_close.exists: closest_enemy = enemy_threats_close.sorted_by_distance_to( immortal).first if immortal.weapon_cooldown == 0: actions.append(immortal.attack(closest_enemy)) elif not bot.known_enemy_units.filter( lambda x: x.can_attack_ground).closer_than( 5, immortal).exists: actions.append(immortal.move(closest_enemy.position)) else: target = bot.known_enemy_structures.random_or( bot.known_enemy_units.random_or(bot.enemy_start_locations[0])) actions.append(immortal.attack(target)) await bot.do_actions(actions)
async def on_step(self, bot: sc2.BotAI, iteration): ramp_pos = bot.main_base_ramp.protoss_wall_warpin if self.state == "DEFENCE": if len(self.army) >= self.squad_size: self.squads.append(self.army.copy()) self.army.clear() for unit in self.army: bot.do(unit.attack(ramp_pos)) # select all enemy units, filter out invis units targets = ( bot.enemy_units | bot.enemy_structures).filter(lambda unit: unit.can_be_attacked) for squad in self.squads: for unit in squad: if targets: target = targets.closest_to(unit) bot.do(unit.attack(target)) else: bot.do(unit.attack(bot.enemy_start_locations[0]))
def get_random_larva(bot: BotAI) -> Union[Unit, None]: larva = bot.units(UnitTypeId.LARVA).ready return larva.exists and larva.random
async def build_overlord(bot: BotAI, larva): if bot.can_afford(UnitTypeId.OVERLORD): unit = larva if larva else get_random_larva(bot) if unit: bot_logger.log_action(bot, "building overlord") return await bot.do(unit.train(UnitTypeId.OVERLORD))
async def build_zergling(bot: BotAI, larva): unit = larva if larva else get_random_larva(bot) if unit and bot.can_afford(UnitTypeId.ZERGLING) and bot.units(UnitTypeId.SPAWNINGPOOL).ready.exists: bot_logger.log_action(bot, "building zergling") return await bot.do(unit.train(UnitTypeId.ZERGLING))
async def __train_scv(self, bot: sc2.BotAI): # train SCVs if bot.can_afford( UnitTypeId.SCV) and bot.workers.amount < 20 and bot.cc.noqueue: await bot.do(bot.cc.train(UnitTypeId.SCV))
def mock_ai() -> BotAI: ai = BotAI() ai.unit_command_uses_self_do = True ai._initialize_variables() # ai = mock.Mock(bot_object) ai._distances_override_functions(0) ai.actions = [] ai.config = {"general": mock.Mock(), "debug_log": mock.Mock()} ai.config["general"].getboolean = lambda x: False ai.config["debug_log"].getboolean = lambda x, fallback: False ai.my_race = Race.Protoss ai.enemy_race = Race.Protoss ai.state = mock.Mock() ai.state.effects = [] ai.state.visibility.__getitem__ = lambda s, x: 2 ai._client = mock.Mock() ai._game_info = mock.Mock() ai._game_info.player_start_location = MAIN_POINT ai._game_info.start_locations = [ENEMY_MAIN_POINT] ai._game_info.placement_grid.height = 100 ai._game_info.placement_grid.width = 100 ai._game_info.map_center = Point2((50, 50)) ai._game_info.map_name = "Mock" ai._game_info.terrain_height.__getitem__ = lambda s, x: 0 ai._game_info.terrain_height.data_numpy = [0] ai._game_info.map_ramps = [] ai._game_data = mock.Mock() ai._game_data.unit_types = {} ai._game_data.units = { UnitTypeId.MINERALFIELD.value: mock.Mock(), UnitTypeId.PROBE.value: mock.Mock(), } ai._game_data.units[UnitTypeId.MINERALFIELD.value].has_minerals = True ai._game_data.units[UnitTypeId.MINERALFIELD.value].attributes = {} ai._game_data.units[UnitTypeId.PROBE.value].attributes = {} ai._game_data.abilities = {AbilityId.HARVEST_GATHER.value: mock.Mock()} ai._game_data.abilities[ AbilityId.HARVEST_GATHER.value].id = AbilityId.HARVEST_GATHER for typedata in BUILDING_IDS: ai._game_data.units[typedata.value] = mock.Mock() ai._game_data.units[typedata.value].attributes = {IS_STRUCTURE} ai._game_data.units[UnitTypeId.ASSIMILATOR.value].has_vespene = True ai._game_data.units[UnitTypeId.ASSIMILATORRICH.value].has_vespene = True mineral = create_mineral(ai, Point2((16, 10))) mineral2 = create_mineral(ai, Point2((16, 60))) ai._expansion_positions_list = [ MAIN_POINT, NATURAL_POINT, ENEMY_MAIN_POINT, ENEMY_NATURAL_POINT ] ai._resource_location_to_expansion_position_dict = { mineral.position: MAIN_POINT, mineral2.position: NATURAL_POINT } return ai
async def __train_siegetank(self, bot: sc2.BotAI): for fact in bot.units(UnitTypeId.FACTORY).ready.noqueue: if not bot.can_afford(UnitTypeId.SIEGETANK): break await bot.do(fact.train(UnitTypeId.SIEGETANK))
async def __train_reapers(self, bot: sc2.BotAI): for rax in bot.units(UnitTypeId.BARRACKS).ready.noqueue: if not bot.can_afford(UnitTypeId.REAPER): break await bot.do(rax.train(UnitTypeId.REAPER))
def get_closest_pylon_to_enemy_base(bot: BotAI): proxy_pylon = bot.units(PYLON).closest_to(bot.enemy_start_locations[0]) return proxy_pylon