From 586a0dcf3dd06daa0c800bccf9d340902c8f91e8 Mon Sep 17 00:00:00 2001 From: Douglas Camata Date: Mon, 25 Jul 2016 07:01:25 +0200 Subject: [PATCH] big refactor to Stepper class (#737) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * big refactor to Stepper class We now have a: - StepWalker class, responsible for walking a small distance towards a final destination. - SpiralNavigator class, which around an area in a spiral. It determines points to walk to and uses to StepWalker to slowly go from one point to another. This architecture enables us to have different Navigators, although it doesn’t implement a nice Navigator configuration YET. * small fixes to workers * Merging * Fixing arguments * Moving the distance log line --- pokemongo_bot/__init__.py | 49 +++++- .../cell_workers/move_to_fort_worker.py | 5 +- .../cell_workers/seen_fort_worker.py | 1 - pokemongo_bot/cell_workers/utils.py | 20 +++ pokemongo_bot/spiral_navigator.py | 70 ++++++++ pokemongo_bot/step_walker.py | 29 ++++ pokemongo_bot/stepper.py | 158 ------------------ 7 files changed, 164 insertions(+), 168 deletions(-) create mode 100644 pokemongo_bot/spiral_navigator.py create mode 100644 pokemongo_bot/step_walker.py delete mode 100644 pokemongo_bot/stepper.py diff --git a/pokemongo_bot/__init__.py b/pokemongo_bot/__init__.py index d6df1a38d7..410bdac704 100644 --- a/pokemongo_bot/__init__.py +++ b/pokemongo_bot/__init__.py @@ -12,10 +12,12 @@ import logger import re from pgoapi import PGoApi +from pgoapi.utilities import f2i, h2f from cell_workers import PokemonCatchWorker, SeenFortWorker, MoveToFortWorker, InitialTransferWorker, EvolveAllWorker -from cell_workers.utils import distance +from cell_workers.utils import distance, get_cellid, encode +from step_walker import StepWalker from human_behaviour import sleep -from stepper import Stepper +from spiral_navigator import SpiralNavigator from geopy.geocoders import GoogleV3 from math import radians, sqrt, sin, cos, atan2 from item_list import Item @@ -30,13 +32,45 @@ def __init__(self, config): def start(self): self._setup_logging() self._setup_api() - self.stepper = Stepper(self) + self.step_walker = StepWalker(self) + self.navigator = SpiralNavigator(self) random.seed() def take_step(self): - self.stepper.take_step() - - def work_on_cell(self, cell, position, include_fort_on_path): + location = self.navigator.take_step() + cells = self.find_close_cells(*location) + + for cell in cells: + self.work_on_cell(cell, location) + + def find_close_cells(self, lat, lng): + cellid = get_cellid(lat, lng) + timestamp = [0, ] * len(cellid) + + self.api.get_map_objects( + latitude=f2i(lat), + longitude=f2i(lng), + since_timestamp_ms=timestamp, + cell_id=cellid + ) + response_dict = self.api.call() + map_objects = response_dict.get('responses', {}).get('GET_MAP_OBJECTS', {}) + status = map_objects.get('status', None) + + map_cells = [] + if status and status == 1: + map_cells = map_objects['map_cells'] + position = (lat, lng, 0) + map_cells.sort( + key=lambda x: distance( + lat, + lng, + x['forts'][0]['latitude'], + x['forts'][0]['longitude']) if x.get('forts', []) else 1e6 + ) + return map_cells + + def work_on_cell(self, cell, position): if self.config.evolve_all: # Run evolve all once. Flip the bit. print('[#] Attempting to evolve all pokemons ...') @@ -78,7 +112,7 @@ def work_on_cell(self, cell, position, include_fort_on_path): if self.catch_pokemon(pokemon) == PokemonCatchWorker.NO_POKEBALLS: break if (self.config.mode == "all" or - self.config.mode == "farm") and include_fort_on_path: + self.config.mode == "farm"): if 'forts' in cell: # Only include those with a lat/long forts = [fort @@ -90,6 +124,7 @@ def work_on_cell(self, cell, position, include_fort_on_path): # build graph & A* it forts.sort(key=lambda x: distance(self.position[ 0], self.position[1], x['latitude'], x['longitude'])) + for fort in forts: worker = MoveToFortWorker(fort, self) worker.work() diff --git a/pokemongo_bot/cell_workers/move_to_fort_worker.py b/pokemongo_bot/cell_workers/move_to_fort_worker.py index 3af7befcd3..ffbdd934ac 100644 --- a/pokemongo_bot/cell_workers/move_to_fort_worker.py +++ b/pokemongo_bot/cell_workers/move_to_fort_worker.py @@ -7,7 +7,8 @@ def __init__(self, fort, bot): self.fort = fort self.api = bot.api self.config = bot.config - self.stepper = bot.stepper + self.navigator = bot.navigator + self.step_walker = bot.step_walker self.position = bot.position def work(self): @@ -27,7 +28,7 @@ def work(self): position = (lat, lng, 0.0) if self.config.walk > 0: - self.stepper._walk_to(self.config.walk, *position) + self.step_walker.step(self.config.walk, *position[0:2]) else: self.api.set_position(*position) diff --git a/pokemongo_bot/cell_workers/seen_fort_worker.py b/pokemongo_bot/cell_workers/seen_fort_worker.py index a1faafa66d..03f565da9c 100644 --- a/pokemongo_bot/cell_workers/seen_fort_worker.py +++ b/pokemongo_bot/cell_workers/seen_fort_worker.py @@ -18,7 +18,6 @@ def __init__(self, fort, bot): self.config = bot.config self.item_list = bot.item_list self.rest_time = 50 - self.stepper = bot.stepper def work(self): lat = self.fort['latitude'] diff --git a/pokemongo_bot/cell_workers/utils.py b/pokemongo_bot/cell_workers/utils.py index bd31375ccc..4d866e6dde 100644 --- a/pokemongo_bot/cell_workers/utils.py +++ b/pokemongo_bot/cell_workers/utils.py @@ -3,9 +3,29 @@ import struct from math import cos, asin, sqrt from colorama import init +from s2sphere import CellId, LatLng init() +def get_cellid(lat, long, radius=10): + origin = CellId.from_lat_lng(LatLng.from_degrees(lat, long)).parent(15) + walk = [origin.id()] + + # 10 before and 10 after + next = origin.next() + prev = origin.prev() + for i in range(radius): + walk.append(prev.id()) + walk.append(next.id()) + next = next.next() + prev = prev.prev() + return sorted(walk) + +def encode(cellid): + output = [] + encoder._VarintEncoder()(output.append, cellid) + return ''.join(output) + def distance(lat1, lon1, lat2, lon2): p = 0.017453292519943295 a = 0.5 - cos((lat2 - lat1) * p) / 2 + cos(lat1 * p) * \ diff --git a/pokemongo_bot/spiral_navigator.py b/pokemongo_bot/spiral_navigator.py new file mode 100644 index 0000000000..2ef34d2f23 --- /dev/null +++ b/pokemongo_bot/spiral_navigator.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- + +import os +import json +import time +import pprint + +from math import ceil +from s2sphere import CellId, LatLng +from google.protobuf.internal import encoder + +from human_behaviour import sleep, random_lat_long_delta +from cell_workers.utils import distance, i2f, format_time, format_dist + +from pgoapi.utilities import f2i, h2f +import logger + + +class SpiralNavigator(object): + def __init__(self, bot): + self.bot = bot + self.api = bot.api + self.config = bot.config + + self.pos = 1 + self.x = 0 + self.y = 0 + self.dx = 0 + self.dy = -1 + self.steplimit = self.config.max_steps + self.steplimit2 = self.steplimit**2 + self.origin_lat = self.bot.position[0] + self.origin_lon = self.bot.position[1] + + def take_step(self): + position = (self.origin_lat, self.origin_lon, 0.0) + + logger.log('[#] Scanning area for objects....') + # logger.log('[#] Scanning area for objects ({} / {})'.format( + # (step + 1), self.steplimit**2)) + if self.config.debug: + logger.log( + 'steplimit: {} x: {} y: {} pos: {} dx: {} dy {}'.format( + self.steplimit2, self.x, self.y, self.pos, self.dx, + self.dy)) + # Scan location math + + if -self.steplimit2 / 2 < self.x <= self.steplimit2 / 2 and -self.steplimit2 / 2 < self.y <= self.steplimit2 / 2: + position = (self.x * 0.0025 + self.origin_lat, + self.y * 0.0025 + self.origin_lon, 0) + if self.config.walk > 0: + + dist = distance( + i2f(self.api._position_lat), + i2f(self.api._position_lng), + position[0], + position[1] + ) + + logger.log('[#] Walking from ' + str((i2f(self.api._position_lat), i2f( + self.api._position_lng))) + " to " + str((str(position[0:2]))) + " " + format_dist(dist, self.config.distance_unit)) + self.bot.step_walker.step(self.config.walk, *position[0:2]) + else: + self.api.set_position(*position) + if self.x == self.y or self.x < 0 and self.x == -self.y or self.x > 0 and self.x == 1 - self.y: + (self.dx, self.dy) = (-self.dy, self.dx) + + (self.x, self.y) = (self.x + self.dx, self.y + self.dy) + sleep(10) + return position[0:2] diff --git a/pokemongo_bot/step_walker.py b/pokemongo_bot/step_walker.py new file mode 100644 index 0000000000..efc284e320 --- /dev/null +++ b/pokemongo_bot/step_walker.py @@ -0,0 +1,29 @@ +import logger + +from cell_workers.utils import distance, i2f, format_time +from human_behaviour import random_lat_long_delta, sleep +from math import ceil + + +class StepWalker(object): + + def __init__(self, bot): + self.bot = bot + self.api = bot.api + + def step(self, speed, lat, lng): + if self.api._position_lat == lat and self.api._position_lng == lng: + return True + + dLat = (lat - i2f(self.api._position_lat)) + dLng = (lng - i2f(self.api._position_lng)) + + cLat = i2f(self.api._position_lat) + dLat + random_lat_long_delta() + cLng = i2f(self.api._position_lng) + dLng + random_lat_long_delta() + + self.api.set_position(cLat, cLng, 0) + self.bot.heartbeat() + sleep(1) # sleep one second plus a random delta + # self._work_at_position( + # i2f(self.api._position_lat), i2f(self.api._position_lng), + # alt, False) diff --git a/pokemongo_bot/stepper.py b/pokemongo_bot/stepper.py deleted file mode 100644 index fda5e63fb8..0000000000 --- a/pokemongo_bot/stepper.py +++ /dev/null @@ -1,158 +0,0 @@ -# -*- coding: utf-8 -*- - -import os -import json -import time -import pprint - -from math import ceil -from s2sphere import CellId, LatLng -from google.protobuf.internal import encoder - -from human_behaviour import sleep, random_lat_long_delta -from cell_workers.utils import distance, i2f, format_time - -from pgoapi.utilities import f2i, h2f -import logger - - -class Stepper(object): - def __init__(self, bot): - self.bot = bot - self.api = bot.api - self.config = bot.config - - self.pos = 1 - self.x = 0 - self.y = 0 - self.dx = 0 - self.dy = -1 - self.steplimit = self.config.max_steps - self.steplimit2 = self.steplimit**2 - self.origin_lat = self.bot.position[0] - self.origin_lon = self.bot.position[1] - - def take_step(self): - position = (self.origin_lat, self.origin_lon, 0.0) - - self.api.set_position(*position) - for step in range(self.steplimit2): - # starting at 0 index - logger.log('[#] Scanning area for objects ({} / {})'.format( - (step + 1), self.steplimit**2)) - if self.config.debug: - logger.log( - 'steplimit: {} x: {} y: {} pos: {} dx: {} dy {}'.format( - self.steplimit2, self.x, self.y, self.pos, self.dx, - self.dy)) - # Scan location math - if -self.steplimit2 / 2 < self.x <= self.steplimit2 / 2 and -self.steplimit2 / 2 < self.y <= self.steplimit2 / 2: - position = (self.x * 0.0025 + self.origin_lat, - self.y * 0.0025 + self.origin_lon, 0) - if self.config.walk > 0: - self._walk_to(self.config.walk, *position) - else: - self.api.set_position(*position) - print('[#] {}'.format(position)) - if self.x == self.y or self.x < 0 and self.x == -self.y or self.x > 0 and self.x == 1 - self.y: - (self.dx, self.dy) = (-self.dy, self.dx) - - (self.x, self.y) = (self.x + self.dx, self.y + self.dy) - - self._work_at_position(position[0], position[1], position[2], True) - sleep(10) - - def _walk_to(self, speed, lat, lng, alt): - dist = distance( - i2f(self.api._position_lat), i2f(self.api._position_lng), lat, lng) - steps = (dist + 0.0) / (speed + 0.0) # may be rational number - intSteps = int(steps) - residuum = steps - intSteps - logger.log('[#] Walking from ' + str((i2f(self.api._position_lat), i2f( - self.api._position_lng))) + " to " + str(str((lat, lng))) + - " for approx. " + str(format_time(ceil(steps)))) - if steps != 0: - dLat = (lat - i2f(self.api._position_lat)) / steps - dLng = (lng - i2f(self.api._position_lng)) / steps - - for i in range(intSteps): - cLat = i2f(self.api._position_lat) + \ - dLat + random_lat_long_delta() - cLng = i2f(self.api._position_lng) + \ - dLng + random_lat_long_delta() - self.api.set_position(cLat, cLng, alt) - self.bot.heartbeat() - sleep(1) # sleep one second plus a random delta - self._work_at_position( - i2f(self.api._position_lat), i2f(self.api._position_lng), - alt, False) - - self.api.set_position(lat, lng, alt) - self.bot.heartbeat() - logger.log("[#] Finished walking") - - def _work_at_position(self, lat, lng, alt, pokemon_only=False): - cellid = self._get_cellid(lat, lng) - timestamp = [0, ] * len(cellid) - self.api.get_map_objects(latitude=f2i(lat), - longitude=f2i(lng), - since_timestamp_ms=timestamp, - cell_id=cellid) - - response_dict = self.api.call() - # pprint.pprint(response_dict) - # Passing Variables through a file - if response_dict and 'responses' in response_dict: - if 'GET_MAP_OBJECTS' in response_dict['responses']: - if 'map_cells' in response_dict['responses'][ - 'GET_MAP_OBJECTS']: - user_web_location = 'web/location-%s.json' % (self.config.username) - if os.path.isfile(user_web_location): - with open(user_web_location, 'w') as outfile: - json.dump( - {'lat': lat, - 'lng': lng, - 'cells': response_dict[ - 'responses']['GET_MAP_OBJECTS']['map_cells']}, - outfile) - - user_data_lastlocation = 'data/last-location-%s.json' % (self.config.username) - if os.path.isfile(user_data_lastlocation): - with open(user_data_lastlocation, 'w') as outfile: - outfile.truncate() - json.dump({'lat': lat, 'lng': lng}, outfile) - - if response_dict and 'responses' in response_dict: - if 'GET_MAP_OBJECTS' in response_dict['responses']: - if 'status' in response_dict['responses']['GET_MAP_OBJECTS']: - if response_dict['responses']['GET_MAP_OBJECTS'][ - 'status'] is 1: - map_cells = response_dict['responses'][ - 'GET_MAP_OBJECTS']['map_cells'] - position = (lat, lng, alt) - # Sort all by distance from current pos- eventually this should build graph & A* it - # print(map_cells) - #print( s2sphere.from_token(x['s2_cell_id']) ) - map_cells.sort(key=lambda x: distance(lat, lng, x['forts'][0]['latitude'], x[ - 'forts'][0]['longitude']) if 'forts' in x and x['forts'] != [] else 1e6) - for cell in map_cells: - self.bot.work_on_cell(cell, position, pokemon_only) - - def _get_cellid(self, lat, long, radius=10): - origin = CellId.from_lat_lng(LatLng.from_degrees(lat, long)).parent(15) - walk = [origin.id()] - - # 10 before and 10 after - next = origin.next() - prev = origin.prev() - for i in range(radius): - walk.append(prev.id()) - walk.append(next.id()) - next = next.next() - prev = prev.prev() - return sorted(walk) - - def _encode(self, cellid): - output = [] - encoder._VarintEncoder()(output.append, cellid) - return ''.join(output)