Skip to content

Commit

Permalink
Refactoring the StepWalker to support 0.0 and float(inf)
Browse files Browse the repository at this point in the history
  • Loading branch information
th3w4y committed Sep 2, 2016
1 parent 865832a commit 582e793
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 108 deletions.
53 changes: 38 additions & 15 deletions pokemongo_bot/test/follow_cluster_test.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,43 @@
import unittest, pickle, os
from mock import patch
from mock import MagicMock, patch
from pokemongo_bot.cell_workers.follow_cluster import FollowCluster


class FollowClusterTestCase(unittest.TestCase):

@patch('pokemongo_bot.PokemonGoBot')
def testWorkAway(self, mock_pokemongo_bot):
def setUp(self):
self.patcherSleep = patch('pokemongo_bot.walkers.step_walker.sleep')
self.patcherRandomLat = patch('pokemongo_bot.walkers.step_walker.random_lat_long_delta', return_value=0)
self.patcherSleep.start()
self.patcherRandomLat.start()

self.bot = MagicMock()
self.bot.position = [0, 0, 0]
self.bot.api = MagicMock()

self.lat, self.lng, self.alt = 0, 0, 0

# let us get back the position set by the StepWalker
def api_set_position(lat, lng, alt):
self.lat, self.lng, self.alt = lat, lng, alt
self.bot.api.set_position = api_set_position

def tearDown(self):
self.patcherSleep.stop()
self.patcherRandomLat.stop()

def testWorkAway(self):
forts_path = os.path.join(os.path.dirname(__file__), 'resources', 'example_forts.pickle')
with open(forts_path, 'rb') as forts:
ex_forts = pickle.load(forts)
config = {'radius': 50, 'lured': False}
mock_pokemongo_bot.position = (37.396787, -5.994587)
mock_pokemongo_bot.config.walk_max = 4.16
mock_pokemongo_bot.config.walk_min = 2.16
mock_pokemongo_bot.get_forts.return_value = ex_forts
follow_cluster = FollowCluster(mock_pokemongo_bot, config)
self.bot.position = [37.396787, -5.994587, 0.0]
self.bot.config.walk_max = 4.16
self.bot.config.walk_min = 2.16
self.bot.config.alt_max = 15.0
self.bot.config.alt_min = 10.0
self.bot.get_forts.return_value = ex_forts
follow_cluster = FollowCluster(self.bot, config)

expected = (37.397183750142624, -5.9932912500000013)
result = follow_cluster.work()
Expand All @@ -24,17 +46,18 @@ def testWorkAway(self, mock_pokemongo_bot):
assert follow_cluster.is_at_destination == False
assert follow_cluster.announced == False

@patch('pokemongo_bot.PokemonGoBot')
def testWorkArrived(self, mock_pokemongo_bot):
def testWorkArrived(self):
forts_path = os.path.join(os.path.dirname(__file__), 'resources', 'example_forts.pickle')
with open(forts_path, 'rb') as forts:
ex_forts = pickle.load(forts)
config = {'radius': 50, 'lured': False}
mock_pokemongo_bot.position = (37.39718375014263, -5.9932912500000013)
mock_pokemongo_bot.config.walk_max = 4.16
mock_pokemongo_bot.config.walk_min = 2.16
mock_pokemongo_bot.get_forts.return_value = ex_forts
follow_cluster = FollowCluster(mock_pokemongo_bot, config)
self.bot.position = [37.39718375014263, -5.9932912500000013, 0.0]
self.bot.config.walk_max = 4.16
self.bot.config.walk_min = 2.16
self.bot.config.alt_max = 15.0
self.bot.config.alt_min = 10.0
self.bot.get_forts.return_value = ex_forts
follow_cluster = FollowCluster(self.bot, config)

expected = (37.397183750142624, -5.9932912500000013)
result = follow_cluster.work()
Expand Down
49 changes: 31 additions & 18 deletions pokemongo_bot/test/step_walker_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ def test_normalized_distance(self):
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)
sw = StepWalker(self.bot, 0.1, 0.1, 0.1)
self.assertGreater(sw.dest_lat, 0)
self.assertGreater(sw.dest_lng, 0)

stayInPlace = sw.step()
self.assertFalse(stayInPlace)
Expand All @@ -55,9 +55,9 @@ def test_normalized_distance_times_2(self):
self.bot.config.walk_max = 2
self.bot.config.walk_min = 2

sw = StepWalker(self.bot, 0.1, 0.1)
self.assertTrue(sw.dLat > 0)
self.assertTrue(sw.dLng > 0)
sw = StepWalker(self.bot, 0.1, 0.1, 0.1)
self.assertTrue(sw.dest_lat > 0)
self.assertTrue(sw.dest_lng > 0)

stayInPlace = sw.step()
self.assertFalse(stayInPlace)
Expand All @@ -75,9 +75,9 @@ def test_small_distance_same_spot(self):
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')
sw = StepWalker(self.bot, 0, 0, 0)
self.assertEqual(sw.dest_lat, 0, 'dest_lat should be 0')
self.assertEqual(sw.dest_lng, 0, 'dest_lng should be 0')

self.assertTrue(sw.step(), 'step should return True')
self.assertTrue(self.lat == self.bot.position[0])
Expand All @@ -93,17 +93,30 @@ def test_small_distance_small_step(self):
self.bot.config.walk_max = 1
self.bot.config.walk_min = 1

sw = StepWalker(self.bot, 1e-5, 1e-5)
self.assertEqual(sw.dLat, 0)
self.assertEqual(sw.dLng, 0)
# If we want to test if bot stays put on small distance then with speed =1
# we need a distance smaller then 1e-5 that was previously set...
# distance between (0.0,0.0) and (1e-5,1e-5)
# haversine.haversine((0.0, 0.0), (1e-5, 1e-5)) * 1000 = 1.5725337332781602 meters
# thus is bigger then the speed
sw = StepWalker(self.bot, 1e-6, 1e-6, 1e-6)
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

@unittest.skip('This behavior is To Be Defined')

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
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, 10, 10, 10)
stayInPlace = sw.step()
self.assertFalse(stayInPlace)

self.bot.config.walk_max = walk_max
self.bot.config.walk_min = walk_min
172 changes: 97 additions & 75 deletions pokemongo_bot/walkers/step_walker.py
Original file line number Diff line number Diff line change
@@ -1,108 +1,130 @@
from math import sqrt

from random import uniform

from pokemongo_bot.cell_workers.utils import distance
from pokemongo_bot.human_behaviour import random_lat_long_delta, sleep, random_alt_delta


class StepWalker(object):

def __init__(self, bot, dest_lat, dest_lng, dest_alt=None, fixed_speed=False):
"""
StepWalker is used to handle bot position altering
"""

def __init__(self, bot, dest_lat, dest_lng, dest_alt=None,
fixed_speed=None, add_deltas=True,
stationary_deltas=False, stationary_updates=False):
"""
:param bot: PokemonGoBot object
:param dest_lat: Latitude of final destination
:param dest_lng: Longitude of final destination
:param dest_alt: Altitude of final destination (meters)
:param fixed_speed: Fix the speed to particular values
Default: None thus a random value between walk_min and walk_max is used
(Used by PolylineWalker to pass same values from the PolylineObjectHandler
Use 0.0 to stay put.
Use float("inf") to teleport
:param add_deltas: Add random_lat_long_delta() and random_alt_delta() to lat,lng, alt Default: True
:param stationary_deltas: Use the deltas in the case of speed 0.0 or small distance Default: False
:param stationary_updates: Force the StepWalker to set_position even on small distance and speed 0.0 Default: False
"""
self.bot = bot
self.api = bot.api

self.initLat, self.initLng = self.bot.position[0:2]
self.add_deltas = add_deltas
self.stationary_deltas = stationary_deltas
self.stationary_updates = stationary_updates
self.init_lat, self.init_lng, self.init_alt = self.bot.position

self.dist = distance(
self.initLat,
self.initLng,
self.init_lat,
self.init_lng,
dest_lat,
dest_lng
)

if dest_alt == None:
if dest_alt is None:
self.alt = uniform(self.bot.config.alt_min, self.bot.config.alt_max)
else:
self.alt = dest_alt

if not fixed_speed:
if fixed_speed is None:
self.speed = uniform(self.bot.config.walk_min, self.bot.config.walk_max)
else:
# PolylineWalker uses a fixed speed!
self.speed = self.bot.config.walk_min
# Use this to pass custom values from 0.0 to stay put to float("inf") to teleport
self.speed = fixed_speed

if len(self.bot.position) == 3:
self.initAlt = self.bot.position[2]
else:
self.initAlt = self.alt;

self.destLat = dest_lat
self.destLng = dest_lng
self.totalDist = max(1, self.dist)
self.dest_lat = dest_lat
self.dest_lng = dest_lng
# If distance <= speed we consider the that distance = speed
# Will pe used for percentage_walked calculations
self.distance = max(self.dist, self.speed)

def step(self):
"""
Sets the bot.position to the next calculated point according to the speed and distance
:return: boolean True/False reflecting if the full distance to final destination was walked in the step.
"""
completed_walk = False
# If speed 0.0 we stay put and we don't add deltas
if self.speed == 0:
raise Exception("Walking speed cannot be 0, change your walking speed higher than 1!")
else:
self.steps = (self.dist + 0.0) / (self.speed + 0.0)

if self.dist < self.speed or int(self.steps) <= 1:
self.dLat = 0
self.dLng = 0
self.magnitude = 0
completed_walk = True
if self.stationary_updates:
return self.set_position(self.init_lat, self.init_lng, self.alt,
add_deltas=self.stationary_deltas,
completed_walk=completed_walk)
# If speed is infinite we teleport
if self.speed == float("inf"):
completed_walk = True
return self.set_position(self.dest_lat, self.dest_lng, self.alt,
add_deltas=self.add_deltas,
completed_walk=completed_walk)
# If distance <= speed percentage = 1
# else the percentage_walked is between 0 and 1
percentage_walked = self.speed / self.distance
if percentage_walked == 1:
completed_walk = True
lat = self.init_lat + (self.dest_lat - self.init_lat) * percentage_walked
lng = self.init_lng + (self.dest_lng - self.init_lng ) * percentage_walked
alt = self.init_alt + (self.alt - self.init_alt) * percentage_walked
if self.dist >= self.speed:
return self.set_position(lat, lng, alt,
add_deltas=self.add_deltas,
completed_walk=completed_walk)
else:
self.dLat = (dest_lat - self.initLat) / int(self.steps)
self.dLng = (dest_lng - self.initLng) / int(self.steps)
self.magnitude = self._pythagorean(self.dLat, self.dLng)
self.unitAlt = (self.alt - self.initAlt) / int(self.steps)

def step(self):
if (self.dLat == 0 and self.dLng == 0) or self.dist < self.speed:
self.api.set_position(self.destLat + random_lat_long_delta(), self.destLng + random_lat_long_delta(), self.alt)
self.bot.event_manager.emit(
'position_update',
sender=self,
level='debug',
data={
'current_position': (self.destLat, self.destLng),
'last_position': (self.initLat, self.initLng),
'distance': '',
'distance_unit': ''
}
)
self.bot.heartbeat()
return True

totalDLat = (self.destLat - self.initLat)
totalDLng = (self.destLng - self.initLng)
magnitude = self._pythagorean(totalDLat, totalDLng)
unitLat = totalDLat / magnitude
unitLng = totalDLng / magnitude

scaledDLat = unitLat * self.magnitude
scaledDLng = unitLng * self.magnitude

cLat = self.initLat + scaledDLat + random_lat_long_delta()
cLng = self.initLng + scaledDLng + random_lat_long_delta()
cAlt = self.initAlt + self.unitAlt + random_alt_delta()

self.api.set_position(cLat, cLng, cAlt)
if self.stationary_updates:
return self.set_position(lat, lng, alt,
add_deltas=self.stationary_deltas,
completed_walk=completed_walk)
else:
return True

def set_position(self, lat, lng, alt, add_deltas=True, completed_walk=False):
"""
:param lat: Latitude of the calculated position
:param lng: longitude of the calculated position
:param alt: Altitde of the calculated position
:param add_deltas: Add random_lat_long_delta() and random_alt_delta() to lat,lng, alt Default: True
:param completed_walk: boolean reflecting if the distance was fully walked or not Default: False
:return: boolean True/False reflecting if the full distance to final destination was walked in the step.
"""
if add_deltas:
lat += random_lat_long_delta()
lng += random_lat_long_delta()
alt += random_alt_delta()
self.api.set_position(lat, lng, alt)
self.bot.event_manager.emit(
'position_update',
sender=self,
level='debug',
data={
'current_position': (cLat, cLng, cAlt),
'last_position': (self.initLat, self.initLng, self.initAlt),
'current_position': (lat, lng, alt),
'last_position': (self.init_lat, self.init_lng, self.init_alt),
'distance': '',
'distance_unit': ''
}
)
self.bot.heartbeat()

sleep(1) # sleep one second plus a random delta
# self._work_at_position(
# self.initLat, self.initLng,
# alt, False)

def _pythagorean(self, lat, lng):
return sqrt((lat ** 2) + (lng ** 2))
if completed_walk:
return True

0 comments on commit 582e793

Please sign in to comment.