gameboy_worlds.utils
1# These are all the utils functions or classes that you may want to import in your project 2from gameboy_worlds.utils.parameter_handling import load_parameters 3from gameboy_worlds.utils.log_handling import log_error, log_info, log_warn, log_dict 4from gameboy_worlds.utils.fundamental import file_makedir, check_optional_installs 5from pandas import isna 6from typing import Type, List 7import numpy as np 8import os 9import matplotlib.pyplot as plt 10import pandas as pd 11from time import perf_counter_ns 12from typing import Optional, List, Dict 13 14 15def import_cv2(parameters: dict = None): 16 """ 17 Import cv2 lazily so headless runs without video writing do not load extra SDL libraries. 18 """ 19 try: 20 import cv2 21 except ImportError: 22 log_error( 23 "OpenCV (cv2) is required for emulator video recording utilities.", 24 parameters, 25 ) 26 return cv2 27 28 29def import_pygame(parameters: dict): 30 """ 31 Import pygame lazily so headless runs don't pull in SDL unless rendering is requested. 32 """ 33 try: 34 import pygame 35 except ImportError: 36 log_error( 37 "pygame is required for render() / human play display. Install pygame or run without render.", 38 parameters, 39 ) 40 return pygame 41 42 43def is_none_str(s) -> bool: 44 """ 45 Checks if a string is None or represents a null value. 46 47 Args: 48 s (str or None): The string to check. 49 50 Returns: 51 bool: True if the string is None or represents a null value, False otherwise. 52 """ 53 if s is None: 54 return True 55 if isinstance(s, str): 56 options = ["none", "null", "nan", ""] 57 for option in options: 58 if s.lower() == option: 59 return True 60 return isna(s) 61 62 63def nested_dict_to_str( 64 nested_dict: dict, *, indent: int = 0, indent_char: str = " " 65) -> str: 66 """ 67 Converts a nested dictionary to a formatted string representation. 68 Example Usage: 69 ```python 70 nested_dict={2: 4, 3: {4: 5, 6: {7: 8}}} 71 print(nested_dict_to_str(nested_dict)) 72 2: 4 73 3: Dict: 74 4: 5 75 6: Dict: 76 7: 8 77 ``` 78 79 Args: 80 nested_dict (dict): The nested dictionary to convert. 81 indent (int): The current indentation level. 82 indent_char (str): The character(s) used for indentation. 83 Returns: 84 str: A formatted string representation of the nested dictionary. 85 86 """ 87 result = "" 88 for key, value in nested_dict.items(): 89 result += indent_char * indent + str(key) + ": " 90 if isinstance(value, dict): 91 result += "Dict: \n" + nested_dict_to_str( 92 value, indent + 1, indent_char=indent_char 93 ) 94 else: 95 result += str(value) + "\n" 96 return result 97 98 99def verify_parameters(parameters: dict): 100 """ 101 Does a basic sanity check to ensure parameters is a non-empty dictionary. 102 """ 103 if parameters is None: 104 raise ValueError("Parameters cannot be None.") 105 if not isinstance(parameters, dict): 106 raise ValueError("Parameters must be a dictionary.") 107 if len(parameters) == 0: 108 raise ValueError("Parameters dictionary cannot be empty.") 109 110 111def get_lowest_level_subclass(class_list: List[Type]) -> Type: 112 """ 113 Given a list of classes, returns the class that is the lowest level subclass in the inheritance hierarchy. 114 """ 115 lowest_level_tracker = None 116 for cls in class_list: 117 if lowest_level_tracker is None: 118 lowest_level_tracker = cls 119 elif issubclass(cls, lowest_level_tracker): 120 lowest_level_tracker = cls 121 return lowest_level_tracker 122 123 124def show_frames( 125 frames: np.ndarray, titles: List[str] = None, save=False, parameters: dict = None 126): 127 """ 128 Plots each frame as an image in matplotlib. If save is true, will save each frame as title.png in the frame_saves/ directory. 129 titles length must be equal to frame length if specified. 130 """ 131 parameters = load_parameters(parameters) 132 if isinstance(frames, list): 133 for i in range(len(frames)): 134 if frames[i].ndim == 2: 135 frames[i] = np.expand_dims(frames[i], axis=-1) 136 else: 137 if not isinstance(frames, np.ndarray): 138 log_error( 139 f"Frames must be a numpy array or list of numpy arrays, but got {type(frames)}", 140 parameters, 141 ) 142 if frames.ndim == 2: 143 frames = [np.expand_dims(frames, axis=-1)] 144 elif frames.ndim == 3: 145 # now either we have (num_frames, height, width) or (height, width, channels) 146 if frames.shape[2] == 1: 147 frames = [frames] 148 else: 149 frames = [ 150 np.expand_dims(frames[i], axis=-1) for i in range(frames.shape[0]) 151 ] 152 if isinstance(titles, str): 153 titles = [titles] 154 if save: 155 if titles is None: 156 log_error(f"Cannot save frames without titles specified.", parameters) 157 if titles is not None: 158 if len(titles) == 1 and len(frames) > 1: 159 titles = [titles[0] + f"_{i}" for i in range(len(frames))] 160 if titles is not None and len(titles) != len(frames): 161 log_error( 162 f"Length of titles {len(titles)} does not match number of frames {len(frames)}", 163 parameters, 164 ) 165 save_dir = "frame_saves/" 166 os.makedirs(save_dir, exist_ok=True) 167 168 for i in range(len(frames)): 169 plt.imshow(frames[i]) 170 if titles is not None: 171 plt.title(titles[i]) 172 if save: 173 filename = os.path.join( 174 save_dir, titles[i].replace(" ", "_").replace("/", "_") + ".png" 175 ) 176 plt.imsave(filename, frames[i][:, :, 0], cmap="gray") 177 else: 178 plt.show() 179 180 181def get_train_games(game: str, parameters: dict = None) -> List[str]: 182 """ 183 Returns a list of games that can be used for training the specified game variant. 184 185 Args: 186 game (str): The variant of the game to get training games for. 187 parameters (dict, optional): Additional parameters for error logging. 188 """ 189 parameters = load_parameters(parameters) 190 tasks_df = get_benchmark_tasks(game, parameters, shifted_included=True) 191 training_rows = tasks_df[tasks_df["can_train_from_init_state"] == True] 192 if len(training_rows) == 0: 193 log_error( 194 f"No training games specified for game variant '{game}' in benchmark tasks. This is an error.", 195 parameters, 196 ) 197 training_games = list(set(training_rows["game"].tolist())) 198 return training_games 199 200 201def get_benchmark_tasks_dfs(parameters: dict = None) -> dict[str, pd.DataFrame]: 202 """ 203 Loads the benchmark tasks from the benchmark/tests/tasks.csv file 204 205 Args: 206 parameters (dict, optional): Additional parameters for error logging. 207 Returns: 208 dict[str, pd.DataFrame]: A dictionary mapping game names to their corresponding DataFrames containing the benchmark tasks. 209 """ 210 parameters = load_parameters(parameters) 211 project_root = parameters["project_root"] 212 module_paths = os.listdir(project_root + "/benchmark/tests/") 213 benchmark_dfs = {} 214 for module_path in module_paths: 215 if module_path.endswith(".csv"): 216 tasks_filepath = os.path.join(project_root, "benchmark/tests", module_path) 217 benchmark_name = module_path.strip(".csv") 218 benchmark_dfs[benchmark_name] = pd.read_csv(tasks_filepath) 219 return benchmark_dfs 220 221 222def get_benchmark_tasks( 223 game: str, parameters: dict = None, shifted_included: bool = False 224) -> pd.DataFrame: 225 """ 226 Loads the benchmark tasks for the specified game from the benchmark/tests/tasks.csv file 227 228 Args: 229 game (str): The variant of the game to get benchmark tasks for. 230 parameters (dict, optional): Additional parameters for error logging. 231 shifted_included (bool, optional): Whether to include task rows from other titles in the same series as part of the benchmark tasks. 232 233 Returns: 234 pd.DataFrame: DataFrame containing the benchmark tasks for the specified game. 235 """ 236 parameters = load_parameters(parameters) 237 tasks_dfs = get_benchmark_tasks_dfs(parameters) 238 available_games = set() 239 task_df = None 240 for benchmark_name, df in tasks_dfs.items(): 241 available_games.update(set(df["game"].unique())) 242 if game in df["game"].unique(): 243 if task_df is not None: 244 log_error( 245 f"Multiple benchmark modules contain tasks for game variant '{game}'. Please ensure that only one benchmark CSV file contains tasks for this game.", 246 parameters, 247 ) 248 task_df = df 249 250 if game not in available_games: 251 log_error( 252 f"Game variant '{game}' not found in benchmark tasks. Available game variants: {available_games}", 253 parameters=parameters, 254 ) 255 if not shifted_included: 256 game_tasks_df = task_df[task_df["game"] == game].reset_index(drop=True) 257 else: 258 game_tasks_df = task_df 259 return game_tasks_df 260 261 262def get_training_states(game: str, parameters: dict = None) -> Optional[List[str]]: 263 """ 264 Loads the training states for the specified game from the benchmark/tasks.csv file 265 266 Args: 267 game (str): The variant of the game to get training states for. 268 parameters (dict, optional): Additional parameters for error logging. 269 270 Returns: 271 Optional[List[str]]: A list of training states for the specified game, or None if no training states are specified. 272 """ 273 parameters = load_parameters(parameters) 274 tasks_df = get_benchmark_tasks(game, parameters, shifted_included=False) 275 training_rows = tasks_df[tasks_df["can_train_from_init_state"] == True] 276 if len(training_rows) == 0: 277 log_warn( 278 f"No training states specified for game variant '{game}' in benchmark tasks.", 279 parameters, 280 ) 281 return None 282 training_states = list(set(training_rows["init_state"].tolist())) 283 return training_states 284 285 286def get_shifted_training_states( 287 game: str, parameters: dict = None 288) -> Dict[str, List[str]]: 289 """ 290 Loads the shifted training states for the specified game from the benchmark/tasks.csv file. 291 292 This ends up being all available training states from every other title of the same series. So if you ask for pokemon_red's shifted states, you will get pokemon_crystal's training states. 293 294 Args: 295 game (str): The variant of the game to get shifted training states for. 296 parameters (dict, optional): Additional parameters for error logging. 297 Returns: 298 Dict[str, List[str]]: A dictionary of lists of shifted training states for the specified game. Each entry is in format {game: [training_states]}, where game is a different title in the same series as the input game. 299 """ 300 parameters = load_parameters(parameters) 301 tasks_df = get_benchmark_tasks(game, parameters, shifted_included=True) 302 other_task_rows = tasks_df[tasks_df["game"] != game].reset_index(drop=True) 303 training_rows = other_task_rows[ 304 other_task_rows["can_train_from_init_state"] == True 305 ] 306 if len(training_rows) == 0: 307 log_error( 308 f"No shifted training states specified for game variant '{game}' in benchmark tasks. This shouldn't happen.", 309 parameters, 310 ) 311 shifted_training_states = {} 312 for _, row in training_rows.iterrows(): 313 other_game = row["game"] 314 if other_game not in shifted_training_states: 315 shifted_training_states[other_game] = [] 316 shifted_training_states[other_game].append(row["init_state"]) 317 return shifted_training_states 318 319 320def get_all_training_states(parameters: dict = None) -> Dict[str, List[str]]: 321 """ 322 Gets all regular training states for the all games (for which training states are specified). 323 324 Args: 325 parameters (dict, optional): Additional parameters for error logging. 326 327 Returns: 328 Dict[str, List[str]]: A dictionary containing {game: training_states} entries for all games for which training states are specified in the benchmark tasks. 329 """ 330 parameters = load_parameters(parameters) 331 tasks_dfs = get_benchmark_tasks_dfs(parameters) 332 all_games = set() 333 for df in tasks_dfs.values(): 334 all_games.update(df["game"].unique()) 335 all_training_states = {} 336 for game in all_games: 337 training_states = get_training_states(game, parameters) 338 if training_states is not None: 339 all_training_states[game] = training_states 340 return all_training_states 341 342 343def get_all_shifted_training_states( 344 parameters: dict = None, 345) -> Dict[str, Dict[str, List[str]]]: 346 """ 347 Gets all shifted training states for all games. 348 349 Args: 350 parameters (dict, optional): Additional parameters for error logging. 351 352 Returns: 353 Dict[str, Dict[str, List[str]]]: A nested dictionary containing {game: {other_game: shifted_training_states}} entries for all games for which shifted training states are specified in the benchmark tasks. 354 """ 355 parameters = load_parameters(parameters) 356 tasks_dfs = get_benchmark_tasks_dfs(parameters) 357 all_games = set() 358 for df in tasks_dfs.values(): 359 all_games.update(df["game"].unique()) 360 all_shifted_training_states = {} 361 for game in all_games: 362 shifted_states = get_shifted_training_states(game, parameters) 363 all_shifted_training_states[game] = shifted_states 364 return all_shifted_training_states 365 366 367class _Profiler: 368 """ 369 A simple profiler class to track the time taken by different events in the code. It can also group events together and show the percentage of time taken by each event in the group. 370 """ 371 372 LOG_EVENTS = False 373 last_event_time = None 374 last_event = None 375 group = None 376 group_name = None 377 378 @staticmethod 379 def event(event_name): 380 current_time = perf_counter_ns() 381 if _Profiler.last_event_time is not None: 382 elapsed_time = current_time - _Profiler.last_event_time 383 if _Profiler.LOG_EVENTS: 384 log_info( 385 f"{_Profiler.last_event} -> {event_name}: {elapsed_time / 1e6:.2f} ms", 386 ) 387 _Profiler.last_event_time = current_time 388 _Profiler.last_event = event_name 389 if _Profiler.group_name is not None: 390 _Profiler.group.append((event_name, current_time)) 391 392 @staticmethod 393 def start_group(group_name): 394 if _Profiler.group_name is not None: 395 fractions = [] 396 total_time = 0 397 for i in range(1, len(_Profiler.group)): 398 event_name, event_time = _Profiler.group[i] 399 prev_event_name, prev_event_time = _Profiler.group[i - 1] 400 elapsed_time = event_time - prev_event_time 401 total_time += elapsed_time 402 for i in range(1, len(_Profiler.group)): 403 event_name, event_time = _Profiler.group[i] 404 prev_event_name, prev_event_time = _Profiler.group[i - 1] 405 elapsed_time = event_time - prev_event_time 406 fractions.append(elapsed_time / total_time if total_time > 0 else 0) 407 log_info( 408 f"{_Profiler.group_name} - {prev_event_name} -> {event_name}: {elapsed_time / 1e6:.2f} ms ({fractions[-1]*100:.2f}%)", 409 ) 410 411 _Profiler.group_name = group_name 412 _Profiler.group = [] 413 414 @staticmethod 415 def close_group(): 416 _Profiler.start_group(None)
16def import_cv2(parameters: dict = None): 17 """ 18 Import cv2 lazily so headless runs without video writing do not load extra SDL libraries. 19 """ 20 try: 21 import cv2 22 except ImportError: 23 log_error( 24 "OpenCV (cv2) is required for emulator video recording utilities.", 25 parameters, 26 ) 27 return cv2
Import cv2 lazily so headless runs without video writing do not load extra SDL libraries.
30def import_pygame(parameters: dict): 31 """ 32 Import pygame lazily so headless runs don't pull in SDL unless rendering is requested. 33 """ 34 try: 35 import pygame 36 except ImportError: 37 log_error( 38 "pygame is required for render() / human play display. Install pygame or run without render.", 39 parameters, 40 ) 41 return pygame
Import pygame lazily so headless runs don't pull in SDL unless rendering is requested.
44def is_none_str(s) -> bool: 45 """ 46 Checks if a string is None or represents a null value. 47 48 Args: 49 s (str or None): The string to check. 50 51 Returns: 52 bool: True if the string is None or represents a null value, False otherwise. 53 """ 54 if s is None: 55 return True 56 if isinstance(s, str): 57 options = ["none", "null", "nan", ""] 58 for option in options: 59 if s.lower() == option: 60 return True 61 return isna(s)
Checks if a string is None or represents a null value.
Arguments:
- s (str or None): The string to check.
Returns:
bool: True if the string is None or represents a null value, False otherwise.
64def nested_dict_to_str( 65 nested_dict: dict, *, indent: int = 0, indent_char: str = " " 66) -> str: 67 """ 68 Converts a nested dictionary to a formatted string representation. 69 Example Usage: 70 ```python 71 nested_dict={2: 4, 3: {4: 5, 6: {7: 8}}} 72 print(nested_dict_to_str(nested_dict)) 73 2: 4 74 3: Dict: 75 4: 5 76 6: Dict: 77 7: 8 78 ``` 79 80 Args: 81 nested_dict (dict): The nested dictionary to convert. 82 indent (int): The current indentation level. 83 indent_char (str): The character(s) used for indentation. 84 Returns: 85 str: A formatted string representation of the nested dictionary. 86 87 """ 88 result = "" 89 for key, value in nested_dict.items(): 90 result += indent_char * indent + str(key) + ": " 91 if isinstance(value, dict): 92 result += "Dict: \n" + nested_dict_to_str( 93 value, indent + 1, indent_char=indent_char 94 ) 95 else: 96 result += str(value) + "\n" 97 return result
Converts a nested dictionary to a formatted string representation. Example Usage:
nested_dict={2: 4, 3: {4: 5, 6: {7: 8}}}
print(nested_dict_to_str(nested_dict))
2: 4
3: Dict:
4: 5
6: Dict:
7: 8
Arguments:
- nested_dict (dict): The nested dictionary to convert.
- indent (int): The current indentation level.
- indent_char (str): The character(s) used for indentation.
Returns:
str: A formatted string representation of the nested dictionary.
100def verify_parameters(parameters: dict): 101 """ 102 Does a basic sanity check to ensure parameters is a non-empty dictionary. 103 """ 104 if parameters is None: 105 raise ValueError("Parameters cannot be None.") 106 if not isinstance(parameters, dict): 107 raise ValueError("Parameters must be a dictionary.") 108 if len(parameters) == 0: 109 raise ValueError("Parameters dictionary cannot be empty.")
Does a basic sanity check to ensure parameters is a non-empty dictionary.
112def get_lowest_level_subclass(class_list: List[Type]) -> Type: 113 """ 114 Given a list of classes, returns the class that is the lowest level subclass in the inheritance hierarchy. 115 """ 116 lowest_level_tracker = None 117 for cls in class_list: 118 if lowest_level_tracker is None: 119 lowest_level_tracker = cls 120 elif issubclass(cls, lowest_level_tracker): 121 lowest_level_tracker = cls 122 return lowest_level_tracker
Given a list of classes, returns the class that is the lowest level subclass in the inheritance hierarchy.
125def show_frames( 126 frames: np.ndarray, titles: List[str] = None, save=False, parameters: dict = None 127): 128 """ 129 Plots each frame as an image in matplotlib. If save is true, will save each frame as title.png in the frame_saves/ directory. 130 titles length must be equal to frame length if specified. 131 """ 132 parameters = load_parameters(parameters) 133 if isinstance(frames, list): 134 for i in range(len(frames)): 135 if frames[i].ndim == 2: 136 frames[i] = np.expand_dims(frames[i], axis=-1) 137 else: 138 if not isinstance(frames, np.ndarray): 139 log_error( 140 f"Frames must be a numpy array or list of numpy arrays, but got {type(frames)}", 141 parameters, 142 ) 143 if frames.ndim == 2: 144 frames = [np.expand_dims(frames, axis=-1)] 145 elif frames.ndim == 3: 146 # now either we have (num_frames, height, width) or (height, width, channels) 147 if frames.shape[2] == 1: 148 frames = [frames] 149 else: 150 frames = [ 151 np.expand_dims(frames[i], axis=-1) for i in range(frames.shape[0]) 152 ] 153 if isinstance(titles, str): 154 titles = [titles] 155 if save: 156 if titles is None: 157 log_error(f"Cannot save frames without titles specified.", parameters) 158 if titles is not None: 159 if len(titles) == 1 and len(frames) > 1: 160 titles = [titles[0] + f"_{i}" for i in range(len(frames))] 161 if titles is not None and len(titles) != len(frames): 162 log_error( 163 f"Length of titles {len(titles)} does not match number of frames {len(frames)}", 164 parameters, 165 ) 166 save_dir = "frame_saves/" 167 os.makedirs(save_dir, exist_ok=True) 168 169 for i in range(len(frames)): 170 plt.imshow(frames[i]) 171 if titles is not None: 172 plt.title(titles[i]) 173 if save: 174 filename = os.path.join( 175 save_dir, titles[i].replace(" ", "_").replace("/", "_") + ".png" 176 ) 177 plt.imsave(filename, frames[i][:, :, 0], cmap="gray") 178 else: 179 plt.show()
Plots each frame as an image in matplotlib. If save is true, will save each frame as title.png in the frame_saves/ directory. titles length must be equal to frame length if specified.
182def get_train_games(game: str, parameters: dict = None) -> List[str]: 183 """ 184 Returns a list of games that can be used for training the specified game variant. 185 186 Args: 187 game (str): The variant of the game to get training games for. 188 parameters (dict, optional): Additional parameters for error logging. 189 """ 190 parameters = load_parameters(parameters) 191 tasks_df = get_benchmark_tasks(game, parameters, shifted_included=True) 192 training_rows = tasks_df[tasks_df["can_train_from_init_state"] == True] 193 if len(training_rows) == 0: 194 log_error( 195 f"No training games specified for game variant '{game}' in benchmark tasks. This is an error.", 196 parameters, 197 ) 198 training_games = list(set(training_rows["game"].tolist())) 199 return training_games
Returns a list of games that can be used for training the specified game variant.
Arguments:
- game (str): The variant of the game to get training games for.
- parameters (dict, optional): Additional parameters for error logging.
202def get_benchmark_tasks_dfs(parameters: dict = None) -> dict[str, pd.DataFrame]: 203 """ 204 Loads the benchmark tasks from the benchmark/tests/tasks.csv file 205 206 Args: 207 parameters (dict, optional): Additional parameters for error logging. 208 Returns: 209 dict[str, pd.DataFrame]: A dictionary mapping game names to their corresponding DataFrames containing the benchmark tasks. 210 """ 211 parameters = load_parameters(parameters) 212 project_root = parameters["project_root"] 213 module_paths = os.listdir(project_root + "/benchmark/tests/") 214 benchmark_dfs = {} 215 for module_path in module_paths: 216 if module_path.endswith(".csv"): 217 tasks_filepath = os.path.join(project_root, "benchmark/tests", module_path) 218 benchmark_name = module_path.strip(".csv") 219 benchmark_dfs[benchmark_name] = pd.read_csv(tasks_filepath) 220 return benchmark_dfs
Loads the benchmark tasks from the benchmark/tests/tasks.csv file
Arguments:
- parameters (dict, optional): Additional parameters for error logging.
Returns:
dict[str, pd.DataFrame]: A dictionary mapping game names to their corresponding DataFrames containing the benchmark tasks.
223def get_benchmark_tasks( 224 game: str, parameters: dict = None, shifted_included: bool = False 225) -> pd.DataFrame: 226 """ 227 Loads the benchmark tasks for the specified game from the benchmark/tests/tasks.csv file 228 229 Args: 230 game (str): The variant of the game to get benchmark tasks for. 231 parameters (dict, optional): Additional parameters for error logging. 232 shifted_included (bool, optional): Whether to include task rows from other titles in the same series as part of the benchmark tasks. 233 234 Returns: 235 pd.DataFrame: DataFrame containing the benchmark tasks for the specified game. 236 """ 237 parameters = load_parameters(parameters) 238 tasks_dfs = get_benchmark_tasks_dfs(parameters) 239 available_games = set() 240 task_df = None 241 for benchmark_name, df in tasks_dfs.items(): 242 available_games.update(set(df["game"].unique())) 243 if game in df["game"].unique(): 244 if task_df is not None: 245 log_error( 246 f"Multiple benchmark modules contain tasks for game variant '{game}'. Please ensure that only one benchmark CSV file contains tasks for this game.", 247 parameters, 248 ) 249 task_df = df 250 251 if game not in available_games: 252 log_error( 253 f"Game variant '{game}' not found in benchmark tasks. Available game variants: {available_games}", 254 parameters=parameters, 255 ) 256 if not shifted_included: 257 game_tasks_df = task_df[task_df["game"] == game].reset_index(drop=True) 258 else: 259 game_tasks_df = task_df 260 return game_tasks_df
Loads the benchmark tasks for the specified game from the benchmark/tests/tasks.csv file
Arguments:
- game (str): The variant of the game to get benchmark tasks for.
- parameters (dict, optional): Additional parameters for error logging.
- shifted_included (bool, optional): Whether to include task rows from other titles in the same series as part of the benchmark tasks.
Returns:
pd.DataFrame: DataFrame containing the benchmark tasks for the specified game.
263def get_training_states(game: str, parameters: dict = None) -> Optional[List[str]]: 264 """ 265 Loads the training states for the specified game from the benchmark/tasks.csv file 266 267 Args: 268 game (str): The variant of the game to get training states for. 269 parameters (dict, optional): Additional parameters for error logging. 270 271 Returns: 272 Optional[List[str]]: A list of training states for the specified game, or None if no training states are specified. 273 """ 274 parameters = load_parameters(parameters) 275 tasks_df = get_benchmark_tasks(game, parameters, shifted_included=False) 276 training_rows = tasks_df[tasks_df["can_train_from_init_state"] == True] 277 if len(training_rows) == 0: 278 log_warn( 279 f"No training states specified for game variant '{game}' in benchmark tasks.", 280 parameters, 281 ) 282 return None 283 training_states = list(set(training_rows["init_state"].tolist())) 284 return training_states
Loads the training states for the specified game from the benchmark/tasks.csv file
Arguments:
- game (str): The variant of the game to get training states for.
- parameters (dict, optional): Additional parameters for error logging.
Returns:
Optional[List[str]]: A list of training states for the specified game, or None if no training states are specified.
287def get_shifted_training_states( 288 game: str, parameters: dict = None 289) -> Dict[str, List[str]]: 290 """ 291 Loads the shifted training states for the specified game from the benchmark/tasks.csv file. 292 293 This ends up being all available training states from every other title of the same series. So if you ask for pokemon_red's shifted states, you will get pokemon_crystal's training states. 294 295 Args: 296 game (str): The variant of the game to get shifted training states for. 297 parameters (dict, optional): Additional parameters for error logging. 298 Returns: 299 Dict[str, List[str]]: A dictionary of lists of shifted training states for the specified game. Each entry is in format {game: [training_states]}, where game is a different title in the same series as the input game. 300 """ 301 parameters = load_parameters(parameters) 302 tasks_df = get_benchmark_tasks(game, parameters, shifted_included=True) 303 other_task_rows = tasks_df[tasks_df["game"] != game].reset_index(drop=True) 304 training_rows = other_task_rows[ 305 other_task_rows["can_train_from_init_state"] == True 306 ] 307 if len(training_rows) == 0: 308 log_error( 309 f"No shifted training states specified for game variant '{game}' in benchmark tasks. This shouldn't happen.", 310 parameters, 311 ) 312 shifted_training_states = {} 313 for _, row in training_rows.iterrows(): 314 other_game = row["game"] 315 if other_game not in shifted_training_states: 316 shifted_training_states[other_game] = [] 317 shifted_training_states[other_game].append(row["init_state"]) 318 return shifted_training_states
Loads the shifted training states for the specified game from the benchmark/tasks.csv file.
This ends up being all available training states from every other title of the same series. So if you ask for pokemon_red's shifted states, you will get pokemon_crystal's training states.
Arguments:
- game (str): The variant of the game to get shifted training states for.
- parameters (dict, optional): Additional parameters for error logging.
Returns:
Dict[str, List[str]]: A dictionary of lists of shifted training states for the specified game. Each entry is in format {game: [training_states]}, where game is a different title in the same series as the input game.
321def get_all_training_states(parameters: dict = None) -> Dict[str, List[str]]: 322 """ 323 Gets all regular training states for the all games (for which training states are specified). 324 325 Args: 326 parameters (dict, optional): Additional parameters for error logging. 327 328 Returns: 329 Dict[str, List[str]]: A dictionary containing {game: training_states} entries for all games for which training states are specified in the benchmark tasks. 330 """ 331 parameters = load_parameters(parameters) 332 tasks_dfs = get_benchmark_tasks_dfs(parameters) 333 all_games = set() 334 for df in tasks_dfs.values(): 335 all_games.update(df["game"].unique()) 336 all_training_states = {} 337 for game in all_games: 338 training_states = get_training_states(game, parameters) 339 if training_states is not None: 340 all_training_states[game] = training_states 341 return all_training_states
Gets all regular training states for the all games (for which training states are specified).
Arguments:
- parameters (dict, optional): Additional parameters for error logging.
Returns:
Dict[str, List[str]]: A dictionary containing {game: training_states} entries for all games for which training states are specified in the benchmark tasks.
344def get_all_shifted_training_states( 345 parameters: dict = None, 346) -> Dict[str, Dict[str, List[str]]]: 347 """ 348 Gets all shifted training states for all games. 349 350 Args: 351 parameters (dict, optional): Additional parameters for error logging. 352 353 Returns: 354 Dict[str, Dict[str, List[str]]]: A nested dictionary containing {game: {other_game: shifted_training_states}} entries for all games for which shifted training states are specified in the benchmark tasks. 355 """ 356 parameters = load_parameters(parameters) 357 tasks_dfs = get_benchmark_tasks_dfs(parameters) 358 all_games = set() 359 for df in tasks_dfs.values(): 360 all_games.update(df["game"].unique()) 361 all_shifted_training_states = {} 362 for game in all_games: 363 shifted_states = get_shifted_training_states(game, parameters) 364 all_shifted_training_states[game] = shifted_states 365 return all_shifted_training_states
Gets all shifted training states for all games.
Arguments:
- parameters (dict, optional): Additional parameters for error logging.
Returns:
Dict[str, Dict[str, List[str]]]: A nested dictionary containing {game: {other_game: shifted_training_states}} entries for all games for which shifted training states are specified in the benchmark tasks.