def test_teleport(self): walk_max = self.bot.config.walk_max walk_min = self.bot.config.walk_min precision = 0.0 speed = float("inf") self.bot.config.walk_max = 4 self.bot.config.walk_min = 2 pw = PolylineWalker(self.bot, ex_dest[0], ex_dest[1], precision=precision) self.assertEqual(pw.dest_lat, ex_dest[0], 'dest_lat did not match') self.assertEqual(pw.dest_lng, ex_dest[1], 'dest_lng did not match') finishedWalking = pw.step(speed=speed) self.assertTrue(finishedWalking, 'step should return True') distance = Geodesic.WGS84.Inverse(ex_dest[0], ex_dest[1], self.bot.position[0], self.bot.position[1])["s12"] self.assertTrue(0.0 <= distance <= (pw.precision + pw.epsilon)) self.polyline._last_pos = self.polyline.destination self.assertTrue(abs(self.polyline.get_alt() - self.bot.position[2]) <= 1) self.bot.config.walk_max = walk_max self.bot.config.walk_min = walk_min
def test_stay_put(self): altitude = 429.5 self.bot.position = [47.1706378, 8.5167405, altitude] walk_max = self.bot.config.walk_max walk_min = self.bot.config.walk_min precision = 0.0 speed = 0.0 self.bot.config.walk_max = 4 self.bot.config.walk_min = 2 pw = PolylineWalker(self.bot, ex_dest[0], ex_dest[1], precision=precision) self.assertEqual(pw.dest_lat, ex_dest[0], 'dest_lat did not match') self.assertEqual(pw.dest_lng, ex_dest[1], 'dest_lng did not match') finishedWalking = pw.step(speed=speed) self.assertFalse(finishedWalking, 'step should return False') distance = Geodesic.WGS84.Inverse(ex_orig[0], ex_orig[1], self.bot.position[0], self.bot.position[1])["s12"] self.assertTrue(0.0 <= distance <= (pw.precision + pw.epsilon)) self.assertTrue(altitude - 1 <= self.bot.position[2] <= altitude + 1) self.bot.config.walk_max = walk_max self.bot.config.walk_min = walk_min
class CampFort(BaseTask): SUPPORTED_TASK_API_VERSION = 1 def __init__(self, bot, config): super(CampFort, self).__init__(bot, config) def initialize(self): self.clusters = None self.cluster = None self.walker = None self.stay_until = 0 self.move_until = 0 self.no_log_until = 0 self.config_max_distance = self.config.get("max_distance", 2000) self.config_min_forts_count = self.config.get("min_forts_count", 2) self.config_min_lured_forts_count = self.config.get( "min_lured_forts_count", 1) self.config_camping_time = self.config.get("camping_time", 1800) self.config_moving_time = self.config.get("moving_time", 600) def work(self): if not self.enabled: return WorkerResult.SUCCESS now = time.time() if now < self.move_until: return WorkerResult.SUCCESS if 0 < self.stay_until < now: self.cluster = None self.stay_until = 0 self.move_until = now + self.config_moving_time if self.config_moving_time > 0: return WorkerResult.SUCCESS # Let's make sure we have balls before we sit at a lure! # See also catch_pokemon.py if self.get_pokeball_count() <= 0: self.emit_event( 'refuse_to_sit', formatted='No pokeballs left, refuse to sit at lure!', ) # Move away from lures for a time self.cluster = None self.stay_until = 0 self.move_until = now + max(self.config_moving_time, NO_BALLS_MOVING_TIME) return WorkerResult.SUCCESS forts = self.get_forts() if self.cluster is None: if self.clusters is None: self.clusters = self.get_clusters(forts.values()) available_clusters = self.get_available_clusters(forts) if len(available_clusters) > 0: self.cluster = available_clusters[0] self.walker = PolylineWalker(self.bot, self.cluster["center"][0], self.cluster["center"][1]) self.no_log_until = now + LOG_TIME_INTERVAL self.emit_event( "new_destination", formatted= 'New destination at {distance:.2f} meters: {size} forts, {lured} lured' .format(**self.cluster)) else: return WorkerResult.SUCCESS self.update_cluster_distance(self.cluster) self.update_cluster_lured(self.cluster, forts) if self.stay_until >= now: if self.no_log_until < now: self.no_log_until = now + LOG_TIME_INTERVAL self.emit_event( "staying_at_destination", formatted= 'Staying at destination: {size} forts, {lured} lured'. format(**self.cluster)) if self.cluster["lured"] == 0: self.stay_until -= NO_LURED_TIME_MALUS self.walker.step(speed=0) elif self.walker.step(): self.stay_until = now + self.config_camping_time self.emit_event( "arrived_at_destination", formatted="Arrived at destination: {size} forts, {lured} lured" .format(**self.cluster)) elif self.no_log_until < now: if self.cluster["lured"] == 0: self.cluster = None self.emit_event("reset_destination", formatted="Lures gone! Resetting destination!") else: self.no_log_until = now + LOG_TIME_INTERVAL self.emit_event( "moving_to_destination", formatted= "Moving to destination at {distance:.2f} meters: {size} forts, {lured} lured" .format(**self.cluster)) return WorkerResult.RUNNING def get_pokeball_count(self): return sum([ inventory.items().get(ball.value).count for ball in [Item.ITEM_POKE_BALL, Item.ITEM_GREAT_BALL, Item.ITEM_ULTRA_BALL] ]) def get_forts(self): radius = self.config_max_distance + Constants.MAX_DISTANCE_FORT_IS_REACHABLE forts = [ f for f in self.bot.cell["forts"] if ("latitude" in f) and ("type" in f) ] forts = [ f for f in forts if self.get_distance(self.bot.start_position, f) <= radius ] return {f["id"]: f for f in forts} def get_available_clusters(self, forts): for cluster in self.clusters: self.update_cluster_distance(cluster) self.update_cluster_lured(cluster, forts) available_clusters = [ c for c in self.clusters if c["lured"] >= self.config_min_lured_forts_count ] available_clusters = [ c for c in available_clusters if c["size"] >= self.config_min_forts_count ] available_clusters.sort(key=lambda c: self.get_cluster_key(c), reverse=True) return available_clusters def get_clusters(self, forts): clusters = [] points = self.get_all_snap_points(forts) for c1, c2, fort1, fort2 in points: cluster_1 = self.get_cluster(forts, c1) cluster_2 = self.get_cluster(forts, c2) self.update_cluster_distance(cluster_1) self.update_cluster_distance(cluster_2) key_1 = self.get_cluster_key(cluster_1) key_2 = self.get_cluster_key(cluster_2) radius = Constants.MAX_DISTANCE_FORT_IS_REACHABLE if key_1 >= key_2: cluster = cluster_1 while True: new_circle, _ = self.get_enclosing_circles( fort1, fort2, radius - 1) if not new_circle: break new_cluster = self.get_cluster(cluster["forts"], new_circle) if len(new_cluster["forts"]) < len(cluster["forts"]): break cluster = new_cluster radius -= 1 else: cluster = cluster_2 while True: _, new_circle = self.get_enclosing_circles( fort1, fort2, radius - 1) if not new_circle: break new_cluster = self.get_cluster(cluster["forts"], new_circle) if len(new_cluster["forts"]) < len(cluster["forts"]): break cluster = new_cluster radius -= 1 clusters.append(cluster) return clusters def get_all_snap_points(self, forts): points = [] radius = Constants.MAX_DISTANCE_FORT_IS_REACHABLE for i in range(0, len(forts)): for j in range(i + 1, len(forts)): c1, c2 = self.get_enclosing_circles(forts[i], forts[j], radius) if c1 and c2: points.append((c1, c2, forts[i], forts[j])) return points def get_enclosing_circles(self, fort1, fort2, radius): x1, y1 = coord2merc(fort1["latitude"], fort1["longitude"]) x2, y2 = coord2merc(fort2["latitude"], fort2["longitude"]) dx = x2 - x1 dy = y2 - y1 d = math.sqrt(dx**2 + dy**2) if (d == 0) or (d > 2 * radius): return None, None cx, cy = (x1 + x2) / 2, (y1 + y2) / 2 cd = math.sqrt(radius**2 - (d / 2)**2) c1 = merc2coord((cx - cd * dy / d, cy + cd * dx / d)) + (radius, ) c2 = merc2coord((cx + cd * dy / d, cy - cd * dx / d)) + (radius, ) return c1, c2 def get_cluster(self, forts, circle): forts_in_circle = [ f for f in forts if self.get_distance(circle, f) <= circle[2] ] cluster = { "center": (circle[0], circle[1]), "distance": 0, "forts": forts_in_circle, "size": len(forts_in_circle), "lured": sum(1 for f in forts_in_circle if f.get("active_fort_modifier", None) is not None) } return cluster def get_cluster_key(self, cluster): return (cluster["lured"], cluster["size"], -cluster["distance"]) def update_cluster_distance(self, cluster): cluster["distance"] = great_circle(self.bot.position, cluster["center"]).meters def update_cluster_lured(self, cluster, forts): cluster["lured"] = sum(1 for f in cluster["forts"] if forts.get( f["id"], {}).get("active_fort_modifier", None) is not None) def get_distance(self, location, fort): return great_circle(location, (fort["latitude"], fort["longitude"])).meters
class CampFort(BaseTask): SUPPORTED_TASK_API_VERSION = 1 def __init__(self, bot, config): super(CampFort, self).__init__(bot, config) def initialize(self): self.clusters = None self.cluster = None self.walker = None self.bot.camping_forts = False self.stay_until = 0 self.move_until = 0 self.no_log_until = 0 self.no_recheck_cluster_until = 0 self.config_max_distance = self.config.get("max_distance", 2000) self.config_min_forts_count = self.config.get("min_forts_count", 2) self.config_min_lured_forts_count = self.config.get("min_lured_forts_count", 1) self.config_camping_time = self.config.get("camping_time", 1800) self.config_moving_time = self.config.get("moving_time", 600) def work(self): if not self.enabled: return WorkerResult.SUCCESS if self.bot.catch_disabled: if not hasattr(self.bot,"camper_disabled_global_warning") or \ (hasattr(self.bot,"camper_disabled_global_warning") and not self.bot.camper_disabled_global_warning): self.logger.info("All catching tasks are currently disabled until {}. Camping of lured forts disabled till then.".format(self.bot.catch_resume_at.strftime("%H:%M:%S"))) self.bot.camper_disabled_global_warning = True return WorkerResult.SUCCESS else: self.bot.camper_disabled_global_warning = False if self.bot.softban: if not hasattr(self.bot, "camper_softban_global_warning") or \ (hasattr(self.bot, "camper_softban_global_warning") and not self.bot.camper_softban_global_warning): self.logger.info("Possible softban! Not camping forts till fixed.") self.bot.camper_softban_global_warning = True return WorkerResult.SUCCESS else: self.bot.softban_global_warning = False now = time.time() if now < self.move_until: return WorkerResult.SUCCESS if 0 < self.stay_until < now: self.cluster = None self.stay_until = 0 self.move_until = now + self.config_moving_time if self.config_moving_time > 0: return WorkerResult.SUCCESS # Let's make sure we have balls before we sit at a lure! # See also catch_pokemon.py if self.get_pokeball_count() <= 0: self.emit_event( 'refuse_to_sit', formatted='No pokeballs left, refuse to sit at lure!', ) # Move away from lures for a time self.cluster = None self.stay_until = 0 self.move_until = now + max(self.config_moving_time, NO_BALLS_MOVING_TIME) return WorkerResult.SUCCESS forts = self.get_forts() if self.cluster is None: if self.clusters is None: self.clusters = self.get_clusters(forts.values()) # self.logger.info("Forts: {}".format(len(forts))) # self.logger.info("Checking {} clusters for availiblity....".format(len(self.clusters))) available_clusters = self.get_available_clusters(forts) if len(available_clusters) > 0: self.cluster = available_clusters[0] self.walker = PolylineWalker(self.bot, self.cluster["center"][0], self.cluster["center"][1]) self.no_log_until = now + LOG_TIME_INTERVAL self.no_recheck_cluster_until = now + NO_BALLS_MOVING_TIME self.emit_event("new_destination", formatted='New destination at {distance:.2f} meters: {size} forts, {lured} lured'.format(**self.cluster)) else: # self.logger.info("No clusters found.") self.cluster = None self.clusters = None return WorkerResult.SUCCESS # We can check if the cluster is still the best elif self.no_recheck_cluster_until < now: self.clusters = self.get_clusters(forts.values()) available_clusters = self.get_available_clusters(forts) if len(available_clusters) > 0: if self.cluster is not available_clusters[0]: self.cluster = available_clusters[0] self.stay_until = 0 self.emit_event("new_destination", formatted='Better destination found at {distance:.2f} meters: {size} forts, {lured} lured'.format(**self.cluster)) self.no_recheck_cluster_until = now + NO_BALLS_MOVING_TIME self.update_cluster_distance(self.cluster) self.update_cluster_lured(self.cluster, forts) if self.stay_until >= now: if self.no_log_until < now: self.no_log_until = now + LOG_TIME_INTERVAL self.bot.camping_forts = True self.emit_event("staying_at_destination", formatted='Staying at destination: {size} forts, {lured} lured'.format(**self.cluster)) if self.cluster["lured"] == 0: self.bot.camping_forts = False # Allow hunter to move self.stay_until -= NO_LURED_TIME_MALUS self.walker.step(speed=0) elif self.walker.step(): self.stay_until = now + self.config_camping_time self.bot.camping_forts = True self.emit_event("arrived_at_destination", formatted="Arrived at destination: {size} forts, {lured} lured.".format(**self.cluster)) elif self.no_log_until < now: if self.cluster["lured"] == 0: self.cluster = None self.bot.camping_forts = False self.emit_event("reset_destination", formatted="Lures gone! Resetting destination!") else: self.no_log_until = now + LOG_TIME_INTERVAL self.emit_event("moving_to_destination", formatted="Moving to destination at {distance:.2f} meters: {size} forts, {lured} lured".format(**self.cluster)) return WorkerResult.RUNNING def get_pokeball_count(self): return sum([inventory.items().get(ball.value).count for ball in [Item.ITEM_POKE_BALL, Item.ITEM_GREAT_BALL, Item.ITEM_ULTRA_BALL]]) def get_forts(self): radius = self.config_max_distance + Constants.MAX_DISTANCE_FORT_IS_REACHABLE forts = [f for f in self.bot.cell["forts"] if ("latitude" in f) and ("type" in f)] forts = [f for f in forts if self.get_distance(self.bot.start_position, f) <= radius] return {f["id"]: f for f in forts} def get_available_clusters(self, forts): for cluster in self.clusters: self.update_cluster_distance(cluster) self.update_cluster_lured(cluster, forts) available_clusters = [c for c in self.clusters if c["lured"] >= self.config_min_lured_forts_count] available_clusters = [c for c in available_clusters if c["size"] >= self.config_min_forts_count] available_clusters.sort(key=lambda c: self.get_cluster_key(c), reverse=True) return available_clusters def get_clusters(self, forts): clusters = [] points = self.get_all_snap_points(forts) for c1, c2, fort1, fort2 in points: cluster_1 = self.get_cluster(forts, c1) cluster_2 = self.get_cluster(forts, c2) self.update_cluster_distance(cluster_1) self.update_cluster_distance(cluster_2) key_1 = self.get_cluster_key(cluster_1) key_2 = self.get_cluster_key(cluster_2) radius = Constants.MAX_DISTANCE_FORT_IS_REACHABLE if key_1 >= key_2: cluster = cluster_1 while True: new_circle, _ = self.get_enclosing_circles(fort1, fort2, radius - 1) if not new_circle: break new_cluster = self.get_cluster(cluster["forts"], new_circle) if len(new_cluster["forts"]) < len(cluster["forts"]): break cluster = new_cluster radius -= 1 else: cluster = cluster_2 while True: _, new_circle = self.get_enclosing_circles(fort1, fort2, radius - 1) if not new_circle: break new_cluster = self.get_cluster(cluster["forts"], new_circle) if len(new_cluster["forts"]) < len(cluster["forts"]): break cluster = new_cluster radius -= 1 clusters.append(cluster) return clusters def get_all_snap_points(self, forts): points = [] radius = Constants.MAX_DISTANCE_FORT_IS_REACHABLE for i in range(0, len(forts)): for j in range(i + 1, len(forts)): c1, c2 = self.get_enclosing_circles(forts[i], forts[j], radius) if c1 and c2: points.append((c1, c2, forts[i], forts[j])) return points def get_enclosing_circles(self, fort1, fort2, radius): x1, y1 = coord2merc(fort1["latitude"], fort1["longitude"]) x2, y2 = coord2merc(fort2["latitude"], fort2["longitude"]) dx = x2 - x1 dy = y2 - y1 d = math.sqrt(dx ** 2 + dy ** 2) if (d == 0) or (d > 2 * radius): return None, None cx, cy = (x1 + x2) / 2, (y1 + y2) / 2 cd = math.sqrt(radius ** 2 - (d / 2) ** 2) c1 = merc2coord((cx - cd * dy / d, cy + cd * dx / d)) + (radius,) c2 = merc2coord((cx + cd * dy / d, cy - cd * dx / d)) + (radius,) return c1, c2 def get_cluster(self, forts, circle): forts_in_circle = [f for f in forts if self.get_distance(circle, f) <= circle[2]] cluster = {"center": (circle[0], circle[1]), "distance": 0, "forts": forts_in_circle, "size": len(forts_in_circle), "lured": sum(1 for f in forts_in_circle if f.get("active_fort_modifier", None) is not None)} return cluster def get_cluster_key(self, cluster): return (cluster["lured"], cluster["size"], -cluster["distance"]) def update_cluster_distance(self, cluster): cluster["distance"] = great_circle(self.bot.position, cluster["center"]).meters def update_cluster_lured(self, cluster, forts): cluster["lured"] = sum(1 for f in cluster["forts"] if forts.get(f["id"], {}).get("active_fort_modifier", None) is not None) def get_distance(self, location, fort): return great_circle(location, (fort["latitude"], fort["longitude"])).meters
class CampFort(BaseTask): SUPPORTED_TASK_API_VERSION = 1 def __init__(self, bot, config): super(CampFort, self).__init__(bot, config) def initialize(self): self.destination = None self.stay_until = 0 self.move_until = 0 self.last_position_update = 0 self.walker = None self.config_max_distance = self.config.get("max_distance", 2000) self.config_min_forts_count = self.config.get("min_forts_count", 2) self.config_min_lured_forts_count = self.config.get("min_lured_forts_count", 1) self.config_camping_time = self.config.get("camping_time", 1800) self.config_moving_time = self.config.get("moving_time", 600) def work(self): if not self.enabled: return WorkerResult.SUCCESS now = time.time() if now < self.move_until: return WorkerResult.SUCCESS if 0 < self.stay_until < now: self.destination = None self.stay_until = 0 self.move_until = now + self.config_moving_time if self.config_moving_time > 0: return WorkerResult.SUCCESS if self.destination is None: forts = self.get_forts() forts_clusters = self.get_forts_clusters(forts) if len(forts_clusters) > 0: self.destination = forts_clusters[0] self.logger.info("New destination at %s meters: %s forts, %s lured.", int(self.destination[4]), self.destination[3], self.destination[2]) else: # forts = [f for f in forts if f.get("cooldown_complete_timestamp_ms", 0) < now * 1000] # fort = min(forts, key=lambda f: self.dist(self.bot.position, f)) # self.destination = (fort["latitude"], fort["longitude"]) return WorkerResult.SUCCESS if (now - self.last_position_update) < 1: return WorkerResult.RUNNING else: self.last_position_update = now if self.stay_until >= now: lat = self.destination[0] + random_lat_long_delta() / 5 lon = self.destination[1] + random_lat_long_delta() / 5 alt = self.walker.pol_alt + random_alt_delta() / 2 self.bot.api.set_position(lat, lon, alt) else: self.walker = PolylineWalker(self.bot, self.destination[0], self.destination[1]) self.walker.step() dst = distance(self.bot.position[0], self.bot.position[1], self.destination[0], self.destination[1]) if dst < 1: forts = self.get_forts() circle = (self.destination[0], self.destination[1], Constants.MAX_DISTANCE_FORT_IS_REACHABLE) cluster = self.get_cluster(forts, circle) self.logger.info("Arrived at destination: %s forts, %s lured.", cluster[3], cluster[2]) self.stay_until = now + self.config_camping_time return WorkerResult.RUNNING def get_forts(self): radius = self.config_max_distance + Constants.MAX_DISTANCE_FORT_IS_REACHABLE forts = [f for f in self.bot.cell["forts"] if ("latitude" in f) and ("type" in f)] forts = [f for f in forts if self.dist(self.bot.start_position, f) <= radius] return forts def get_forts_clusters(self, forts): clusters = [] points = self.get_all_snap_points(forts) for c1, c2, fort1, fort2 in points: cluster_1 = self.get_cluster(forts, c1) cluster_2 = self.get_cluster(forts, c2) cluster_key_1 = self.get_cluster_key(cluster_1) cluster_key_2 = self.get_cluster_key(cluster_2) radius = Constants.MAX_DISTANCE_FORT_IS_REACHABLE if cluster_key_1 >= cluster_key_2: cluster = cluster_1 while True: new_circle, _ = self.get_enclosing_circles(fort1, fort2, radius - 1) if not new_circle: break new_cluster = self.get_cluster(forts, new_circle) if new_cluster[3] < cluster[3]: break cluster = new_cluster radius -= 1 else: cluster = cluster_2 while True: _, new_circle = self.get_enclosing_circles(fort1, fort2, radius - 1) if not new_circle: break new_cluster = self.get_cluster(forts, new_circle) if new_cluster[3] < cluster[3]: break cluster = new_cluster radius -= 1 clusters.append(cluster) clusters = [c for c in clusters if c[2] >= self.config_min_lured_forts_count] clusters = [c for c in clusters if c[3] >= self.config_min_forts_count] clusters.sort(key=lambda c: self.get_cluster_key(c), reverse=True) return clusters def get_all_snap_points(self, forts): points = [] radius = Constants.MAX_DISTANCE_FORT_IS_REACHABLE for i in range(0, len(forts)): for j in range(i + 1, len(forts)): c1, c2 = self.get_enclosing_circles(forts[i], forts[j], radius) if c1 and c2: points.append((c1, c2, forts[i], forts[j])) return points def get_enclosing_circles(self, fort1, fort2, radius): # This is an approximation which is good enough for us # since we are dealing with small distances x1, y1 = coord2merc(fort1["latitude"], fort1["longitude"]) x2, y2 = coord2merc(fort2["latitude"], fort2["longitude"]) dx = x2 - x1 dy = y2 - y1 d = math.sqrt(dx ** 2 + dy ** 2) if (d == 0) or (d > 2 * radius): return None, None cx, cy = (x1 + x2) / 2, (y1 + y2) / 2 cd = math.sqrt(radius ** 2 - (d / 2) ** 2) c1 = merc2coord((cx - cd * dy / d, cy + cd * dx / d)) + (radius,) c2 = merc2coord((cx + cd * dy / d, cy - cd * dx / d)) + (radius,) return c1, c2 def get_cluster(self, forts, circle): forts_in_circle = [f for f in forts if self.dist(circle, f) <= circle[2]] count = len(forts_in_circle) lured = len([f for f in forts_in_circle if "active_fort_modifier" in f]) dst = distance(self.bot.position[0], self.bot.position[1], circle[0], circle[1]) return (circle[0], circle[1], lured, count, dst) def get_cluster_key(self, cluster): return (cluster[2], cluster[3], -cluster[4]) def dist(self, location, fort): return distance(location[0], location[1], fort["latitude"], fort["longitude"])