def prepare_attack(self, military_ratio, interval=10): """ Prepares an attack wave every interval when ready. Attack waves are comprised of military units specified by the given ratio. :param military_ratio: <dict> [UnitId: int] specifying military composition. :param interval: <int> time interval in seconds to prepare a wave. """ if self.seconds_elapsed % interval != 0: return added_non_medic_units = False attack_wave = None for unit in military_ratio: if not added_non_medic_units and unit == MEDIVAC: continue amount = military_ratio[unit] units = self.units(unit) if units.idle.amount >= amount: if unit != MEDIVAC: added_non_medic_units = True if attack_wave is None: attack_wave = ControlGroup(units.idle) else: attack_wave.add_units(units.idle) if attack_wave is not None and 0 < len(attack_wave) > 12: self.attack_waves.add(attack_wave)
async def addattackgroup(self): if self.units(MARINE).amount > 25: ad = ControlGroup(self.units(MARINE).idle) self.attack_groups.add(ad) elif self.units(MARINE).idle.amount > 15: ad = ControlGroup(self.units(MARINE).idle) self.attack_groups.add(ad)
def create_observer_control_groups(self): """ Create control groups """ observers = self.units.of_type(OBSERVER) # will do nothing until an observer is made # then sequentially assign them to control groups one at a time valid_observers = [] for obs in observers: try: o1 = self.control_groups['Observer 1'] if obs.tag == o1.select_units(self.units)[0].tag: continue # to next observer except (KeyError, IndexError): pass try: o2 = self.control_groups['Observer 2'] if obs.tag == o2.select_units(self.units)[0].tag: continue # to next observer except (KeyError, IndexError): pass valid_observers.append(obs) # valid_observers are not assigned to another control group for obs in valid_observers: # make sure valid_observers is of length 1 try: assert len(valid_observers) == 1 # debug - observers must be assigned immediately except AssertionError: logger.error('valid_observers should be of length 1') valid_observers = Units(valid_observers[0]) # if this observer is already in a control group do not reassign it try: o1 = self.control_groups['Observer 1'] if o1.empty: raise KeyError # KeyError if control group does not exist or is empty # TODO: maybe i should just init a bunch of control groups at the start of the game # and not have to worry about all this garbage except KeyError: self.control_groups['Observer 1'] = ControlGroup(valid_observers) logger.success('Assigned Observer to `Observer 1` Control Group') return try: o2 = self.control_groups['Observer 2'] if o2.empty: raise KeyError except KeyError: self.control_groups['Observer 2'] = ControlGroup(valid_observers) logger.success('Assigned Observer to `Observer 2` Control Group') return
def __init__(self, bot): self.bot = bot self.logger = bot.logger self.opponent = bot.opponent self.first_overlord_tag = None self.first_overlord_ordered = False self.early_warning_overlord_tag = None self.early_warning_overlord_ordered = False self.has_verified_front_door = False self.all_combat_units = None self.reserve = ControlGroup([]) self.harassing_base_scouts = ControlGroup([]) self.no_mans_expansions_scouts = ControlGroup([]) self.muta_flankers = ControlGroup([]) self.base_defenders = ControlGroup([])
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 on_step(self, iteration): actions = [] cc = self.units(COMMANDCENTER) if not cc.exists: target = self.known_enemy_structures.random_or( self.enemy_start_locations[0]).position for unit in self.workers | self.units(MARINE): actions.append(unit.attack(target)) await self.do_actions(actions) return else: cc = cc.first if self.units(MARINE).idle.amount > 15 and iteration % 50 == 1: cg = ControlGroup(self.units(MARINE).idle) self.attack_groups.add(cg) if self.can_afford(SCV) and self.workers.amount < 16 and cc.noqueue: actions.append(cc.train(SCV)) elif self.supply_left < (2 if self.units(BARRACKS).amount < 3 else 4): if self.can_afford( SUPPLYDEPOT) and self.already_pending(SUPPLYDEPOT) < 2: await self.build(SUPPLYDEPOT, near=cc.position.towards( self.game_info.map_center, 5)) elif self.units(BARRACKS).amount < 3 or ( self.minerals > 400 and self.units(BARRACKS).amount < 5): if self.can_afford(BARRACKS): p = self.game_info.map_center.towards( self.enemy_start_locations[0], 25) await self.build(BARRACKS, near=p) for rax in self.units(BARRACKS).ready.noqueue: if not self.can_afford(MARINE): break actions.append(rax.train(MARINE)) for scv in self.units(SCV).idle: actions.append(scv.gather(self.state.mineral_field.closest_to(cc))) for ac in list(self.attack_groups): alive_units = ac.select_units(self.units) if alive_units.exists and alive_units.idle.exists: target = self.known_enemy_structures.random_or( self.enemy_start_locations[0]).position for marine in ac.select_units(self.units): actions.append(marine.attack(target)) else: self.attack_groups.remove(ac) await self.do_actions(actions)
def prepare_attack(self, military_ratio): """ Prepares an attack wave when ready. :param military_ratio: <dict> [UnitId: int] specifying military composition. """ attack_wave = None for unit in military_ratio: amount = military_ratio[unit] units = self.units(unit) if units.idle.amount >= amount: if attack_wave is None: attack_wave = ControlGroup(units.idle) else: attack_wave.add_units(units.idle) if attack_wave is not None: self.attack_waves.add(attack_wave)
def prepare_attack(self): """ Prepares an attack wave when ready. """ total = 0 for unit in self.military_distribution: units = self.units(unit) total += units.idle.amount if total >= self.num_troops_per_wave: attack_wave = None for unit in self.military_distribution: units = self.units(unit) if attack_wave is None: attack_wave = ControlGroup(units.idle) else: attack_wave.add_units(units.idle) self.attack_waves.add(attack_wave)
async def attack(self): if self.units(MARINE).idle.amount > 15: cg = ControlGroup(self.units(MARINE).idle) self.attack_groups.add(cg) for ac in list(self.attack_groups): alive_units = ac.select_units(self.units) if alive_units.exists and alive_units.idle.exists: target = self.known_enemy_structures.random_or( self.enemy_start_locations[0]).position for marine in ac.select_units(self.units): await self.do(marine.attack(target)) else: self.attack_groups.remove(ac)
async def on_step(self, iteration): if iteration == 0: if iteration == 0: await self.chat_send("(glhf)") #for selecting our Units if self.units(REAPER).idle.amount > 15: for reaper in self.units(REAPER).idle: #Making the initial population for the first 10 reapers if reaper.tag in self.reapergenes: print("already done") else: if len(self.reaperrewards.values()) > 2: v = list(self.reaperrewards.values()) k = list(self.reaperrewards.keys()) curmax = 0 for i in range(10): pick = random.randint(0, len(v) - 1) if (v[pick] >= curmax): curmax = v[pick] curpick = k[pick] self.reapergenes[reaper.tag] = [0, 0, 0] self.reapergenes[ reaper.tag][0] = self.reapergenes[curpick][0] self.reapergenes[ reaper.tag][1] = self.reapergenes[curpick][1] self.reapergenes[ reaper.tag][2] = self.reapergenes[curpick][2] if random.randint(1, 100) / 100 <= .50: print("mutate") print([ reaper.tag, self.reapergenes[reaper.tag], "Son of:", curpick, self.reapergenes[curpick], self.reaperrewards[curpick] ]) #mutate pick = random.randint(0, 2) if (random.randint(1, 2) == 1): print("minus .1") self.reapergenes[reaper.tag][pick] -= .3 else: print("plus .1") self.reapergenes[reaper.tag][pick] += .3 print([ reaper.tag, self.reapergenes[reaper.tag], "Son of:", curpick, self.reapergenes[curpick], self.reaperrewards[curpick] ]) else: #since there are no rewarded reapers seed = [0, 0, 0] seed[0] = (random.randint(1, 100) / 100) seed[1] = (random.randint(1, 100) / 100) seed[2] = (random.randint(1, 100) / 100) self.reapergenes[reaper.tag] = seed #del v[v.index(max(v))] print(self.reapergenes) cg = ControlGroup(self.units(REAPER).idle) self.attack_groups.add(cg) for cc in self.units(UnitTypeId.COMMANDCENTER).ready.noqueue: if self.can_afford( SCV) and self.workers.amount < 20 and cc.noqueue: await self.do(cc.train(SCV)) cc = self.units(COMMANDCENTER).ready.first bobthebuilder = self.units(SCV)[0] #We must construct additional pylons if self.supply_left < 2: if self.can_afford( SUPPLYDEPOT) and self.already_pending(SUPPLYDEPOT) < 2: await self.build(SUPPLYDEPOT, near=cc.position.towards( self.game_info.map_center, 5)) #For building reapers if self.units(BARRACKS).amount < 3 or ( self.minerals > 400 and self.units(BARRACKS).amount < 5): if self.can_afford(BARRACKS): err = await self.build(BARRACKS, near=cc.position.towards( self.game_info.map_center, 5)) elif self.units(BARRACKS).ready.exists and self.units( REFINERY).ready.exists: barracks = self.units(BARRACKS).ready if self.can_afford(REAPER) and barracks.noqueue: await self.do(barracks.random.train(REAPER)) ''' if self.units(MISSILETURRET).amount < 3: if self.can_afford(MISSILETURRET): err = await self.build(MISSILETURRET, near=cc.position.towards(self.game_info.map_center, 5)) if self.units(ENGINEERINGBAY).amount < 1: if self.can_afford(ENGINEERINGBAY): err = await self.build(ENGINEERINGBAY, near=cc.position.towards(self.game_info.map_center, 5)) ''' if self.refinerys < 2: if self.can_afford(REFINERY): worker = self.workers.random target = self.state.vespene_geyser.closest_to(worker.position) err = await self.do(bobthebuilder.build(REFINERY, target)) if not err: self.refinerys += 1 for a in self.units(REFINERY): if a.assigned_harvesters < a.ideal_harvesters: w = self.workers.closer_than(20, a) if w.exists: await self.do(w.random.gather(a)) #TODO hash the reapers with info on health wieghts #TODO hash the enemy health totals to reward focusing #TODO reward reaper health maximization by hashing reaper health changes #TODO Create population #genes relevant to reaper movement #reaper attack allocation #weight for # # barracks = self.units(BARRACKS).ready for ac in list(self.attack_groups): alive_units = ac.select_units(self.units) total_x = [] total_y = [] total_z = [] if alive_units.amount > 10: #Calculating which units are better to attack for reaper in ac.select_units(self.units): ting = 10 targets = self.known_enemy_units.prefer_close_to(reaper) self.enemyreward = {} self.enemyindexer = {} for enemy in targets: if ting == 0: break self.enemyindexer[enemy.tag] = enemy #the negative 1 gives more reward for enemies that are easily killed by your team/ if reaper.tag in self.reapergenes: self.enemyreward[enemy.tag] = -1 * ( enemy.health - alive_units.amount * 8) * self.reapergenes[reaper.tag][1] #since they are already in order of closeness we can add a value for being closest #and decrement it each time to give different rewards self.enemyreward[enemy.tag] += self.reapergenes[ reaper.tag][2] * (ting) ting -= 1 if len(self.enemyreward.values()) > 0: v = list(self.enemyreward.values()) k = list(self.enemyreward.keys()) selectedtarget = self.enemyindexer[k[v.index(max(v))]] target = selectedtarget.position else: enemystart = 1 target = self.enemy_start_locations[0] #For loop deciding what a reaper should do for reaper in ac.select_units(self.units): if reaper.tag in self.reaper_health and reaper.tag in self.reapergenes: #If you've been hit kite away towards a safe place based on your genes if reaper.health < self.reaper_health[reaper.tag]: await self.do( reaper.move( reaper.position.towards( barracks.random.position, self.reapergenes[reaper.tag][2] * 10))) #if you are too injured to fight, run away elif reaper.health < reaper.health_max * .4: await self.do(reaper.move(barracks.random.position) ) #if you aren't attacking or running you have to fight elif reaper.is_idle: await self.do(reaper.attack(target)) #Setting up a reward slot for new reapers if reaper.tag not in self.reaper_health: self.reaperrewards[reaper.tag] = 0 #rewarding reapers for taking damage and surviving it if reaper.tag in self.reaper_health: self.reaperrewards[reaper.tag] += 60 - reaper.health #rewarding reapers for Killing units if reaper.tag in self.reaperlasttarget: #reward for a unit killed if not self.reaperlasttarget[reaper.tag].health > 0: self.reaperrewards[reaper.tag] += 2000 del self.reaperlasttarget[reaper.tag] #reward for a unit maimed elif self.reaperlasttarget[ reaper. tag].health < self.reaperlasttargethealth[ reaper.tag]: self.reaperrewards[reaper.tag] += .1 * ( self.reaperlasttargethealth[reaper.tag] - self.reaperlasttarget[reaper.tag].health) #resetting sentinel dictionaries if len(self.enemyreward.values()) > 0: self.reaperlasttarget[reaper.tag] = selectedtarget self.reaperlasttargethealth[ reaper.tag] = selectedtarget.health self.reaper_health[reaper.tag] = reaper.health #if we don't have enough units to feasably continue the fight run back home else: for reaper in ac.select_units(self.units): await self.do(reaper.move(cc.position)) self.attack_groups.remove(ac)
class ArmyManager: def __init__(self, bot): self.bot = bot self.logger = bot.logger self.opponent = bot.opponent self.first_overlord_tag = None self.first_overlord_ordered = False self.early_warning_overlord_tag = None self.early_warning_overlord_ordered = False self.has_verified_front_door = False self.all_combat_units = None self.reserve = ControlGroup([]) self.harassing_base_scouts = ControlGroup([]) self.no_mans_expansions_scouts = ControlGroup([]) self.muta_flankers = ControlGroup([]) self.base_defenders = ControlGroup([]) def deferred_init(self): self.first_overlord_tag = self.bot.units(UnitTypeId.OVERLORD).first.tag def refresh(self): self.all_combat_units = self.bot.units(UnitTypeId.ZERGLING).ready | self.bot.units(UnitTypeId.ROACH).ready | self.bot.units(UnitTypeId.HYDRALISK).ready | self.bot.units(UnitTypeId.MUTALISK).ready self.strength = util.get_units_strength(self.bot, self.all_combat_units) """ ControlGroup is actually just a set of unit tags. When units whose tag is added to a CG die, their tags remains in the CG. This is probably not a problem, but we could also cleanup the CGs by cycling tags into units and then back to tags. Not sure if worth it performance-wise. 1) alive = self.bot.units.ready.tags_in(self.reserve) 2) alive = self.reserve.select_units(self.all_combat_units) """ # Add unassigned units to reserve unassigned = self.all_combat_units.tags_not_in(self.reserve | self.harassing_base_scouts | self.no_mans_expansions_scouts | self.muta_flankers | self.base_defenders) if unassigned: self.reserve.add_units(unassigned) # Early warning lookout against proxy rax overlords = self.bot.units(UnitTypeId.OVERLORD) early_warning = overlords.find_by_tag(self.early_warning_overlord_tag) if not early_warning: volunteers = overlords.ready.tags_not_in([self.first_overlord_tag]) if volunteers: self.early_warning_overlord_tag = volunteers.first.tag self.early_warning_overlord_ordered = False self.logger.log("Found new volunteer to become early warning lookout") self._reinforce_from_reserve_if_empty(self.muta_flankers, UnitTypeId.MUTALISK, 10) self._reinforce_from_reserve_if_empty(self.harassing_base_scouts, UnitTypeId.ZERGLING, 1, True) if self.bot.time > 120: self._reinforce_from_reserve_if_empty(self.no_mans_expansions_scouts, UnitTypeId.ZERGLING, 1, True) def _reinforce_from_reserve_if_empty(self, group, unit_type, up_to=200, drone_fallback=False): survivors = group.select_units(self.bot.units) if not survivors: reserves = self.reserve.select_units(self.all_combat_units(unit_type)).take(up_to, require_all=False) for reserve in reserves: self.reserve.remove_unit(reserve) group.add_unit(reserve) if len(reserves) == 0 and drone_fallback: drones_available = self.bot.units(UnitTypeId.DRONE) # TODO filter drones that have a special job if drones_available: group.add_unit(drones_available.first) async def kamikaze(self): bot = self.bot if not bot.hq_loss_handled: try: actions = [] bot.hq_loss_handled = True self.logger.warn("All townhalls lost, loss is probably imminent!") if bot.enemy_start_locations: for unit in bot.units(UnitTypeId.DRONE) | bot.units(UnitTypeId.QUEEN) | self.all_combat_units: actions.append(unit.attack(bot.enemy_start_locations[0])) await bot.do_actions(actions) except Exception as e: print(e) def guess_front_door(self): bot = self.bot # Bot has main_base_ramp but it sometimes points to the back door ramp if base has multiple ramps bot.ramps_distance_sorted = sorted(bot._game_info.map_ramps, key=lambda ramp: ramp.top_center.distance_to(bot.start_location)) doors = [] for ramp in bot.ramps_distance_sorted: if ramp.top_center.distance_to(bot.start_location) <= MAX_BASE_DOOR_RANGE: doors.append(ramp) if len(doors) == 1: self.logger.log("This base seems to have only one ramp") return doors[0].top_center else: self.logger.warn("Base seems to have several ramps, scout will verify") return bot.start_location.towards(bot.game_info.map_center, 10) def _unit_dispersion(self, units): if units: center = units.center return statistics.median([unit.distance_to(center) for unit in units]) else: return 0 def get_seek_and_destroy_actions(self, units): # TODO sub-optimize by sending mutas to map corners actions = [] for unit in units: if self.opponent.units: point = self.opponent.units.random.position.random_on_distance(random.randrange(5, 15)) else: point = self.bot.map.get_random_point() actions.append(unit.attack(point)) return actions def _large_enough_army(self, strength): enough = (ARMY_SIZE_BASE_LEVEL + ((self.bot.time / 60) * ARMY_SIZE_TIME_MULTIPLIER)) if Strategy.PROXY in self.opponent.strategies: enough = 50 return strength >= enough or self.bot.supply_used > ARMY_SIZE_MAX # Attack to enemy base def get_army_actions(self): bot = self.bot actions = [] # TODO FIXME This should not manipulate reserve but only attack group units = self.reserve.select_units(bot.units) if units: bot.debugger.world_text("center", units.center) towards = None if self._large_enough_army(util.get_units_strength(bot, units)): towards = bot.opponent.get_next_potential_building_closest_to(bot.army_attack_point) if towards is None and Strategy.HIDDEN_BASE not in self.opponent.strategies: self.logger.warn("Army does not know where to go, time to seek & destroy!") self.opponent.strategies.add(Strategy.HIDDEN_BASE) elif towards and Strategy.HIDDEN_BASE in self.opponent.strategies: self.logger.log("Found enemy from hiding!") self.opponent.strategies.remove(Strategy.HIDDEN_BASE) if Strategy.HIDDEN_BASE in self.opponent.strategies: return self.get_seek_and_destroy_actions(units.idle) if towards: leader = units.closest_to(towards) if leader: bot.debugger.world_text("leader", leader.position) main_pack = units.closer_than(ARMY_MAIN_FORCE_RADIUS, leader.position) if main_pack.amount > 1: bot.debugger.world_text("blob", main_pack.center) dispersion = self._unit_dispersion(main_pack) if dispersion < ARMY_MAIN_FORCE_DISPERSION_MAX: # Attack! self.logger.debug(f"Tight main force advancing ({dispersion:.0f})") else: # Regroup, too dispersed self.logger.log(f"Main force is slightly dispersed ({dispersion:.0f})") towards = leader.position else: self.logger.warning(f"Leader is too alone, pulling back!") towards = units.center else: # Retreat, too weak! self.logger.debug(f"Army is too small, retreating!") towards = bot.hq_front_door bot.debugger.world_text("towards", towards) bot.army_attack_point = towards for unit in units: actions.append(unit.attack(bot.army_attack_point)) return actions def flank(self): actions = [] mutas = self.muta_flankers.select_units(self.bot.units).idle if mutas: for muta in mutas: actions.append(muta.move(self.bot.map.flanker_waypoint, queue=False)) actions.append(muta.move(self.bot.map.opponent_corner, queue=True)) actions.append(muta.attack(self.opponent.known_hq_location, queue=True)) actions.append(muta.attack(self.opponent.known_natural, queue=True)) return actions def scout_and_harass(self): actions = [] scouts = self.harassing_base_scouts.select_units(self.bot.units) if scouts: for scout in scouts: # Harass workers if self.opponent.known_hq_location and scout.distance_to(self.opponent.known_hq_location) < 3: worker_enemies = self.opponent.units(UnitTypeId.DRONE) | self.opponent.units(UnitTypeId.PROBE) | self.opponent.units(UnitTypeId.SCV) if worker_enemies and not scout.is_attacking: victim = worker_enemies.closest_to(scout.position) actions.append(scout.attack(victim)) else: location = self.opponent.get_next_scoutable_location() if location: actions.append(scout.move(location)) # Kite if self.opponent.units: enemies_closeby = self.opponent.units.filter(lambda unit: unit.can_attack_ground).closer_than(2, scout) if enemies_closeby and scout.health_percentage < 0.4: closest_enemy = enemies_closeby.closest_to(scout) actions.append(scout.move(util.away(scout.position, closest_enemy.position, 4))) # Home base door verification if not self.has_verified_front_door: for ramp in self.bot._game_info.map_ramps: if scout.distance_to(ramp.top_center) < 6: self.has_verified_front_door = True self.bot.hq_front_door = ramp.top_center self.logger.log("Scout verified front door") return actions def scout_no_mans_expansions(self): actions = [] scouts = self.no_mans_expansions_scouts.select_units(self.bot.units) if scouts.idle: exps = list(self.bot.expansion_locations) if self.opponent.known_hq_location: exps.remove(self.opponent.known_hq_location) if self.opponent.known_natural: exps.remove(self.opponent.known_natural) for scout in scouts: self.logger.debug(f"Sending scout {scout} to no man's land") actions.append(scout.move(self.bot.hq_front_door, queue=False)) for exp in exps: actions.append(scout.move(exp, queue=True)) return actions # Scout home base with overlords def patrol_with_overlords(self): actions = [] overlords = self.bot.units(UnitTypeId.OVERLORD) # First overlord will scout enemy natural firstborn = overlords.find_by_tag(self.first_overlord_tag) if firstborn and not self.first_overlord_ordered: if self.opponent.known_natural: near_enemy_front_door = self.opponent.known_natural.towards(self.opponent.known_hq_location, 4) safepoint_near_natural = util.away(self.opponent.known_natural, self.opponent.known_hq_location, 10) actions += [firstborn.move(near_enemy_front_door), firstborn.move(safepoint_near_natural, queue=True)] else: for enemy_loc in self.bot.enemy_start_locations: actions.append(firstborn.move(enemy_loc, queue=True)) actions.append(firstborn.move(self.bot.start_location, queue=True)) self.first_overlord_ordered = True # Second overlord will scout proxy rax early_warner = overlords.find_by_tag(self.early_warning_overlord_tag) if early_warner: if Strategy.PROXY not in self.opponent.strategies: if not self.early_warning_overlord_ordered: hq = self.bot.start_location center = self.bot.game_info.map_center dist_between_hq_and_center = hq.distance_to(center) halfway = hq.towards(center, dist_between_hq_and_center * 0.7) actions.append(early_warner.move(halfway, queue=False)) actions.append(early_warner.patrol(halfway.random_on_distance(5), queue=True)) actions.append(early_warner.patrol(halfway.random_on_distance(5), queue=True)) self.early_warning_overlord_ordered = True else: actions.append(early_warner.move(self.bot.start_location, queue=False)) # Others will patrol around hq if len(overlords) < 4: patrol = self.bot.hq_front_door.random_on_distance(random.randrange(3, 8)) else: patrol = self.bot.start_location.random_on_distance(40) for overlord in overlords.idle.tags_not_in([self.first_overlord_tag, self.early_warning_overlord_tag]): actions.append(overlord.move(patrol)) return actions def is_worker_rush(self, town, enemies_approaching): enemies = enemies_approaching.closer_than(6, town) worker_enemies = enemies(UnitTypeId.DRONE) | enemies(UnitTypeId.PROBE) | enemies(UnitTypeId.SCV) if worker_enemies.amount > 1 and (worker_enemies.amount / enemies.amount) >= 0.8: return True return False def _get_enemies_that_should_be_evicted_from_base(self, town): enemies = self.opponent.units.closer_than(6, town).exclude_type(UnitTypeId.OVERLORD) if enemies: return enemies else: if self.opponent.structures: buildings = self.opponent.structures.closer_than(15, town) if buildings: return buildings return None # Base defend def base_defend(self): actions = [] for town in self.bot.townhalls: if self.opponent.units: enemies = self._get_enemies_that_should_be_evicted_from_base(town) if enemies and enemies.not_flying: # Ground enemies are in this town enemy = enemies.closest_to(town) # Gather defenders new_defenders = self.reserve.select_units(self.all_combat_units).idle.closer_than(30, town) self.reserve.remove_units(new_defenders) self.base_defenders.add_units(new_defenders) armed_and_existing_defenders = self.base_defenders.select_units(self.bot.units) if not armed_and_existing_defenders: drones = self.bot.units(UnitTypeId.DRONE).closer_than(15, town) if drones: self.base_defenders.add_units(drones) self.logger.log(f"Resorting to add {drones.amount} drones to defenders") # TODO FIXME This will probably bug if several bases are under attack at the same time all_defenders = self.base_defenders.select_units(self.bot.units) if all_defenders: self.logger.log(f"Defending our base against {enemies.amount} enemies with {all_defenders.amount} defenders: {all_defenders}") for defender in all_defenders: actions.append(defender.attack(enemy.position)) # if self.is_worker_rush(town, enemies) or Strategy.CANNON_RUSH in self.opponent.strategies: # self.logger.warn("We are being cheesed!") # for drone in bot.units(UnitTypeId.DRONE).closer_than(30, town): # actions.append(drone.attack(enemy.position)) else: if enemies and enemies.flying: self.logger.warn("Enemies (not-overlords) flying in our base, not implemented!") # Base defenders back to work if self.base_defenders and not (self.opponent.units and self.opponent.units.closer_than(10, town).exclude_type(UnitTypeId.OVERLORD)): defenders = self.base_defenders.select_units(self.bot.units) self.logger.log(f"{defenders.amount} defenders calming down") for unit in defenders: self.base_defenders.remove_unit(unit) if unit.type_id == UnitTypeId.DRONE: actions.append(unit.move(town.position)) else: self.reserve.add_unit(unit) actions.append(unit.move(self.bot.hq_front_door)) return actions
async def add_marines_to_cg(self, iteration): if self.units(MARINE).idle.amount > 15 + ( 2 * self.units(BARRACKS).ready.amount) and iteration % 42 == 0: idle_marines = ControlGroup(self.units(MARINE).idle) self.attack_groups.add(idle_marines)
async def on_step(self, iteration): # State Available_Reapers = [] Cooldown_Reapers = [] Enemy_Units = [] for reaper in self.units(REAPER): abilities = await self.get_available_abilities(reaper) if AbilityId.KD8CHARGE_KD8CHARGE in abilities: Available_Reapers.append(reaper) else: Cooldown_Reapers.append(reaper) for enemy in self.known_enemy_units: if (enemy in self.prev_Enemy) == False and enemy.is_structure == False: Enemy_Units.append(enemy) current_state = [ Available_Reapers, Cooldown_Reapers, Enemy_Units, ] # Reward reward = 0 if self.prev_action != None: for bomber in Cooldown_Reapers: reward += 1000 for lazy in Available_Reapers: reward += -100 for enemy in Enemy_Units: reward += 100 self.qlearn.learn(str(self.prev_state), self.prev_action, reward, str(current_state)) # Choose action rl_action = self.qlearn.choose_action(str(current_state)) smart_action = smart_actions[rl_action] # Prep next step self.prev_score = reward self.prev_state = current_state self.prev_action = rl_action self.prev_Enemy = Enemy_Units # Actions if smart_action == ACTION_DO_NOTHING: # Run Away for reaper in self.units(REAPER): targetx = (random.randrange(0, 100))/20 targety = (random.randrange(0, 100))/20 home = Pointlike((24 + targetx, 24 + targety)) moveto = reaper.position await self.do(reaper.move(moveto)) if smart_action == ACTION_KD8_CHARGE: # default spot on the map targetx = (random.randrange(0, 100))/20 targety = (random.randrange(0, 100))/20 home = Pointlike((20 + targetx, 20 + targety)) moveto = Point2(home) # throw grenade at enemy if len(Available_Reapers) > 5 and len(Enemy_Units) > 0: for reaper in Available_Reapers: if reaper == self.units(REAPER).closest_to(Enemy_Units[0].position): bomb = Point3(Enemy_Units[0].position) await self.do(reaper(KD8CHARGE_KD8CHARGE, bomb)) else: await self.do(reaper.move(moveto)) else: for reaper in Available_Reapers: await self.do(reaper.move(moveto)) if iteration == 0: if iteration == 0: await self.chat_send("(glhf)") #creates control groups of 14 reapers if self.units(REAPER).idle.amount > 14: cg = ControlGroup(self.units(REAPER).idle) self.attack_groups.add(cg) # trains workers for cc in self.units(UnitTypeId.COMMANDCENTER).ready.noqueue: if self.can_afford(SCV) and self.workers.amount < 20 and cc.noqueue: await self.do(cc.train(SCV)) cc = self.units(COMMANDCENTER).ready.first bobthebuilder = self.units(SCV)[0] #build supply depots if self.supply_left < 2: if self.can_afford(SUPPLYDEPOT) and self.already_pending(SUPPLYDEPOT) < 2: await self.build(SUPPLYDEPOT, near=cc.position.towards(self.game_info.map_center, 5)) #build barracks if self.units(BARRACKS).amount < 3: # or (self.minerals > 400 and self.units(BARRACKS).amount < 5): if self.can_afford(BARRACKS): err = await self.build(BARRACKS, near=cc.position.towards(self.game_info.map_center, 5)) #train reapers elif self.units(BARRACKS).ready.exists and self.units(REAPER).amount < 28: barracks = self.units(BARRACKS).ready if self.can_afford(REAPER) and barracks.noqueue: await self.do(barracks.random.train(REAPER)) #build refinerys if self.refinerys < 2: if self.can_afford(REFINERY): worker = self.workers.random target = self.state.vespene_geyser.closest_to(worker.position) err = await self.do(bobthebuilder.build(REFINERY, target)) if not err: self.refinerys += 1 #workers in the mines/gas for a in self.units(REFINERY): if a.assigned_harvesters < a.ideal_harvesters: w = self.workers.closer_than(20, a) if w.exists: await self.do(w.random.gather(a))
async def on_step(self, iteration): if iteration == 0: if iteration == 0: await self.chat_send("(glhf)") #creates control groups of 14 reapers if self.units(REAPER).idle.amount > 14: cg = ControlGroup(self.units(REAPER).idle) self.attack_groups.add(cg) # trains workers for cc in self.units(UnitTypeId.COMMANDCENTER).ready.noqueue: if self.can_afford( SCV) and self.workers.amount < 20 and cc.noqueue: await self.do(cc.train(SCV)) cc = self.units(COMMANDCENTER).ready.first bobthebuilder = self.units(SCV)[0] #build supply depots if self.supply_left < 2: if self.can_afford( SUPPLYDEPOT) and self.already_pending(SUPPLYDEPOT) < 2: await self.build(SUPPLYDEPOT, near=cc.position.towards( self.game_info.map_center, 5)) #build barracks if self.units( BARRACKS ).amount < 1: # or (self.minerals > 400 and self.units(BARRACKS).amount < 5): if self.can_afford(BARRACKS): err = await self.build(BARRACKS, near=cc.position.towards( self.game_info.map_center, 5)) #train reapers elif self.units(BARRACKS).ready.exists and self.units( REFINERY).ready.exists and self.units(REAPER).amount < 1: barracks = self.units(BARRACKS).ready if self.can_afford(REAPER) and barracks.noqueue: await self.do(barracks.random.train(REAPER)) #build refinerys if self.refinerys < 2: if self.can_afford(REFINERY): worker = self.workers.random target = self.state.vespene_geyser.closest_to(worker.position) err = await self.do(bobthebuilder.build(REFINERY, target)) if not err: self.refinerys += 1 #workers in the mines/gas for a in self.units(REFINERY): if a.assigned_harvesters < a.ideal_harvesters: w = self.workers.closer_than(20, a) if w.exists: await self.do(w.random.gather(a)) #send out reapers if self.units(REAPER).amount > 0: for reaper in self.units(REAPER).idle: #randomize where bomb lands a bit x = self.Target.x - .5 y = self.Target.y - .5 rand = random.random() * 100 randx = (rand / 100) + x rand = random.random() * 100 randy = (rand / 100) + y lz = Pointlike((randx, randy)) boom = Point2(lz) #use grenade abilities = await self.get_available_abilities(reaper) if AbilityId.KD8CHARGE_KD8CHARGE in abilities: await self.do(reaper(KD8CHARGE_KD8CHARGE, boom))