Skip to content

Commit

Permalink
Better enforce rules about Pokemons to retain. (PokemonGoF#2073)
Browse files Browse the repository at this point in the history
Rules about maximum CP and IV to transfer Pokemons are applied
also when keeping the best ones.
  • Loading branch information
giomasce authored and johnbuluba committed Aug 1, 2016
1 parent 248ac53 commit 1ea32ca
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 5 deletions.
3 changes: 2 additions & 1 deletion pokemongo_bot/cell_workers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@
from follow_spiral import FollowSpiral
from collect_level_up_reward import CollectLevelUpReward
from base_task import BaseTask
from follow_cluster import FollowCluster
from follow_cluster import FollowCluster
from sleeper import Sleeper
98 changes: 98 additions & 0 deletions pokemongo_bot/cell_workers/sleeper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
from datetime import datetime, timedelta
from time import sleep
from random import uniform
from pokemongo_bot import logger
from pokemongo_bot.cell_workers.base_task import BaseTask


class Sleeper(BaseTask):
"""Pauses the execution of the bot every day for some time
Simulates the user going to sleep every day for some time, the sleep time
and the duration is changed every day by a random offset defined in the
config file
Example Config:
{
"type": "Sleeper",
"config": {
"time": "12:00",
"duration":"5:30",
"time_random_offset": "00:30",
"duration_random_offset": "00:30"
}
}
time: (HH:MM) local time that the bot should sleep
duration: (HH:MM) the duration of sleep
time_random_offset: (HH:MM) random offset of time that the sleep will start
for this example the possible start time is 11:30-12:30
duration_random_offset: (HH:MM) random offset of duration of sleep
for this example the possible duration is 5:00-6:00
"""

LOG_INTERVAL_SECONDS = 600
SCHEDULING_MARGIN = timedelta(minutes=10) # Skip if next sleep is RESCHEDULING_MARGIN from now

def initialize(self):
# self.bot.event_manager.register_event('sleeper_scheduled', parameters=('datetime',))
self._process_config()
self._schedule_next_sleep()

def work(self):
if datetime.now() >= self._next_sleep:
self._sleep()
self._schedule_next_sleep()
self.bot.login()

def _process_config(self):
self.time = datetime.strptime(self.config.get('time', '01:00'), '%H:%M')

# Using datetime for easier stripping of timedeltas
duration = datetime.strptime(self.config.get('duration', '07:00'), '%H:%M')
self.duration = int(timedelta(hours=duration.hour, minutes=duration.minute).total_seconds())

time_random_offset = datetime.strptime(self.config.get('time_random_offset', '01:00'), '%H:%M')
self.time_random_offset = int(
timedelta(
hours=time_random_offset.hour, minutes=time_random_offset.minute).total_seconds())

duration_random_offset = datetime.strptime(self.config.get('duration_random_offset', '00:30'), '%H:%M')
self.duration_random_offset = int(
timedelta(
hours=duration_random_offset.hour, minutes=duration_random_offset.minute).total_seconds())

def _schedule_next_sleep(self):
self._next_sleep = self._get_next_sleep_schedule()
self._next_duration = self._get_next_duration()
logger.log('Sleeper: next sleep at {}'.format(str(self._next_sleep)), color='green')

def _get_next_sleep_schedule(self):
now = datetime.now() + self.SCHEDULING_MARGIN
next_time = now.replace(hour=self.time.hour, minute=self.time.minute)

next_time += timedelta(seconds=self._get_random_offset(self.time_random_offset))

# If sleep time is passed add one day
if next_time <= now:
next_time += timedelta(days=1)

return next_time

def _get_next_duration(self):
duration = self.duration + self._get_random_offset(self.duration_random_offset)
return duration

def _get_random_offset(self, max_offset):
offset = uniform(-max_offset, max_offset)
return int(offset)

def _sleep(self):
sleep_to_go = self._next_duration
logger.log('It\'s time for sleep.')
while sleep_to_go > 0:
logger.log('Sleeping for {} more seconds'.format(sleep_to_go), 'yellow')
if sleep_to_go < self.LOG_INTERVAL_SECONDS:
sleep(sleep_to_go)
sleep_to_go = 0
else:
sleep(self.LOG_INTERVAL_SECONDS)
sleep_to_go -= self.LOG_INTERVAL_SECONDS
13 changes: 9 additions & 4 deletions pokemongo_bot/cell_workers/transfer_pokemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ def work(self):
all_pokemons.remove(pokemon)
best_pokemons.append(pokemon)

if best_pokemons and all_pokemons:
transfer_pokemons = [pokemon for pokemon in all_pokemons
if self.should_release_pokemon(pokemon_name,
pokemon['cp'],
pokemon['iv'])]

if transfer_pokemons:
logger.log("Keep {} best {}, based on {}".format(len(best_pokemons),
pokemon_name,
order_criteria), "green")
Expand All @@ -50,10 +55,10 @@ def work(self):
best_pokemon['cp'],
best_pokemon['iv']), 'green')

logger.log("Transferring {} pokemon".format(len(all_pokemons)), "green")
logger.log("Transferring {} pokemon".format(len(transfer_pokemons)), "green")

for pokemon in all_pokemons:
self.release_pokemon(pokemon_name, pokemon['cp'], pokemon['iv'], pokemon['pokemon_data']['id'])
for pokemon in transfer_pokemons:
self.release_pokemon(pokemon_name, pokemon['cp'], pokemon['iv'], pokemon['pokemon_data']['id'])
else:
group = sorted(group, key=lambda x: x['cp'], reverse=True)
for item in group:
Expand Down
107 changes: 107 additions & 0 deletions pokemongo_bot/test/sleeper_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import unittest
from datetime import timedelta, datetime
from mock import patch, MagicMock
from pokemongo_bot.cell_workers.sleeper import Sleeper
from tests import FakeBot


class SleeperTestCase(unittest.TestCase):
config = {'time': '12:20', 'duration': '01:05', 'time_random_offset': '00:05', 'duration_random_offset': '00:05'}

def setUp(self):
self.bot = FakeBot()
self.worker = Sleeper(self.bot, self.config)

def test_config(self):
self.assertEqual(self.worker.time.hour, 12)
self.assertEqual(self.worker.time.minute, 20)
self.assertEqual(self.worker.duration, timedelta(hours=1, minutes=5).total_seconds())
self.assertEqual(self.worker.time_random_offset, timedelta(minutes=5).total_seconds())
self.assertEqual(self.worker.duration_random_offset, timedelta(minutes=5).total_seconds())

@patch('pokemongo_bot.cell_workers.sleeper.datetime')
def test_get_next_time(self, mock_datetime):
mock_datetime.now.return_value = datetime(year=2016, month=8, day=01, hour=8, minute=0)

next_time = self.worker._get_next_sleep_schedule()
from_date = datetime(year=2016, month=8, day=1, hour=12, minute=15)
to_date = datetime(year=2016, month=8, day=1, hour=12, minute=25)

self.assertGreaterEqual(next_time, from_date)
self.assertLessEqual(next_time, to_date)

@patch('pokemongo_bot.cell_workers.sleeper.datetime')
def test_get_next_time_called_near_activation_time(self, mock_datetime):
mock_datetime.now.return_value = datetime(year=2016, month=8, day=1, hour=12, minute=25)

next = self.worker._get_next_sleep_schedule()
from_date = datetime(year=2016, month=8, day=02, hour=12, minute=15)
to_date = datetime(year=2016, month=8, day=02, hour=12, minute=25)

self.assertGreaterEqual(next, from_date)
self.assertLessEqual(next, to_date)

@patch('pokemongo_bot.cell_workers.sleeper.datetime')
def test_get_next_time_called_when_this_days_time_passed(self, mock_datetime):
mock_datetime.now.return_value = datetime(year=2016, month=8, day=1, hour=14, minute=0)

next = self.worker._get_next_sleep_schedule()
from_date = datetime(year=2016, month=8, day=02, hour=12, minute=15)
to_date = datetime(year=2016, month=8, day=02, hour=12, minute=25)

self.assertGreaterEqual(next, from_date)
self.assertLessEqual(next, to_date)

def test_get_next_duration(self):
from_seconds = int(timedelta(hours=1).total_seconds())
to_seconds = int(timedelta(hours=1, minutes=10).total_seconds())

duration = self.worker._get_next_duration()

self.assertGreaterEqual(duration, from_seconds)
self.assertLessEqual(duration, to_seconds)

@patch('pokemongo_bot.cell_workers.sleeper.sleep')
def test_sleep(self, mock_sleep):
self.worker._next_duration = Sleeper.LOG_INTERVAL_SECONDS * 10
self.worker._sleep()
#Sleep should be called 10 times with LOG_INTERVAL_SECONDS as argument
self.assertEqual(mock_sleep.call_count, 10)
calls = [x[0][0] for x in mock_sleep.call_args_list]
for arg in calls:
self.assertEqual(arg, Sleeper.LOG_INTERVAL_SECONDS)

@patch('pokemongo_bot.cell_workers.sleeper.sleep')
def test_sleep_not_divedable_by_interval(self, mock_sleep):
self.worker._next_duration = Sleeper.LOG_INTERVAL_SECONDS * 10 + 5
self.worker._sleep()
self.assertEqual(mock_sleep.call_count, 11)

calls = [x[0][0] for x in mock_sleep.call_args_list]
for arg in calls[:-1]:
self.assertEqual(arg, Sleeper.LOG_INTERVAL_SECONDS)
#Last call must be 5
self.assertEqual(calls[-1], 5)

@patch('pokemongo_bot.cell_workers.sleeper.sleep')
@patch('pokemongo_bot.cell_workers.sleeper.datetime')
def test_call_work_before_schedule(self, mock_datetime, mock_sleep):
self.worker._next_sleep = datetime(year=2016, month=8, day=1, hour=12, minute=0)
mock_datetime.now.return_value = self.worker._next_sleep - timedelta(minutes=5)

self.worker.work()

self.assertEqual(mock_sleep.call_count, 0)

@patch('pokemongo_bot.cell_workers.sleeper.sleep')
@patch('pokemongo_bot.cell_workers.sleeper.datetime')
def test_call_work_after_schedule(self, mock_datetime, mock_sleep):
self.bot.login = MagicMock()
self.worker._next_sleep = datetime(year=2016, month=8, day=1, hour=12, minute=0)
# Change time to be after schedule
mock_datetime.now.return_value = self.worker._next_sleep + timedelta(minutes=5)

self.worker.work()

self.assertGreater(mock_sleep.call_count, 0)
self.assertGreater(self.bot.login.call_count, 0)

0 comments on commit 1ea32ca

Please sign in to comment.