def test_opponent_has_no_moves_when_uturn_happens_after_switch(self): self.battle.user.active.moves = [ Move('tackle'), Move('charm'), Move('uturn'), ] self.battle.opponent.active.moves = [ Move('tackle'), Move('charm'), ] self.battle.user.reserve = [ Pokemon('caterpie', 100), Pokemon('spinarak', 100) ] self.battle.opponent.reserve = [Pokemon('caterpie', 100)] # using uturn on the previous turn would cause force_switch to be True self.battle.force_switch = True self.battle.turn = 5 self.battle.user.last_used_move = LastUsedMove( move='uturn', pokemon_name='pikachu', turn=5, ) self.battle.opponent.last_used_move = LastUsedMove( move='switch pikachu', pokemon_name=None, turn=5) expected_options = (['switch caterpie', 'switch spinarak'], ['splash']) self.assertEqual(expected_options, self.battle.get_all_options())
def move(battle, split_msg): move_name = normalize_name(split_msg[3].strip().lower()) if is_opponent(battle, split_msg): side = battle.opponent pkmn = battle.opponent.active else: side = battle.user pkmn = battle.user.active # add the move to it's moves if it hasn't been seen # decrement the PP by one # if the move is unknown, do nothing move_object = pkmn.get_move(move_name) if move_object is None: new_move = pkmn.add_move(move_name) if new_move is not None: new_move.current_pp -= 1 else: move_object.current_pp -= 1 logger.debug("{} already has the move {}. Decrementing the PP by 1".format(pkmn.name, move_name)) # if this pokemon used two different moves without switching, # set a flag to signify that it cannot have a choice item if ( is_opponent(battle, split_msg) and side.last_used_move.pokemon_name == side.active.name and side.last_used_move.move != move_name ): logger.debug("{} used two different moves - it cannot have a choice item".format(pkmn.name)) pkmn.can_have_choice_item = False if pkmn.item in constants.CHOICE_ITEMS: logger.warning("{} has a choice item, but used two different moves - setting it's item to UNKNOWN".format(pkmn.name)) pkmn.item = constants.UNKNOWN_ITEM try: category = all_move_json[move_name][constants.CATEGORY] logger.debug("Setting {}'s last used move: {}".format(pkmn.name, move_name)) side.last_used_move = LastUsedMove( pokemon_name=pkmn.name, move=move_name ) except KeyError: category = None side.last_used_move = LastUsedMove( pokemon_name=pkmn.name, move=constants.DO_NOTHING_MOVE ) # if this pokemon used a damaging move, eliminate the possibility of it having a lifeorb # the lifeorb will reveal itself if it has it if category in constants.DAMAGING_CATEGORIES and not any([normalize_name(a) in ['sheerforce', 'magicguard'] for a in pokedex[pkmn.name][constants.ABILITIES].values()]): logger.debug("{} used a damaging move - not guessing lifeorb anymore".format(pkmn.name)) pkmn.can_have_life_orb = False # there is nothing special in the protocol for "wish" - it must be extracted here if move_name == constants.WISH and 'still' not in split_msg[4]: logger.debug("{} used wish - expecting {} health of recovery next turn".format(side.active.name, side.active.max_hp/2)) side.wish = (2, side.active.max_hp/2)
def move(battle, split_msg): """The opponent's pokemon has made a move - add it to that pokemon's move list if necessary""" move_name = normalize_name(split_msg[3].strip().lower()) if is_opponent(battle, split_msg): pkmn = battle.opponent.active move = pkmn.get_move(move_name) if move is None: new_move = pkmn.add_move(move_name) if new_move is not None: new_move.current_pp -= 1 else: move.current_pp -= 1 logger.debug( "{} already has the move {}. Decrementing the PP by 1".format( pkmn.name, move_name)) logger.debug("Setting opponent's last used move: {} - {}".format( battle.opponent.active.name, move_name)) # if this pokemon used two different moves without switching, # set a flag to signify that it cannot have a choice item if (battle.opponent.last_used_move.pokemon_name == battle.opponent.active.name and battle.opponent.last_used_move.move != move_name): logger.debug( "Opponent's {} used two different moves - it cannot have a choice item" .format(battle.opponent.active.name)) battle.opponent.active.can_have_choice_item = False battle.opponent.last_used_move = LastUsedMove( pokemon_name=battle.opponent.active.name, move=move_name)
async def async_pick_move(battle): battle_copy = deepcopy(battle) if battle_copy.request_json: battle_copy.user.from_json(battle_copy.request_json) loop = asyncio.get_event_loop() with concurrent.futures.ThreadPoolExecutor() as pool: best_move = await loop.run_in_executor( pool, battle_copy.find_best_move ) choice = best_move[0] if constants.SWITCH_STRING in choice: battle.user.last_used_move = LastUsedMove(battle.user.active.name, "switch {}".format(choice.split()[-1]), battle.turn) else: battle.user.last_used_move = LastUsedMove(battle.user.active.name, choice.split()[2], battle.turn) return best_move
def switch_or_drag(battle, split_msg): if is_opponent(battle, split_msg): side = battle.opponent logger.debug("Opponent has switched - clearing the last used move") else: side = battle.user side.side_conditions[constants.TOXIC_COUNT] = 0 if side.active is not None: # set the pkmn's types back to their original value if the types were changed if constants.TYPECHANGE in side.active.volatile_statuses: original_types = pokedex[side.active.name][constants.TYPES] logger.debug( "{} had it's type changed - changing its types back to {}". format(side.active.name, original_types)) side.active.types = original_types # if the target was transformed, reset its transformed attributes if constants.TRANSFORM in side.active.volatile_statuses: logger.debug( "{} was transformed. Resetting its transformed attributes". format(side.active.name)) side.active.stats = calculate_stats(side.active.base_stats, side.active.level) side.active.ability = None side.active.moves = [] side.active.types = pokedex[side.active.name][constants.TYPES] # reset the boost of the pokemon being replaced side.active.boosts.clear() # reset the volatile statuses of the pokemon being replaced side.active.volatile_statuses.clear() # reset toxic count for this side side.side_conditions[constants.TOXIC_COUNT] = 0 # check if the pokemon exists in the reserves # if it does not, then the newly-created pokemon is used (for formats without team preview) pkmn = Pokemon.from_switch_string(split_msg[3]) pkmn = find_pokemon_in_reserves(pkmn.name, side.reserve) if pkmn is None: pkmn = Pokemon.from_switch_string(split_msg[3]) else: side.reserve.remove(pkmn) side.last_used_move = LastUsedMove(pokemon_name=None, move='switch {}'.format(pkmn.name), turn=battle.turn) # pkmn != active is a special edge-case for Zoroark if side.active is not None and pkmn != side.active: side.reserve.append(side.active) side.active = pkmn if side.active.name in constants.UNKOWN_POKEMON_FORMES: side.active = Pokemon.from_switch_string(split_msg[3])
def test_non_choice_item_possession_returns_false(self): self.battler.active.item = '' self.battler.last_used_move = LastUsedMove(pokemon_name='pikachu', move='tackle') self.battler.lock_moves() self.assertFalse(self.battler.active.get_move('volttackle').disabled) self.assertFalse(self.battler.active.get_move('thunderbolt').disabled) self.assertFalse(self.battler.active.get_move('agility').disabled) self.assertFalse(self.battler.active.get_move('doubleteam').disabled)
def test_choice_item_with_previous_move_being_a_switch_returns_false(self): self.battler.active.item = 'choicescarf' self.battler.last_used_move = LastUsedMove(pokemon_name='caterpie', move='switch') self.battler.lock_moves() self.assertFalse(self.battler.active.get_move('volttackle').disabled) self.assertFalse(self.battler.active.get_move('thunderbolt').disabled) self.assertFalse(self.battler.active.get_move('agility').disabled) self.assertFalse(self.battler.active.get_move('doubleteam').disabled)
def test_fakeout_is_not_disabled_when_the_last_used_move_was_a_switch( self): self.battler.active.moves.append(Move('fakeout')) self.battler.last_used_move = LastUsedMove(pokemon_name='caterpie', move='switch', turn=0) self.battler.lock_moves() self.assertFalse(self.battler.active.get_move('fakeout').disabled)
def test_fakeout_gets_locked_when_last_used_move_was_by_the_active_pokemon( self): self.battler.active.moves.append(Move('fakeout')) self.battler.last_used_move = LastUsedMove( pokemon_name='pikachu', # the current active pokemon move='volttackle', turn=0) self.battler.lock_moves() self.assertTrue(self.battler.active.get_move('fakeout').disabled)
def test_choice_item_with_previous_move_used_by_this_pokemon_returns_true( self): self.battler.active.item = 'choicescarf' self.battler.last_used_move = LastUsedMove(pokemon_name='pikachu', move='volttackle') self.battler.lock_moves() self.assertFalse(self.battler.active.get_move('volttackle').disabled) self.assertTrue(self.battler.active.get_move('thunderbolt').disabled) self.assertTrue(self.battler.active.get_move('agility').disabled) self.assertTrue(self.battler.active.get_move('doubleteam').disabled)
def test_opponent_has_no_moves_when_uturn_kills_and_opponent_has_not_moved_yet( self): self.battle.user.active.moves = [ Move('tackle'), Move('charm'), Move('uturn'), ] self.battle.opponent.active.moves = [ Move('tackle'), Move('charm'), ] self.battle.user.reserve = [ Pokemon('caterpie', 100), Pokemon('spinarak', 100) ] self.battle.opponent.reserve = [Pokemon('caterpie', 100)] # using uturn on the previous turn would cause force_switch to be True self.battle.force_switch = True # opponent has died from uturn self.battle.opponent.active.hp = 0 self.battle.turn = 5 self.battle.user.last_used_move = LastUsedMove( move='uturn', pokemon_name='pikachu', turn=5, ) # the opponent's last move would have been from the turn before (turn 4), meaning it hasn't moved yet self.battle.opponent.last_used_move = LastUsedMove( move='tackle', pokemon_name='pikachu', turn=4) expected_options = (['switch caterpie', 'switch spinarak'], ['splash']) self.assertEqual(expected_options, self.battle.get_all_options())
async def async_pick_move(battle, agent=None): battle_copy = deepcopy(battle) if battle_copy.request_json: battle_copy.user.from_json(battle_copy.request_json) loop = asyncio.get_event_loop() if agent == None: # so we are dealing with a non neural network bot with concurrent.futures.ThreadPoolExecutor() as pool: best_move = await loop.run_in_executor( pool, battle_copy.find_best_move ) else: # modify this? #with concurrent.futures.ThreadPoolExecutor() as pool: #best_move = await loop.run_in_executor( # pool, battle_copy.find_best_move, agent #) best_move = await battle_copy.find_best_move(agent) choice = best_move[0] if constants.SWITCH_STRING in choice: battle.user.last_used_move = LastUsedMove(battle.user.active.name, "switch {}".format(choice.split()[-1]), battle.turn) else: battle.user.last_used_move = LastUsedMove(battle.user.active.name, choice.split()[2], battle.turn) return best_move
def switch_or_drag(battle, split_msg): """The opponent's pokemon has changed If the new one hasn't been seen, create it""" if is_opponent(battle, split_msg): logger.debug("Opponent has switched - clearing the last used move") battle.opponent.last_used_move = LastUsedMove(pokemon_name=None, move='switch') battle.opponent.side_conditions[constants.TOXIC_COUNT] = 0 if battle.opponent.active is not None: # reset the boost of the pokemon being replaced battle.opponent.active.boosts.clear() if constants.DYNAMAX in battle.opponent.active.volatile_statuses: battle.opponent.active.hp /= 2 battle.opponent.active.max_hp /= 2 logger.debug( "Opponent ended dynamax - halving their HP to {}/{}". format(battle.opponent.active.hp, battle.opponent.active.max_hp)) # reset the volatile statuses of the pokemon being replaced battle.opponent.active.volatile_statuses.clear() # check if the pokemon exists in the reserves # if it does not, then the newly-created pokemon is used (for formats without team preview) pkmn = Pokemon.from_switch_string(split_msg[3]) pkmn = find_pokemon_in_reserves(pkmn.name, battle.opponent.reserve) if pkmn is None: pkmn = Pokemon.from_switch_string(split_msg[3]) else: battle.opponent.reserve.remove(pkmn) # pkmn != active is a special edge-case for Zoroark if battle.opponent.active is not None and pkmn != battle.opponent.active: battle.opponent.reserve.append(battle.opponent.active) battle.opponent.active = pkmn if battle.opponent.active.name in constants.UNKOWN_POKEMON_FORMES: battle.opponent.active = Pokemon.from_switch_string(split_msg[3]) else: battle.user.side_conditions[constants.TOXIC_COUNT] = 0 battle.user.active.boosts.clear()
def move(battle, split_msg): if '[from]' in split_msg[-1] and split_msg[-1] != "[from]lockedmove": return move_name = normalize_name(split_msg[3].strip().lower()) if is_opponent(battle, split_msg): side = battle.opponent pkmn = battle.opponent.active else: side = battle.user pkmn = battle.user.active # remove volatile status if they have it # this is for preparation moves like Phantom Force if move_name in pkmn.volatile_statuses: logger.debug("Removing volatile status {} from {}".format( move_name, pkmn.name)) pkmn.volatile_statuses.remove(move_name) # add the move to it's moves if it hasn't been seen # decrement the PP by one # if the move is unknown, do nothing move_object = pkmn.get_move(move_name) if move_object is None: new_move = pkmn.add_move(move_name) if new_move is not None: new_move.current_pp -= 1 else: move_object.current_pp -= 1 logger.debug( "{} already has the move {}. Decrementing the PP by 1".format( pkmn.name, move_name)) # if this pokemon used two different moves without switching, # set a flag to signify that it cannot have a choice item if (is_opponent(battle, split_msg) and side.last_used_move.pokemon_name == side.active.name and side.last_used_move.move != move_name): logger.debug( "{} used two different moves - it cannot have a choice item". format(pkmn.name)) pkmn.can_have_choice_item = False if pkmn.item in constants.CHOICE_ITEMS: logger.warning( "{} has a choice item, but used two different moves - setting it's item to UNKNOWN" .format(pkmn.name)) pkmn.item = constants.UNKNOWN_ITEM # if the opponent uses a boosting status move, they cannot have a choice item # this COULD be set for any status move, but some pkmn uncommonly run things like specs + wisp try: if constants.BOOSTS in all_move_json[move_name] and all_move_json[ move_name][constants.CATEGORY] == constants.STATUS: logger.debug( "{} used a boosting status-move. Setting can_have_choice_item to False" .format(pkmn.name)) pkmn.can_have_choice_item = False except KeyError: pass try: if all_move_json[move_name][constants.CATEGORY] == constants.STATUS: logger.debug( "{} used a status-move. Setting can_have_assultvest to False". format(pkmn.name)) pkmn.can_have_assaultvest = False except KeyError: pass try: category = all_move_json[move_name][constants.CATEGORY] logger.debug("Setting {}'s last used move: {}".format( pkmn.name, move_name)) side.last_used_move = LastUsedMove(pokemon_name=pkmn.name, move=move_name, turn=battle.turn) except KeyError: category = None side.last_used_move = LastUsedMove(pokemon_name=pkmn.name, move=constants.DO_NOTHING_MOVE, turn=battle.turn) # if this pokemon used a damaging move, eliminate the possibility of it having a lifeorb # the lifeorb will reveal itself if it has it if category in constants.DAMAGING_CATEGORIES and not any([ normalize_name(a) in ['sheerforce', 'magicguard'] for a in pokedex[pkmn.name][constants.ABILITIES].values() ]): logger.debug( "{} used a damaging move - not guessing lifeorb anymore".format( pkmn.name)) pkmn.can_have_life_orb = False # there is nothing special in the protocol for "wish" - it must be extracted here if move_name == constants.WISH and 'still' not in split_msg[4]: logger.debug( "{} used wish - expecting {} health of recovery next turn".format( side.active.name, side.active.max_hp / 2)) side.wish = (2, side.active.max_hp / 2)