def work(self):
     if not self.next_point:
         self.next_point = next(self.points)
     point = self.next_point
     step_walker = StepWalker(self.bot, point['lat'], point['lng'])
     if step_walker.step():
         self.next_point = None
     return [point['lat'], point['lng']]
Example #2
0
 def work(self):
     if not self.next_point:
         self.next_point = next(self.points)
     point = self.next_point
     step_walker = StepWalker(self.bot, point['lat'], point['lng'])
     if step_walker.step():
         self.next_point = None
     return [point['lat'], point['lng']]
Example #3
0
    def work(self):
        last_lat, last_lng, last_alt = self.bot.api.get_position()

        point = self.points[self.ptr]
        self.cnt += 1

        dist = distance(
            last_lat,
            last_lng,
            point['lat'],
            point['lng']
        )

        alt = uniform(self.bot.config.alt_min, self.bot.config.alt_max)
        if self.bot.config.walk_max > 0:
            step_walker = StepWalker(
                self.bot,
                point['lat'],
                point['lng']
            )

            if self.cnt == 1:
                self.emit_event(
                    'position_update',
                    formatted="Walking from {last_position} to {current_position} ({distance} {distance_unit})",
                    data={
                        'last_position': (last_lat, last_lng, last_alt),
                        'current_position': (point['lat'], point['lng'], alt),
                        'distance': dist,
                        'distance_unit': 'm'
                    }
                )

            if step_walker.step():
                step_walker = None
        else:
            self.bot.api.set_position(point['lat'], point['lng'], alt)
            self.emit_event(
                'position_update',
                formatted="Teleported from {last_position} to {current_position} ({distance} {distance_unit})",
                data={
                    'last_position': (last_lat, last_lng, last_alt),
                    'current_position': (point['lat'], point['lng'], alt),
                    'distance': dist,
                    'distance_unit': 'm'
                }
            )

        if dist <= 1 or (self.bot.config.walk_min > 0 and step_walker == None):
            if self.ptr + self.direction >= len(self.points) or self.ptr + self.direction <= -1:
                self.direction *= -1
            if len(self.points) != 1:
                self.ptr += self.direction
            else:
                self.ptr = 0
            self.cnt = 0

        return [point['lat'], point['lng']]
Example #4
0
    def work(self):
        last_lat = self.bot.api._position_lat
        last_lng = self.bot.api._position_lng
        last_alt = self.bot.api._position_alt

        point = self.points[self.ptr]
        lat = float(point['lat'])
        lng = float(point['lng'])
        alt = uniform(self.bot.config.alt_min, self.bot.config.alt_max)
        if 'alt' in point:
            alt = float(point['alt'])

        if self.bot.config.walk_max > 0:
            step_walker = StepWalker(self.bot, lat, lng)

            is_at_destination = False
            if step_walker.step():
                is_at_destination = True

        else:
            self.bot.api.set_position(lat, lng, alt)

        dist = distance(last_lat, last_lng, lat, lng)

        self.emit_event(
            'position_update',
            formatted=
            "Walking from {last_position} to {current_position}, distance left: ({distance} {distance_unit}) ..",
            data={
                'last_position': (last_lat, last_lng, last_alt),
                'current_position': (lat, lng, alt),
                'distance': dist,
                'distance_unit': 'm'
            })

        if dist <= 1 or (self.bot.config.walk_min > 0 and is_at_destination):
            if (self.ptr + 1) == len(self.points):
                self.ptr = 0
                if self.path_mode == 'linear':
                    self.points = list(reversed(self.points))
                if self.number_lap_max >= 0:
                    self.number_lap += 1
                    self.emit_event(
                        'path_lap_update',
                        formatted=
                        "number lap : {number_lap} / {number_lap_max}",
                        data={
                            'number_lap': str(self.number_lap),
                            'number_lap_max': str(self.number_lap_max)
                        })
                    if self.number_lap >= self.number_lap_max:
                        self.endLaps()
            else:
                self.ptr += 1

        return [lat, lng]
Example #5
0
    def work(self):
        last_lat, last_lng, last_alt = self.bot.api.get_position()

        point = self.points[self.ptr]
        self.cnt += 1

        dist = distance(last_lat, last_lng, point['lat'], point['lng'])

        alt = uniform(self.bot.config.alt_min, self.bot.config.alt_max)
        if self.bot.config.walk_max > 0:
            step_walker = StepWalker(self.bot, point['lat'], point['lng'])

            if self.cnt == 1:
                self.emit_event(
                    'position_update',
                    formatted=
                    "Walking from {last_position} to {current_position} ({distance} {distance_unit})",
                    data={
                        'last_position': (last_lat, last_lng, last_alt),
                        'current_position': (point['lat'], point['lng'], alt),
                        'distance': dist,
                        'distance_unit': 'm'
                    })

            if step_walker.step():
                step_walker = None
        else:
            self.bot.api.set_position(point['lat'], point['lng'], alt)
            self.emit_event(
                'position_update',
                formatted=
                "Teleported from {last_position} to {current_position} ({distance} {distance_unit})",
                data={
                    'last_position': (last_lat, last_lng, last_alt),
                    'current_position': (point['lat'], point['lng'], alt),
                    'distance': dist,
                    'distance_unit': 'm'
                })

        if dist <= 1 or (self.bot.config.walk_min > 0 and step_walker == None):
            if self.ptr + self.direction >= len(
                    self.points) or self.ptr + self.direction <= -1:
                self.direction *= -1
            if len(self.points) != 1:
                self.ptr += self.direction
            else:
                self.ptr = 0
            self.cnt = 0

        return [point['lat'], point['lng']]
Example #6
0
    def test_small_distance_same_spot(self):
        walk_max = self.bot.config.walk_max
        walk_min = self.bot.config.walk_min

        self.bot.config.walk_max = 1
        self.bot.config.walk_min = 1

        sw = StepWalker(self.bot, 0, 0)
        self.assertEqual(sw.dLat, 0, 'dLat should be 0')
        self.assertEqual(sw.dLng, 0, 'dLng should be 0')

        self.assertTrue(sw.step(), 'step should return True')
        self.assertTrue(self.lat == self.bot.position[0])
        self.assertTrue(self.lng == self.bot.position[1])

        self.bot.config.walk_max = walk_max
        self.bot.config.walk_min = walk_min
Example #7
0
    def test_normalized_distance_times_2(self):
        walk_max = self.bot.config.walk_max
        walk_min = self.bot.config.walk_min

        self.bot.config.walk_max = 2
        self.bot.config.walk_min = 2

        sw = StepWalker(self.bot, 0.1, 0.1, precision=0.0)
        self.assertTrue(sw.dLat > 0)
        self.assertTrue(sw.dLng > 0)

        stayInPlace = sw.step()
        self.assertFalse(stayInPlace)

        self.assertTrue(float_equal(self.lat, NORMALIZED_LAT_LNG_DISTANCE[0] * 2))
        self.assertTrue(float_equal(self.lng, NORMALIZED_LAT_LNG_DISTANCE[1] * 2))

        self.bot.config.walk_max = walk_max
        self.bot.config.walk_min = walk_min
Example #8
0
    def test_normalized_distance(self):
        walk_max = self.bot.config.walk_max
        walk_min = self.bot.config.walk_min

        self.bot.config.walk_max = 1
        self.bot.config.walk_min = 1

        sw = StepWalker(self.bot, 0.1, 0.1)
        self.assertGreater(sw.dLat, 0)
        self.assertGreater(sw.dLng, 0)

        stayInPlace = sw.step()
        self.assertFalse(stayInPlace)

        self.assertTrue(float_equal(self.lat, NORMALIZED_LAT_LNG_DISTANCE_STEP))
        self.assertTrue(float_equal(self.lng, NORMALIZED_LAT_LNG_DISTANCE_STEP))

        self.bot.config.walk_max = walk_max
        self.bot.config.walk_min = walk_min
Example #9
0
    def work(self):
        forts = self.bot.get_forts()
        log_lure_avail_str = ''
        log_lured_str = ''
        if self.lured:
            lured_forts = [x for x in forts if 'active_fort_modifier' in x]
            if len(lured_forts) > 0:
                log_lured_str = 'lured '
                self.dest = find_biggest_cluster(self.radius, lured_forts,
                                                 '9QM=')
            else:
                log_lure_avail_str = 'No lured pokestops in vicinity. Search for normal ones instead. '
                self.dest = find_biggest_cluster(self.radius, forts)
        else:
            self.dest = find_biggest_cluster(self.radius, forts)

        if self.dest is not None:
            lat = self.dest['latitude']
            lng = self.dest['longitude']
            cnt = self.dest['num_points']

            if not self.is_at_destination:
                msg = log_lure_avail_str + (
                    "Move to cluster: {num_points} {forts} "
                    "pokestops will be in range of {radius}. Walking {distance}m."
                )
                self.emit_event('found_cluster',
                                formatted=msg,
                                data={
                                    'num_points':
                                    cnt,
                                    'forts':
                                    log_lured_str,
                                    'radius':
                                    str(self.radius),
                                    'distance':
                                    str(
                                        round(
                                            distance(self.bot.position[0],
                                                     self.bot.position[1], lat,
                                                     lng), 2))
                                })

                self.announced = False

                step_walker = StepWalker(self.bot, lat, lng)

                self.is_at_destination = False
                if step_walker.step():
                    self.is_at_destination = True
            elif not self.announced:
                self.emit_event(
                    'arrived_at_cluster',
                    formatted=
                    "Arrived at cluster. {num_points} {forts} pokestops are in a range of {radius}m radius.",
                    data={
                        'num_points': cnt,
                        'forts': log_lured_str,
                        'radius': self.radius
                    })
                self.announced = True
        else:
            lat = self.bot.position[0]
            lng = self.bot.position[1]

        return [lat, lng]
Example #10
0
    def work(self):
        forts = self.bot.get_forts()
        log_lure_avail_str = ''
        log_lured_str = ''
        if self.lured:
            lured_forts = [x for x in forts if 'active_fort_modifier' in x]
            if len(lured_forts) > 0:
                log_lured_str = 'lured '
                self.dest = find_biggest_cluster(self.radius, lured_forts, '9QM=')
            else:
                log_lure_avail_str = 'No lured pokestops in vicinity. Search for normal ones instead. '
                self.dest = find_biggest_cluster(self.radius, forts)
        else:
            self.dest = find_biggest_cluster(self.radius, forts)

        if self.dest is not None:

            lat = self.dest['latitude']
            lng = self.dest['longitude']
            cnt = self.dest['num_points']

            if not self.is_at_destination:
                msg = log_lure_avail_str + (
                    "Move to cluster: {num_points} {forts} "
                    "pokestops will be in range of {radius}. Walking {distance}m."
                )
                self.emit_event(
                    'found_cluster',
                    formatted=msg,
                    data={
                        'num_points': cnt,
                        'forts': log_lured_str,
                        'radius': str(self.radius),
                        'distance': str(round(distance(self.bot.position[0], self.bot.position[1], lat, lng), 2))
                    }
                )

                self.announced = False

                if self.bot.config.walk_max > 0:
                    step_walker = StepWalker(
                        self.bot,
                        lat,
                        lng
                    )

                    self.is_at_destination = False
                    if step_walker.step():
                        self.is_at_destination = True
                else:
                    alt = uniform(self.bot.config.alt_min, self.bot.config.alt_max)
                    self.bot.api.set_position(lat, lng, alt)

            elif not self.announced:
                self.emit_event(
                    'arrived_at_cluster',
                    formatted="Arrived at cluster. {num_points} {forts} pokestops are in a range of {radius}m radius.",
                    data={
                        'num_points': cnt,
                        'forts': log_lured_str,
                        'radius': self.radius
                    }
                )
                self.announced = True
        else:
            lat = self.bot.position[0]
            lng = self.bot.position[1]

        return [lat, lng]
Example #11
0
 def test_big_distances(self):
     # FIXME currently the StepWalker acts like it won't move if big distances gives as input
     # see args below
     # with self.assertRaises(RuntimeError):
     sw = StepWalker(self.bot, 10, 10)
     sw.step() # equals True i.e act like the distance is too short for a step
Example #12
0
class PokemonHunter(BaseTask):
    SUPPORTED_TASK_API_VERSION = 1

    def __init__(self, bot, config):
        super(PokemonHunter, self).__init__(bot, config)

    def initialize(self):
        self.notified_second_gen = []
        self.destination = None
        self.walker = None
        self.search_cell_id = None
        self.search_points = []
        self.lost_counter = 0
        self.no_log_until = 0
        self.distance_to_target = 0
        self.distance_counter = 0
        self.recent_tries = []
        self.no_hunt_until = None
        self.hunt_started_at = None

        self.config_max_distance = self.config.get("max_distance", 2000)
        self.config_hunt_all = self.config.get("hunt_all", False)
        self.config_hunt_vip = self.config.get("hunt_vip", True)
        self.config_hunt_pokedex = self.config.get("hunt_pokedex", True)
        # Lock on Target; ignore all other Pokémon until we found our target.
        self.config_lock_on_target = self.config.get("lock_on_target", False)
        # Lock only VIP Pokemon (unseen / VIP)
        self.config_lock_vip_only = self.config.get("lock_vip_only", True)
        # If we are camping forts, disable hunting (see CampFort)
        self.config_disabled_while_camping = self.config.get("disabled_while_camping", True)
        # Hunt unseens as VIP?
        self.config_treat_unseen_as_vip = self.config.get("treat_unseen_as_vip", True)
        self.bot.hunter_locked_target = None

    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.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 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_nearby_pokemons(self):
        radius = self.config_max_distance

        pokemons = [p for p in self.bot.cell["nearby_pokemons"] if self.get_distance(self.bot.start_position, p) <= radius]

        for pokemon in pokemons:
            pokemon["distance"] = self.get_distance(self.bot.position, p)
            pokemon["name"] = inventory.pokemons().name_for(pokemon["pokemon_id"])
            pokemon["candies"] = inventory.candies().get(pokemon["pokemon_id"]).quantity

        pokemons.sort(key=lambda p: p["distance"])

        return pokemons

    def _is_vip_pokemon(self, pokemon):
        # having just a name present in the list makes them vip
        # Not seen pokemons also will become vip if it's not disabled in config
        if self.bot.config.vips.get(pokemon["name"]) == {} or (self.config_treat_unseen_as_vip and not inventory.pokedex().seen(pokemon["pokemon_id"])):
            return True

    def _is_needed_pokedex(self, pokemon):
        candies = inventory.candies().get(pokemon["pokemon_id"]).quantity
        if candies > 150:
            # We have enough candies, pass on hunting this Pokemon
            return False

        # get family ids, gets ALL ids, also for previous evo!
        # We could see a Ivysaur on the map, and need a Bulbasaur
        # Then we have no need for a Ivysaur. If we see a Bulbasaur and need
        # a Ivysaur, then we DO need this pokemon.
        got_current_evo = False
        ids = []
        for fid in self.get_family_ids(pokemon):
            if got_current_evo:
                ids += [fid]
            else:
                if fid == pokemon["pokemon_id"]:
                    ids += [fid]
                    got_current_evo = True
        # Check if we need this, or a next EVO in the Pokedex
        if any(not inventory.pokedex().seen(fid) for fid in ids):
            return True

    def get_worth_pokemons(self, pokemons):
        if self.config_hunt_all:
            worth_pokemons = pokemons
        else:
            worth_pokemons = []

            if self.config_hunt_vip:
                worth_pokemons += [p for p in pokemons if p["name"] in self.bot.config.vips]

            if self.config_hunt_pokedex:
                worth_pokemons += [p for p in pokemons if (p not in worth_pokemons) and self._is_needed_pokedex(p)]

        worth_pokemons.sort(key=lambda p: inventory.candies().get(p["pokemon_id"]).quantity)

        return worth_pokemons

    def get_family_ids(self, pokemon):
        family_id = inventory.pokemons().data_for(pokemon["pokemon_id"]).first_evolution_id
        ids = [family_id]
        ids += inventory.pokemons().data_for(family_id).next_evolutions_all[:]

        return ids

    def get_distance(self, location, pokemon):
        return great_circle(location, (pokemon["latitude"], pokemon["longitude"])).meters

    def get_search_points(self, cell_id):
        points = []

        # For cell level 15
        for c in Cell(CellId(cell_id)).subdivide():
            for cc in c.subdivide():
                latlng = LatLng.from_point(cc.get_center())
                point = (latlng.lat().degrees, latlng.lng().degrees)
                points.append(point)

        points[0], points[1] = points[1], points[0]
        points[14], points[15] = points[15], points[14]
        point = points.pop(2)
        points.insert(7, point)
        point = points.pop(13)
        points.insert(8, point)

        closest = min(points, key=lambda p: great_circle(self.bot.position, p).meters)
        index = points.index(closest)

        return points[index:] + points[:index]

    def destination_caught(self):
        # self.logger.info("Searching for a {} since {}".format(self.destination["name"], self.hunt_started_at.strftime("%Y-%m-%d %H:%M:%S")))

        with self.bot.database as conn:
            c = conn.cursor()
            c.execute(
                "SELECT COUNT(pokemon) FROM catch_log where pokemon = '{}' and  datetime(dated, 'localtime') > Datetime('{}')".format(self.destination["name"], self.hunt_started_at.strftime("%Y-%m-%d %H:%M:%S")))
        # Now check if there is 1 or more caught
        amount = c.fetchone()[0]
        caught = amount > 0
        if caught:
            self.logger.info("We caught {} {}(s) since {}".format(amount, self.destination["name"], self.hunt_started_at.strftime("%Y-%m-%d %H:%M:%S")))

        return caught
Example #13
0
class PokemonHunter(BaseTask):
    SUPPORTED_TASK_API_VERSION = 1

    def __init__(self, bot, config):
        super(PokemonHunter, self).__init__(bot, config)

    def initialize(self):
        self.destination = None
        self.walker = None
        self.search_cell_id = None
        self.search_points = []
        self.lost_counter = 0
        self.no_log_until = 0

        self.config_max_distance = self.config.get("max_distance", 2000)
        self.config_hunt_all = self.config.get("hunt_all", False)
        self.config_hunt_vip = self.config.get("hunt_vip", True)
        self.config_hunt_pokedex = self.config.get("hunt_pokedex", True)

    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 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_nearby_pokemons(self):
        radius = self.config_max_distance

        pokemons = [p for p in self.bot.cell["nearby_pokemons"] if self.get_distance(self.bot.start_position, p) <= radius]

        for pokemon in pokemons:
            pokemon["distance"] = self.get_distance(self.bot.position, p)
            pokemon["name"] = inventory.pokemons().name_for(pokemon["pokemon_id"])

        pokemons.sort(key=lambda p: p["distance"])

        return pokemons

    def get_worth_pokemons(self, pokemons):
        if self.config_hunt_all:
            worth_pokemons = pokemons
        else:
            worth_pokemons = []

            if self.config_hunt_vip:
                worth_pokemons += [p for p in pokemons if p["name"] in self.bot.config.vips]

            if self.config_hunt_pokedex:
                worth_pokemons += [p for p in pokemons if (p not in worth_pokemons) and any(not inventory.pokedex().seen(fid) for fid in self.get_family_ids(p))]

        worth_pokemons.sort(key=lambda p: inventory.candies().get(p["pokemon_id"]).quantity)

        return worth_pokemons

    def get_family_ids(self, pokemon):
        family_id = inventory.pokemons().data_for(pokemon["pokemon_id"]).first_evolution_id
        ids = [family_id]
        ids += inventory.pokemons().data_for(family_id).next_evolutions_all[:]

        return ids

    def get_distance(self, location, pokemon):
        return great_circle(location, (pokemon["latitude"], pokemon["longitude"])).meters

    def get_search_points(self, cell_id):
        points = []

        # For cell level 15
        for c in Cell(CellId(cell_id)).subdivide():
            for cc in c.subdivide():
                latlng = LatLng.from_point(cc.get_center())
                point = (latlng.lat().degrees, latlng.lng().degrees)
                points.append(point)

        points[0], points[1] = points[1], points[0]
        points[14], points[15] = points[15], points[14]
        point = points.pop(2)
        points.insert(7, point)
        point = points.pop(13)
        points.insert(8, point)

        closest = min(points, key=lambda p: great_circle(self.bot.position, p).meters)
        index = points.index(closest)

        return points[index:] + points[:index]
Example #14
0
    def work(self):
        last_lat = self.bot.api._position_lat
        last_lng = self.bot.api._position_lng
        last_alt = self.bot.api._position_alt

        point = self.points[self.ptr]
        lat = float(point['lat'])
        lng = float(point['lng'])
        alt = uniform(self.bot.config.alt_min, self.bot.config.alt_max)
        if 'alt' in point:
            alt = float(point['alt'])

        if self.bot.config.walk_max > 0:
            step_walker = StepWalker(
                self.bot,
                lat,
                lng
            )

            is_at_destination = False
            if step_walker.step():
                is_at_destination = True

        else:
            self.bot.api.set_position(lat, lng, alt)

        dist = distance(
            last_lat,
            last_lng,
            lat,
            lng
        )

        self.emit_event(
            'position_update',
            formatted="Walking from {last_position} to {current_position}, distance left: ({distance} {distance_unit}) ..",
            data={
                'last_position': (last_lat, last_lng, last_alt),
                'current_position': (lat, lng, alt),
                'distance': dist,
                'distance_unit': 'm'
            }
        )
        
        if dist <= 1 or (self.bot.config.walk_min > 0 and is_at_destination):
            if (self.ptr + 1) == len(self.points):
                self.ptr = 0
                if self.path_mode == 'linear':
                    self.points = list(reversed(self.points))
                if self.number_lap_max >= 0:
                    self.number_lap+=1
                    self.emit_event(
                        'path_lap_update',
                        formatted="number lap : {number_lap} / {number_lap_max}",
                        data={
                            'number_lap': str(self.number_lap),
                            'number_lap_max': str(self.number_lap_max)
                        }
                    )
                    if self.number_lap >= self.number_lap_max:
                        self.endLaps()
            else:
                self.ptr += 1
        
        return [lat, lng]
Example #15
0
class PokemonHunter(BaseTask):
    SUPPORTED_TASK_API_VERSION = 1

    def __init__(self, bot, config):
        super(PokemonHunter, self).__init__(bot, config)

    def initialize(self):
        self.notified_second_gen = []
        self.destination = None
        self.walker = None
        self.search_cell_id = None
        self.search_points = []
        self.lost_counter = 0
        self.no_log_until = 0
        self.distance_to_target = 0
        self.distance_counter = 0
        self.recent_tries = []
        self.no_hunt_until = None
        self.hunt_started_at = None

        self.config_max_distance = self.config.get("max_distance", 2000)
        self.config_hunt_all = self.config.get("hunt_all", False)
        self.config_hunt_vip = self.config.get("hunt_vip", True)
        self.config_hunt_pokedex = self.config.get("hunt_pokedex", True)
        # Lock on Target; ignore all other Pokémon until we found our target.
        self.config_lock_on_target = self.config.get("lock_on_target", False)
        # Lock only VIP Pokemon (unseen / VIP)
        self.config_lock_vip_only = self.config.get("lock_vip_only", True)
        # If we are camping forts, disable hunting (see CampFort)
        self.config_disabled_while_camping = self.config.get(
            "disabled_while_camping", True)
        # Hunt unseens as VIP?
        self.config_treat_unseen_as_vip = self.config.get(
            "treat_unseen_as_vip", True)
        self.bot.hunter_locked_target = None

    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 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_nearby_pokemons(self):
        radius = self.config_max_distance

        pokemons = [
            p for p in self.bot.cell["nearby_pokemons"]
            if self.get_distance(self.bot.start_position, p) <= radius
        ]

        for pokemon in pokemons:
            pokemon["distance"] = self.get_distance(self.bot.position, p)
            pokemon["name"] = inventory.pokemons().name_for(
                pokemon["pokemon_id"])
            pokemon["candies"] = inventory.candies().get(
                pokemon["pokemon_id"]).quantity

        pokemons.sort(key=lambda p: p["distance"])

        return pokemons

    def _is_vip_pokemon(self, pokemon):
        # having just a name present in the list makes them vip
        # Not seen pokemons also will become vip if it's not disabled in config
        if self.bot.config.vips.get(pokemon["name"]) == {} or (
                self.config_treat_unseen_as_vip
                and not inventory.pokedex().seen(pokemon["pokemon_id"])):
            return True

    def _is_needed_pokedex(self, pokemon):
        candies = inventory.candies().get(pokemon["pokemon_id"]).quantity
        if candies > 150:
            # We have enough candies, pass on hunting this Pokemon
            return False

        # get family ids, gets ALL ids, also for previous evo!
        # We could see a Ivysaur on the map, and need a Bulbasaur
        # Then we have no need for a Ivysaur. If we see a Bulbasaur and need
        # a Ivysaur, then we DO need this pokemon.
        got_current_evo = False
        ids = []
        for fid in self.get_family_ids(pokemon):
            if got_current_evo:
                ids += [fid]
            else:
                if fid == pokemon["pokemon_id"]:
                    ids += [fid]
                    got_current_evo = True
        # Check if we need this, or a next EVO in the Pokedex
        if any(not inventory.pokedex().seen(fid) for fid in ids):
            return True

    def get_worth_pokemons(self, pokemons):
        if self.config_hunt_all:
            worth_pokemons = pokemons
        else:
            worth_pokemons = []

            if self.config_hunt_vip:
                worth_pokemons += [
                    p for p in pokemons if p["name"] in self.bot.config.vips
                ]

            if self.config_hunt_pokedex:
                worth_pokemons += [
                    p for p in pokemons
                    if (p not in worth_pokemons) and self._is_needed_pokedex(p)
                ]

        worth_pokemons.sort(
            key=lambda p: inventory.candies().get(p["pokemon_id"]).quantity)

        return worth_pokemons

    def get_family_ids(self, pokemon):
        family_id = inventory.pokemons().data_for(
            pokemon["pokemon_id"]).first_evolution_id
        ids = [family_id]
        ids += inventory.pokemons().data_for(family_id).next_evolutions_all[:]

        return ids

    def get_distance(self, location, pokemon):
        return great_circle(location,
                            (pokemon["latitude"], pokemon["longitude"])).meters

    def get_search_points(self, cell_id):
        points = []

        # For cell level 15
        for c in Cell(CellId(cell_id)).subdivide():
            for cc in c.subdivide():
                latlng = LatLng.from_point(cc.get_center())
                point = (latlng.lat().degrees, latlng.lng().degrees)
                points.append(point)

        points[0], points[1] = points[1], points[0]
        points[14], points[15] = points[15], points[14]
        point = points.pop(2)
        points.insert(7, point)
        point = points.pop(13)
        points.insert(8, point)

        closest = min(points,
                      key=lambda p: great_circle(self.bot.position, p).meters)
        index = points.index(closest)

        return points[index:] + points[:index]

    def destination_caught(self):
        # self.logger.info("Searching for a {} since {}".format(self.destination["name"], self.hunt_started_at.strftime("%Y-%m-%d %H:%M:%S")))

        with self.bot.database as conn:
            c = conn.cursor()
            c.execute(
                "SELECT COUNT(pokemon) FROM catch_log where pokemon = '{}' and  datetime(dated, 'localtime') > Datetime('{}')"
                .format(self.destination["name"],
                        self.hunt_started_at.strftime("%Y-%m-%d %H:%M:%S")))
        # Now check if there is 1 or more caught
        amount = c.fetchone()[0]
        caught = amount > 0
        if caught:
            self.logger.info("We caught {} {}(s) since {}".format(
                amount, self.destination["name"],
                self.hunt_started_at.strftime("%Y-%m-%d %H:%M:%S")))

        return caught