diff --git a/pokemongo_bot/cell_workers/camp_fort.py b/pokemongo_bot/cell_workers/camp_fort.py index 75d3f38..9e84e3b 100644 --- a/pokemongo_bot/cell_workers/camp_fort.py +++ b/pokemongo_bot/cell_workers/camp_fort.py @@ -25,10 +25,11 @@ def __init__(self, bot, config): super(CampFort, self).__init__(bot, config) def initialize(self): - self.destination = None + self.clusters = None + self.cluster = None + self.walker = None self.stay_until = 0 self.move_until = 0 - self.walker = None self.no_log_until = 0 self.config_max_distance = self.config.get("max_distance", 2000) @@ -47,7 +48,7 @@ def work(self): return WorkerResult.SUCCESS if 0 < self.stay_until < now: - self.destination = None + self.cluster = None self.stay_until = 0 self.move_until = now + self.config_moving_time @@ -62,67 +63,56 @@ def work(self): formatted='No pokeballs left, refuse to sit at lure!', ) # Move away from lures for a time - self.destination = None + self.cluster = None self.stay_until = 0 self.move_until = now + max(self.config_moving_time, NO_BALLS_MOVING_TIME) 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.walker = PolylineWalker(self.bot, self.destination[0], self.destination[1]) - self.emit_event( - 'new_destination', - formatted='New destination at {} meters: {} forts, {} lured'.format( - round(self.destination[4], 2), self.destination[3], self.destination[2])) + 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 - if self.stay_until >= now: - cluster = self.get_current_cluster() + 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.emit_event( - 'staying_at_destination', - formatted='Staying at destination: {} forts, {} lured'.format( - cluster[3], cluster[2])) - 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 cluster[2] == 0: + if self.cluster["lured"] == 0: self.stay_until -= NO_LURED_TIME_MALUS self.walker.step(speed=0) elif self.walker.step(): - cluster = self.get_current_cluster() - self.emit_event( - 'arrived_at_destination', - formatted='Arrived at destination: {} forts, {} lured'.format( - cluster[3], cluster[2])) 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: - cluster = self.get_current_cluster() - - if cluster[2] == 0: - - self.emit_event( - 'reset_destination', - formatted="Lures gone! Resetting destination!") - self.destination = None - self.stay_until = 0 - return WorkerResult.SUCCESS - - self.emit_event( - 'moving_to_destination', - formatted="Moving to destination at {} meters: {} forts, {} lured".format( - round(cluster[4], 2), cluster[3], cluster[2]) - ) - self.no_log_until = now + LOG_TIME_INTERVAL + 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 @@ -135,20 +125,36 @@ def get_forts(self): 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 forts + 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_forts_clusters(self, forts): + 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) - cluster_key_1 = self.get_cluster_key(cluster_1) - cluster_key_2 = self.get_cluster_key(cluster_2) + + 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 cluster_key_1 >= cluster_key_2: + if key_1 >= key_2: cluster = cluster_1 while True: @@ -157,9 +163,9 @@ def get_forts_clusters(self, forts): if not new_circle: break - new_cluster = self.get_cluster(forts, new_circle) + new_cluster = self.get_cluster(cluster["forts"], new_circle) - if new_cluster[3] < cluster[3]: + if len(new_cluster["forts"]) < len(cluster["forts"]): break cluster = new_cluster @@ -173,9 +179,9 @@ def get_forts_clusters(self, forts): if not new_circle: break - new_cluster = self.get_cluster(forts, new_circle) + new_cluster = self.get_cluster(cluster["forts"], new_circle) - if new_cluster[3] < cluster[3]: + if len(new_cluster["forts"]) < len(cluster["forts"]): break cluster = new_cluster @@ -183,10 +189,6 @@ def get_forts_clusters(self, forts): 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): @@ -203,8 +205,6 @@ def get_all_snap_points(self, forts): 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 @@ -224,19 +224,23 @@ def get_enclosing_circles(self, fort1, fort2, radius): def get_cluster(self, forts, circle): forts_in_circle = [f for f in forts if self.get_distance(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 = great_circle(self.bot.position, circle).meters - return (circle[0], circle[1], lured, count, dst) + 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[2], cluster[3], -cluster[4]) + return (cluster["lured"], cluster["size"], -cluster["distance"]) - def get_current_cluster(self): - forts = self.get_forts() - circle = (self.destination[0], self.destination[1], Constants.MAX_DISTANCE_FORT_IS_REACHABLE) - return self.get_cluster(forts, circle) + 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