def setUp(self): self.state = State( Side( Pokemon.from_state_pokemon_dict(StatePokemon("pikachu", 100).to_dict()), { "rattata": Pokemon.from_state_pokemon_dict(StatePokemon("rattata", 100).to_dict()), "charmander": Pokemon.from_state_pokemon_dict(StatePokemon("charmander", 100).to_dict()), "squirtle": Pokemon.from_state_pokemon_dict(StatePokemon("squirtle", 100).to_dict()), "bulbasaur": Pokemon.from_state_pokemon_dict(StatePokemon("bulbasaur", 100).to_dict()), "pidgey": Pokemon.from_state_pokemon_dict(StatePokemon("pidgey", 100).to_dict()) }, (0, 0), defaultdict(lambda: 0) ), Side( Pokemon.from_state_pokemon_dict(StatePokemon("pikachu", 100).to_dict()), { "rattata": Pokemon.from_state_pokemon_dict(StatePokemon("rattata", 100).to_dict()), "charmander": Pokemon.from_state_pokemon_dict(StatePokemon("charmander", 100).to_dict()), "squirtle": Pokemon.from_state_pokemon_dict(StatePokemon("squirtle", 100).to_dict()), "bulbasaur": Pokemon.from_state_pokemon_dict(StatePokemon("bulbasaur", 100).to_dict()), "pidgey": Pokemon.from_state_pokemon_dict(StatePokemon("pidgey", 100).to_dict()) }, (0, 0), defaultdict(lambda: 0) ), None, None, False ) self.mutator = StateMutator(self.state)
def pick_safest_move_using_dynamic_search_depth(battles): """ Dynamically decides how far to look into the game. This requires a strong computer to be able to search 3/4 turns ahead. Using a pypy interpreter will also result in better performance. """ all_scores = dict() num_battles = len(battles) if num_battles > 1: search_depth = 2 for i, b in enumerate(battles): state = b.create_state() mutator = StateMutator(state) user_options, opponent_options = b.get_all_options() logger.debug("Searching through the state: {}".format(mutator.state)) scores = get_payoff_matrix(mutator, user_options, opponent_options, depth=search_depth, prune=True) prefixed_scores = prefix_opponent_move(scores, str(i)) all_scores = {**all_scores, **prefixed_scores} elif num_battles == 1: search_depth = 3 b = battles[0] state = b.create_state() mutator = StateMutator(state) user_options, opponent_options = b.get_all_options() num_user_options = len(user_options) num_opponent_options = len(opponent_options) options_product = num_user_options * num_opponent_options if options_product < 20 and num_user_options > 1 and num_opponent_options > 1: logger.debug("Low options product, looking an additional depth") search_depth += 1 logger.debug("Searching through the state: {}".format(mutator.state)) logger.debug("Options Product: {}".format(options_product)) logger.debug("My Options: {}".format(user_options)) logger.debug("Opponent Options: {}".format(opponent_options)) logger.debug("Search depth: {}".format(search_depth)) all_scores = get_payoff_matrix(mutator, user_options, opponent_options, depth=search_depth, prune=True) else: raise ValueError("less than 1 battle?: {}".format(battles)) decision, payoff = pick_safest(all_scores, remove_guaranteed=True) bot_choice = decision[0] logger.debug("Safest: {}, {}".format(bot_choice, payoff)) logger.debug("Depth: {}".format(search_depth)) return bot_choice
def generate_next_child(self, chosen_transition): """ Generates a child node by choosing the most likely mutation instructions (instructions are potential results of a transition) of the given transition. Params: - chosen_transition: the pair of (our move : opponent move) to apply """ mutator = StateMutator(copy.deepcopy(self.state)) state_instructions = get_all_state_instructions( mutator, chosen_transition[0], chosen_transition[1]) choice = max(state_instructions, key=lambda i: i.percentage).instructions mutator.apply(choice) return MonteCarloTree(mutator.state)
def find_best_move(self): battles = self.prepare_battles() if len(battles) > 7: logger.debug( "Not enough is known about the opponent's active pokemon - falling back to safest decision making" ) battles = self.prepare_battles(join_moves_together=True) decision = pick_safest_move_from_battles(battles) else: list_of_payoffs = list() for b in battles: state = b.create_state() mutator = StateMutator(state) logger.debug("Attempting to find best move from: {}".format( mutator.state)) user_options, opponent_options = b.get_all_options() scores = get_payoff_matrix(mutator, user_options, opponent_options, prune=False) list_of_payoffs.append(scores) decision = pick_move_in_equilibrium_from_multiple_score_lookups( list_of_payoffs) return format_decision(self, decision)
def pick_bfs_move(self, battle): state = battle.create_state() mutator = StateMutator(state) user_options, opponent_options = battle.get_all_options() logger.debug("Attempting to find best move from: {}".format(mutator.state)) is_winning = self.is_winning(state) # Builds a tree to search for opponent's moves, assume opponent picks safest scores = get_payoff_matrix(mutator, user_options, opponent_options, is_winning, depth=3, prune=True) logger.debug(f"\nScores: {scores}") move_string = "Aggresive" if is_winning: decision, payoff = self.pick_safest(scores) move_string = "Safest" else: decision, payoff = self.pick_aggresive(scores) bot_choice = decision[0] logger.debug(f"{move_string}: {bot_choice}, {payoff}") return bot_choice
def calculate_value(state, transition, depth): """ Takes in the current state, a specific transition (pair of our move and opponent move), and estimates the value associated with applying this transition at current search depth, taking into account the probability of this transition occuring """ state_instructions = get_all_state_instructions(StateMutator(state), transition[0], transition[1]) total_value = 0 for instruction in state_instructions: mutator = StateMutator(copy.deepcopy(state)) mutator.apply(instruction.instructions) value = expectiminimax(mutator.state, depth) total_value += value * instruction.percentage return total_value
def random_playout(self, initial_position, depth): """ Random playout of from this node. If max depth is reached then the evaluation function of the state is compared against initial_position. If the evaluation of the state is better than the initial position, it is counted as win, since the bot position was improved. """ self.total += 1 mutator = StateMutator(copy.deepcopy(self.state)) while True: if depth == MAX_DEPTH: if evaluate(mutator.state) >= initial_position: self.wins += 1 return True else: return False winner = mutator.state.battle_is_finished() if winner: if winner == 1: self.wins += 1 return True else: return False transition = random.choice(get_transitions(mutator.state)) state_instructions = get_all_state_instructions( mutator, transition[0], transition[1]) possible_instrucitons = [ i.instructions for i in state_instructions ] weights = [i.percentage for i in state_instructions] choice = random.choices(possible_instrucitons, weights=weights)[0] mutator.apply(choice) depth += 1
def pick_safest_move_from_battles(battles): all_scores = dict() for i, b in enumerate(battles): state = b.create_state() mutator = StateMutator(state) user_options, opponent_options = b.get_all_options() logger.debug("Searching through the state: {}".format(mutator.state)) scores = get_payoff_matrix(mutator, user_options, opponent_options, depth=config.search_depth, prune=True) prefixed_scores = prefix_opponent_move(scores, str(i)) all_scores = {**all_scores, **prefixed_scores} decision, payoff = pick_safest(all_scores, remove_guaranteed=True) bot_choice = decision[0] logger.debug("Safest: {}, {}".format(bot_choice, payoff)) return bot_choice
def pick_safest_move_from_battles(battles): all_scores = dict() for i, b in enumerate(battles): state = b.create_state() mutator = StateMutator(state) user_options, opponent_options = b.get_all_options() logger.debug("Attempting to find best move from: {}".format( mutator.state)) scores = get_payoff_matrix(mutator, user_options, opponent_options, prune=True) prefixed_scores = prefix_opponent_move(scores, str(i)) all_scores = {**all_scores, **prefixed_scores} decision, payoff = pick_safest(all_scores) bot_choice = decision[0] logger.debug("Safest: {}, {}".format(bot_choice, payoff)) return bot_choice
def make_a_choice(battles): # Battles is a list of possible states the opponent's team might be in # The list is based on guesses done by the framework, one battle per guess # There's always at least one battle # The probability of each battle/state can be assumed to be equal # Below a battle is transformed into a StateMutator # We can apply choices to the StateMutator in order to create new states a_battle = battles[0] a_state = a_battle.to_object() a_mutator = StateMutator(a_state) # Fetch possible options for you and your opponent by calling get_all_options on a StateMutator # user_options includes using one of your four moves or switching to a remaining Pokémon # opponent_options is based on assumptions about what the opponent might be able to do user_options, opponent_options = get_all_options(a_mutator) # We can evaluate a state using the pre-defined evaluate method # The method is already fine-tuned in order to save us some time # But you can play around with it by checking out evaluate.py a_score = evaluate(a_mutator.state) # By picking an option and assuming the opponent's option, we can generate a list of low-level state instructions # Because certain moves can lead to several different states (because of randomness), the list of possible instructions might be long # Applying one of the possible instructions to the StateMutator mutates the state # After evaluating the score of the new state, we might want to reverse the changes and try something else all_possible_instructions = get_all_state_instructions( a_mutator, user_options[0], opponent_options[0]) a_mutator.apply(all_possible_instructions[0].instructions) a_mutator.reverse(all_possible_instructions[0].instructions) # Logging data might be handy logger.debug("User's options: {}".format(user_options)) logger.debug("Opponent's options: {}".format(opponent_options)) logger.debug("Current state's score: {}".format(a_score)) # For now, let's just return a random choice a_random_choice = random.choice(user_options) logger.debug("Move chosen: {}".format(a_random_choice)) return a_random_choice
class TestStatemutator(unittest.TestCase): def setUp(self): self.state = State( Side( Pokemon.from_state_pokemon_dict( StatePokemon("pikachu", 100).to_dict()), { "rattata": Pokemon.from_state_pokemon_dict( StatePokemon("rattata", 100).to_dict()), "charmander": Pokemon.from_state_pokemon_dict( StatePokemon("charmander", 100).to_dict()), "squirtle": Pokemon.from_state_pokemon_dict( StatePokemon("squirtle", 100).to_dict()), "bulbasaur": Pokemon.from_state_pokemon_dict( StatePokemon("bulbasaur", 100).to_dict()), "pidgey": Pokemon.from_state_pokemon_dict( StatePokemon("pidgey", 100).to_dict()) }, defaultdict(lambda: 0)), Side( Pokemon.from_state_pokemon_dict( StatePokemon("pikachu", 100).to_dict()), { "rattata": Pokemon.from_state_pokemon_dict( StatePokemon("rattata", 100).to_dict()), "charmander": Pokemon.from_state_pokemon_dict( StatePokemon("charmander", 100).to_dict()), "squirtle": Pokemon.from_state_pokemon_dict( StatePokemon("squirtle", 100).to_dict()), "bulbasaur": Pokemon.from_state_pokemon_dict( StatePokemon("bulbasaur", 100).to_dict()), "pidgey": Pokemon.from_state_pokemon_dict( StatePokemon("pidgey", 100).to_dict()) }, defaultdict(lambda: 0)), None, None, False) self.mutator = StateMutator(self.state) def test_switch_instruction_replaces_active(self): instruction = (constants.MUTATOR_SWITCH, constants.SELF, "pikachu", "rattata") list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) self.assertEqual("rattata", self.state.self.active.id) def test_switch_instruction_replaces_active_for_opponent(self): instruction = (constants.MUTATOR_SWITCH, constants.OPPONENT, "pikachu", "rattata") list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) self.assertEqual("rattata", self.state.opponent.active.id) def test_switch_instruction_places_active_into_reserve(self): instruction = (constants.MUTATOR_SWITCH, constants.SELF, "pikachu", "rattata") list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) try: self.state.self.reserve["pikachu"] except KeyError: self.fail("`pikachu` is not in `self.reserve`") def test_reverse_switch_instruction_replaces_active(self): instruction = (constants.MUTATOR_SWITCH, constants.SELF, "rattata", "pikachu") list_of_instructions = [instruction] self.mutator.reverse(list_of_instructions) self.assertEqual("rattata", self.state.self.active.id) def test_apply_volatile_status_properly_applies_status(self): instruction = (constants.MUTATOR_APPLY_VOLATILE_STATUS, constants.SELF, "leechseed") list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) self.assertIn("leechseed", self.state.self.active.volatile_status) def test_reverse_volatile_status_properly_removes_status(self): self.state.self.active.volatile_status.add("leechseed") instruction = (constants.MUTATOR_APPLY_VOLATILE_STATUS, constants.SELF, "leechseed") list_of_instructions = [instruction] self.mutator.reverse(list_of_instructions) self.assertNotIn("leechseed", self.state.self.active.volatile_status) def test_damage_is_properly_applied(self): instruction = (constants.MUTATOR_DAMAGE, constants.SELF, 50) list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) damage_taken = self.state.self.active.maxhp - self.state.self.active.hp self.assertEqual(50, damage_taken) def test_damage_is_properly_reversed(self): self.state.self.active.hp -= 50 instruction = (constants.MUTATOR_DAMAGE, constants.SELF, 50) list_of_instructions = [instruction] self.mutator.reverse(list_of_instructions) damage_taken = self.state.self.active.maxhp - self.state.self.active.hp self.assertEqual(0, damage_taken) def test_healing_is_properly_applied(self): self.state.self.active.hp -= 50 instruction = (constants.MUTATOR_HEAL, constants.SELF, 50) list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) damage_taken = self.state.self.active.maxhp - self.state.self.active.hp self.assertEqual(0, damage_taken) def test_healing_is_properly_reversed(self): instruction = (constants.MUTATOR_HEAL, constants.SELF, 50) list_of_instructions = [instruction] self.mutator.reverse(list_of_instructions) damage_taken = self.state.self.active.maxhp - self.state.self.active.hp self.assertEqual(50, damage_taken) def test_boost_is_properly_applied(self): instruction = (constants.MUTATOR_BOOST, constants.SELF, constants.ATTACK, 1) list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) self.assertEqual(1, self.state.self.active.attack_boost) def test_boost_is_properly_reversed(self): self.state.self.active.attack_boost = 1 instruction = (constants.MUTATOR_BOOST, constants.SELF, constants.ATTACK, 1) list_of_instructions = [instruction] self.mutator.reverse(list_of_instructions) self.assertEqual(0, self.state.self.active.attack_boost) def test_boost_is_properly_reversed_when_a_boost_previously_existed(self): # the pokemon had attack_boost=2 before # it boosted to 4, and now it is being reversed self.state.self.active.attack_boost = 4 instruction = (constants.MUTATOR_BOOST, constants.SELF, constants.ATTACK, 2) list_of_instructions = [instruction] self.mutator.reverse(list_of_instructions) self.assertEqual(2, self.state.self.active.attack_boost) def test_unboost_is_properly_applied(self): instruction = (constants.MUTATOR_UNBOOST, constants.SELF, constants.ATTACK, 1) list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) self.assertEqual(-1, self.state.self.active.attack_boost) def test_unboost_is_properly_reversed(self): self.state.self.active.attack_boost = -1 instruction = (constants.MUTATOR_UNBOOST, constants.SELF, constants.ATTACK, 1) list_of_instructions = [instruction] self.mutator.reverse(list_of_instructions) self.assertEqual(0, self.state.self.active.attack_boost) def test_apply_status_properly_applies_status(self): instruction = (constants.MUTATOR_APPLY_STATUS, constants.SELF, constants.BURN) list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) self.assertEqual(constants.BURN, self.state.self.active.status) def test_apply_status_is_properly_reversed(self): self.state.self.active.status = constants.BURN instruction = (constants.MUTATOR_APPLY_STATUS, constants.SELF, constants.BURN) list_of_instructions = [instruction] self.mutator.reverse(list_of_instructions) self.assertEqual(None, self.state.self.active.status) def test_remove_status_properly_removes_status(self): self.state.self.active.status = constants.BURN instruction = (constants.MUTATOR_REMOVE_STATUS, constants.SELF, constants.BURN) list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) self.assertEqual(None, self.state.self.active.status) def test_remove_status_is_properly_reversed(self): instruction = (constants.MUTATOR_REMOVE_STATUS, constants.SELF, constants.BURN) list_of_instructions = [instruction] self.mutator.reverse(list_of_instructions) self.assertEqual(constants.BURN, self.state.self.active.status) def test_side_start_is_properly_applied(self): instruction = (constants.MUTATOR_SIDE_START, constants.SELF, constants.STEALTH_ROCK, 1) list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) self.assertEqual( 1, self.state.self.side_conditions[constants.STEALTH_ROCK]) def test_side_start_is_properly_reversed(self): self.state.self.side_conditions[constants.STEALTH_ROCK] = 1 instruction = (constants.MUTATOR_SIDE_START, constants.SELF, constants.STEALTH_ROCK, 1) list_of_instructions = [instruction] self.mutator.reverse(list_of_instructions) self.assertEqual( 0, self.state.self.side_conditions[constants.STEALTH_ROCK]) def test_side_end_is_properly_applied(self): self.state.self.side_conditions[constants.STEALTH_ROCK] = 2 instruction = (constants.MUTATOR_SIDE_END, constants.SELF, constants.STEALTH_ROCK, 2) list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) self.assertEqual( 0, self.state.self.side_conditions[constants.STEALTH_ROCK]) def test_side_end_is_properly_reversed(self): instruction = (constants.MUTATOR_SIDE_END, constants.SELF, constants.STEALTH_ROCK, 2) list_of_instructions = [instruction] self.mutator.reverse(list_of_instructions) self.assertEqual( 2, self.state.self.side_conditions[constants.STEALTH_ROCK]) def test_disable_move(self): move = {'id': 'return', 'disabled': False, 'current_pp': 16} self.state.self.active.moves = [move] instruction = ( constants.MUTATOR_DISABLE_MOVE, constants.SELF, "return", ) list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) self.assertTrue(move[constants.DISABLED]) def test_reverse_disable_move(self): move = {'id': 'return', 'disabled': True, 'current_pp': 16} self.state.self.active.moves = [move] instruction = ( constants.MUTATOR_DISABLE_MOVE, constants.SELF, "return", ) list_of_instructions = [instruction] self.mutator.reverse(list_of_instructions) self.assertFalse(move[constants.DISABLED]) def test_enable_move(self): move = {'id': 'return', 'disabled': True, 'current_pp': 16} self.state.self.active.moves = [move] instruction = ( constants.MUTATOR_ENABLE_MOVE, constants.SELF, "return", ) list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) self.assertFalse(move[constants.DISABLED]) def test_reverse_enable_move(self): move = {'id': 'return', 'disabled': False, 'current_pp': 16} self.state.self.active.moves = [move] instruction = ( constants.MUTATOR_ENABLE_MOVE, constants.SELF, "return", ) list_of_instructions = [instruction] self.mutator.reverse(list_of_instructions) self.assertTrue(move[constants.DISABLED]) def test_setting_weather(self): self.state.weather = None instruction = (constants.MUTATOR_WEATHER_START, constants.SUN, None) list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) self.assertEqual(constants.SUN, self.state.weather) def test_setting_weather_when_previous_weather_exists(self): self.state.weather = constants.RAIN instruction = (constants.MUTATOR_WEATHER_START, constants.SUN, constants.RAIN) list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) self.assertEqual(constants.SUN, self.state.weather) def test_reversing_weather_when_previous_weather_exists(self): self.state.weather = constants.SUN instruction = (constants.MUTATOR_WEATHER_START, constants.SUN, constants.RAIN) list_of_instructions = [instruction] self.mutator.reverse(list_of_instructions) self.assertEqual(constants.RAIN, self.state.weather) def test_reverse_setting_weather(self): self.state.weather = constants.SUN instruction = (constants.MUTATOR_WEATHER_START, constants.SUN, None) list_of_instructions = [instruction] self.mutator.reverse(list_of_instructions) self.assertEqual(None, self.state.weather) def test_apply_and_reverse_setting_weather_works(self): self.state.weather = None instruction = (constants.MUTATOR_WEATHER_START, constants.SUN, None) list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) if not self.state.weather == constants.SUN: self.fail("Sun was not set") self.mutator.reverse(list_of_instructions) self.assertEqual(None, self.state.weather) def test_apply_and_reverse_setting_weather_works_with_weather_previously_existing( self): self.state.weather = constants.RAIN instruction = (constants.MUTATOR_WEATHER_START, constants.SUN, constants.RAIN) list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) if not self.state.weather == constants.SUN: self.fail("Sun was not set") self.mutator.reverse(list_of_instructions) self.assertEqual(constants.RAIN, self.state.weather) def test_setting_field(self): self.state.field = None instruction = (constants.MUTATOR_FIELD_START, constants.PSYCHIC_TERRAIN, None) list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) self.assertEqual(constants.PSYCHIC_TERRAIN, self.state.field) def test_reverse_setting_field(self): self.state.field = constants.PSYCHIC_TERRAIN instruction = (constants.MUTATOR_FIELD_START, constants.PSYCHIC_TERRAIN, None) list_of_instructions = [instruction] self.mutator.reverse(list_of_instructions) self.assertEqual(None, self.state.field) def test_apply_and_reverse_field(self): self.state.field = None instruction = (constants.MUTATOR_FIELD_START, constants.PSYCHIC_TERRAIN, None) list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) if self.state.field != constants.PSYCHIC_TERRAIN: self.fail("Terrain was not set") self.mutator.reverse(list_of_instructions) self.assertEqual(None, self.state.field) def test_apply_and_reverse_field_when_previous_field_exists(self): self.state.field = constants.GRASSY_TERRAIN instruction = (constants.MUTATOR_FIELD_START, constants.PSYCHIC_TERRAIN, constants.GRASSY_TERRAIN) list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) if self.state.field != constants.PSYCHIC_TERRAIN: self.fail("Terrain was not set") self.mutator.reverse(list_of_instructions) self.assertEqual(constants.GRASSY_TERRAIN, self.state.field) def test_end_active_field(self): self.state.field = constants.GRASSY_TERRAIN instruction = (constants.MUTATOR_FIELD_END, constants.GRASSY_TERRAIN) list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) if self.state.field is not None: self.fail("Terrain was not removed") self.mutator.reverse(list_of_instructions) self.assertEqual(constants.GRASSY_TERRAIN, self.state.field) def test_reversing_end_active_field(self): self.state.field = None instruction = (constants.MUTATOR_FIELD_END, constants.GRASSY_TERRAIN) list_of_instructions = [instruction] self.mutator.reverse(list_of_instructions) if self.state.field != constants.GRASSY_TERRAIN: self.fail("Terrain was not reset") self.mutator.apply(list_of_instructions) self.assertEqual(None, self.state.field) def test_toggle_trickroom_sets_trickroom(self): self.state.trick_room = False instruction = (constants.MUTATOR_TOGGLE_TRICKROOM, ) list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) self.assertTrue(self.state.trick_room) def test_reverse_instruction_unsets_trickroom(self): self.state.trick_room = True instruction = (constants.MUTATOR_TOGGLE_TRICKROOM, ) list_of_instructions = [instruction] self.mutator.reverse(list_of_instructions) self.assertFalse(self.state.trick_room) def test_reverse_instruction_sets_trickroom(self): self.state.trick_room = False instruction = (constants.MUTATOR_TOGGLE_TRICKROOM, ) list_of_instructions = [instruction] self.mutator.reverse(list_of_instructions) self.assertTrue(self.state.trick_room) def test_toggle_trickroom_unsets_trickroom(self): self.state.trick_room = True instruction = (constants.MUTATOR_TOGGLE_TRICKROOM, ) list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) self.assertFalse(self.state.trick_room) def test_apply_and_reverse_trickroom(self): self.state.trick_room = False instruction = (constants.MUTATOR_TOGGLE_TRICKROOM, ) list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) if not self.state.trick_room: self.fail("Trickroom was not set") self.mutator.reverse(list_of_instructions) self.assertFalse(self.state.trick_room) def test_change_types_properly_changes_types(self): self.state.self.active.types = ['normal'] instruction = (constants.MUTATOR_CHANGE_TYPE, constants.SELF, ['water'], self.state.self.active.types) list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) self.assertEqual(['water'], self.state.self.active.types) def test_reverse_change_types(self): self.state.self.active.types = ['water'] instruction = (constants.MUTATOR_CHANGE_TYPE, constants.SELF, ['water'], ['normal']) list_of_instructions = [instruction] self.mutator.reverse(list_of_instructions) self.assertEqual(['normal'], self.state.self.active.types) def test_apply_and_reverse_change_types(self): self.state.self.active.types = ['normal'] instruction = (constants.MUTATOR_CHANGE_TYPE, constants.SELF, ['water', 'grass'], self.state.self.active.types) list_of_instructions = [instruction] self.mutator.apply(list_of_instructions) if self.state.self.active.types != ['water', 'grass']: self.fail('types were not changed') self.mutator.reverse(list_of_instructions) self.assertEqual(['normal'], self.state.self.active.types)
'id': 'dragondance', 'disabled': False, 'current_pp': 32 }, { 'id': 'earthquake', 'disabled': False, 'current_pp': 16 }, { 'id': 'bounce', 'disabled': False, 'current_pp': 8 }], 'types': ['water', 'flying'], 'canMegaEvo': False } }, 'side_conditions': { 'toxic_count': 0 }, 'trapped': False }, 'weather': None, 'field': None, 'trickroom': False, 'forceSwitch': False, 'wait': False }) mutator = StateMutator(state) state_instructions = get_payoff_matrix(mutator, depth=3)
def setUp(self): self.state = State( Side( Pokemon.from_state_pokemon_dict( StatePokemon("raichu", 73).to_dict()), { "xatu": Pokemon.from_state_pokemon_dict( StatePokemon("xatu", 81).to_dict()), "starmie": Pokemon.from_state_pokemon_dict( StatePokemon("starmie", 81).to_dict()), "gyarados": Pokemon.from_state_pokemon_dict( StatePokemon("gyarados", 81).to_dict()), "dragonite": Pokemon.from_state_pokemon_dict( StatePokemon("dragonite", 81).to_dict()), "hitmonlee": Pokemon.from_state_pokemon_dict( StatePokemon("hitmonlee", 81).to_dict()), }, defaultdict(lambda: 0), False), Side( Pokemon.from_state_pokemon_dict( StatePokemon("aromatisse", 81).to_dict()), { "yveltal": Pokemon.from_state_pokemon_dict( StatePokemon("yveltal", 73).to_dict()), "slurpuff": Pokemon.from_state_pokemon_dict( StatePokemon("slurpuff", 73).to_dict()), "victini": Pokemon.from_state_pokemon_dict( StatePokemon("victini", 73).to_dict()), "toxapex": Pokemon.from_state_pokemon_dict( StatePokemon("toxapex", 73).to_dict()), "bronzong": Pokemon.from_state_pokemon_dict( StatePokemon("bronzong", 73).to_dict()), }, defaultdict(lambda: 0), False), None, None, False, False, False) self.mutator = StateMutator(self.state) self.state.self.active.moves = [ { constants.ID: 'tackle', constants.DISABLED: False }, { constants.ID: 'charm', constants.DISABLED: False }, { constants.ID: 'growl', constants.DISABLED: False }, { constants.ID: 'stringshot', constants.DISABLED: False }, ] self.state.opponent.active.moves = [ { constants.ID: 'tackle', constants.DISABLED: False }, { constants.ID: 'charm', constants.DISABLED: False }, { constants.ID: 'growl', constants.DISABLED: False }, { constants.ID: 'stringshot', constants.DISABLED: False }, ]
def pick_move_from_battles(self, battles): # Only work on current battle # In practice the bot can play several at once, for now we simplified to one battle. battle = battles[0] root = Node("Root") state = battle.create_state() mutator = StateMutator(state) user_options, opponent_options = battle.get_all_options() logger.debug("Attempting to find best move from: {}".format(mutator.state)) #get the scores from the "safest" algorithm provided by the starter code scores = get_payoff_matrix(mutator, user_options, opponent_options, depth=2, prune=False) # Create tree using payoff matrix from "Safest" algorithm checked_moves = {} for (myMove, opponentMove), score in scores.items(): #checked moves keeps track of which nodes have been added to the tree, so we dont duplicate. if myMove not in checked_moves: child = Node(myMove, root) checked_moves[myMove] = child grandchild = Node(opponentMove, checked_moves[myMove]) Node(score, grandchild) # a library function that prints our tree for readability. for pre, _, node in RenderTree(root): print("%s%s" % (pre, node.name)) myTotalHP = 0.0 # max of 600, 100 points for full hp oppTotalHP = 0.0 # calculate my total hp my_pokes = state.self # get active pokemon hp if it isn't dead if my_pokes.active.maxhp != 0: myTotalHP += my_pokes.active.hp / my_pokes.active.maxhp # get reserve pokmeons hps for p in my_pokes.reserve.values(): if p.maxhp !=0: myTotalHP += p.hp / p.maxhp myTotalHP *= 100 # calculate opp total hp opp_pokes = state.opponent # get active pokemon hp if opp_pokes.active.maxhp != 0: oppTotalHP += opp_pokes.active.hp / opp_pokes.active.maxhp # get reserve pokmeons hps for p in opp_pokes.reserve.values(): if p.maxhp !=0: oppTotalHP += p.hp / p.maxhp #accounts for the pokemon of opponent that have not been revealed unseenPoke = 5-len(opp_pokes.reserve) oppTotalHP += unseenPoke oppTotalHP *=100 possibleStatuses = { "psn" : .06, "frz" : .25, "tox" : .19, "par" : .16, "slp" : .16, "brn" : .20, None: 0, } statBuffs = { 6 : .25, 5 : .235, 4 : .22, 3 : .19, 2 : .15, 1 : .08, 0 : 0, -1 : -.08, -2 : -.15, -3 : -.19, -4 : -.22, -5 : -.235, -6 : -.25, } # check how many status conditions we have myStatuses = 0 # active pokemon myStatuses += possibleStatuses[my_pokes.active.status] # reserve pokemon for p in my_pokes.reserve.values(): myStatuses += possibleStatuses[p.status] # check how many status conditions opponent has opponentStatuses = 0 # active pokemon opponentStatuses += possibleStatuses[opp_pokes.active.status] # reserve pokemon for p in opp_pokes.reserve.values(): opponentStatuses += possibleStatuses[p.status] status_aggression_multiplier = 1 status_aggression_modifier = (opponentStatuses - myStatuses) * status_aggression_multiplier print(f"Status modifier:{status_aggression_modifier}") # Stat Buffs # check how many stat boosts/nerfs we have myBuffs = 0 # active pokemon myBuffs += statBuffs[my_pokes.active.accuracy_boost] myBuffs += statBuffs[my_pokes.active.attack_boost] myBuffs += statBuffs[my_pokes.active.defense_boost] myBuffs += statBuffs[my_pokes.active.evasion_boost] myBuffs += statBuffs[my_pokes.active.special_attack_boost] myBuffs += statBuffs[my_pokes.active.special_defense_boost] myBuffs += statBuffs[my_pokes.active.speed_boost] # check how many stat boosts/nerfs opponent has oppBuffs = 0 # active pokemon oppBuffs += statBuffs[opp_pokes.active.accuracy_boost] oppBuffs += statBuffs[opp_pokes.active.attack_boost] oppBuffs += statBuffs[opp_pokes.active.defense_boost] oppBuffs += statBuffs[opp_pokes.active.evasion_boost] oppBuffs += statBuffs[opp_pokes.active.special_attack_boost] oppBuffs += statBuffs[opp_pokes.active.special_defense_boost] oppBuffs += statBuffs[opp_pokes.active.speed_boost] buff_aggression_multiplier = 1 buff_aggression_modifier = (myBuffs - oppBuffs) * buff_aggression_multiplier print(f"Buff modifier:{buff_aggression_modifier}") safety = (3 + status_aggression_modifier + buff_aggression_modifier) * myTotalHP/oppTotalHP if safety < 0: print("WARNING: safety constant is less than 0, changing to 0.1") safety = 0.1 print(f"bot will play with safety constant of {safety}") bot_choice = self.aggressive_pick(root,safety) print(f"choice: {bot_choice}") print(f"the safest pick was {self.safest_pick(root)}") print("Choice: {}".format(bot_choice)) return bot_choice