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 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 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
'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)
'canMegaEvo': False } }, 'side_conditions': { 'toxic_count': 0 }, 'trapped': False }, 'weather': None, 'field': None, 'trickroom': False, 'forceSwitch': False, 'wait': False } # second = {'self': {'active': {'id': 'audino', 'level': 81, 'hp': 299, 'maxhp': 299, 'ability': 'regenerator', 'baseStats': {'hp': 103, 'attack': 60, 'defense': 86, 'special-attack': 60, 'special-defense': 86, 'speed': 50}, 'attack': 144, 'defense': 186, 'special-attack': 144, 'special-defense': 186, 'speed': 128, 'attack_boost': 0, 'defense_boost': 0, 'special_attack_boost': 0, 'special_defense_boost': 0, 'speed_boost': 0, 'status': None, 'volatileStatus': [], 'moves': [{'id': 'healbell', 'disabled': False, 'current_pp': 8}, {'id': 'protect', 'disabled': False, 'current_pp': 16}, {'id': 'wish', 'disabled': False, 'current_pp': 16}, {'id': 'hypervoice', 'disabled': False, 'current_pp': 16}], 'types': ['normal']}, 'reserve': {'heatran': {'id': 'heatran', 'level': 75, 'hp': 0, 'maxhp': 260, 'ability': 'flashfire', 'baseStats': {'hp': 91, 'attack': 90, 'defense': 106, 'special-attack': 130, 'special-defense': 106, 'speed': 77}, 'attack': 179, 'defense': 203, 'special-attack': 239, 'special-defense': 203, 'speed': 159, 'attack_boost': 0, 'defense_boost': 0, 'special_attack_boost': 0, 'special_defense_boost': 0, 'speed_boost': 0, 'status': None, 'volatileStatus': [], 'moves': [{'id': 'roar', 'disabled': False, 'current_pp': 32}, {'id': 'stealthrock', 'disabled': False, 'current_pp': 32}, {'id': 'earthpower', 'disabled': False, 'current_pp': 16}, {'id': 'lavaplume', 'disabled': False, 'current_pp': 24}], 'types': ['fire', 'steel']}, 'omastar': {'id': 'omastar', 'level': 81, 'hp': 246, 'maxhp': 246, 'ability': 'swiftswim', 'baseStats': {'hp': 70, 'attack': 60, 'defense': 125, 'special-attack': 115, 'special-defense': 70, 'speed': 55}, 'attack': 144, 'defense': 249, 'special-attack': 233, 'special-defense': 160, 'speed': 136, 'attack_boost': 0, 'defense_boost': 0, 'special_attack_boost': 0, 'special_defense_boost': 0, 'speed_boost': 0, 'status': None, 'volatileStatus': [], 'moves': [{'id': 'scald', 'disabled': False, 'current_pp': 24}, {'id': 'shellsmash', 'disabled': False, 'current_pp': 24}, {'id': 'earthpower', 'disabled': False, 'current_pp': 16}, {'id': 'icebeam', 'disabled': False, 'current_pp': 16}], 'types': ['rock', 'water']}, 'alomomola': {'id': 'alomomola', 'level': 77, 'hp': 381, 'maxhp': 381, 'ability': 'regenerator', 'baseStats': {'hp': 165, 'attack': 75, 'defense': 80, 'special-attack': 40, 'special-defense': 45, 'speed': 65}, 'attack': 160, 'defense': 168, 'special-attack': 106, 'special-defense': 114, 'speed': 145, 'attack_boost': 0, 'defense_boost': 0, 'special_attack_boost': 0, 'special_defense_boost': 0, 'speed_boost': 0, 'status': None, 'volatileStatus': [], 'moves': [{'id': 'knockoff', 'disabled': False, 'current_pp': 32}, {'id': 'wish', 'disabled': False, 'current_pp': 16}, {'id': 'toxic', 'disabled': False, 'current_pp': 16}, {'id': 'scald', 'disabled': False, 'current_pp': 24}], 'types': ['water']}, 'hawlucha': {'id': 'hawlucha', 'level': 75, 'hp': 105, 'maxhp': 241, 'ability': 'unburden', 'baseStats': {'hp': 78, 'attack': 92, 'defense': 75, 'special-attack': 74, 'special-defense': 63, 'speed': 118}, 'attack': 182, 'defense': 156, 'special-attack': 155, 'special-defense': 138, 'speed': 221, 'attack_boost': 0, 'defense_boost': 0, 'special_attack_boost': 0, 'special_defense_boost': 0, 'speed_boost': 0, 'status': None, 'volatileStatus': [], 'moves': [{'id': 'swordsdance', 'disabled': False, 'current_pp': 32}, {'id': 'stoneedge', 'disabled': False, 'current_pp': 8}, {'id': 'roost', 'disabled': False, 'current_pp': 16}, {'id': 'highjumpkick', 'disabled': False, 'current_pp': 16}], 'types': ['fighting', 'flying']}, 'swalot': {'id': 'swalot', 'level': 83, 'hp': 129, 'maxhp': 302, 'ability': 'stickyhold', 'baseStats': {'hp': 100, 'attack': 73, 'defense': 83, 'special-attack': 73, 'special-defense': 83, 'speed': 55}, 'attack': 169, 'defense': 185, 'special-attack': 169, 'special-defense': 185, 'speed': 139, 'attack_boost': 0, 'defense_boost': 0, 'special_attack_boost': 0, 'special_defense_boost': 0, 'speed_boost': 0, 'status': None, 'volatileStatus': [], 'moves': [{'id': 'icebeam', 'disabled': False, 'current_pp': 16}, {'id': 'encore', 'disabled': False, 'current_pp': 8}, {'id': 'yawn', 'disabled': False, 'current_pp': 16}, {'id': 'sludgebomb', 'disabled': False, 'current_pp': 16}], 'types': ['poison']}}, 'side_conditions': {'stealthrock': 0, 'spikes': 0}, 'trapped': False}, 'opponent': {'active': {'id': 'zekrom', 'level': 73, 'hp': 202.16, 'maxhp': 266, 'ability': 'teravolt', 'baseStats': {'hp': 100, 'attack': 150, 'defense': 120, 'special-attack': 120, 'special-defense': 100, 'speed': 90}, 'attack': 261, 'defense': 218, 'special-attack': 218, 'special-defense': 188, 'speed': 174, 'attack_boost': 0, 'defense_boost': 0, 'special_attack_boost': 0, 'special_defense_boost': 0, 'speed_boost': 0, 'status': None, 'volatileStatus': [], 'moves': [{'id': 'boltstrike', 'disabled': False, 'current_pp': 8}, {'id': 'substitute', 'disabled': False, 'current_pp': 16}], 'types': ['dragon', 'electric']}, 'reserve': {'rotomfrost': {'id': 'rotomfrost', 'level': 83, 'hp': 199.29000000000002, 'maxhp': 219, 'ability': 'levitate', 'baseStats': {'hp': 50, 'attack': 65, 'defense': 107, 'special-attack': 105, 'special-defense': 107, 'speed': 86}, 'attack': 156, 'defense': 225, 'special-attack': 222, 'special-defense': 225, 'speed': 190, 'attack_boost': 0, 'defense_boost': 0, 'special_attack_boost': 0, 'special_defense_boost': 0, 'speed_boost': 0, 'status': None, 'volatileStatus': [], 'moves': [{'id': 'voltswitch', 'disabled': False, 'current_pp': 32}], 'types': ['electric', 'ice']}, 'bibarel': {'id': 'bibarel', 'level': 83, 'hp': 0, 'maxhp': 267, 'ability': None, 'baseStats': {'hp': 79, 'attack': 85, 'defense': 60, 'special-attack': 55, 'special-defense': 60, 'speed': 71}, 'attack': 189, 'defense': 147, 'special-attack': 139, 'special-defense': 147, 'speed': 166, 'attack_boost': 0, 'defense_boost': 0, 'special_attack_boost': 0, 'special_defense_boost': 0, 'speed_boost': 0, 'status': None, 'volatileStatus': [], 'moves': [{'id': 'return', 'disabled': False, 'current_pp': 32}], 'types': ['normal', 'water']}, 'passimian': {'id': 'passimian', 'level': 83, 'hp': 193.28, 'maxhp': 302, 'ability': None, 'baseStats': {'hp': 100, 'attack': 120, 'defense': 90, 'special-attack': 40, 'special-defense': 60, 'speed': 80}, 'attack': 247, 'defense': 197, 'special-attack': 114, 'special-defense': 147, 'speed': 180, 'attack_boost': 0, 'defense_boost': 0, 'special_attack_boost': 0, 'special_defense_boost': 0, 'speed_boost': 0, 'status': None, 'volatileStatus': [], 'moves': [{'id': 'uturn', 'disabled': False, 'current_pp': 32}], 'types': ['fighting']}}, 'side_conditions': {'stealthrock': 0, 'spikes': 0}, 'trapped': False}, 'weather': None, 'field': None, 'forceSwitch': False, 'wait': False} state = State.from_dict(first) mutator = StateMutator(state) instruction = get_all_state_instructions(mutator, 'flareblitz', 'nastyplot') scores = get_payoff_matrix(mutator, depth=2) df = pd.Series(scores).unstack() averages = df.mean(axis=1) safest = pick_safest(scores) pass
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