def test_position_point2(x1, y1, x2, y2): pos1 = Point2((x1, y1)) pos2 = Point2((x2, y2)) assert pos1.x == x1 assert pos1.y == y1 assert pos1.to2 == pos1 assert pos1.to3 == Point3((x1, y1, 0)) length1 = (pos1.x**2 + pos1.y**2)**0.5 assert abs(pos1.length - length1) < 0.001 if length1: normalized1 = pos1 / length1 assert abs(pos1.normalized.is_same_as(pos1 / length1)) assert abs(normalized1.length - 1) < 0.001 length2 = (pos2.x**2 + pos2.y**2)**0.5 assert abs(pos2.length - length2) < 0.001 if length2: normalized2 = pos2 / length2 assert abs(pos2.normalized.is_same_as(normalized2)) assert abs(normalized2.length - 1) < 0.001 assert isinstance(pos1.distance_to(pos2), float) assert isinstance(pos1.distance_to_point2(pos2), float) if 0 < x2: assert pos1.random_on_distance(x2) != pos1 assert pos1.towards_with_random_angle(pos2, x2) != pos1 assert pos1.towards_with_random_angle(pos2) != pos1 if pos1 != pos2: dist = pos1.distance_to(pos2) intersections1 = pos1.circle_intersection(pos2, r=dist / 2) assert len(intersections1) == 1 intersections2 = pos1.circle_intersection(pos2, r=dist * 2 / 3) assert len(intersections2) == 2 neighbors4 = pos1.neighbors4 assert len(neighbors4) == 4 neighbors8 = pos1.neighbors8 assert len(neighbors8) == 8 assert pos1 + pos2 == Point2((x1 + x2, y1 + y2)) assert pos1 - pos2 == Point2((x1 - x2, y1 - y2)) assert pos1 * pos2 == Point2((x1 * x2, y1 * y2)) if 0 not in {x2, y2}: assert pos2 assert pos1 / pos2 == Point2((x1 / x2, y1 / y2)) if pos1._distance_squared(pos2) < 0.1: assert pos1.is_same_as(pos2, dist=0.1) assert pos1.unit_axes_towards(pos2) == pos1.direction_vector(pos2)
def position_around_unit(self, pos: Union[Unit, Point2, Point3], distance: int = 1, step_size: int = 1, exclude_out_of_bounds: bool = True): pos = pos.position.to2.rounded positions = { pos.offset(Point2((x, y))) for x in range(-distance, distance + 1, step_size) for y in range(-distance, distance + 1, step_size) if (x, y) != (0, 0) } if exclude_out_of_bounds: positions = { p for p in positions if 0 <= p[0] < self._game_info.pathing_grid.width and 0 <= p[1] < self._game_info.pathing_grid.height } return positions
def __init__(self, knowledge): self.knowledge: 'Knowledge' = knowledge self.ai: sc2.BotAI = knowledge.ai self.unit_values: UnitValue = knowledge.unit_values self.cd_manager: CooldownManager = knowledge.cooldown_manager self.pather: PathingManager = knowledge.pathing_manager self.cache: UnitCacheManager = knowledge.unit_cache self.delay_to_shoot = self.ai._client.game_step + 1.5 self.enemy_groups: List[CombatUnits] = [] self.ready_to_attack_ratio: float = 0.0 self.center: Point2 = Point2((0, 0)) self.group: CombatUnits self.engage_ratio = 0 self.can_engage_ratio = 0 self.closest_group: CombatUnits self.engaged: Dict[int, List[int]] = dict() self.engaged_power = ExtendedPower(knowledge.unit_values) self.our_power = ExtendedPower(knowledge.unit_values) self.closest_units: Dict[int, Optional[Unit]] = dict() self.move_type = MoveType.Assault
def find_influence_ground_path( self, start: Point2, target: Point2, target_index: int = 5, map_type: MapType = MapType.Ground) -> Point2: result = self.map.find_path_influence(map_type, start, target) path = result[0] if len(path) < 1: self.print(f"No path found {start}, {target}") return target if len(path) <= target_index: return target target = path[target_index] if self.debug: self.found_points_air.extend(path) return Point2((target[0], target[1]))
async def on_step(self, iteration): if iteration == 1: self.cannon_positions = [ Point2((max({p.x for p in d}), min({p.y for p in d}))) for d in self.api.main_base_ramp.top_wall_depos ] for nexus in self.api.units(NEXUS).ready: await self.train_new_workers(nexus) await self.build_pylons(nexus) await self.build_gateway(nexus) await self.build_stargate(nexus, iteration) await self.manage_workers(nexus, iteration) await self.build_cannons(nexus, iteration) await self.build_gas_stuff() await self.build_cybernetics_core() await self.build_forge() await self.build_twilight_cauncil() await self.expanse(iteration)
async def build_ramp_depots(self, bot): depos = [ Point2((max({p.x for p in d}), min({p.y for p in d}))) for d in bot.main_base_ramp.top_wall_depos ] depo_count = (bot.units(SUPPLYDEPOT) | bot.units(SUPPLYDEPOTLOWERED)).amount if bot.can_afford(SUPPLYDEPOT) and not bot.already_pending(SUPPLYDEPOT): if depo_count >= len(depos): return depo = list(depos)[depo_count] pos = await find_place_to_build(bot, self.agent, SUPPLYDEPOT, max_distance=2, position_close=depo, placement_step=1) await bot.do(self.agent.build(SUPPLYDEPOT, pos))
def points_on_circumference_sorted(center: Point2, closest_to: Point2, radius, n=10) -> List[Point2]: """Calculates all points on the circumference of a circle, and sorts the points so that first one on the list has shortest distance to closest_to parameter.""" points = points_on_circumference(center, radius, n) closest_point = closest_to.closest(points) closest_point_index = points.index(closest_point) sorted_points = [] # Points from closest point to the end sorted_points.extend(points[closest_point_index:]) # Points from start of list to closest point (closest point not included) sorted_points.extend(points[0:closest_point_index]) return sorted_points
def _set_sides(self): org = self.top pts = [self.bottom, self.right, self.left] res = self.map_data.closest_towards_point(points=pts, target=org) self.side_a = int(round( (res[0] + org[0]) / 2)), int(round((res[1] + org[1]) / 2)) if res != self.bottom: org = self.bottom pts = [self.top, self.right, self.left] res = self.map_data.closest_towards_point(points=pts, target=org) self.side_b = int(round( (res[0] + org[0]) / 2)), int(round((res[1] + org[1]) / 2)) else: self.side_b = int(round((self.right[0] + self.left[0]) / 2)), int( round((self.right[1] + self.left[1]) / 2)) points = list(self.points) points.append(self.side_a) points.append(self.side_b) self.points = set([Point2((int(p[0]), int(p[1]))) for p in points]) self.indices = self.map_data.points_to_indices(self.points)
def harass_move(self, unit, target): harassment_home = Point2( (self.bot.start_location.y, self.bot.enemy_start_locations[0].x)) harass_target = self.bot.known_enemy_structures.closest_to( harassment_home ) if self.bot.known_enemy_structures.exists else self.bot.enemy_start_locations[ 0] if abs(unit.position.x - harassment_home.x) > 15: if unit.is_idle: self.actions.append(unit.move(harassment_home)) elif unit.distance_to(target) > 25: if self.bot.iteration % 10 == 0: self.actions.append(unit.move(target)) else: self.unit_move(unit, harass_target, order="harass", retreat_to=harassment_home, exclude_buildings=True)
async def spawn_units(self, ressources): """ Spawns units for the amount of ressources given, for each player """ spawn_info = [] self.wave_units = [] ressources_left = ressources spawn_location = self._game_info.map_center - Point2((12, 12)) if self.race == Race.Zerg else self._game_info.map_center + Point2((12, 12)) # Selecting random units to be spawned while ressources_left > 100: for unit in self.spawnable_units: unit_cost = self.calculate_cost(unit) # Adding the cost of previous tech unit if necessary if unit == UnitTypeId.BROODLORD: unit_cost += self.calculate_cost(UnitTypeId.CORRUPTOR) elif unit == UnitTypeId.BANELING: unit_cost += self.calculate_cost(UnitTypeId.ZERGLING) elif unit == UnitTypeId.LURKER: unit_cost += self.calculate_cost(UnitTypeId.HYDRALISK) elif unit == UnitTypeId.ZERGLING: unit_cost = Cost(25, 0) # A pair of Zerglings cost 50 minerals unit_cost = unit_cost.minerals + unit_cost.vespene amount = int(random.uniform(0, 1)*int(ressources_left/unit_cost)) if amount > 0 and random.uniform(0, 1) < 1./len(self.spawnable_units): # Adding the unit to the list of units that will fight # If the unit is already in the list, just adding the amount found = False for i in range(len(self.wave_units)): if self.wave_units[i][0] == unit: self.wave_units[i][1] += amount spawn_info[i][1] += amount found = True # Else, just create a new couple [unit, amount] if not found: spawn_info.append([unit, amount, spawn_location, self.playerID]) self.wave_units.append([unit, amount]) # Saving which units were spawned for this wave # Updating ressources left ressources_left -= unit_cost*amount print("Added "+str(amount)+" unit "+str(unit)+" for a cost of "+str(unit_cost*amount)+" (already in list: "+str(found)+")") print("Ressources left for "+str(self.race)+": "+str(ressources_left)+" (Used "+str(ressources-ressources_left)+")") # Spawning selected units await self._client.debug_create_unit(spawn_info) print("Spawned units of wave "+str(self.nwaves))
async def attack(self, phys_swarm, iteration): if phys_swarm.amount > 10: orders = [] # I should be able to dynamically add and subtract particles from the swarm... should only init once at beginning... if # The business of adding/subrtracting from swarm should be done in on created/destroyed methods... # do a comprehension that deletes matches positions and alive units? # calcuate the current self.log_swarm.current_cost = self.fitness( self.log_swarm.position, phys_swarm ) self.log_swarm.pbest_cost = self.fitness( self.log_swarm.pbest_pos, phys_swarm ) self.log_swarm.pbest_pos, self.log_swarm.pbest_cost = P.compute_pbest( self.log_swarm ) # if np.min( self.log_swarm.pbest_cost ) < self.log_swarm.best_cost: self.log_swarm.best_pos, self.log_swarm.best_cost = self.my_topology.compute_gbest( self.log_swarm ) # self.logical_swarm.velocity = self.my_topology.compute_velocity( self.log_swarm ) self.logical_swarm.position = self.my_topology.compute_position( self.log_swarm ) #this should be parameterized as aggression... wounded_units = phys_swarm.filter(lambda u: u.health_percentage <= .7) for unit in wounded_units: unit.move(self.townhalls.first.position) phys_swarm = phys_swarm.filter(lambda u: u.health_percentage > .7) for row, unit in zip(self.logical_swarm.position, phys_swarm): orders.append(unit.attack(Point2(Pointlike((row[0], row[1]))))) # might need to get the nearest unit to do this... also check to make sure nearest unit not already assigned a task await self.do_actions(orders)
def test_points_on_circumference_sorted_with_unit_circle(self): center = Point2((0, 0)) closest_to = Point2((0, 10)) radius = 1 n = 4 points = points_on_circumference_sorted(center, closest_to, radius, n) assert len(points) == n # Points should be sorted so that first item has shortest distance to # closest_to parameter assert points[0] == Point2((0, 1)) assert points[1] == Point2((-1, 0)) assert points[2] == Point2((0, -1)) assert points[3] == Point2((1, 0))
def get_next_plant_position(self, queen: Unit) -> Optional[Point2]: """ Tries to find a suitable position for queens to plant tumors at. """ # Map is covered in creep, no need to place more tumors if not self.available_tumor_locations: return None # If queen is close to cached target location, don't return it instantly as creep could've evolved further while the queen was moving to target location old_cached_location: Optional[ Point2] = self.queen_plant_location_cache.pop(queen.tag, None) queen_pos: Point2 = queen.position # TODO Find the closest by ground path instead of air distance target_pos: Point2 = queen_pos.closest(self.available_tumor_locations) # Find path and move along the path and find the last location where it is possible to plant a tumor path = self.knowledge.pathing_manager.path_finder_terrain.find_path( queen_pos, target_pos)[0] # TODO Figure out why sometimes a queen is stuck and doesn't plant a tumor for position_tuple in path[::-1]: if queen.tag in self.queen_plant_location_cache: # A position to plant tumor was found break position = Point2(position_tuple) if self.is_placeable(position): self.queen_plant_location_cache[queen.tag] = position # Return the position if one was found if queen.tag in self.queen_plant_location_cache: return self.queen_plant_location_cache[queen.tag] # If no position could be found, return old cached location if it existed if old_cached_location and self.is_placeable(old_cached_location): self.queen_plant_location_cache[queen.tag] = old_cached_location return old_cached_location # Mark queen location as possible creep tumor plant location if no location was found if queen.tag not in self.queen_plant_location_cache and self.is_placeable( queen_pos): self.queen_plant_location_cache[queen.tag] = queen_pos return queen_pos
async def build_at(self, building, at, worker=None, detailed_failures=False): """ Build the given building at the x,y coordinate of 'at'. Where the x,y coordinate denotes the top left corner of the structure's grid. Uses the given worker but by default will find the closest available worker. Returns True on success False otherwise. If detailed_failures is True (by default False) then it returns the ActionResult of the build command. If no worker can be found ActionResult.Error is returned. """ if isinstance(at, Unit): raise "must specify top left position to place building, not the position of a unit" at[0] = int(at[0]) at[1] = int(at[1]) if building == None or at == None: return False placement = Point2([ at[0] + self.building_size[building] / 2, at[1] + self.building_size[building] / 2 ]) worker = worker or self.select_build_worker(placement) if worker is None: if detailed_failures: return ActionResult.Error return False res = self.do(worker.build(building, placement)) if detailed_failures: return res if res: # returns an empty list if successful otherwise list given contains list of errors return False return True
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
def set_borders(self, r=20): self.coords = self.coords.rounded loc = self.coords row_start = loc.y - r row_end = loc.y + r col_start = loc.x - r col_end = loc.x + r points = [] p_arr = [] for (b, a), value in np.ndenumerate( self.grid ): # TODO should take a sub matrix with the radius and not entire grid p = (a, b) # skip non placements which are zero if value == 0 or not self.same_height(Point2(p), self.coords): continue # skip if not in expansion zone if not (col_start <= a <= col_end): continue if not (row_start <= b <= row_end): continue points.append(p) point = [p[0], p[1]] p_arr.append(point) self.grid_points = points p_arr = np.array(p_arr) raw_edges = get_edge_points(p_arr, 0.8, only_outer=True) edges = [] for edge in raw_edges: if not self.too_close_to_another_expansion(self.coords, edge): edges.append(edge) # final_edges = [] # step = 1 # for i in range(0,len(edges),step): # final_edges.append(edges[i]) # self.borders = final_edges self.borders = edges self.fix_borders()
def pending_building_positions(self, unit_type: UnitTypeId) -> List[Point2]: """Returns positions of buildings of the specified type that have either been ordered to be built by a worker or are currently being built.""" positions: List[Point2] = list() creation_ability: AbilityId = self.ai._game_data.units[unit_type.value].creation_ability # Workers ordered to build for worker in self.ai.workers: # type: Unit for order in worker.orders: # type: UnitOrder if order.ability.id == creation_ability.id: p2: Point2 = Point2.from_proto(order.target) positions.append(p2) # Already building structures # Avoid counting structures twice for Terran SCVs. if self.knowledge.my_race != Race.Terran: pending_buildings: List[Point2] = list( map(lambda structure: structure.position, self.cache.own(unit_type).structure.not_ready) ) positions.extend(pending_buildings) return positions
def get_path(self, source, dest) -> Optional[List[Point2]]: """ Constructs and returns a path, if one exists, from source to dest (or a nearby eligible dest, if dest is not pathable). :return: A path in the form of a list of waypoints. """ nearest_source = (round(source[0] / PATH_RESOLUTION) * PATH_RESOLUTION, round(source[1] / PATH_RESOLUTION) * PATH_RESOLUTION) nearest_dest = (round(dest[0] / PATH_RESOLUTION) * PATH_RESOLUTION, round(dest[1] / PATH_RESOLUTION) * PATH_RESOLUTION) if nearest_source == nearest_dest: # you are close enough that we consider you already there return None nearest_eligible_dest = self.find_eligible_dest( nearest_source, nearest_dest) if nearest_eligible_dest is None: # no destination in the neighborhood of dest which we can path to return None chunk_idx = int(nearest_eligible_dest[1] // PATH_ROW_CHUNK_SIZE) # is int() here redundant? path = [] next_point = nearest_source # because we don't store the whole path in the file, we just store the next step, we need to walk # along these steps and save them in a list. This becomes the path while next_point[0] != nearest_eligible_dest[0] or next_point[ 1] != nearest_eligible_dest[1]: path.append(Point2(next_point)) if (next_point[0], next_point[1], nearest_eligible_dest[0], nearest_eligible_dest[1]) not in self.chunks[chunk_idx]: # should hopefully be impossible. This would mean that at one point it thought there was a path from # the start to the dest, but as it followed it it suddenly stopped being possible. return None next_point = self.chunks[chunk_idx][(next_point[0], next_point[1], nearest_eligible_dest[0], nearest_eligible_dest[1])] if len(path) == 0: return None return path
def _calc_chokes(self) -> None: # compute ChokeArea self._clean_plib_chokes() chokes = [ c for c in self.c_ext_map.chokes if c.id not in self.overlapping_choke_ids ] self.map_chokes = self.map_ramps.copy() self.map_chokes.extend(self.map_vision_blockers) for choke in chokes: points = [Point2(p) for p in choke.pixels] if len(points) > 0: new_choke_array = self.points_to_numpy_array(points) cm = center_of_mass(new_choke_array) cm = int(cm[0]), int(cm[1]) areas = self.where_all(cm) new_choke = RawChoke(map_data=self, array=new_choke_array, raw_choke=choke) for area in areas: if isinstance(area, Region): area.region_chokes.append(new_choke) new_choke.areas.append(area) if area.is_choke and not area.is_ramp and not area.is_vision_blocker: self.polygons.remove(new_choke) area.points.update(new_choke.points) new_choke = None break if new_choke: self.map_chokes.append(new_choke) else: # pragma: no cover logger.debug( f" [{self.map_name}] Cant add {choke} with 0 points")
async def control_scouting_reaper(self): '''Reaper moves to enemy location at the beginning of the game.''' for r in self.units(REAPER): if r: enemy_start_loc = self.enemy_start_locations[0] self.combinedActions.append(r.move(enemy_start_loc)) print(self.find_friendly_unit_pos(r), 'r unit pos') print(self.find_closest_enemy_threat(r), 'enemy threat pos') enemy = self.known_enemy_units.exists if enemy: enemy_threat = self.find_closest_enemy_threat(r) unit_power = (r.health + r.shield) / r.ground_dps enemy_unit_power = (enemy_threat.health + enemy_threat.shield ) / enemy_threat.ground_dps #Update values through self.map (array) new_harass_position = self.Micro.influence_map( self.map, enemy_threat.ground_range, enemy_unit_power, enemy_threat) ## new_harass_position = self.Micro.harass_micro_avg(self, self.find_friendly_unit_pos(r), self.find_closest_enemy_workers(r), self.find_closest_enemy_threat(r)) #convert new_harass_position to Point2 position tuple. new_harass_position_point2 = Point2(tuple(new_harass_position)) #move_closer_to_probes = working on function atm if r.weapon_cooldown != 0: self.combinedActions.append( r.move(new_harass_position_point2)) self.Micro.influence_map(self.map, enemy_threat.ground_range, enemy_unit_power, enemy_threat) else: self.combinedActions.append( r.attack(self.find_closest_enemy_worker(r))) self.Micro.influence_map(self.map, enemy_threat.ground_range, enemy_unit_power, enemy_threat) if r.health_percentage < 45 / 60: self.combinedActions.append( r.move(self.game_info.map_center))
async def on_step(self, iteration): cc = self.units(COMMANDCENTER) if not cc.exists: return else: cc = cc.first if self.can_afford(SCV) and self.workers.amount < 16 and cc.noqueue: await self.do(cc.train(SCV)) # Raise depos when enemies are nearby for depo in self.units(SUPPLYDEPOT).ready: for unit in self.known_enemy_units.not_structure: if unit.position.to2.distance_to(depo.position.to2) < 15: break else: await self.do(depo(MORPH_SUPPLYDEPOT_LOWER)) # Lower depos when no enemies are nearby for depo in self.units(SUPPLYDEPOTLOWERED).ready: for unit in self.known_enemy_units.not_structure: if unit.position.to2.distance_to(depo.position.to2) < 10: await self.do(depo(MORPH_SUPPLYDEPOT_RAISE)) break depos = [ Point2((max({p.x for p in d}), min({p.y for p in d}))) for d in self.main_base_ramp.top_wall_depos ] depo_count = (self.units(SUPPLYDEPOT) | self.units(SUPPLYDEPOTLOWERED)).amount if self.can_afford(SUPPLYDEPOT) and not self.already_pending(SUPPLYDEPOT): if depo_count >= len(depos): return depo = list(depos)[depo_count] r = await self.build(SUPPLYDEPOT, near=depo, max_distance=2, placement_step=1)
async def post_update(self): if self.debug: # and self.chat_count < self.ai.time / 15: self.chat_count += 1 idle = len(self.roles[UnitTask.Idle.value].tags) building = len(self.roles[UnitTask.Building.value].tags) gathering = len(self.roles[UnitTask.Gathering.value].tags) scouting = len(self.roles[UnitTask.Scouting.value].tags) moving = len(self.roles[UnitTask.Moving.value].tags) fighting = len(self.roles[UnitTask.Fighting.value].tags) defending = len(self.roles[UnitTask.Defending.value].tags) attacking = len(self.roles[UnitTask.Attacking.value].tags) reserved = len(self.roles[UnitTask.Reserved.value].tags) hallucination = len(self.roles[UnitTask.Hallucination.value].tags) enemy_total_power: ExtendedPower = self.knowledge.enemy_units_manager.enemy_total_power power_text = f'{enemy_total_power.power} ({enemy_total_power.ground_power}/{enemy_total_power.air_power})' # msg = f'{self.ai.time_formatted} I{idle} B{building} G{gathering} S{scouting} M{moving} ' \ # f'F{fighting} D{defending} A{attacking} R{reserved} H{hallucination} ETP{power_text}' msg = f'I{idle} B{building} G{gathering} S{scouting} M{moving} ' \ f'F{fighting} D{defending} A{attacking} R{reserved} H{hallucination}' client: Client = self.ai._client client.debug_text_2d(msg, Point2((0.4, 0.1)), None, 16)
async def find_tumor_placement(self) -> Point2: # TODO: SLOW function fix fix fix. Also doesn't take into consideration whether theres something blocking tumor or not creep_emitters: Units = self.units( {HATCHERY, UnitTypeId.CREEPTUMORBURROWED}) count = 0 while count < 10: count += 1 target_emitter: Unit = creep_emitters.random angle = random.randint(0, 360) x = math.cos(angle) y = math.sin(angle) target_position: Point2 = target_emitter.position + (9 * Point2( (x, y))) check = True for emitter in creep_emitters: if target_position.distance_to(emitter.position) < 9: check = False break if self.position_blocks_expansion(target_position): check = False if check: return target_position return None
def follow_path(self, unit: Unit) -> None: """ Follow the path set or set a new one if none exists. Args: unit (Unit): the unit moving Returns: None """ if unit.tag not in self.pathing_dict: self.add_to_path_dict(unit, self.target) else: advance_factor = int(unit.movement_speed) + 2 self.pathing_dict[unit.tag]["step"] += advance_factor curr_step = self.pathing_dict[unit.tag]["step"] if curr_step >= len(self.pathing_dict[unit.tag]["path"]): curr_step = len(self.pathing_dict[unit.tag]["path"]) - 1 self.do( unit.attack(Point2( self.pathing_dict[unit.tag]["path"][curr_step]))) if curr_step == len(self.pathing_dict[unit.tag]["path"]) - 1: del self.pathing_dict[unit.tag]
def find_highground_centroids(self, highground_tiles) -> np.array: # using db index, find the optimal number of clusters for kmeans range_of_k = range(4, 22) # store all the davies-bouldin index values dbindexes = [] for k in range_of_k: # try kmeans for each k value kmeans = KMeans(n_clusters=k, random_state=42).fit(highground_tiles) dbindexes.append( self.davis_bouldin_index(highground_tiles, kmeans.labels_, k)) kmeans = KMeans(n_clusters=np.argmin(dbindexes) + 4, random_state=42).fit(highground_tiles) ol_spots: List[Point2] = [ Point2(position.Pointlike((pos[0], pos[1]))) for pos in kmeans.cluster_centers_ ] # each clusters centroid is the overlord positions return ol_spots
async def step(self): actions = list() recon_tag = self.bot.units.filter( lambda unit: unit.tag in self.bot.reconArray) for unit in recon_tag: # 근처에 적들이 있는지 파악 unit.move(Point2((self.pos, 60))) threaten = self.bot.known_enemy_units.closer_than( self.perimeter_radious, unit.position) if unit.type_id == UnitTypeId.RAVEN: if unit.health_percentage > 0.8 and unit.energy >= 50: print("유닛오더? ", unit.orders) if threaten.amount > 0: # 근처에 적이 하나라도 있으면 alert = 1 if unit.orders and unit.orders[ 0].ability.id != AbilityId.RAVENBUILD_AUTOTURRET: closest_threat = threaten.closest_to(unit.position) pos = unit.position.towards( closest_threat.position, 5) pos = await self.bot.find_placement( UnitTypeId.AUTOTURRET, pos) order = unit(AbilityId.BUILDAUTOTURRET_AUTOTURRET, pos) actions.append(order) '''else: if unit.distance_to(self.target) > 5: order = unit.move(self.target) actions.append(order)''' elif unit.type_id == UnitTypeId.MARINE: if self.bot.known_enemy_units.exists: enemy_unit = self.bot.known_enemy_units.closest_to(unit) actions.append(unit.attack(enemy_unit)) return actions
async def on_start(self): self.map_data = MapData(self, loglevel="DEBUG", arcade=True) base = self.townhalls[0] self.base = reg_start = self.map_data.where_all(base.position_tuple)[0] reg_end = self.map_data.where_all( self.enemy_start_locations[0].position)[0] self.p0 = reg_start.center self.p1 = reg_end.center self.influence_grid = self.map_data.get_pyastar_grid() ramps = reg_end.region_ramps logger.info(ramps) if len(ramps) > 1: if self.map_data.distance(ramps[0].top_center, reg_end.center) < self.map_data.distance( ramps[1].top_center, reg_end.center): self.ramp = ramps[0] else: self.ramp = ramps[1] else: self.ramp = ramps[0] self.influence_points = [(self.ramp.top_center, 2), (Point2((66, 66)), 18)] self.influence_points = self._get_random_influence(25, 5) """Uncomment this code block to add random costs and make the path more complex""" # for tup in self.influence_points: # p = tup[0] # r = tup[1] # self.map_data.add_cost(p, r=r, arr=self.influence_grid) self.path = self.map_data.pathfind(start=self.p0, goal=self.p1, grid=self.influence_grid, sensitivity=self.sens) self.hero_tag = self.workers[0].tag
def SolveCliffs(self, ai: sc2.BotAI): maxDifference = 3 hMap = self.game_info.terrain_height correction = Point2((0, 1)) x = 2 while x < self.width - 3: y = 3 while y < self.height - 3: pos = Point2((x, y)) h = hMap[pos + correction] possibles = [ Point2((x - 2, y - 2)), Point2((x + 2, y - 2)), Point2((x - 2, y + 2)), Point2((x + 2, y + 2)), ] for possible in possibles: # To ensure rounding errors don't drop it to previous pixel. middle = (possible + pos) * 0.500001 cell_possible: GridArea = self[possible] cell_middle: GridArea = self[middle] if cell_possible.Area == BuildArea.Empty and cell_middle.Area == BuildArea.NotBuildable: h2 = hMap[possible + correction] difference = h - h2 if abs(difference) > maxDifference: continue cell: GridArea = self.get(x, y) if difference < 0: if cell.Cliff == Cliff.HighCliff: cell.Cliff = Cliff.BothCliff else: cell.Cliff = Cliff.LowCliff if difference > 0: if cell.Cliff == Cliff.LowCliff: cell.Cliff = Cliff.BothCliff else: cell.Cliff = Cliff.HighCliff y += 1 x += 1
async def on_step(self, iteration): self.shared.attackers = self.units.tags_in(self.allocated) if self.attack_objective: await self.attack_objective.tick() if self.attack_objective.is_complete(): self.shared.victims = Units([], self.bot) self.attack_objective = None else: self.shared.victims = self.attack_objective.enemies return # for cleanup in self.cleanup_objectives: # await cleanup.tick() # if cleanup.is_complete(): # self.shared.victims = Units([], self.bot) # self.cleanup_objectives.remove(cleanup) if (self.supply_used > 196 or self.shared.optimism > 1.5) and not self.attack_objective: known_enemy_units = self.shared.known_enemy_units.values() enemy_bases = self.enemy_structures(BaseStructures) if enemy_bases.exists: self.attack_objective = AttackObjective( self, enemy_bases.furthest_to( Point2.center([u.position for u in known_enemy_units] ) if known_enemy_units else self. enemy_start_locations[0]).position) elif self.enemy_structures.exists: self.attack_objective = AttackObjective( self, self.enemy_structures.closest_to( self.units.center).position) else: self.attack_objective = AttackObjective( self, self.enemy_start_locations[0])
def _set_sides(self): ramp_dir = self.ramp.bottom_center - self.ramp.top_center perpendicular_dir = Point2((-ramp_dir[1], ramp_dir[0])).normalized step_size = 1 current = self.ramp.top_center.offset(ramp_dir / 2) side_a = current.rounded next_point = current.rounded while next_point in self.points: side_a = next_point current = current.offset(perpendicular_dir * step_size) next_point = current.rounded self.side_a = side_a current = self.ramp.top_center.offset(ramp_dir / 2) side_b = current.rounded next_point = current.rounded while next_point in self.points: side_b = next_point current = current.offset(-perpendicular_dir * step_size) next_point = current.rounded self.side_b = side_b