def test_one_small_speed(self):
        walk_max = self.bot.config.walk_max
        walk_min = self.bot.config.walk_min
        speed = 0.247503233266
        precision = 0.0
        dlat = 47.17064
        dlng = 8.51674

        self.bot.config.walk_max = speed
        self.bot.config.walk_min = speed

        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')

        @mock.patch('random.uniform')
        def run_step(mock_random):
            mock_random.return_value = 0.0
            return pw.step()

        finishedWalking = run_step()
        self.assertFalse(finishedWalking, 'step should return False')

        distance = Geodesic.WGS84.Inverse(dlat, dlng, self.bot.position[0], self.bot.position[1])["s12"]
        self.assertTrue(0.0 <= distance <= (pw.precision + pw.epsilon))
        self.polyline._last_pos = (dlat, dlng)
        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_bigger_then_total_speed_big_precision_offset(self):
        walk_max = self.bot.config.walk_max
        walk_min = self.bot.config.walk_min
        speed = 300
        precision = 2.5

        self.bot.config.walk_max = speed
        self.bot.config.walk_min = speed

        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')

        @mock.patch('random.uniform')
        def run_step(mock_random):
            mock_random.return_value = 0.0
            return pw.step()

        finishedWalking = run_step()
        self.assertTrue(finishedWalking, 'step should return False')

        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
Exemple #3
0
    def work(self):
        if not self.enabled:
            return WorkerResult.SUCCESS

        if self.get_pokeball_count() <= 0:
            self.destination = None
            self.last_cell_id = None
            return WorkerResult.SUCCESS

        now = time.time()
        pokemons = self.get_nearby_pokemons()

        if self.destination is None:
            worth_pokemons = self.get_worth_pokemons(pokemons)

            if len(worth_pokemons) > 0:
                self.destination = worth_pokemons[0]
                self.lost_counter = 0

                self.logger.info("New destination at %(distance).2f meters: %(name)s", self.destination)
                self.no_log_until = now + 60

                if self.destination["s2_cell_id"] != self.search_cell_id:
                    self.search_points = self.get_search_points(self.destination["s2_cell_id"])
                    self.walker = PolylineWalker(self.bot, self.search_points[0][0], self.search_points[0][1])
                    self.search_cell_id = self.destination["s2_cell_id"]
                    self.search_points = self.search_points[1:] + self.search_points[:1]
            else:
                if self.no_log_until < now:
                    self.logger.info("There is no nearby pokemon worth hunting down [%s]", ", ".join(p["name"] for p in pokemons))
                    self.no_log_until = now + 120

                self.last_cell_id = None

                return WorkerResult.SUCCESS

        if any(self.destination["encounter_id"] == p["encounter_id"] for p in self.bot.cell["catchable_pokemons"] + self.bot.cell["wild_pokemons"]):
            self.destination = None
        elif self.walker.step():
            if not any(self.destination["encounter_id"] == p["encounter_id"] for p in pokemons):
                self.lost_counter += 1
            else:
                self.lost_counter = 0

            if self.lost_counter >= 3:
                self.destination = None
            else:
                self.logger.info("Now searching for %(name)s", self.destination)

                self.walker = StepWalker(self.bot, self.search_points[0][0], self.search_points[0][1])
                self.search_points = self.search_points[1:] + self.search_points[:1]
        elif self.no_log_until < now:
            distance = great_circle(self.bot.position, (self.walker.dest_lat, self.walker.dest_lng)).meters
            self.logger.info("Moving to destination at %s meters: %s", round(distance, 2), self.destination["name"])
            self.no_log_until = now + 30

        return WorkerResult.RUNNING
    def set_target(self):
        if not 's2_cell_id' in self.destination:
            # This Pokemon has coords
            self.search_points = []
            self.walker = PolylineWalker(self.bot,
                                         self.destination["latitude"],
                                         self.destination["longitude"])
            self.logger.info("Target must be close by...")
            # self.logger.info("destination: %s" % self.destination)
            # self.search_points = self.get_search_points(self.bot.cell["s2_cell_id"])
            # self.search_cell_id = self.bot.cell["s2_cell_id"]
            # self.search_points = self.search_points[1:] + self.search_points[:1]
        else:
            self.search_points = self.get_search_points(
                self.destination["s2_cell_id"])
            self.walker = PolylineWalker(self.bot, self.search_points[0][0],
                                         self.search_points[0][1])
            self.search_cell_id = self.destination["s2_cell_id"]
            self.search_points = self.search_points[1:] + self.search_points[:1]

        if "fort_id" in self.destination:
            # The Pokemon is hding at a POkestop, so move to that Pokestop!
            # Get forts
            forts = self.bot.get_forts(order_by_distance=True)
            for fort in forts:
                if fort['id'] == self.destination['fort_id']:
                    # Found our fort!
                    lat = fort['latitude']
                    lng = fort['longitude']
                    details = fort_details(self.bot, fort['id'], lat, lng)
                    fort_name = details.get('name', 'Unknown')
                    self.logger.info("%s is hiding at %s, going there first!" %
                                     (self.destination["name"], fort_name))
                    self.walker = PolylineWalker(self.bot, lat, lng)
        else:
            nearest_fort = self.get_nearest_fort_on_the_way()
            if nearest_fort is not None:
                lat = nearest_fort['latitude']
                lng = nearest_fort['longitude']
                details = fort_details(self.bot, nearest_fort['id'], lat, lng)
                fort_name = details.get('name', 'Unknown')
                self.logger.info("Moving to %s via %s." %
                                 (self.destination["name"], fort_name))
                self.walker = PolylineWalker(self.bot, lat, lng)
Exemple #5
0
    def set_target(self):
        self.search_points = self.get_search_points(self.destination["s2_cell_id"])
        self.walker = PolylineWalker(self.bot, self.search_points[0][0], self.search_points[0][1])
        self.search_cell_id = self.destination["s2_cell_id"]
        self.search_points = self.search_points[1:] + self.search_points[:1]

        if "fort_id" in self.destination:
            # The Pokemon is hding at a POkestop, so move to that Pokestop!
            # Get forts
            forts = self.bot.get_forts(order_by_distance=True)
            for fort in forts:
                if fort['id'] == self.destination['fort_id']:
                    # Found our fort!
                    lat = fort['latitude']
                    lng = fort['longitude']
                    details = fort_details(self.bot, fort['id'], lat, lng)
                    fort_name = details.get('name', 'Unknown')
                    self.logger.info("%s is hiding at %s, going there first!" % (self.destination["name"], fort_name))
                    self.walker = PolylineWalker(self.bot, lat, lng)
Exemple #6
0
    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 walker_factory(name, bot, dest_lat, dest_lng, *args, **kwargs):
    '''
    Charlie and the Walker Factory
    '''
    if 'StepWalker' == name:
        ret = StepWalker(bot, dest_lat, dest_lng)
    elif 'PolylineWalker' == name:
        try:
            ret = PolylineWalker(bot, dest_lat, dest_lng)
        except:
            ret = StepWalker(bot, dest_lat, dest_lng)
    return ret
    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
Exemple #10
0
    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
Exemple #11
0
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"])
Exemple #12
0
    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
Exemple #13
0
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
Exemple #14
0
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
Exemple #15
0
    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

        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
Exemple #16
0
    def work(self):
        if not self.enabled:
            return WorkerResult.SUCCESS

        if self.config_disabled_while_camping and hasattr(
                self.bot, 'camping_forts') and self.bot.camping_forts:
            return WorkerResult.SUCCESS

        if not self.config_lock_on_target:
            self.bot.hunter_locked_target = None

        if self.no_hunt_until != None and self.no_hunt_until > time.time():
            # No hunting now, cooling down
            return WorkerResult.SUCCESS
        else:
            # Resume hunting
            self.no_hunt_until = None

        if self.bot.catch_disabled:
            if not hasattr(self.bot,"hunter_disabled_global_warning") or \
                        (hasattr(self.bot,"hunter_disabled_global_warning") and not self.bot.hunter_disabled_global_warning):
                self.logger.info(
                    "All catching tasks are currently disabled until {}. Pokemon Hunter will resume when catching tasks are re-enabled"
                    .format(self.bot.catch_resume_at.strftime("%H:%M:%S")))
            self.bot.hunter_disabled_global_warning = True
            return WorkerResult.SUCCESS
        else:
            self.bot.hunter_disabled_global_warning = False

        if self.bot.softban:
            if not hasattr(self.bot, "softban_global_warning") or \
                        (hasattr(self.bot, "softban_global_warning") and not self.bot.softban_global_warning):
                self.logger.info(
                    "Possible softban! Not trying to catch Pokemon.")
            self.bot.softban_global_warning = True
            return WorkerResult.SUCCESS
        else:
            self.bot.softban_global_warning = False

        if self.get_pokeball_count() <= 0:
            self.destination = None
            self.last_cell_id = None
            return WorkerResult.SUCCESS

        if self.destination is not None:
            if self.destination_caught():
                self.logger.info(
                    "We found a %(name)s while hunting. Aborting the current search.",
                    self.destination)
                self.destination = None
                wait = uniform(120, 600)
                self.no_hunt_until = time.time() + wait
                self.logger.info("Hunting on cooldown until {}.".format(
                    (datetime.now() +
                     timedelta(seconds=wait)).strftime("%H:%M:%S")))
                return WorkerResult.SUCCESS

        now = time.time()
        pokemons = self.get_nearby_pokemons()
        pokemons = filter(lambda x: x["pokemon_id"] not in self.recent_tries,
                          pokemons)

        if self.destination is None:
            worth_pokemons = self.get_worth_pokemons(pokemons)

            if len(worth_pokemons) > 0:
                # Pick a random target from the list
                random.shuffle(worth_pokemons)
                # Prevents the bot from looping the same Pokemon
                self.destination = worth_pokemons[0]
                self.lost_counter = 0
                self.hunt_started_at = datetime.now()

                self.logger.info(
                    "New destination at %(distance).2f meters: %(name)s",
                    self.destination)
                if self._is_vip_pokemon(self.destination):
                    self.logger.info("This is a VIP Pokemon! Starting hunt.")
                    if self.config_lock_on_target:
                        self.bot.hunter_locked_target = self.destination
                elif self._is_needed_pokedex(self.destination):
                    self.logger.info(
                        "I need a %(name)s to complete the Pokedex! I have %(candies)s candies.",
                        self.destination)
                    if self.config_lock_on_target and not self.config_lock_vip_only:
                        self.bot.hunter_locked_target = self.destination
                    else:
                        self.bot.hunter_locked_target = None

                self.no_log_until = now + 60

                if self.destination["s2_cell_id"] != self.search_cell_id:
                    self.search_points = self.get_search_points(
                        self.destination["s2_cell_id"])
                    self.walker = PolylineWalker(self.bot,
                                                 self.search_points[0][0],
                                                 self.search_points[0][1])
                    self.search_cell_id = self.destination["s2_cell_id"]
                    self.search_points = self.search_points[
                        1:] + self.search_points[:1]
            else:
                if self.no_log_until < now:
                    # Show like "Pidgey (12), Zubat(2)"
                    names = Counter((p["name"] for p in pokemons))
                    sorted(names)  # unicode object, no lower? , key=str.lower)

                    self.logger.info(
                        "There is no nearby pokemon worth hunting down [%s]",
                        ", ".join('{}({})'.format(key, val)
                                  for key, val in names.items()))
                    self.no_log_until = now + 120
                    self.destination = None
                    wait = uniform(120, 600)
                    self.no_hunt_until = now + wait
                    self.logger.info("Will look again around {}.".format(
                        (datetime.now() +
                         timedelta(seconds=wait)).strftime("%H:%M:%S")))

                self.last_cell_id = None

                return WorkerResult.SUCCESS

        if self.config_lock_on_target and not self.config_lock_vip_only:
            if self.bot.hunter_locked_target == None:
                self.logger.info(
                    "We found a %(name)s while hunting. Aborting the current search.",
                    self.destination)
                self.destination = None
                wait = uniform(120, 600)
                self.no_hunt_until = now + wait
                self.logger.info("Hunting on cooldown until {}.".format(
                    (datetime.now() +
                     timedelta(seconds=wait)).strftime("%H:%M:%S")))
                return WorkerResult.SUCCESS

        if any(self.destination["encounter_id"] == p["encounter_id"]
               for p in self.bot.cell["catchable_pokemons"] +
               self.bot.cell["wild_pokemons"]):
            self.destination = None
        elif self.walker.step():
            if not any(self.destination["encounter_id"] == p["encounter_id"]
                       for p in pokemons):
                self.lost_counter += 1
            else:
                self.lost_counter = 0

            if self.lost_counter >= 3:
                self.logger.info("I haven't found %(name)s", self.destination)
                self.bot.hunter_locked_target = None
                self.destination = None
                wait = uniform(120, 600)
                self.no_hunt_until = now + wait
                self.logger.info("Hunting on cooldown until {}.".format(
                    (datetime.now() +
                     timedelta(seconds=wait)).strftime("%H:%M:%S")))
            else:
                self.logger.info("Now searching for %(name)s",
                                 self.destination)

                self.walker = StepWalker(self.bot, self.search_points[0][0],
                                         self.search_points[0][1])
                self.search_points = self.search_points[
                    1:] + self.search_points[:1]
        elif self.no_log_until < now:
            distance = great_circle(
                self.bot.position,
                (self.walker.dest_lat, self.walker.dest_lng)).meters
            if round(distance, 2) == self.distance_to_target:
                # Hmm, not moved toward the Pokemon?
                self.distance_counter += 1
            else:
                self.distance_counter = 0

            if self.distance_counter >= 3:
                # Ignore last 3
                if len(self.recent_tries) > 3:
                    self.recent_tries.pop()

                self.recent_tries.append(self.destination['pokemon_id'])
                self.logger.info(
                    "I cant move toward %(name)s! Aborting search.",
                    self.destination)
                self.bot.hunter_locked_target = None
                self.destination = None
                wait = uniform(120, 600)
                self.no_hunt_until = now + wait
                self.logger.info("Hunting on cooldown until {}.".format(
                    (datetime.now() +
                     timedelta(seconds=wait)).strftime("%H:%M:%S")))
                return WorkerResult.ERROR
            else:
                self.logger.info("Moving to destination at %s meters: %s",
                                 round(distance, 2), self.destination["name"])
                # record the new distance...
                self.distance_to_target = round(distance, 2)
                if self.config_lock_on_target and not self.config_lock_vip_only:
                    # Just to ensure we stay on target
                    self.bot.hunter_locked_target = self.destination
                self.no_log_until = now + 30

        return WorkerResult.RUNNING
    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 work(self):
        if not self.enabled:
            return WorkerResult.SUCCESS

        if self.bot.catch_disabled:
            if not hasattr(self.bot,"hunter_disabled_global_warning") or \
                        (hasattr(self.bot,"hunter_disabled_global_warning") and not self.bot.hunter_disabled_global_warning):
                self.logger.info(
                    "All catching tasks are currently disabled until {}. Pokemon Hunter will resume when catching tasks are re-enabled"
                    .format(self.bot.catch_resume_at.strftime("%H:%M:%S")))
            self.bot.hunter_disabled_global_warning = True
            return WorkerResult.SUCCESS
        else:
            self.bot.hunter_disabled_global_warning = False

        if self.get_pokeball_count() <= 0:
            self.destination = None
            self.last_cell_id = None
            return WorkerResult.SUCCESS

        now = time.time()
        pokemons = self.get_nearby_pokemons()

        if self.destination is None:
            worth_pokemons = self.get_worth_pokemons(pokemons)

            if len(worth_pokemons) > 0:
                self.destination = worth_pokemons[0]
                self.lost_counter = 0

                self.logger.info(
                    "New destination at %(distance).2f meters: %(name)s",
                    self.destination)
                self.no_log_until = now + 60

                if self.destination["s2_cell_id"] != self.search_cell_id:
                    self.search_points = self.get_search_points(
                        self.destination["s2_cell_id"])
                    self.walker = PolylineWalker(self.bot,
                                                 self.search_points[0][0],
                                                 self.search_points[0][1])
                    self.search_cell_id = self.destination["s2_cell_id"]
                    self.search_points = self.search_points[
                        1:] + self.search_points[:1]
            else:
                if self.no_log_until < now:
                    self.logger.info(
                        "There is no nearby pokemon worth hunting down [%s]",
                        ", ".join(p["name"] for p in pokemons))
                    self.no_log_until = now + 120

                self.last_cell_id = None

                return WorkerResult.SUCCESS

        if any(self.destination["encounter_id"] == p["encounter_id"]
               for p in self.bot.cell["catchable_pokemons"] +
               self.bot.cell["wild_pokemons"]):
            self.destination = None
        elif self.walker.step():
            if not any(self.destination["encounter_id"] == p["encounter_id"]
                       for p in pokemons):
                self.lost_counter += 1
            else:
                self.lost_counter = 0

            if self.lost_counter >= 3:
                self.destination = None
            else:
                self.logger.info("Now searching for %(name)s",
                                 self.destination)

                self.walker = StepWalker(self.bot, self.search_points[0][0],
                                         self.search_points[0][1])
                self.search_points = self.search_points[
                    1:] + self.search_points[:1]
        elif self.no_log_until < now:
            distance = great_circle(
                self.bot.position,
                (self.walker.dest_lat, self.walker.dest_lng)).meters
            self.logger.info("Moving to destination at %s meters: %s",
                             round(distance, 2), self.destination["name"])
            self.no_log_until = now + 30

        return WorkerResult.RUNNING