def predict_dqn(self): # get size of state and action from environment state_size = 4 action_size = 2 agent = DQNAgent(state_size, action_size, load_model=True) done = False score = 0 self.reset() state, _, _, _ = self.step(-1) state = np.reshape(state, [1, state_size]) while not done: # get action for the current state and go one step in environment action = agent.get_action(state) next_state, reward, done, info = self.step(action) next_state = np.reshape(next_state, [1, state_size]) score += reward state = next_state if done or score >= 500: print("score:", score) break
class Player: def __init__(self, player_name=None, letter=None): if player_name is None: self.player_name = common_utils.get_random_name() else: self.player_name = player_name if letter is not None: self.letter = letter else: pass # TODO: Handle this if letter == 'X': self.enemy_letter = 'O' else: self.enemy_letter = 'X' logger.debug("Initializing player {} with letter {} ...".format( self.player_name, self.letter)) self.agent = DQNAgent() def get_move(self, game_state): move = self.rule_based_algo(game_state) return move def __get_board_copy(self, board): # Make a duplicate of the board list and return it the duplicate. dup_board = [] for i in board: dup_board.append(i) return dup_board def __make_mock_move(self, board, letter, move): board[move] = letter def __choose_random_move_from_list(self, board, moves_list): # Returns a valid move from the passed list on the passed board. # Returns None if there is no valid move. possible_moves = [] for i in moves_list: if game_env.is_space_free(board, i): possible_moves.append(i) if len(possible_moves) != 0: return random.choice(possible_moves) else: return None def get_player_name(self): return self.player_name def rule_based_algo(self, game_state): """ Simple rule based algorithm that returns the move after computing checking if we can win in the next move or else block the enemy's move if he has a chance to win. :param game_state: :return: """ board = game_state.get_board() # determine where to move and return that move. # Here is our algorithm for our Tic Tac Toe AI: # First, check if we can win in the next move for i in range(1, 10): board_copy = self.__get_board_copy(board) if game_env.is_space_free(board_copy, i): self.__make_mock_move(board_copy, self.letter, i) if game_env.is_winner(board_copy, self.letter): return i # Check if the player could win on his next move, and block them. for i in range(1, 10): board_copy = self.__get_board_copy(board) if game_env.is_space_free(board_copy, i): self.__make_mock_move(board_copy, self.enemy_letter, i) if game_env.is_winner(board_copy, self.enemy_letter): return i # Try to take one of the corners, if they are free. move = self.__choose_random_move_from_list(board, [1, 3, 7, 9]) if move is not None: return move # Try to take the center, if it is free. if game_env.is_space_free(board, 5): return 5 # Move on one of the sides. return self.__choose_random_move_from_list(board, [2, 4, 6, 8]) def drl_based_action_selection(self, game_state): """ A DRL based approach for action selection. :return: action """ board = game_state.get_board() legal_moves = game_state.get_legal_moves(board) move = None if len(legal_moves) == 0: logger.warn("Something wrong. Check implementation!") return if len(legal_moves) == 1: # if only one move allowed then simply make that move move = legal_moves[0] return move # encode the board vector encoded_state = self.__encode_state(board) # If multiple moves available then select the most apt one using DRL self.agent.get_action(encoded_state, legal_moves) def update_learning(self, game_state, action, reward, new_state): pass def __encode_state(self, state): """ Encodes the state vector with the following vector - (S, O, E). This is done so that the learning remains consistent across games. There is a clear distinction between the player's own pieces and opponents pieces irrespective of the player_letter chosen(X / O). S - Self O - Opponent E - Empty :return: encoded state vector """ encoded_state = [] for s in state: if s == self.letter: encoded_state.append('S') elif s == self.enemy_letter: encoded_state.append('O') else: encoded_state.append('E') return encoded_state
def train_dqn(self): # get size of state and action from environment state_size = 4 action_size = 2 agent = DQNAgent(state_size, action_size) scores, episodes = [], [] for episode in range(1, EPISODES + 1): # run reinforcement learning for every episode done = False score = 0 self.reset() state, _, _, _ = self.step(-1) state = np.reshape(state, [1, state_size]) rospy.loginfo("Episode %d: starting", episode) while not done: action = agent.get_action(state) next_state, reward, done, _, = self.step(action) rospy.loginfo("Episode %d: action: %d pitch: %f", episode, action, next_state[0]) next_state = np.reshape(next_state, [1, state_size]) # if an action make the episode end, then gives penalty of -100 reward = reward if not done or score == 499 else -100 # save the sample <s, a, r, s'> to the replay memory agent.append_sample(state, action, reward, next_state, done) # every time step do the training agent.train_model() score += reward state = next_state if done or score >= 500: # every episode update the target model to be same with model agent.update_target_model() # every episode, plot the play time score = score if score == 500 else score + 100 scores.append(score) episodes.append(episode) pylab.plot(episodes, scores, "b") pylab.savefig("./cartpole_dqn.png") print( "episode:", episode, " score:", score, " memory length:", len(agent.memory), " epsilon:", agent.epsilon, ) break # if the mean of scores of last 10 episode is bigger than 490 # stop training if np.mean(scores[-min(10, len(scores)):]) > 490: break # save the model if episode % 20 == 0: agent.model.save_weights("./cartpole_dqn.h5") rospy.loginfo("Episode %d: completed", episode)
class BotClient(showdown.Client): health_regex = re.compile(r'(?P<numerator>[0-9]+)/(?P<denominator>[0-9]+)') def __init__(self, name='', password='', loop=None, max_room_logs=5000, server_id='showdown', server_host=None, expected_opponent=None, team=None, challenge=False, runType=RunType.Iterations, runTypeData=1, agent=None, print_stats=False, trainer=False, save_model=True, should_write_replay=False): self.should_write_replay = should_write_replay self.done = False if expected_opponent == None: raise Exception("No expected opponent found in arguments") else: self.expected_opponent = expected_opponent if team == None: raise Exception("No team found in arguments") else: self.team_text = team self.iterations_run = 0 self.runType = runType if self.runType == RunType.Iterations: self.iterations = runTypeData elif self.runType == RunType.Epochs: self.epochs = runTypeData if agent == None: self.agent = RandomAgent() else: self.agent = agent self.state_vl = None self.action = None self.logs_dir = LOGS_DIR if not os.path.exists(self.logs_dir): os.mkdir(self.logs_dir) self.replay_memory_dir = REPLAY_MEMORY_DIR if not os.path.exists(self.replay_memory_dir): os.mkdir(self.replay_memory_dir) self.datestring = datetime.now().strftime('%y-%m-%d-%H-%M-%S') self.update_log_paths() self.challenge = challenge self.has_challenged = False # flag used to detect the first 'request' inp_type # first request is used to initialize moves for gamestate # Rreset to false for every battle self.is_first_request = True # Keep a track of zmoves used, as each pokemon can use z moves only once # per battle, we update each zmove here used per battle so it can't be used # again. Reset to empty list after battle ends # type: [{pokemon_name : zmove}] self.zmoves_tracker = [] self.wins = 0 self.losses = 0 self.print_stats = print_stats self.trainer = trainer super().__init__(name=name, password=password, loop=loop, max_room_logs=max_room_logs, server_id=server_id, server_host=server_host) def update_log_paths(self): self.log_file = os.path.join( self.logs_dir, f'{self.datestring}_Iteration{self.iterations_run}.txt') self.agent.log_path = self.log_file self.agent.replay_memory_path = os.path.join( self.logs_dir, 'replay_memory', f'{self.datestring}_Iteration{self.iterations_run}_replaymemory.txt' ) self.agent.model_path = os.path.join( self.logs_dir, f'{self.datestring}_Iteration{self.iterations_run}.model') def write_replay_memory(self): self.agent.write_replay_memory() def log(self, *args): now = datetime.now() l = [str(arg) for arg in args] string = ' '.join(l) with open(self.log_file, 'a') as fd: fd.write(f'[{datetime.now()}] {string}\n') def should_play_new_game(self): if self.runType == RunType.Iterations: return self.iterations_run < self.iterations elif self.runType == RunType.Epochs: return self.agent.current_epoch < self.epochs elif self.runType == RunType.Forever: return True else: self.log(f'Unexpected run type {self.runType}') raise Exception('Unexpected run type') def save_replay(self, room_obj): replays_dir = os.path.join(BOT_DIR, 'replays') if not os.path.exists(replays_dir): os.mkdir(replays_dir) replay_file = f'{self.datestring}_Iteration{self.iterations_run}.html' with open(os.path.join(replays_dir, replay_file), 'wb') as f: f.write(util.get_replay_header()) joined = '\n'.join(room_obj.logs) f.write('\n'.join(room_obj.logs).encode('utf-8')) f.write(util.get_replay_footer()) @staticmethod def get_team_info(data): return data['side']['pokemon'] async def switch_pokemon(self, room_obj, data): ''' For use with forceswitch scenarios ''' try: valid_actions = [] team_info = self.get_team_info(data) for pokemon_index, pokemon_info in enumerate(team_info): fainted = 'fnt' in pokemon_info.get('condition') if (not pokemon_info.get('active', False) and not fainted): pokemon_name = self.gs.pokemon_name_clean( pokemon_info['details']) valid_actions.append( (pokemon_index + 1, pokemon_name, ActionType.Switch)) self.log(f'valid_actions: {valid_actions}') action_index, action_string, action_type, _ = \ self.agent.get_action(self.gs.vector_list, valid_actions) except Exception as err: self.log_error(err) if action_type == ActionType.Switch: await room_obj.switch(action_index) else: self.log(f'Unexpected action type {action_type}') async def take_action(self, room_obj, data, delay=0): await asyncio.sleep(delay) #NOTE: delay is here to make sure we get all the data before taking action try: moves = data.get('active')[0].get('moves') valid_actions = [] for move_index, move_data in enumerate(moves): if ((move_data.get('pp', 0) > 0 and not move_data.get('disabled')) or move_data.get('move') == 'Struggle'): valid_actions.append( (move_index + 1, clean_move_name(move_data['move']), ActionType.Move)) move_count = len(moves) team_info = self.get_team_info(data) for pokemon_index, pokemon_info in enumerate(team_info): fainted = 'fnt' in pokemon_info.get('condition') if (not pokemon_info.get('active', False) and not fainted): pokemon_name = self.gs.pokemon_name_clean( pokemon_info['details']) valid_actions.append( (pokemon_index + 1, pokemon_name, ActionType.Switch)) self.log(f'valid_actions: {valid_actions}') action_index, action_string, action_type, self.action = \ self.agent.get_action(self.gs.vector_list, valid_actions) except Exception as err: self.log_error(err) if action_type == ActionType.Move: await room_obj.move(action_index) elif action_type == ActionType.Switch: await room_obj.switch(action_index) else: self.log(f'Unexpected action type {action_type}') def own_pokemon(self, pokemon_data): return pokemon_data.startswith(self.position) @staticmethod def get_owner(pokemon_data): return pokemon_data.split(':')[0].strip() @staticmethod def get_pokemon(pokemon_data): return pokemon_data.split(':')[1].strip() def add_status(self, player_name, pokemon_name, status): self.gs.set_status(player_name, pokemon_name, status) def remove_status(self, player_name, pokemon_name, status): self.gs.remove_status(player_name, pokemon_name, status) async def challenge_expected(self): self.log("Challenging {}".format(self.expected_opponent)) await self.cancel_challenge() time.sleep(1) await self.send_challenge(self.expected_opponent, self.team_text, 'gen7ou') def set_and_check_team(self, player, team): self.gs.set_team(player, team) for position, member in enumerate(team): vector_pokemon = self.gs.check_team_position(player, position) if member != vector_pokemon: self.log('WARNING: mismatched pokemon') else: types = TYPE_MAP.get(vector_pokemon) self.log(f'{vector_pokemon} has types from TYPE_MAP: {types}') self.gs.set_types(player, vector_pokemon, types) has_types = self.gs.check_types(player, vector_pokemon) if set(has_types) != set(types): self.log(f'WARNING: {vector_pokemon} has unexpected types') self.log(f'{vector_pokemon} has types {has_types}') def log_error(self, err): self.log('ERROR') self.log(''.join(traceback.format_tb(err.__traceback__))) def update_replay_memory(self, transition): self.agent.update_replay_memory(transition) async def on_receive(self, room_id, inp_type, params): try: self.log(f'Input type: {inp_type}') self.log(f'Params: {params}') except Exception as err: self.log_error(err) try: room_obj = self.rooms.get(room_id) if room_obj and room_obj.id.startswith('battle-'): if inp_type == 'poke': owner = params[0] pokename = GameState.pokemon_name_clean(params[1]) if owner == self.position: self.team.append(pokename) else: self.opp_team.append(pokename) if (len(self.team) == self.teamsize and len(self.opp_team) == self.opp_teamsize): self.gs = GameState() self.set_and_check_team(GameState.Player.one, self.team) self.set_and_check_team(GameState.Player.two, self.opp_team) self.gs.init_health(GameState.Player.one) self.gs.init_health(GameState.Player.two) self.gs.reset_boosts(GameState.Player.one) self.gs.reset_boosts(GameState.Player.two) #NOTE: Select starting pokemon here valid_actions = [] for pokemon_index, pokemon_name in enumerate( self.team): pokemon_name = self.gs.pokemon_name_clean( pokemon_name) valid_actions.append( (pokemon_index + 1, pokemon_name, ActionType.Switch)) action_index, action_string, action_type, _ = \ self.agent.get_action(self.gs.vector_list, valid_actions) if action_type == ActionType.Switch: await room_obj.start_poke(action_index) else: self.log(f'Unexpected action type {action_type}') elif inp_type == 'teamsize': position = params[0] if position == self.position: self.teamsize = int(params[1]) self.team = [] else: self.opp_teamsize = int(params[1]) self.opp_team = [] elif inp_type == 'player': name = params[1] position = params[0] if name == self.name: self.position = position elif inp_type == 'turn': self.turn_number = int(params[0]) self.log(f'Weather: {self.gs.all_weather()}') self.log( f'P1 Boosts: {self.gs.all_boosts(GameState.Player.one)}' ) self.log( f'P2 Boosts: {self.gs.all_boosts(GameState.Player.two)}' ) active_pokemon = self.gs.all_active(GameState.Player.one) self.log(f'P1 active: {active_pokemon}') if len(active_pokemon) > 1: self.log('ERROR: More than one active pokemon') active_pokemon = self.gs.all_active(GameState.Player.two) self.log(f'P2 active: {active_pokemon}') if len(active_pokemon) > 1: self.log('ERROR: More than one active pokemon') self.log( f'P1 health: {self.gs.all_health(GameState.Player.one)}' ) self.log( f'P2 health: {self.gs.all_health(GameState.Player.two)}' ) self.log( f'P1 fainted: {self.gs.all_fainted(GameState.Player.one)}' ) self.log( f'P2 fainted: {self.gs.all_fainted(GameState.Player.two)}' ) self.log( f'P1 types: {self.gs.all_types(GameState.Player.one)}') self.log( f'P2 types: {self.gs.all_types(GameState.Player.two)}') self.log( f'P1 statuses: {self.gs.all_statuses(GameState.Player.one)}' ) self.log( f'P2 statuses: {self.gs.all_statuses(GameState.Player.two)}' ) self.log( f'P1 moves: {self.gs.all_moves(GameState.Player.one)}') self.log( f'P2 moves: {self.gs.all_moves(GameState.Player.two)}') self.log( f'P1 active slot check: {self.gs.check_active_slot(GameState.Player.one)}' ) self.log( f'P2 active slot check: {self.gs.check_active_slot(GameState.Player.two)}' ) if self.turn_number == 1: self.state_vl = self.gs.vector_list reward = 0 else: last_state = [element for element in self.state_vl] self.state_vl = [ element for element in self.gs.vector_list ] done = False reward = calculate_reward(self, last_state, self.state_vl) transition = (last_state, self.action, reward, self.state_vl, done) self.update_replay_memory(transition) self.agent.train(False) self.log(f'This transition\'s reward was {reward}') elif inp_type == 'request': json_string = params[0] data = json.loads(json_string) wait = data.get('wait', False) if not wait: team_info = self.get_team_info(data) self.last_request_data = data # Initialize all available pokemon moves for game stats if self.is_first_request: for pokemon_info in team_info: pokemon_name = GameState.pokemon_name_clean( pokemon_info['details']) for move_name in pokemon_info['moves']: # Initially PP = Max PP, so pseudo PP, Max PP values to set move # as PP, Max PP are available for only active Pokemons self.gs.set_move(GameState.Player.one, pokemon_name, move_name, 1.0, 1.0) self.is_first_request = False # Update PP for the active pokemon only else: for pokemon_info in team_info: pokemon_name = GameState.pokemon_name_clean( pokemon_info['details']) if pokemon_info['active'] == True: if 'active' in data: moves = data['active'][0]['moves'] for move in moves: self.gs.set_move( GameState.Player.one, pokemon_name, move['id'], move['pp'], move['maxpp']) if 'canZMove' in data['active'][0]: if pokemon_name not in self.zmoves_tracker: zmove_id = util.move_name_to_id( data['active'][0] ['canZMove'][1]['move']) self.gs.set_move( GameState.Player.one, pokemon_name, zmove_id, 1.0, 1.0) # Update pokemon stat and items for game state for pokemon_info in team_info: pokemon_name = GameState.pokemon_name_clean( pokemon_info['details']) stats = pokemon_info['stats'] for stat_name in stats: self.gs.set_stat(GameState.Player.one, pokemon_name, stat_name, stats[stat_name]) item = pokemon_info['item'] # If item key has empty string if no item possesed by Pokemon, # item could have been knocked out or used my Pokemon if item == '': self.gs.clear_all_items( GameState.Player.one, pokemon_name) # Else update item with the current item even if there is no change # clear old item and set new item as a Pokemon can possess only an item at a time else: self.gs.clear_all_items( GameState.Player.one, pokemon_name) self.gs.set_item(GameState.Player.one, pokemon_name, item) force_switch = data.get('forceSwitch', [False])[0] if force_switch == True: await self.switch_pokemon(room_obj, data) else: await self.take_action(room_obj, data, delay=0.3) elif inp_type == '-status': pokemon_data = params[0] pokemon_name = self.get_pokemon(pokemon_data) #TODO: remove this hack and have a good way of handling #TODO: detailed vs. non-detailed pokemon names pokemon_name = hack_name(pokemon_name) status = params[1] if self.own_pokemon(pokemon_data): self.add_status(GameState.Player.one, pokemon_name, status) else: self.add_status(GameState.Player.two, pokemon_name, status) elif inp_type == '-start': pokemon_data = params[0] pokemon_name = self.get_pokemon(pokemon_data) status = params[1] if self.own_pokemon(pokemon_data): self.add_status(GameState.Player.one, pokemon_name, status) else: self.add_status(GameState.Player.two, pokemon_name, status) elif inp_type == '-end': pokemon_data = params[0] pokemon_name = self.get_pokemon(pokemon_data) status = params[1] if self.own_pokemon(pokemon_data): self.remove_status(GameState.Player.one, pokemon_name, status) else: self.remove_status(GameState.Player.two, pokemon_name, status) elif inp_type == '-curestatus': pokemon_data = params[0] pokemon_name = self.get_pokemon(pokemon_data) status = params[1] if self.own_pokemon(pokemon_data): self.remove_status(GameState.Player.one, pokemon_name, status) else: self.remove_status(GameState.Player.two, pokemon_name, status) elif inp_type == 'switch': name_with_owner = params[0] name_with_details = params[1] my_pokemon = self.own_pokemon(name_with_owner) volatile_statuses = ['confusion', 'curse'] if my_pokemon: pokemon_name = self.active_pokemon gs_player = GameState.Player.one else: pokemon_name = self.opp_active_pokemon gs_player = GameState.Player.two self.gs.reset_boosts(gs_player) if pokemon_name != None: for volatile_status in volatile_statuses: self.remove_status(gs_player, pokemon_name, volatile_status) new_active_name = GameState.pokemon_name_clean( name_with_details) if not my_pokemon: self.opp_active_pokemon = new_active_name self.gs.set_active(GameState.Player.two, self.opp_active_pokemon) if not self.gs.check_active(GameState.Player.two, self.opp_active_pokemon): self.log(f'WARNING: {self.opp_active_pokemon}' ' was not active as expected') else: self.active_pokemon = new_active_name self.gs.set_active(GameState.Player.one, self.active_pokemon) if not self.gs.check_active(GameState.Player.one, self.active_pokemon): self.log(f'WARNING: {self.active_pokemon}' ' was not active as expected') elif inp_type == '-sidestart': position = params[0].split(':')[0] hazard = params[1].lstrip('move: ') if position.startswith(self.position): self.gs.increment_entry_hazard(GameState.Player.one, hazard) else: self.gs.increment_entry_hazard(GameState.Player.two, hazard) elif inp_type == '-sideend': position = params[0].split(':')[0] hazard = params[1] if position.startswith(self.position): self.gs.clear_entry_hazard(GameState.Player.one, hazard) else: self.gs.clear_entry_hazard(GameState.Player.two, hazard) elif inp_type == 'error': self.save_replay(room_obj) if params[0].startswith('[Invalid choice]'): if ("Can't switch: You can't switch to an active Pokémon" in params[0]): await self.switch_pokemon(room_obj, self.last_request_data) else: await self.take_action(room_obj, self.last_request_data) elif inp_type == 'win': done = True winner = params[0] if winner == self.name: self.wins += 1 self.log("We won") reward = 96 else: self.losses += 1 self.log("We lost") reward = -96 last_state = [element for element in self.state_vl] self.state_vl = self.gs.vector_list transition = (last_state, self.action, reward, self.state_vl, done) self.update_replay_memory(transition) trained = self.agent.train(True) if trained and self.save_model: self.log(f'Trained') path = self.agent.save_model() old_epoch = self.agent.current_epoch epoch = self.agent.update_epoch() if old_epoch < epoch: self.log('Saving epoch model') epoch_model_path = os.path.join( self.logs_dir, f'Epoch{epoch}.model') self.agent.save_model(path=epoch_model_path) self.agent.restart_epoch() else: self.log(f'Not trained') await room_obj.leave() self.iterations_run += 1 self.update_log_paths() if self.should_play_new_game(): self.is_first_request = True self.zmoves_tracker = [] self.log("Starting iteration {}".format( self.iterations_run)) if self.challenge: time.sleep(5) await self.challenge_expected() else: if self.print_stats: win_ratio = (float(self.wins) / float(self.wins + self.losses)) print(f'Win ratio: {win_ratio}') self.done = True if self.should_write_replay: self.write_replay_memory() sys.exit(0) elif inp_type == '-ability': # might work to track some abilities but so far weather abilities and other abilities # are only made known when a specific game action is made. self.log('ability') #self.log(params) elif inp_type == 'faint': player = params[0][0:2] pokemon = params[0][4:].strip() #TODO: remove this hack and have a good way of handling #TODO: detailed vs. non-detailed pokemon names pokemon = hack_name(pokemon) self.log(f'{player}\'s {pokemon} has fainted') if player == self.position: gs_player = GameState.Player.one else: gs_player = GameState.Player.two self.gs.set_fainted(gs_player, pokemon) self.log('set_fainted called successfully') if not self.gs.check_fainted(gs_player, pokemon): self.log('ERROR: Gamestate fainted was not set as ' f'expected for {pokemon}') elif inp_type == '-damage': if len(params) <= 3: player = params[0][0:2] pokemon = params[0].lstrip('p1a: ').lstrip( 'p2a: ').strip() pokemon = hack_name(pokemon) health_data = params[1] match = self.health_regex.search(health_data) if match: normalized_health = ( float(match.group('numerator')) / float(match.group('denominator'))) self.log( f'{pokemon} has health {normalized_health}') if player == self.position: gs_player = GameState.Player.one else: gs_player = GameState.Player.two self.gs.set_health(gs_player, pokemon, normalized_health) else: self.log( f'Could not track health for pokemon {pokemon}' ) # this section to track the enemy abilities elif len(params) == 4: pokemon = params[3].strip('[of] p1a: ') ability = params[2].strip('[from] ability: ') # self.enemy_state.update_abilities(pokemon, ability) self.log('Pokemon: ', pokemon, 'Enemy Ability: ', self.enemy_state.team_abilities[pokemon]) elif inp_type == '-heal': player = params[0][0:2] pokemon = params[0].lstrip('p1a: ').lstrip('p2a: ').strip() pokemon = hack_name(pokemon) health_data = params[1] match = self.health_regex.search(health_data) if match: normalized_health = (float(match.group('numerator')) / float(match.group('denominator'))) self.log(f'{pokemon} has health {normalized_health}') if player == self.position: gs_player = GameState.Player.one else: gs_player = GameState.Player.two self.gs.set_health(gs_player, pokemon, normalized_health) else: self.log( f'Could not track health for pokemon {pokemon}') elif inp_type == '-mega': if ('p1a' in str(params[0])): # Opposing player Mega # TODO: Add which pokemon used pokemon = params[0].strip( 'p1a: ') # just easier to read this way # self.enemy_state.update_team_mega(pokemon) self.opp_mega = True self.log('Enemy Mega Active: ', self.enemy_state.team_mega[pokemon]) else: # AI uses mega self.mega = True elif inp_type == '-item': ''' -item|POKEMON|ITEM The ITEM held by the POKEMON has been changed or revealed due to a move or ability. In addition, Air Balloon reveals itself when the Pokémon holding it switches in, so it will also cause this message to appear. ''' position = self.get_owner(params) pokemon_name = self.get_pokemon(params) item = util.item_name_to_id(params[1]) if position.startswith(self.position): self.gs.set_item(GameState.Player.one, pokemon_name, item) else: self.gs.set_item(GameState.Player.two, pokemon_name, item) elif inp_type == '-enditem': ''' -enditem|POKEMON|ITEM The ITEM held by POKEMON has been destroyed, and it now holds no item. This can be because of an item's own effects (consumed Berries, Air Balloon), or by a move or ability, like Knock Off. If a berry is consumed, it also has an additional modifier |[eat] to indicate that it was consumed. This message does not appear if the item's ownership was changed (with a move or ability like Thief or Trick), even if the move or ability would result in a Pokémon without an item. Note: Kept for legacy and inclusiveness reasons Actual tracking of this hook done based on changed Item in 'request' inp_type ''' pass elif inp_type == 'move': ''' move|POKEMON|MOVE|TARGET The specified Pokémon has used move MOVE at TARGET. If a move has multiple targets or no target, TARGET should be ignored. If a move targets a side, TARGET will be a (possibly fainted) Pokémon on that side. If |[miss] is present, the move missed. ''' if len(params) == 4: if params[3] == '[zeffect]': if self.get_owner(params[0]) == 'p1a': pokemon_name = self.get_pokemon(params[0]) zmove_id = util.move_name_to_id(params[1]) # Add (pokemon : zmove) in the zmove_tracker to # ensure, this pokemon can't re-use zmove self.zmoves_tracker[pokemon_name] = zmove_name self.gs.set_move(GameState.Player.one, pokemon_name, zmove_id, 0.0, 1.0) elif inp_type == '-zpower': ''' |-zpower|POKEMON The Pokémon POKEMON has used the z-move version of its move. Note: Kept for legacy and inclusiveness reasons Actual tracking of zpower done in the '-move' hook ''' pass elif inp_type == '-weather': weather_name = params[0] if weather_name == 'none': self.gs.clear_all_weather() else: self.gs.set_weather(weather_name) elif inp_type == '-boost': mine = params[0].startswith(self.position) if mine: gs_player = GameState.Player.one else: gs_player = GameState.Player.two boost_name = params[1] stages = float(params[2]) self.gs.add_boost(gs_player, boost_name, stages) elif inp_type == '-unboost': mine = params[0].startswith(self.position) if mine: gs_player = GameState.Player.one else: gs_player = GameState.Player.two boost_name = params[1] stages = float(params[2]) self.gs.add_boost(gs_player, boost_name, -1 * stages) else: if inp_type == 'updateuser': if (self.name == params[0].strip() and self.challenge and not self.has_challenged): self.has_challenged = True time.sleep(1) await self.challenge_expected() elif inp_type == 'popup': if 'Due to high load, you are limited to 12 battles and team validations every 3 minutes.' in params[ 0]: self.log('killing') self.kill() except Exception as err: self.log_error(err) async def on_private_message(self, pm): if pm.recipient == self: await pm.reply(pm.content) async def on_challenge_update(self, challenge_data): incoming = challenge_data.get('challengesFrom', {}) if self.expected_opponent.lower() in incoming: if self.trainer: model_paths = [ os.path.join(self.logs_dir, content) for content in os.listdir(self.logs_dir) if content.endswith('.model') and content.startswith('Epoch') ] if len(model_paths) > 0: sorted_model_paths = sorted( model_paths, key=lambda x: int( os.path.basename(x).lstrip('Epoch').rstrip('.model' ))) model_to_load = sorted_model_paths[-1] self.log(f'Loading model {model_to_load}') self.agent = DQNAgent(INPUT_SHAPE, training=False) self.agent.load_model(model_to_load) await self.accept_challenge(self.expected_opponent, self.team_text) async def on_room_init(self, room_obj): if room_obj.id.startswith('battle-'): self.log(f'Room ID: {room_obj.id}') self.active_pokemon = None self.opp_active_pokemon = None self.weather = 'none' def kill(self): sys.exit(0)