<commit_before># -*- coding: utf-8 -*-

import os
import time
import json
import logging
import time
from random import random, randrange
from pokemongo_bot import inventory
from pokemongo_bot.base_task import BaseTask
from pokemongo_bot.human_behaviour import sleep, action_delay
from pokemongo_bot.inventory import Pokemon
from pokemongo_bot.worker_result import WorkerResult
from pokemongo_bot.datastore import Datastore
from pokemongo_bot.base_dir import _base_dir
from datetime import datetime, timedelta

CATCH_STATUS_SUCCESS = 1
CATCH_STATUS_FAILED = 2
CATCH_STATUS_VANISHED = 3
CATCH_STATUS_MISSED = 4

ENCOUNTER_STATUS_SUCCESS = 1
ENCOUNTER_STATUS_NOT_IN_RANGE = 5
ENCOUNTER_STATUS_POKEMON_INVENTORY_FULL = 7

ITEM_POKEBALL = 1
ITEM_GREATBALL = 2
ITEM_ULTRABALL = 3
ITEM_RAZZBERRY = 701

LOGIC_TO_FUNCTION = {
    'or': lambda x, y, z: x or y or z,
    'and': lambda x, y, z: x and y and z,
    'orand': lambda x, y, z: x or y and z,
    'andor': lambda x, y, z: x and y or z
}


class PokemonCatchWorker(Datastore, BaseTask):

    def __init__(self, pokemon, bot, config):
        self.pokemon = pokemon
        super(PokemonCatchWorker, self).__init__(bot, config)

    def initialize(self):
        self.api = self.bot.api
        self.position = self.bot.position
        self.pokemon_list = self.bot.pokemon_list
        self.inventory = inventory.items()
        self.spawn_point_guid = ''
        self.response_key = ''
        self.response_status_key = ''

        #Config
        self.min_ultraball_to_keep = self.config.get('min_ultraball_to_keep', 10)
        self.berry_threshold = self.config.get('berry_threshold', 0.35)
        self.vip_berry_threshold = self.config.get('vip_berry_threshold', 0.9)

        self.catch_throw_parameters = self.config.get('catch_throw_parameters', {})
        self.catch_throw_parameters_spin_success_rate = self.catch_throw_parameters.get('spin_success_rate', 0.6)
        self.catch_throw_parameters_excellent_rate = self.catch_throw_parameters.get('excellent_rate', 0.1)
        self.catch_throw_parameters_great_rate = self.catch_throw_parameters.get('great_rate', 0.5)
        self.catch_throw_parameters_nice_rate = self.catch_throw_parameters.get('nice_rate', 0.3)
        self.catch_throw_parameters_normal_rate = self.catch_throw_parameters.get('normal_rate', 0.1)
        self.catch_throw_parameters_hit_rate = self.catch_throw_parameters.get('hit_rate', 0.8)

        self.catchsim_config = self.config.get('catch_simulation', {})
        self.catchsim_catch_wait_min = self.catchsim_config.get('catch_wait_min', 2)
        self.catchsim_catch_wait_max = self.catchsim_config.get('catch_wait_max', 6)
        self.catchsim_flee_count = int(self.catchsim_config.get('flee_count', 3))
        self.catchsim_flee_duration = self.catchsim_config.get('flee_duration', 2)
        self.catchsim_berry_wait_min = self.catchsim_config.get('berry_wait_min', 2)
        self.catchsim_berry_wait_max = self.catchsim_config.get('berry_wait_max', 3)
        self.catchsim_changeball_wait_min = self.catchsim_config.get('changeball_wait_min', 2)
        self.catchsim_changeball_wait_max = self.catchsim_config.get('changeball_wait_max', 3)


    ############################################################################
    # public methods
    ############################################################################

    def work(self, response_dict=None):
        response_dict = response_dict or self.create_encounter_api_call()

        # validate response
        if not response_dict:
            return WorkerResult.ERROR

        try:
            responses = response_dict['responses']
            response = responses[self.response_key]
            if response[self.response_status_key] != ENCOUNTER_STATUS_SUCCESS:
                if response[self.response_status_key] == ENCOUNTER_STATUS_NOT_IN_RANGE:
                    self.emit_event('pokemon_not_in_range', formatted='Pokemon went out of range!')
                elif response[self.response_status_key] == ENCOUNTER_STATUS_POKEMON_INVENTORY_FULL:
                    self.emit_event('pokemon_inventory_full', formatted='Your Pokemon inventory is full! Could not catch!')
                return WorkerResult.ERROR
        except KeyError:
            return WorkerResult.ERROR

        # get pokemon data
        pokemon_data = response['wild_pokemon']['pokemon_data'] if 'wild_pokemon' in response else response['pokemon_data']
        pokemon = Pokemon(pokemon_data)

        # skip ignored pokemon
        if not self._should_catch_pokemon(pokemon):
            return WorkerResult.SUCCESS

        is_vip = self._is_vip_pokemon(pokemon)
        if inventory.items().get(ITEM_POKEBALL).count < 1:
            if inventory.items().get(ITEM_GREATBALL).count < 1:
                if inventory.items().get(ITEM_ULTRABALL).count < 1:
                    return WorkerResult.SUCCESS
                elif (not is_vip) and inventory.items().get(ITEM_ULTRABALL).count < self.min_ultraball_to_keep:
                    return WorkerResult.SUCCESS

        # log encounter
        self.emit_event(
            'pokemon_appeared',
            formatted='A wild {pokemon} appeared! [CP {cp}] [NCP {ncp}] [Potential {iv}] [A/D/S {iv_display}]',
            data={
                'pokemon': pokemon.name,
                'ncp': round(pokemon.cp_percent, 2),
                'cp': pokemon.cp,
                'iv': pokemon.iv,
                'iv_display': pokemon.iv_display,
                'encounter_id': self.pokemon['encounter_id'],
                'latitude': self.pokemon['latitude'],
                'longitude': self.pokemon['longitude'],
                'pokemon_id': pokemon.pokemon_id
            }
        )

        # simulate app
        time.sleep(3)
<commit_msg>Fix edge case when remaining ultraball eq min to keep (#4462)<commit_after># -*- coding: utf-8 -*-

import os
import time
import json
import logging
import time
from random import random, randrange
from pokemongo_bot import inventory
from pokemongo_bot.base_task import BaseTask
from pokemongo_bot.human_behaviour import sleep, action_delay
from pokemongo_bot.inventory import Pokemon
from pokemongo_bot.worker_result import WorkerResult
from pokemongo_bot.datastore import Datastore
from pokemongo_bot.base_dir import _base_dir
from datetime import datetime, timedelta

CATCH_STATUS_SUCCESS = 1
CATCH_STATUS_FAILED = 2
CATCH_STATUS_VANISHED = 3
CATCH_STATUS_MISSED = 4

ENCOUNTER_STATUS_SUCCESS = 1
ENCOUNTER_STATUS_NOT_IN_RANGE = 5
ENCOUNTER_STATUS_POKEMON_INVENTORY_FULL = 7

ITEM_POKEBALL = 1
ITEM_GREATBALL = 2
ITEM_ULTRABALL = 3
ITEM_RAZZBERRY = 701

LOGIC_TO_FUNCTION = {
    'or': lambda x, y, z: x or y or z,
    'and': lambda x, y, z: x and y and z,
    'orand': lambda x, y, z: x or y and z,
    'andor': lambda x, y, z: x and y or z
}


class PokemonCatchWorker(Datastore, BaseTask):

    def __init__(self, pokemon, bot, config):
        self.pokemon = pokemon
        super(PokemonCatchWorker, self).__init__(bot, config)

    def initialize(self):
        self.api = self.bot.api
        self.position = self.bot.position
        self.pokemon_list = self.bot.pokemon_list
        self.inventory = inventory.items()
        self.spawn_point_guid = ''
        self.response_key = ''
        self.response_status_key = ''

        #Config
        self.min_ultraball_to_keep = self.config.get('min_ultraball_to_keep', 10)
        self.berry_threshold = self.config.get('berry_threshold', 0.35)
        self.vip_berry_threshold = self.config.get('vip_berry_threshold', 0.9)

        self.catch_throw_parameters = self.config.get('catch_throw_parameters', {})
        self.catch_throw_parameters_spin_success_rate = self.catch_throw_parameters.get('spin_success_rate', 0.6)
        self.catch_throw_parameters_excellent_rate = self.catch_throw_parameters.get('excellent_rate', 0.1)
        self.catch_throw_parameters_great_rate = self.catch_throw_parameters.get('great_rate', 0.5)
        self.catch_throw_parameters_nice_rate = self.catch_throw_parameters.get('nice_rate', 0.3)
        self.catch_throw_parameters_normal_rate = self.catch_throw_parameters.get('normal_rate', 0.1)
        self.catch_throw_parameters_hit_rate = self.catch_throw_parameters.get('hit_rate', 0.8)

        self.catchsim_config = self.config.get('catch_simulation', {})
        self.catchsim_catch_wait_min = self.catchsim_config.get('catch_wait_min', 2)
        self.catchsim_catch_wait_max = self.catchsim_config.get('catch_wait_max', 6)
        self.catchsim_flee_count = int(self.catchsim_config.get('flee_count', 3))
        self.catchsim_flee_duration = self.catchsim_config.get('flee_duration', 2)
        self.catchsim_berry_wait_min = self.catchsim_config.get('berry_wait_min', 2)
        self.catchsim_berry_wait_max = self.catchsim_config.get('berry_wait_max', 3)
        self.catchsim_changeball_wait_min = self.catchsim_config.get('changeball_wait_min', 2)
        self.catchsim_changeball_wait_max = self.catchsim_config.get('changeball_wait_max', 3)


    ############################################################################
    # public methods
    ############################################################################

    def work(self, response_dict=None):
        response_dict = response_dict or self.create_encounter_api_call()

        # validate response
        if not response_dict:
            return WorkerResult.ERROR

        try:
            responses = response_dict['responses']
            response = responses[self.response_key]
            if response[self.response_status_key] != ENCOUNTER_STATUS_SUCCESS:
                if response[self.response_status_key] == ENCOUNTER_STATUS_NOT_IN_RANGE:
                    self.emit_event('pokemon_not_in_range', formatted='Pokemon went out of range!')
                elif response[self.response_status_key] == ENCOUNTER_STATUS_POKEMON_INVENTORY_FULL:
                    self.emit_event('pokemon_inventory_full', formatted='Your Pokemon inventory is full! Could not catch!')
                return WorkerResult.ERROR
        except KeyError:
            return WorkerResult.ERROR

        # get pokemon data
        pokemon_data = response['wild_pokemon']['pokemon_data'] if 'wild_pokemon' in response else response['pokemon_data']
        pokemon = Pokemon(pokemon_data)

        # skip ignored pokemon
        if not self._should_catch_pokemon(pokemon):
            return WorkerResult.SUCCESS

        is_vip = self._is_vip_pokemon(pokemon)
        if inventory.items().get(ITEM_POKEBALL).count < 1:
            if inventory.items().get(ITEM_GREATBALL).count < 1:
                if inventory.items().get(ITEM_ULTRABALL).count < 1:
                    return WorkerResult.SUCCESS
                elif (not is_vip) and inventory.items().get(ITEM_ULTRABALL).count <= self.min_ultraball_to_keep:
                    return WorkerResult.SUCCESS

        # log encounter
        self.emit_event(
            'pokemon_appeared',
            formatted='A wild {pokemon} appeared! [CP {cp}] [NCP {ncp}] [Potential {iv}] [A/D/S {iv_display}]',
            data={
                'pokemon': pokemon.name,
                'ncp': round(pokemon.cp_percent, 2),
                'cp': pokemon.cp,
                'iv': pokemon.iv,
                'iv_display': pokemon.iv_display,
                'encounter_id': self.pokemon['encounter_id'],
                'latitude': self.pokemon['latitude'],
                'longitude': self.pokemon['longitude'],
                'pokemon_id': pokemon.pokemon_id
            }
        )

        # simulate app
        time.sleep(3)

{'fraction_non_alphanumeric': 0.11787954291605808, 'fraction_numerical': 0.008248131282756251, 'mean_word_length': 3.143823424706301, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '129530', 'n_tokens_mistral': 3798, 'n_tokens_neox': 3604, 'n_words': 668}