From f3ff83034a240dfafb3b872069b5f6f04ecb4991 Mon Sep 17 00:00:00 2001 From: Jet Date: Tue, 6 Feb 2024 00:53:09 +0000 Subject: [PATCH 01/16] gymnasiumify things --- src/python/env/gymnasium.py | 383 ++++++++++++++++++++++++++++++++++++ 1 file changed, 383 insertions(+) create mode 100644 src/python/env/gymnasium.py diff --git a/src/python/env/gymnasium.py b/src/python/env/gymnasium.py new file mode 100644 index 000000000..606ccba45 --- /dev/null +++ b/src/python/env/gymnasium.py @@ -0,0 +1,383 @@ +from __future__ import annotations + +import sys +from typing import Any, Literal, Optional, Sequence, Union + +import ale_py +import ale_py.roms as roms +import ale_py.roms.utils as rom_utils +from gymnasium.utils import seeding +import numpy as np + +import gymnasium +import gymnasium.logger as logger +from gymnasium import error, spaces, utils + +if sys.version_info < (3, 11): + from typing_extensions import NotRequired, TypedDict +else: + from typing import NotRequired, TypedDict + + +class AtariEnvStepMetadata(TypedDict): + lives: int + episode_frame_number: int + frame_number: int + seeds: NotRequired[Sequence[int]] + + +class AtariEnv(gymnasium.Env, utils.EzPickle): + """ + (A)rcade (L)earning (Gym) (Env)ironment. + A Gym wrapper around the Arcade Learning Environment (ALE). + """ + + # No render modes + metadata = {"render_modes": ["human", "rgb_array"]} + + def __init__( + self, + game: str = "pong", + mode: Optional[int] = None, + difficulty: Optional[int] = None, + obs_type: Literal["rgb", "grayscale", "ram"] = "rgb", + frameskip: Union[tuple[int, int], int] = 4, + repeat_action_probability: float = 0.25, + full_action_space: bool = False, + max_num_frames_per_episode: Optional[int] = None, + render_mode: Optional[Literal["human", "rgb_array"]] = None, + ) -> None: + """ + Initialize the ALE for Gymnasium. + Default parameters are taken from Machado et al., 2018. + + Args: + game: str => Game to initialize env with. + mode: Optional[int] => Game mode, see Machado et al., 2018 + difficulty: Optional[int] => Game difficulty,see Machado et al., 2018 + obs_type: str => Observation type in { 'rgb', 'grayscale', 'ram' } + frameskip: Union[tuple[int, int], int] => + Stochastic frameskip as tuple or fixed. + repeat_action_probability: int => + Probability to repeat actions, see Machado et al., 2018 + full_action_space: bool => Use full action space? + max_num_frames_per_episode: int => Max number of frame per epsiode. + Once `max_num_frames_per_episode` is reached the episode is + truncated. + render_mode: str => One of { 'human', 'rgb_array' }. + If `human` we'll interactively display the screen and enable + game sounds. This will lock emulation to the ROMs specified FPS + If `rgb_array` we'll return the `rgb` key in step metadata with + the current environment RGB frame. + + Note: + - The game must be installed, see ale-import-roms, or ale-py-roms. + - Frameskip values of (low, high) will enable stochastic frame skip + which will sample a random frameskip uniformly each action. + - It is recommended to enable full action space. + See Machado et al., 2018 for more details. + + References: + `Revisiting the Arcade Learning Environment: Evaluation Protocols + and Open Problems for General Agents`, Machado et al., 2018, JAIR + URL: https://jair.org/index.php/jair/article/view/11182 + """ + if obs_type not in {"rgb", "grayscale", "ram"}: + raise error.Error( + f"Invalid observation type: {obs_type}. Expecting: rgb, grayscale, ram." + ) + + if type(frameskip) not in (int, tuple): + raise error.Error(f"Invalid frameskip type: {type(frameskip)}.") + if isinstance(frameskip, int) and frameskip <= 0: + raise error.Error( + f"Invalid frameskip of {frameskip}, frameskip must be positive." + ) + elif isinstance(frameskip, tuple) and len(frameskip) != 2: + raise error.Error( + f"Invalid stochastic frameskip length of {len(frameskip)}, expected length 2." + ) + elif isinstance(frameskip, tuple) and frameskip[0] > frameskip[1]: + raise error.Error( + f"Invalid stochastic frameskip, lower bound is greater than upper bound." + ) + elif isinstance(frameskip, tuple) and frameskip[0] <= 0: + raise error.Error( + f"Invalid stochastic frameskip lower bound is greater than upper bound." + ) + + if render_mode is not None and render_mode not in {"rgb_array", "human"}: + raise error.Error( + f"Render mode {render_mode} not supported (rgb_array, human)." + ) + + utils.EzPickle.__init__( + self, + game, + mode, + difficulty, + obs_type, + frameskip, + repeat_action_probability, + full_action_space, + max_num_frames_per_episode, + render_mode, + ) + + # Initialize ALE + self.ale = ale_py.ALEInterface() + + self._game = rom_utils.rom_id_to_name(game) + + self._game_mode = mode + self._game_difficulty = difficulty + + self._frameskip = frameskip + self._obs_type = obs_type + self.render_mode = render_mode + + # Set logger mode to error only + self.ale.setLoggerMode(ale_py.LoggerMode.Error) + # Config sticky action prob. + self.ale.setFloat("repeat_action_probability", repeat_action_probability) + + if max_num_frames_per_episode is not None: + self.ale.setInt("max_num_frames_per_episode", max_num_frames_per_episode) + + # If render mode is human we can display screen and sound + if render_mode == "human": + self.ale.setBool("display_screen", True) + self.ale.setBool("sound", True) + + # seed + load + self.seed_game() + self.load_game() + + # initialize action space + self._action_set = ( + self.ale.getLegalActionSet() + if full_action_space + else self.ale.getMinimalActionSet() + ) + self.action_space = spaces.Discrete(len(self._action_set)) + + # initialize observation space + if self._obs_type == "ram": + self.observation_space = spaces.Box( + low=0, high=255, dtype=np.uint8, shape=(self.ale.getRAMSize(),) + ) + elif self._obs_type == "rgb" or self._obs_type == "grayscale": + (screen_height, screen_width) = self.ale.getScreenDims() + image_shape = ( + screen_height, + screen_width, + ) + if self._obs_type == "rgb": + image_shape += (3,) + self.observation_space = spaces.Box( + low=0, high=255, dtype=np.uint8, shape=image_shape + ) + else: + raise error.Error(f"Unrecognized observation type: {self._obs_type}") + + def seed_game(self, seed: Optional[int] = None) -> tuple[int, int]: + """Seeds the internal and ALE RNG.""" + ss = np.random.SeedSequence(seed) + np_seed, ale_seed = ss.generate_state(n_words=2) + self._np_random, seed = seeding.np_random(np_seed) + self.ale.setInt("random_seed", int(ale_seed)) + return np_seed, ale_seed + + def load_game(self) -> None: + """This function initializes the ROM and sets the corresponding mode and difficulty.""" + if not hasattr(roms, self._game): + raise error.Error( + f'We\'re Unable to find the game "{self._game}". Note: Gym no longer distributes ROMs. ' + f"If you own a license to use the necessary ROMs for research purposes you can download them " + f'via `pip install gym[accept-rom-license]`. Otherwise, you should try importing "{self._game}" ' + f'via the command `ale-import-roms`. If you believe this is a mistake perhaps your copy of "{self._game}" ' + "is unsupported. To check if this is the case try providing the environment variable " + "`PYTHONWARNINGS=default::ImportWarning:ale_py.roms`. For more information see: " + "https://github.com/mgbellemare/Arcade-Learning-Environment#rom-management" + ) + self.ale.loadROM(getattr(roms, self._game)) + + if self._game_mode is not None: + self.ale.setMode(self._game_mode) + if self._game_difficulty is not None: + self.ale.setDifficulty(self._game_difficulty) + + + def reset( # pyright: ignore[reportIncompatibleMethodOverride] + self, + *, + seed: Optional[int] = None, + options: Optional[dict[str, Any]] = None, + ) -> tuple[np.ndarray, AtariEnvStepMetadata]: + """Resets environment and returns initial observation.""" + # sets the seeds if it's specified for both ALE and frameskip np + # we only want to do this when commanded to so we don't reset all previous states, statistics, etc. + seeded_with = self.seed_game(seed) if seed else None + self.load_game() + + self.ale.reset_game() + + obs = self._get_obs() + info = self._get_info() + if seeded_with is not None: + info["seeds"] = seeded_with + + return obs, info + + def step( # pyright: ignore[reportIncompatibleMethodOverride] + self, + action: int, + ) -> tuple[np.ndarray, float, bool, bool, AtariEnvStepMetadata]: + """ + Perform one agent step, i.e., repeats `action` frameskip # of steps. + + Args: + action_ind: int => Action index to execute + + Returns: + tuple[np.ndarray, float, bool, bool, Dict[str, Any]] => + observation, reward, terminal, truncation, metadata + + Note: `metadata` contains the keys "lives" and "rgb" if + render_mode == 'rgb_array'. + """ + # If frameskip is a length 2 tuple then it's stochastic + # frameskip between [frameskip[0], frameskip[1]] uniformly. + if isinstance(self._frameskip, int): + frameskip = self._frameskip + elif isinstance(self._frameskip, tuple): + frameskip = self.np_random.integers(*self._frameskip) + else: + raise error.Error(f"Invalid frameskip type: {self._frameskip}") + + # Frameskip + reward = 0.0 + for _ in range(frameskip): + reward += self.ale.act(self._action_set[action]) + is_terminal = self.ale.game_over(with_truncation=False) + is_truncated = self.ale.game_truncated() + + return self._get_obs(), reward, is_terminal, is_truncated, self._get_info() + + def render(self) -> Optional[np.ndarray]: + """ + Render is not supported by ALE. We use a paradigm similar to + Gym3 which allows you to specify `render_mode` during construction. + + For example, + gym.make("ale-py:Pong-v0", render_mode="human") + will display the ALE and maintain the proper interval to match the + FPS target set by the ROM. + """ + if self.render_mode == "rgb_array": + return self.ale.getScreenRGB() + elif self.render_mode == "human": + return + else: + raise error.Error( + f"Invalid render mode `{self.render_mode}`. " + "Supported modes: `human`, `rgb_array`." + ) + + def _get_obs(self) -> np.ndarray: + """ + Retreives the current observation. + This is dependent on `self._obs_type`. + """ + if self._obs_type == "ram": + return self.ale.getRAM() + elif self._obs_type == "rgb": + return self.ale.getScreenRGB() + elif self._obs_type == "grayscale": + return self.ale.getScreenGrayscale() + else: + raise error.Error(f"Unrecognized observation type: {self._obs_type}") + + def _get_info(self) -> AtariEnvStepMetadata: + return { + "lives": self.ale.lives(), + "episode_frame_number": self.ale.getEpisodeFrameNumber(), + "frame_number": self.ale.getFrameNumber(), + } + + def get_keys_to_action(self) -> dict[tuple[int], ale_py.Action]: + """ + Return keymapping -> actions for human play. + """ + UP = ord("w") + LEFT = ord("a") + RIGHT = ord("d") + DOWN = ord("s") + FIRE = ord(" ") + + mapping = { + ale_py.Action.NOOP: (None,), + ale_py.Action.UP: (UP,), + ale_py.Action.FIRE: (FIRE,), + ale_py.Action.DOWN: (DOWN,), + ale_py.Action.LEFT: (LEFT,), + ale_py.Action.RIGHT: (RIGHT,), + ale_py.Action.UPFIRE: (UP, FIRE), + ale_py.Action.DOWNFIRE: (DOWN, FIRE), + ale_py.Action.LEFTFIRE: (LEFT, FIRE), + ale_py.Action.RIGHTFIRE: (RIGHT, FIRE), + ale_py.Action.UPLEFT: (UP, LEFT), + ale_py.Action.UPRIGHT: (UP, RIGHT), + ale_py.Action.DOWNLEFT: (DOWN, LEFT), + ale_py.Action.DOWNRIGHT: (DOWN, RIGHT), + ale_py.Action.UPLEFTFIRE: (UP, LEFT, FIRE), + ale_py.Action.UPRIGHTFIRE: (UP, RIGHT, FIRE), + ale_py.Action.DOWNLEFTFIRE: (DOWN, LEFT, FIRE), + ale_py.Action.DOWNRIGHTFIRE: (DOWN, RIGHT, FIRE), + } + + # Map + # (key, key, ...) -> action_idx + # where action_idx is the integer value of the action enum + # + return dict( + zip( + map(lambda action: tuple(sorted(mapping[action])), self._action_set), + range(len(self._action_set)), + ) + ) + + def get_action_meanings(self) -> list[str]: + """ + Return the meaning of each integer action. + """ + keys = ale_py.Action.__members__.values() + values = ale_py.Action.__members__.keys() + mapping = dict(zip(keys, values)) + return [mapping[action] for action in self._action_set] + + def clone_state(self, include_rng=False) -> ale_py.ALEState: + """Clone emulator state w/o system state. Restoring this state will + *not* give an identical environment. For complete cloning and restoring + of the full state, see `{clone,restore}_full_state()`.""" + return self.ale.cloneState(include_rng=include_rng) + + def restore_state(self, state: ale_py.ALEState) -> None: + """Restore emulator state w/o system state.""" + self.ale.restoreState(state) + + def clone_full_state(self) -> ale_py.ALEState: + """Deprecated method which would clone the emulator and system state.""" + logger.warn( + "`clone_full_state()` is deprecated and will be removed in a future release of `ale-py`. " + "Please use `clone_state(include_rng=True)` which is equivalent to `clone_full_state`. " + ) + return self.ale.cloneSystemState() + + def restore_full_state(self, state: ale_py.ALEState) -> None: + """Restore emulator state w/ system state including pseudorandomness.""" + logger.warn( + "restore_full_state() is deprecated and will be removed in a future release of `ale-py`. " + "Please use `restore_state(state)` which will restore the state regardless of being a full or partial state. " + ) + self.ale.restoreSystemState(state) From 9562e9ee55c56207c2d873d94f2ae2303fbdeb67 Mon Sep 17 00:00:00 2001 From: Jet Date: Tue, 6 Feb 2024 00:55:01 +0000 Subject: [PATCH 02/16] remove old file --- src/python/env/gym.py | 425 ------------------------------------------ 1 file changed, 425 deletions(-) delete mode 100644 src/python/env/gym.py diff --git a/src/python/env/gym.py b/src/python/env/gym.py deleted file mode 100644 index 44353418c..000000000 --- a/src/python/env/gym.py +++ /dev/null @@ -1,425 +0,0 @@ -import sys -from typing import Any, Dict, List, Optional, Sequence, Tuple, Union - -import ale_py -import ale_py.roms as roms -import ale_py.roms.utils as rom_utils -import numpy as np - -import gym -import gym.logger as logger -from gym import error, spaces, utils - -if sys.version_info < (3, 11): - from typing_extensions import NotRequired, TypedDict -else: - from typing import NotRequired, TypedDict - - -class AtariEnvStepMetadata(TypedDict): - lives: int - episode_frame_number: int - frame_number: int - seeds: NotRequired[Sequence[int]] - - -class AtariEnv(gym.Env, utils.EzPickle): - """ - (A)rcade (L)earning (Gym) (Env)ironment. - A Gym wrapper around the Arcade Learning Environment (ALE). - """ - - # No render modes - metadata = {"render_modes": ["human", "rgb_array"]} - - def __init__( - self, - game: str = "pong", - mode: Optional[int] = None, - difficulty: Optional[int] = None, - obs_type: str = "rgb", - frameskip: Union[Tuple[int, int], int] = 4, - repeat_action_probability: float = 0.25, - full_action_space: bool = False, - max_num_frames_per_episode: Optional[int] = None, - render_mode: Optional[str] = None, - ) -> None: - """ - Initialize the ALE for Gym. - Default parameters are taken from Machado et al., 2018. - - Args: - game: str => Game to initialize env with. - mode: Optional[int] => Game mode, see Machado et al., 2018 - difficulty: Optional[int] => Game difficulty,see Machado et al., 2018 - obs_type: str => Observation type in { 'rgb', 'grayscale', 'ram' } - frameskip: Union[Tuple[int, int], int] => - Stochastic frameskip as tuple or fixed. - repeat_action_probability: int => - Probability to repeat actions, see Machado et al., 2018 - full_action_space: bool => Use full action space? - max_num_frames_per_episode: int => Max number of frame per epsiode. - Once `max_num_frames_per_episode` is reached the episode is - truncated. - render_mode: str => One of { 'human', 'rgb_array' }. - If `human` we'll interactively display the screen and enable - game sounds. This will lock emulation to the ROMs specified FPS - If `rgb_array` we'll return the `rgb` key in step metadata with - the current environment RGB frame. - - Note: - - The game must be installed, see ale-import-roms, or ale-py-roms. - - Frameskip values of (low, high) will enable stochastic frame skip - which will sample a random frameskip uniformly each action. - - It is recommended to enable full action space. - See Machado et al., 2018 for more details. - - References: - `Revisiting the Arcade Learning Environment: Evaluation Protocols - and Open Problems for General Agents`, Machado et al., 2018, JAIR - URL: https://jair.org/index.php/jair/article/view/11182 - """ - if obs_type == "image": - logger.warn( - 'obs_type "image" should be replaced with the image type, one of: rgb, grayscale' - ) - obs_type = "rgb" - if obs_type not in {"rgb", "grayscale", "ram"}: - raise error.Error( - f"Invalid observation type: {obs_type}. Expecting: rgb, grayscale, ram." - ) - - if type(frameskip) not in (int, tuple): - raise error.Error(f"Invalid frameskip type: {type(frameskip)}.") - if isinstance(frameskip, int) and frameskip <= 0: - raise error.Error( - f"Invalid frameskip of {frameskip}, frameskip must be positive." - ) - elif isinstance(frameskip, tuple) and len(frameskip) != 2: - raise error.Error( - f"Invalid stochastic frameskip length of {len(frameskip)}, expected length 2." - ) - elif isinstance(frameskip, tuple) and frameskip[0] > frameskip[1]: - raise error.Error( - f"Invalid stochastic frameskip, lower bound is greater than upper bound." - ) - elif isinstance(frameskip, tuple) and frameskip[0] <= 0: - raise error.Error( - f"Invalid stochastic frameskip lower bound is greater than upper bound." - ) - - if render_mode is not None and render_mode not in {"rgb_array", "human"}: - raise error.Error( - f"Render mode {render_mode} not supported (rgb_array, human)." - ) - - utils.EzPickle.__init__( - self, - game, - mode, - difficulty, - obs_type, - frameskip, - repeat_action_probability, - full_action_space, - max_num_frames_per_episode, - render_mode, - ) - - # Initialize ALE - self.ale = ale_py.ALEInterface() - - self._game = rom_utils.rom_id_to_name(game) - - self._game_mode = mode - self._game_difficulty = difficulty - - self._frameskip = frameskip - self._obs_type = obs_type - self._render_mode = render_mode - - # Set logger mode to error only - self.ale.setLoggerMode(ale_py.LoggerMode.Error) - # Config sticky action prob. - self.ale.setFloat("repeat_action_probability", repeat_action_probability) - - if max_num_frames_per_episode is not None: - self.ale.setInt("max_num_frames_per_episode", max_num_frames_per_episode) - - # If render mode is human we can display screen and sound - if render_mode == "human": - self.ale.setBool("display_screen", True) - self.ale.setBool("sound", True) - - # Seed + Load - self.seed() - - self._action_set = ( - self.ale.getLegalActionSet() - if full_action_space - else self.ale.getMinimalActionSet() - ) - self._action_space = spaces.Discrete(len(self._action_set)) - - # Initialize observation type - if self._obs_type == "ram": - self._obs_space = spaces.Box( - low=0, high=255, dtype=np.uint8, shape=(self.ale.getRAMSize(),) - ) - elif self._obs_type == "rgb" or self._obs_type == "grayscale": - (screen_height, screen_width) = self.ale.getScreenDims() - image_shape = ( - screen_height, - screen_width, - ) - if self._obs_type == "rgb": - image_shape += (3,) - self._obs_space = spaces.Box( - low=0, high=255, dtype=np.uint8, shape=image_shape - ) - else: - raise error.Error(f"Unrecognized observation type: {self._obs_type}") - - def seed(self, seed: Optional[int] = None) -> Tuple[int, int]: - """ - Seeds both the internal numpy rng for stochastic frame skip - as well as the ALE RNG. - - This function must also initialize the ROM and set the corresponding - mode and difficulty. `seed` may be called to initialize the environment - during deserialization by Gym so these side-effects must reside here. - - Args: - seed: int => Manually set the seed for RNG. - Returns: - tuple[int, int] => (np seed, ALE seed) - """ - ss = np.random.SeedSequence(seed) - seed1, seed2 = ss.generate_state(n_words=2) - - self.np_random = np.random.default_rng(seed1) - # ALE only takes signed integers for `setInt`, it'll get converted back - # to unsigned in StellaEnvironment. - self.ale.setInt("random_seed", seed2.astype(np.int32)) - - if not hasattr(roms, self._game): - raise error.Error( - f'We\'re Unable to find the game "{self._game}". Note: Gym no longer distributes ROMs. ' - f"If you own a license to use the necessary ROMs for research purposes you can download them " - f'via `pip install gym[accept-rom-license]`. Otherwise, you should try importing "{self._game}" ' - f'via the command `ale-import-roms`. If you believe this is a mistake perhaps your copy of "{self._game}" ' - "is unsupported. To check if this is the case try providing the environment variable " - "`PYTHONWARNINGS=default::ImportWarning:ale_py.roms`. For more information see: " - "https://github.com/mgbellemare/Arcade-Learning-Environment#rom-management" - ) - self.ale.loadROM(getattr(roms, self._game)) - - if self._game_mode is not None: - self.ale.setMode(self._game_mode) - if self._game_difficulty is not None: - self.ale.setDifficulty(self._game_difficulty) - - return seed1, seed2 - - def step( - self, - action_ind: int, - ) -> Tuple[np.ndarray, float, bool, bool, AtariEnvStepMetadata]: - """ - Perform one agent step, i.e., repeats `action` frameskip # of steps. - - Args: - action_ind: int => Action index to execute - - Returns: - Tuple[np.ndarray, float, bool, Dict[str, Any]] => - observation, reward, terminal, metadata - - Note: `metadata` contains the keys "lives" and "rgb" if - render_mode == 'rgb_array'. - """ - # Get action enum, terminal bool, metadata - action = self._action_set[action_ind] - - # If frameskip is a length 2 tuple then it's stochastic - # frameskip between [frameskip[0], frameskip[1]] uniformly. - if isinstance(self._frameskip, int): - frameskip = self._frameskip - elif isinstance(self._frameskip, tuple): - frameskip = self.np_random.integers(*self._frameskip) - else: - raise error.Error(f"Invalid frameskip type: {self._frameskip}") - - # Frameskip - reward = 0.0 - for _ in range(frameskip): - reward += self.ale.act(action) - is_terminal = self.ale.game_over(with_truncation=False) - is_truncated = self.ale.game_truncated() - - return self._get_obs(), reward, is_terminal, is_truncated, self._get_info() - - def reset( - self, - *, - seed: Optional[int] = None, - options: Optional[Dict[str, Any]] = None, - ) -> Tuple[np.ndarray, AtariEnvStepMetadata]: - """ - Resets environment and returns initial observation. - """ - del options - # Gym's new seeding API seeds on reset. - # This will cause the console to be recreated - # and loose all previous state, e.g., statistics, etc. - seeded_with = None - if seed is not None: - seeded_with = self.seed(seed) - - self.ale.reset_game() - obs = self._get_obs() - - info = self._get_info() - if seeded_with is not None: - info["seeds"] = seeded_with - return obs, info - - def render(self) -> Any: - """ - Render is not supported by ALE. We use a paradigm similar to - Gym3 which allows you to specify `render_mode` during construction. - - For example, - gym.make("ale-py:Pong-v0", render_mode="human") - will display the ALE and maintain the proper interval to match the - FPS target set by the ROM. - """ - if self._render_mode == "rgb_array": - return self.ale.getScreenRGB() - elif self._render_mode == "human": - pass - else: - raise error.Error( - f"Invalid render mode `{self._render_mode}`. " - "Supported modes: `human`, `rgb_array`." - ) - - def _get_obs(self) -> np.ndarray: - """ - Retreives the current observation. - This is dependent on `self._obs_type`. - """ - if self._obs_type == "ram": - return self.ale.getRAM() - elif self._obs_type == "rgb": - return self.ale.getScreenRGB() - elif self._obs_type == "grayscale": - return self.ale.getScreenGrayscale() - else: - raise error.Error(f"Unrecognized observation type: {self._obs_type}") - - def _get_info(self) -> AtariEnvStepMetadata: - return { - "lives": self.ale.lives(), - "episode_frame_number": self.ale.getEpisodeFrameNumber(), - "frame_number": self.ale.getFrameNumber(), - } - - def get_keys_to_action(self) -> Dict[Tuple[int], ale_py.Action]: - """ - Return keymapping -> actions for human play. - """ - UP = ord("w") - LEFT = ord("a") - RIGHT = ord("d") - DOWN = ord("s") - FIRE = ord(" ") - - mapping = { - ale_py.Action.NOOP: (None,), - ale_py.Action.UP: (UP,), - ale_py.Action.FIRE: (FIRE,), - ale_py.Action.DOWN: (DOWN,), - ale_py.Action.LEFT: (LEFT,), - ale_py.Action.RIGHT: (RIGHT,), - ale_py.Action.UPFIRE: (UP, FIRE), - ale_py.Action.DOWNFIRE: (DOWN, FIRE), - ale_py.Action.LEFTFIRE: (LEFT, FIRE), - ale_py.Action.RIGHTFIRE: (RIGHT, FIRE), - ale_py.Action.UPLEFT: (UP, LEFT), - ale_py.Action.UPRIGHT: (UP, RIGHT), - ale_py.Action.DOWNLEFT: (DOWN, LEFT), - ale_py.Action.DOWNRIGHT: (DOWN, RIGHT), - ale_py.Action.UPLEFTFIRE: (UP, LEFT, FIRE), - ale_py.Action.UPRIGHTFIRE: (UP, RIGHT, FIRE), - ale_py.Action.DOWNLEFTFIRE: (DOWN, LEFT, FIRE), - ale_py.Action.DOWNRIGHTFIRE: (DOWN, RIGHT, FIRE), - } - - # Map - # (key, key, ...) -> action_idx - # where action_idx is the integer value of the action enum - # - actions = self._action_set - return dict( - zip( - map(lambda action: tuple(sorted(mapping[action])), actions), - range(len(actions)), - ) - ) - - def get_action_meanings(self) -> List[str]: - """ - Return the meaning of each integer action. - """ - keys = ale_py.Action.__members__.values() - values = ale_py.Action.__members__.keys() - mapping = dict(zip(keys, values)) - return [mapping[action] for action in self._action_set] - - def clone_state(self, include_rng=False) -> ale_py.ALEState: - """Clone emulator state w/o system state. Restoring this state will - *not* give an identical environment. For complete cloning and restoring - of the full state, see `{clone,restore}_full_state()`.""" - return self.ale.cloneState(include_rng=include_rng) - - def restore_state(self, state: ale_py.ALEState) -> None: - """Restore emulator state w/o system state.""" - self.ale.restoreState(state) - - def clone_full_state(self) -> ale_py.ALEState: - """Deprecated method which would clone the emulator and system state.""" - logger.warn( - "`clone_full_state()` is deprecated and will be removed in a future release of `ale-py`. " - "Please use `clone_state(include_rng=True)` which is equivalent to `clone_full_state`. " - ) - return self.ale.cloneSystemState() - - def restore_full_state(self, state: ale_py.ALEState) -> None: - """Restore emulator state w/ system state including pseudorandomness.""" - logger.warn( - "restore_full_state() is deprecated and will be removed in a future release of `ale-py`. " - "Please use `restore_state(state)` which will restore the state regardless of being a full or partial state. " - ) - self.ale.restoreSystemState(state) - - @property - def action_space(self) -> spaces.Discrete: - """ - Return Gym's action space. - """ - return self._action_space - - @property - def observation_space(self) -> spaces.Box: - """ - Return Gym's observation space. - """ - return self._obs_space - - @property - def render_mode(self) -> str: - """ - Attribute render_mode to comply Gym API. - """ - return self._render_mode From 5e2fa7ca167ea794dd24b9a3b3dfd2a969c6cc85 Mon Sep 17 00:00:00 2001 From: Jet Date: Tue, 6 Feb 2024 00:56:28 +0000 Subject: [PATCH 03/16] move things back to correct order --- src/python/env/gymnasium.py | 43 ++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/src/python/env/gymnasium.py b/src/python/env/gymnasium.py index 606ccba45..531b937e9 100644 --- a/src/python/env/gymnasium.py +++ b/src/python/env/gymnasium.py @@ -207,28 +207,6 @@ def load_game(self) -> None: if self._game_difficulty is not None: self.ale.setDifficulty(self._game_difficulty) - - def reset( # pyright: ignore[reportIncompatibleMethodOverride] - self, - *, - seed: Optional[int] = None, - options: Optional[dict[str, Any]] = None, - ) -> tuple[np.ndarray, AtariEnvStepMetadata]: - """Resets environment and returns initial observation.""" - # sets the seeds if it's specified for both ALE and frameskip np - # we only want to do this when commanded to so we don't reset all previous states, statistics, etc. - seeded_with = self.seed_game(seed) if seed else None - self.load_game() - - self.ale.reset_game() - - obs = self._get_obs() - info = self._get_info() - if seeded_with is not None: - info["seeds"] = seeded_with - - return obs, info - def step( # pyright: ignore[reportIncompatibleMethodOverride] self, action: int, @@ -264,6 +242,27 @@ def step( # pyright: ignore[reportIncompatibleMethodOverride] return self._get_obs(), reward, is_terminal, is_truncated, self._get_info() + def reset( # pyright: ignore[reportIncompatibleMethodOverride] + self, + *, + seed: Optional[int] = None, + options: Optional[dict[str, Any]] = None, + ) -> tuple[np.ndarray, AtariEnvStepMetadata]: + """Resets environment and returns initial observation.""" + # sets the seeds if it's specified for both ALE and frameskip np + # we only want to do this when commanded to so we don't reset all previous states, statistics, etc. + seeded_with = self.seed_game(seed) if seed else None + self.load_game() + + self.ale.reset_game() + + obs = self._get_obs() + info = self._get_info() + if seeded_with is not None: + info["seeds"] = seeded_with + + return obs, info + def render(self) -> Optional[np.ndarray]: """ Render is not supported by ALE. We use a paradigm similar to From a3cc775c81d65abf11165088f15af12e59e7abb2 Mon Sep 17 00:00:00 2001 From: Jet Date: Tue, 6 Feb 2024 00:57:41 +0000 Subject: [PATCH 04/16] back to old method --- src/python/env/gymnasium.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/python/env/gymnasium.py b/src/python/env/gymnasium.py index 531b937e9..7d6dff262 100644 --- a/src/python/env/gymnasium.py +++ b/src/python/env/gymnasium.py @@ -251,8 +251,10 @@ def reset( # pyright: ignore[reportIncompatibleMethodOverride] """Resets environment and returns initial observation.""" # sets the seeds if it's specified for both ALE and frameskip np # we only want to do this when commanded to so we don't reset all previous states, statistics, etc. - seeded_with = self.seed_game(seed) if seed else None - self.load_game() + seeded_with = None + if seed is not None: + seeded_with = self.seed_game(seed) + self.load_game() self.ale.reset_game() From ea1c01fd3c260345dbbfe786fd926465d26c2c16 Mon Sep 17 00:00:00 2001 From: Jet Date: Wed, 7 Feb 2024 15:34:08 +0000 Subject: [PATCH 05/16] put back gym support --- src/python/gym.py | 185 ---------------------------------------------- 1 file changed, 185 deletions(-) delete mode 100644 src/python/gym.py diff --git a/src/python/gym.py b/src/python/gym.py deleted file mode 100644 index 62aec69cf..000000000 --- a/src/python/gym.py +++ /dev/null @@ -1,185 +0,0 @@ -from collections import defaultdict -from typing import Any, Callable, Mapping, NamedTuple, Sequence, Text, Union - -import ale_py.roms as roms -from ale_py.roms import utils as rom_utils - -from gym.envs.registration import register - - -class GymFlavour(NamedTuple): - suffix: str - kwargs: Union[Mapping[Text, Any], Callable[[str], Mapping[Text, Any]]] - - -class GymConfig(NamedTuple): - version: str - kwargs: Mapping[Text, Any] - flavours: Sequence[GymFlavour] - - -def _register_gym_configs( - roms: Sequence[str], - obs_types: Sequence[str], - configs: Sequence[GymConfig], - prefix: str = "", -) -> None: - if len(prefix) > 0 and prefix[-1] != "/": - prefix += "/" - - for rom in roms: - for obs_type in obs_types: - for config in configs: - for flavour in config.flavours: - name = rom_utils.rom_id_to_name(rom) - name = f"{name}-ram" if obs_type == "ram" else name - - # Parse config kwargs - config_kwargs = ( - config.kwargs(rom) if callable(config.kwargs) else config.kwargs - ) - # Parse flavour kwargs - flavour_kwargs = ( - flavour.kwargs(rom) - if callable(flavour.kwargs) - else flavour.kwargs - ) - - # Register the environment - register( - id=f"{prefix}{name}{flavour.suffix}-{config.version}", - entry_point="ale_py.env.gym:AtariEnv", - kwargs=dict( - game=rom, - obs_type=obs_type, - **config_kwargs, - **flavour_kwargs, - ), - ) - - -def register_legacy_gym_envs() -> None: - legacy_games = [ - "adventure", - "air_raid", - "alien", - "amidar", - "assault", - "asterix", - "asteroids", - "atlantis", - "bank_heist", - "battle_zone", - "beam_rider", - "berzerk", - "bowling", - "boxing", - "breakout", - "carnival", - "centipede", - "chopper_command", - "crazy_climber", - "defender", - "demon_attack", - "double_dunk", - "elevator_action", - "enduro", - "fishing_derby", - "freeway", - "frostbite", - "gopher", - "gravitar", - "hero", - "ice_hockey", - "jamesbond", - "journey_escape", - "kangaroo", - "krull", - "kung_fu_master", - "montezuma_revenge", - "ms_pacman", - "name_this_game", - "phoenix", - "pitfall", - "pong", - "pooyan", - "private_eye", - "qbert", - "riverraid", - "road_runner", - "robotank", - "seaquest", - "skiing", - "solaris", - "space_invaders", - "star_gunner", - "tennis", - "time_pilot", - "tutankham", - "up_n_down", - "venture", - "video_pinball", - "wizard_of_wor", - "yars_revenge", - "zaxxon", - ] - obs_types = ["rgb", "ram"] - frameskip = defaultdict(lambda: 4, [("space_invaders", 3)]) - - versions = [ - GymConfig( - version="v0", - kwargs={ - "repeat_action_probability": 0.25, - "full_action_space": False, - "max_num_frames_per_episode": 108_000, - }, - flavours=[ - # Default for v0 has 10k steps, no idea why... - GymFlavour("", {"frameskip": (2, 5)}), - # Deterministic has 100k steps, close to the standard of 108k (30 mins gameplay) - GymFlavour("Deterministic", lambda rom: {"frameskip": frameskip[rom]}), - # NoFrameSkip imposes a max episode steps of frameskip * 100k, weird... - GymFlavour("NoFrameskip", {"frameskip": 1}), - ], - ), - GymConfig( - version="v4", - kwargs={ - "repeat_action_probability": 0.0, - "full_action_space": False, - "max_num_frames_per_episode": 108_000, - }, - flavours=[ - # Unlike v0, v4 has 100k max episode steps - GymFlavour("", {"frameskip": (2, 5)}), - GymFlavour("Deterministic", lambda rom: {"frameskip": frameskip[rom]}), - # Same weird frameskip * 100k max steps for v4? - GymFlavour("NoFrameskip", {"frameskip": 1}), - ], - ), - ] - - _register_gym_configs(legacy_games, obs_types, versions) - - -def register_gym_envs(): - all_games = list(map(rom_utils.rom_name_to_id, dir(roms))) - obs_types = ["rgb", "ram"] - - # max_episode_steps is 108k frames which is 30 mins of gameplay. - # This corresponds to 108k / 4 = 27,000 steps - versions = [ - GymConfig( - version="v5", - kwargs={ - "repeat_action_probability": 0.25, - "full_action_space": False, - "frameskip": 4, - "max_num_frames_per_episode": 108_000, - }, - flavours=[GymFlavour("", {})], - ) - ] - - _register_gym_configs(all_games, obs_types, versions) From 51e603d8433ea7a957b03a26de85954dcb06e217 Mon Sep 17 00:00:00 2001 From: Jet Date: Wed, 7 Feb 2024 15:35:09 +0000 Subject: [PATCH 06/16] whoops wrong file --- src/python/env/gym.py | 425 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 425 insertions(+) create mode 100644 src/python/env/gym.py diff --git a/src/python/env/gym.py b/src/python/env/gym.py new file mode 100644 index 000000000..44353418c --- /dev/null +++ b/src/python/env/gym.py @@ -0,0 +1,425 @@ +import sys +from typing import Any, Dict, List, Optional, Sequence, Tuple, Union + +import ale_py +import ale_py.roms as roms +import ale_py.roms.utils as rom_utils +import numpy as np + +import gym +import gym.logger as logger +from gym import error, spaces, utils + +if sys.version_info < (3, 11): + from typing_extensions import NotRequired, TypedDict +else: + from typing import NotRequired, TypedDict + + +class AtariEnvStepMetadata(TypedDict): + lives: int + episode_frame_number: int + frame_number: int + seeds: NotRequired[Sequence[int]] + + +class AtariEnv(gym.Env, utils.EzPickle): + """ + (A)rcade (L)earning (Gym) (Env)ironment. + A Gym wrapper around the Arcade Learning Environment (ALE). + """ + + # No render modes + metadata = {"render_modes": ["human", "rgb_array"]} + + def __init__( + self, + game: str = "pong", + mode: Optional[int] = None, + difficulty: Optional[int] = None, + obs_type: str = "rgb", + frameskip: Union[Tuple[int, int], int] = 4, + repeat_action_probability: float = 0.25, + full_action_space: bool = False, + max_num_frames_per_episode: Optional[int] = None, + render_mode: Optional[str] = None, + ) -> None: + """ + Initialize the ALE for Gym. + Default parameters are taken from Machado et al., 2018. + + Args: + game: str => Game to initialize env with. + mode: Optional[int] => Game mode, see Machado et al., 2018 + difficulty: Optional[int] => Game difficulty,see Machado et al., 2018 + obs_type: str => Observation type in { 'rgb', 'grayscale', 'ram' } + frameskip: Union[Tuple[int, int], int] => + Stochastic frameskip as tuple or fixed. + repeat_action_probability: int => + Probability to repeat actions, see Machado et al., 2018 + full_action_space: bool => Use full action space? + max_num_frames_per_episode: int => Max number of frame per epsiode. + Once `max_num_frames_per_episode` is reached the episode is + truncated. + render_mode: str => One of { 'human', 'rgb_array' }. + If `human` we'll interactively display the screen and enable + game sounds. This will lock emulation to the ROMs specified FPS + If `rgb_array` we'll return the `rgb` key in step metadata with + the current environment RGB frame. + + Note: + - The game must be installed, see ale-import-roms, or ale-py-roms. + - Frameskip values of (low, high) will enable stochastic frame skip + which will sample a random frameskip uniformly each action. + - It is recommended to enable full action space. + See Machado et al., 2018 for more details. + + References: + `Revisiting the Arcade Learning Environment: Evaluation Protocols + and Open Problems for General Agents`, Machado et al., 2018, JAIR + URL: https://jair.org/index.php/jair/article/view/11182 + """ + if obs_type == "image": + logger.warn( + 'obs_type "image" should be replaced with the image type, one of: rgb, grayscale' + ) + obs_type = "rgb" + if obs_type not in {"rgb", "grayscale", "ram"}: + raise error.Error( + f"Invalid observation type: {obs_type}. Expecting: rgb, grayscale, ram." + ) + + if type(frameskip) not in (int, tuple): + raise error.Error(f"Invalid frameskip type: {type(frameskip)}.") + if isinstance(frameskip, int) and frameskip <= 0: + raise error.Error( + f"Invalid frameskip of {frameskip}, frameskip must be positive." + ) + elif isinstance(frameskip, tuple) and len(frameskip) != 2: + raise error.Error( + f"Invalid stochastic frameskip length of {len(frameskip)}, expected length 2." + ) + elif isinstance(frameskip, tuple) and frameskip[0] > frameskip[1]: + raise error.Error( + f"Invalid stochastic frameskip, lower bound is greater than upper bound." + ) + elif isinstance(frameskip, tuple) and frameskip[0] <= 0: + raise error.Error( + f"Invalid stochastic frameskip lower bound is greater than upper bound." + ) + + if render_mode is not None and render_mode not in {"rgb_array", "human"}: + raise error.Error( + f"Render mode {render_mode} not supported (rgb_array, human)." + ) + + utils.EzPickle.__init__( + self, + game, + mode, + difficulty, + obs_type, + frameskip, + repeat_action_probability, + full_action_space, + max_num_frames_per_episode, + render_mode, + ) + + # Initialize ALE + self.ale = ale_py.ALEInterface() + + self._game = rom_utils.rom_id_to_name(game) + + self._game_mode = mode + self._game_difficulty = difficulty + + self._frameskip = frameskip + self._obs_type = obs_type + self._render_mode = render_mode + + # Set logger mode to error only + self.ale.setLoggerMode(ale_py.LoggerMode.Error) + # Config sticky action prob. + self.ale.setFloat("repeat_action_probability", repeat_action_probability) + + if max_num_frames_per_episode is not None: + self.ale.setInt("max_num_frames_per_episode", max_num_frames_per_episode) + + # If render mode is human we can display screen and sound + if render_mode == "human": + self.ale.setBool("display_screen", True) + self.ale.setBool("sound", True) + + # Seed + Load + self.seed() + + self._action_set = ( + self.ale.getLegalActionSet() + if full_action_space + else self.ale.getMinimalActionSet() + ) + self._action_space = spaces.Discrete(len(self._action_set)) + + # Initialize observation type + if self._obs_type == "ram": + self._obs_space = spaces.Box( + low=0, high=255, dtype=np.uint8, shape=(self.ale.getRAMSize(),) + ) + elif self._obs_type == "rgb" or self._obs_type == "grayscale": + (screen_height, screen_width) = self.ale.getScreenDims() + image_shape = ( + screen_height, + screen_width, + ) + if self._obs_type == "rgb": + image_shape += (3,) + self._obs_space = spaces.Box( + low=0, high=255, dtype=np.uint8, shape=image_shape + ) + else: + raise error.Error(f"Unrecognized observation type: {self._obs_type}") + + def seed(self, seed: Optional[int] = None) -> Tuple[int, int]: + """ + Seeds both the internal numpy rng for stochastic frame skip + as well as the ALE RNG. + + This function must also initialize the ROM and set the corresponding + mode and difficulty. `seed` may be called to initialize the environment + during deserialization by Gym so these side-effects must reside here. + + Args: + seed: int => Manually set the seed for RNG. + Returns: + tuple[int, int] => (np seed, ALE seed) + """ + ss = np.random.SeedSequence(seed) + seed1, seed2 = ss.generate_state(n_words=2) + + self.np_random = np.random.default_rng(seed1) + # ALE only takes signed integers for `setInt`, it'll get converted back + # to unsigned in StellaEnvironment. + self.ale.setInt("random_seed", seed2.astype(np.int32)) + + if not hasattr(roms, self._game): + raise error.Error( + f'We\'re Unable to find the game "{self._game}". Note: Gym no longer distributes ROMs. ' + f"If you own a license to use the necessary ROMs for research purposes you can download them " + f'via `pip install gym[accept-rom-license]`. Otherwise, you should try importing "{self._game}" ' + f'via the command `ale-import-roms`. If you believe this is a mistake perhaps your copy of "{self._game}" ' + "is unsupported. To check if this is the case try providing the environment variable " + "`PYTHONWARNINGS=default::ImportWarning:ale_py.roms`. For more information see: " + "https://github.com/mgbellemare/Arcade-Learning-Environment#rom-management" + ) + self.ale.loadROM(getattr(roms, self._game)) + + if self._game_mode is not None: + self.ale.setMode(self._game_mode) + if self._game_difficulty is not None: + self.ale.setDifficulty(self._game_difficulty) + + return seed1, seed2 + + def step( + self, + action_ind: int, + ) -> Tuple[np.ndarray, float, bool, bool, AtariEnvStepMetadata]: + """ + Perform one agent step, i.e., repeats `action` frameskip # of steps. + + Args: + action_ind: int => Action index to execute + + Returns: + Tuple[np.ndarray, float, bool, Dict[str, Any]] => + observation, reward, terminal, metadata + + Note: `metadata` contains the keys "lives" and "rgb" if + render_mode == 'rgb_array'. + """ + # Get action enum, terminal bool, metadata + action = self._action_set[action_ind] + + # If frameskip is a length 2 tuple then it's stochastic + # frameskip between [frameskip[0], frameskip[1]] uniformly. + if isinstance(self._frameskip, int): + frameskip = self._frameskip + elif isinstance(self._frameskip, tuple): + frameskip = self.np_random.integers(*self._frameskip) + else: + raise error.Error(f"Invalid frameskip type: {self._frameskip}") + + # Frameskip + reward = 0.0 + for _ in range(frameskip): + reward += self.ale.act(action) + is_terminal = self.ale.game_over(with_truncation=False) + is_truncated = self.ale.game_truncated() + + return self._get_obs(), reward, is_terminal, is_truncated, self._get_info() + + def reset( + self, + *, + seed: Optional[int] = None, + options: Optional[Dict[str, Any]] = None, + ) -> Tuple[np.ndarray, AtariEnvStepMetadata]: + """ + Resets environment and returns initial observation. + """ + del options + # Gym's new seeding API seeds on reset. + # This will cause the console to be recreated + # and loose all previous state, e.g., statistics, etc. + seeded_with = None + if seed is not None: + seeded_with = self.seed(seed) + + self.ale.reset_game() + obs = self._get_obs() + + info = self._get_info() + if seeded_with is not None: + info["seeds"] = seeded_with + return obs, info + + def render(self) -> Any: + """ + Render is not supported by ALE. We use a paradigm similar to + Gym3 which allows you to specify `render_mode` during construction. + + For example, + gym.make("ale-py:Pong-v0", render_mode="human") + will display the ALE and maintain the proper interval to match the + FPS target set by the ROM. + """ + if self._render_mode == "rgb_array": + return self.ale.getScreenRGB() + elif self._render_mode == "human": + pass + else: + raise error.Error( + f"Invalid render mode `{self._render_mode}`. " + "Supported modes: `human`, `rgb_array`." + ) + + def _get_obs(self) -> np.ndarray: + """ + Retreives the current observation. + This is dependent on `self._obs_type`. + """ + if self._obs_type == "ram": + return self.ale.getRAM() + elif self._obs_type == "rgb": + return self.ale.getScreenRGB() + elif self._obs_type == "grayscale": + return self.ale.getScreenGrayscale() + else: + raise error.Error(f"Unrecognized observation type: {self._obs_type}") + + def _get_info(self) -> AtariEnvStepMetadata: + return { + "lives": self.ale.lives(), + "episode_frame_number": self.ale.getEpisodeFrameNumber(), + "frame_number": self.ale.getFrameNumber(), + } + + def get_keys_to_action(self) -> Dict[Tuple[int], ale_py.Action]: + """ + Return keymapping -> actions for human play. + """ + UP = ord("w") + LEFT = ord("a") + RIGHT = ord("d") + DOWN = ord("s") + FIRE = ord(" ") + + mapping = { + ale_py.Action.NOOP: (None,), + ale_py.Action.UP: (UP,), + ale_py.Action.FIRE: (FIRE,), + ale_py.Action.DOWN: (DOWN,), + ale_py.Action.LEFT: (LEFT,), + ale_py.Action.RIGHT: (RIGHT,), + ale_py.Action.UPFIRE: (UP, FIRE), + ale_py.Action.DOWNFIRE: (DOWN, FIRE), + ale_py.Action.LEFTFIRE: (LEFT, FIRE), + ale_py.Action.RIGHTFIRE: (RIGHT, FIRE), + ale_py.Action.UPLEFT: (UP, LEFT), + ale_py.Action.UPRIGHT: (UP, RIGHT), + ale_py.Action.DOWNLEFT: (DOWN, LEFT), + ale_py.Action.DOWNRIGHT: (DOWN, RIGHT), + ale_py.Action.UPLEFTFIRE: (UP, LEFT, FIRE), + ale_py.Action.UPRIGHTFIRE: (UP, RIGHT, FIRE), + ale_py.Action.DOWNLEFTFIRE: (DOWN, LEFT, FIRE), + ale_py.Action.DOWNRIGHTFIRE: (DOWN, RIGHT, FIRE), + } + + # Map + # (key, key, ...) -> action_idx + # where action_idx is the integer value of the action enum + # + actions = self._action_set + return dict( + zip( + map(lambda action: tuple(sorted(mapping[action])), actions), + range(len(actions)), + ) + ) + + def get_action_meanings(self) -> List[str]: + """ + Return the meaning of each integer action. + """ + keys = ale_py.Action.__members__.values() + values = ale_py.Action.__members__.keys() + mapping = dict(zip(keys, values)) + return [mapping[action] for action in self._action_set] + + def clone_state(self, include_rng=False) -> ale_py.ALEState: + """Clone emulator state w/o system state. Restoring this state will + *not* give an identical environment. For complete cloning and restoring + of the full state, see `{clone,restore}_full_state()`.""" + return self.ale.cloneState(include_rng=include_rng) + + def restore_state(self, state: ale_py.ALEState) -> None: + """Restore emulator state w/o system state.""" + self.ale.restoreState(state) + + def clone_full_state(self) -> ale_py.ALEState: + """Deprecated method which would clone the emulator and system state.""" + logger.warn( + "`clone_full_state()` is deprecated and will be removed in a future release of `ale-py`. " + "Please use `clone_state(include_rng=True)` which is equivalent to `clone_full_state`. " + ) + return self.ale.cloneSystemState() + + def restore_full_state(self, state: ale_py.ALEState) -> None: + """Restore emulator state w/ system state including pseudorandomness.""" + logger.warn( + "restore_full_state() is deprecated and will be removed in a future release of `ale-py`. " + "Please use `restore_state(state)` which will restore the state regardless of being a full or partial state. " + ) + self.ale.restoreSystemState(state) + + @property + def action_space(self) -> spaces.Discrete: + """ + Return Gym's action space. + """ + return self._action_space + + @property + def observation_space(self) -> spaces.Box: + """ + Return Gym's observation space. + """ + return self._obs_space + + @property + def render_mode(self) -> str: + """ + Attribute render_mode to comply Gym API. + """ + return self._render_mode From 442845cdf7fb125b804b9ff76a04ee7c76ff8aac Mon Sep 17 00:00:00 2001 From: Jet Date: Wed, 7 Feb 2024 15:35:56 +0000 Subject: [PATCH 07/16] add back thing --- src/python/gym.py | 185 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 src/python/gym.py diff --git a/src/python/gym.py b/src/python/gym.py new file mode 100644 index 000000000..62aec69cf --- /dev/null +++ b/src/python/gym.py @@ -0,0 +1,185 @@ +from collections import defaultdict +from typing import Any, Callable, Mapping, NamedTuple, Sequence, Text, Union + +import ale_py.roms as roms +from ale_py.roms import utils as rom_utils + +from gym.envs.registration import register + + +class GymFlavour(NamedTuple): + suffix: str + kwargs: Union[Mapping[Text, Any], Callable[[str], Mapping[Text, Any]]] + + +class GymConfig(NamedTuple): + version: str + kwargs: Mapping[Text, Any] + flavours: Sequence[GymFlavour] + + +def _register_gym_configs( + roms: Sequence[str], + obs_types: Sequence[str], + configs: Sequence[GymConfig], + prefix: str = "", +) -> None: + if len(prefix) > 0 and prefix[-1] != "/": + prefix += "/" + + for rom in roms: + for obs_type in obs_types: + for config in configs: + for flavour in config.flavours: + name = rom_utils.rom_id_to_name(rom) + name = f"{name}-ram" if obs_type == "ram" else name + + # Parse config kwargs + config_kwargs = ( + config.kwargs(rom) if callable(config.kwargs) else config.kwargs + ) + # Parse flavour kwargs + flavour_kwargs = ( + flavour.kwargs(rom) + if callable(flavour.kwargs) + else flavour.kwargs + ) + + # Register the environment + register( + id=f"{prefix}{name}{flavour.suffix}-{config.version}", + entry_point="ale_py.env.gym:AtariEnv", + kwargs=dict( + game=rom, + obs_type=obs_type, + **config_kwargs, + **flavour_kwargs, + ), + ) + + +def register_legacy_gym_envs() -> None: + legacy_games = [ + "adventure", + "air_raid", + "alien", + "amidar", + "assault", + "asterix", + "asteroids", + "atlantis", + "bank_heist", + "battle_zone", + "beam_rider", + "berzerk", + "bowling", + "boxing", + "breakout", + "carnival", + "centipede", + "chopper_command", + "crazy_climber", + "defender", + "demon_attack", + "double_dunk", + "elevator_action", + "enduro", + "fishing_derby", + "freeway", + "frostbite", + "gopher", + "gravitar", + "hero", + "ice_hockey", + "jamesbond", + "journey_escape", + "kangaroo", + "krull", + "kung_fu_master", + "montezuma_revenge", + "ms_pacman", + "name_this_game", + "phoenix", + "pitfall", + "pong", + "pooyan", + "private_eye", + "qbert", + "riverraid", + "road_runner", + "robotank", + "seaquest", + "skiing", + "solaris", + "space_invaders", + "star_gunner", + "tennis", + "time_pilot", + "tutankham", + "up_n_down", + "venture", + "video_pinball", + "wizard_of_wor", + "yars_revenge", + "zaxxon", + ] + obs_types = ["rgb", "ram"] + frameskip = defaultdict(lambda: 4, [("space_invaders", 3)]) + + versions = [ + GymConfig( + version="v0", + kwargs={ + "repeat_action_probability": 0.25, + "full_action_space": False, + "max_num_frames_per_episode": 108_000, + }, + flavours=[ + # Default for v0 has 10k steps, no idea why... + GymFlavour("", {"frameskip": (2, 5)}), + # Deterministic has 100k steps, close to the standard of 108k (30 mins gameplay) + GymFlavour("Deterministic", lambda rom: {"frameskip": frameskip[rom]}), + # NoFrameSkip imposes a max episode steps of frameskip * 100k, weird... + GymFlavour("NoFrameskip", {"frameskip": 1}), + ], + ), + GymConfig( + version="v4", + kwargs={ + "repeat_action_probability": 0.0, + "full_action_space": False, + "max_num_frames_per_episode": 108_000, + }, + flavours=[ + # Unlike v0, v4 has 100k max episode steps + GymFlavour("", {"frameskip": (2, 5)}), + GymFlavour("Deterministic", lambda rom: {"frameskip": frameskip[rom]}), + # Same weird frameskip * 100k max steps for v4? + GymFlavour("NoFrameskip", {"frameskip": 1}), + ], + ), + ] + + _register_gym_configs(legacy_games, obs_types, versions) + + +def register_gym_envs(): + all_games = list(map(rom_utils.rom_name_to_id, dir(roms))) + obs_types = ["rgb", "ram"] + + # max_episode_steps is 108k frames which is 30 mins of gameplay. + # This corresponds to 108k / 4 = 27,000 steps + versions = [ + GymConfig( + version="v5", + kwargs={ + "repeat_action_probability": 0.25, + "full_action_space": False, + "frameskip": 4, + "max_num_frames_per_episode": 108_000, + }, + flavours=[GymFlavour("", {})], + ) + ] + + _register_gym_configs(all_games, obs_types, versions) From e84db3d89ec34f1b74a8723f62b43c4c20ba2d88 Mon Sep 17 00:00:00 2001 From: Jet Date: Wed, 7 Feb 2024 15:38:50 +0000 Subject: [PATCH 08/16] ignore venv --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index c7740586c..5817437c9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +venv/ + ## ALE ale From 314df810094199a7fbc64af07ae17decb91fb184 Mon Sep 17 00:00:00 2001 From: Jet Date: Wed, 7 Feb 2024 15:39:23 +0000 Subject: [PATCH 09/16] update gitrignore --- .gitignore | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 137 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 5817437c9..4ab13955d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,143 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ venv/ +ENV/ +env.bak/ +venv.bak/ -## ALE +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +## ALE ale build doc/examples/*Example @@ -32,5 +168,3 @@ doc/examples/*Example # Python *.pyc - -*.egg-info/ From 9710ac5703f4ad235d509c23a46e70c6c993a231 Mon Sep 17 00:00:00 2001 From: Jet Date: Wed, 7 Feb 2024 15:40:16 +0000 Subject: [PATCH 10/16] clean gitignore --- .gitignore | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.gitignore b/.gitignore index 4ab13955d..5603b7cf5 100644 --- a/.gitignore +++ b/.gitignore @@ -142,8 +142,6 @@ ale build doc/examples/*Example -## Generic - # Emacs temp files *~ .* @@ -158,9 +156,6 @@ doc/examples/*Example *.o *.d -# Compiled Dynamic libraries -*.so - # Compiled Static libraries *.lai *.la From d6210f9a1fc2bf1e001057121c9c28146014c0be Mon Sep 17 00:00:00 2001 From: Jet Date: Wed, 7 Feb 2024 17:23:04 +0000 Subject: [PATCH 11/16] remove junk --- md5.txt | 106 -------------------------------------------------------- 1 file changed, 106 deletions(-) delete mode 100644 md5.txt diff --git a/md5.txt b/md5.txt deleted file mode 100644 index 63b23408c..000000000 --- a/md5.txt +++ /dev/null @@ -1,106 +0,0 @@ -# The following is a list of the md5 checksums for the various roms supported by ALE. - -4b27f5397c442d25f0c418ccdacf1926 adventure.bin -35be55426c1fec32dfb503b4f0651572 air_raid.bin -f1a0a23e6464d954e3a9579c4ccd01c8 alien.bin -acb7750b4d0c4bd34969802a7deb2990 amidar.bin -de78b3a064d374390ac0710f95edde92 assault.bin -89a68746eff7f266bbf08de2483abe55 asterix.bin -ccbd36746ed4525821a8083b0d6d2c2c asteroids.bin -826481f6fc53ea47c9f272f7050eedf7 atlantis2.bin -9ad36e699ef6f45d9eb6c4cf90475c9f atlantis.bin -8556b42aa05f94bc29ff39c39b11bff4 backgammon.bin -00ce0bdd43aed84a983bef38fe7f5ee3 bank_heist.bin -819aeeb9a2e11deb54e6de334f843894 basic_math.bin -41f252a66c6301f1e8ab3612c19bc5d4 battle_zone.bin -79ab4123a83dc11d468fb2108ea09e2e beam_rider.bin -136f75c4dd02c29283752b7e5799f978 berzerk.bin -0a981c03204ac2b278ba392674682560 blackjack.bin -c9b7afad3bfd922e006a6bfc1d4f3fe7 bowling.bin -c3ef5c4653212088eda54dc91d787870 boxing.bin -f34f08e5eb96e500e851a80be3277a56 breakout.bin -028024fb8e5e5f18ea586652f9799c96 carnival.bin -b816296311019ab69a21cb9e9e235d12 casino.bin -91c2098e88a6b13f977af8c003e0bca5 centipede.bin -c1cb228470a87beb5f36e90ac745da26 chopper_command.bin -55ef7b65066428367844342ed59f956c crazy_climber.bin -8cd26dcf249456fe4aeb8db42d49df74 crossbow.bin -106855474c69d08c8ffa308d47337269 darkchambers.bin -0f643c34e40e3f1daafd9c524d3ffe64 defender.bin -f0e0addc07971561ab80d9abe1b8d333 demon_attack.bin -36b20c427975760cb9cf4a47e41369e4 donkey_kong.bin -368d88a6c071caba60b4f778615aae94 double_dunk.bin -5aea9974b975a6a844e6df10d2b861c4 earthworld.bin -71f8bacfbdca019113f3f0801849057e elevator_action.bin -94b92a882f6dbaa6993a46e2dcc58402 enduro.bin -6b683be69f92958abe0e2a9945157ad5 entombed.bin -615a3bf251a38eb6638cdc7ffbde5480 et.bin -b8865f05676e64f3bec72b9defdacfa7 fishing_derby.bin -30512e0e83903fc05541d2f6a6a62654 flag_capture.bin -8e0ab801b1705a740b476b7f588c6d16 freeway.bin -081e2c114c9c20b61acf25fc95c71bf4 frogger.bin -4ca73eb959299471788f0b685c3ba0b5 frostbite.bin -211774f4c5739042618be8ff67351177 galaxian.bin -c16c79aad6272baffb8aae9a7fff0864 gopher.bin -8ac18076d01a6b63acf6e2cab4968940 gravitar.bin -f16c709df0a6c52f47ff52b9d95b7d8d hangman.bin -f0a6e99f5875891246c3dbecbf2d2cea haunted_house.bin -fca4a5be1251927027f2c24774a02160 hero.bin -7972e5101fa548b952d852db24ad6060 human_cannonball.bin -a4c08c4994eb9d24fb78be1793e82e26 ice_hockey.bin -e51030251e440cffaab1ac63438b44ae jamesbond.bin -718ae62c70af4e5fd8e932fee216948a journey_escape.bin -5428cdfada281c569c74c7308c7f2c26 kaboom.bin -4326edb70ff20d0ee5ba58fa5cb09d60 kangaroo.bin -6c1f3f2e359dbf55df462ccbcdd2f6bf keystone_kapers.bin -0dd4c69b5f9a7ae96a7a08329496779a king_kong.bin -eed9eaf1a0b6a2b9bc4c8032cb43e3fb klax.bin -534e23210dd1993c828d944c6ac4d9fb koolaid.bin -4baada22435320d185c95b7dd2bcdb24 krull.bin -5b92a93b23523ff16e2789b820e2a4c5 kung_fu_master.bin -8e4cd60d93fcde8065c1a2b972a26377 laser_gates.bin -2d76c5d1aad506442b9e9fb67765e051 lost_luggage.bin -e908611d99890733be31733a979c62d8 mario_bros.bin -df62a658496ac98a3aa4a6ee5719c251 miniature_golf.bin -3347a6dd59049b15a38394aa2dafa585 montezuma_revenge.bin -aa7bb54d2c189a31bb1fa20099e42859 mr_do.bin -87e79cd41ce136fd4f72cc6e2c161bee ms_pacman.bin -36306070f0c90a72461551a7a4f3a209 name_this_game.bin -113cd09c9771ac278544b7e90efe7df2 othello.bin -fc2233fc116faef0d3c31541717ca2db pacman.bin -7e52a95074a66640fcfde124fffd491a phoenix.bin -6d842c96d5a01967be9680080dd5be54 pitfall2.bin -3e90cf23106f2e08b2781e41299de556 pitfall.bin -60e0ea3cbe0913d39803477945e9e5ec pong.bin -4799a40b6e889370b7ee55c17ba65141 pooyan.bin -ef3a4f64b6494ba770862768caf04b86 private_eye.bin -484b0076816a104875e00467d431c2d2 qbert.bin -393948436d1f4cc3192410bb918f9724 riverraid.bin -ce5cc62608be2cd3ed8abd844efb8919 road_runner.bin -4f618c2429138e0280969193ed6c107e robotank.bin -240bfbac5163af4df5ae713985386f92 seaquest.bin -dd0cbe5351551a538414fb9e37fc56e8 sir_lancelot.bin -b76fbadc8ffb1f83e2ca08b6fb4d6c9f skiing.bin -e72eb8d4410152bdcb69e7fba327b420 solaris.bin -72ffbef6504b75e69ee1045af9075f66 space_invaders.bin -b702641d698c60bcdc922dbd8c9dd49c space_war.bin -a3c1c70024d7aabb41381adbfb6d3b25 star_gunner.bin -a9531c763077464307086ec9a1fd057d superman.bin -4d7517ae69f95cfbc053be01312b7dba surround.bin -42cdd6a9e42a3639e190722b8ea3fc51 tennis.bin -b0e1ee07fbc73493eac5651a52f90f00 tetris.bin -0db4f4150fecf77e4ce72ca4d04c052f tic_tac_toe_3d.bin -fc2104dd2dadf9a6176c1c1c8f87ced9 time_pilot.bin -fb27afe896e7c928089307b32e5642ee trondead.bin -7a5463545dfb2dcfdafa6074b2f2c15e turmoil.bin -085322bae40d904f53bdcc56df0593fc tutankham.bin -a499d720e7ee35c62424de882a3351b6 up_n_down.bin -3e899eba0ca8cd2972da1ae5479b4f0d venture.bin -539d26b6e9df0da8e7465f0f5ad863b7 video_checkers.bin -f0b7db930ca0e548c41a97160b9f6275 video_chess.bin -3f540a30fdee0b20aed7288e4a5ea528 video_cube.bin -107cc025334211e6d29da0b6be46aec7 video_pinball.bin -7e8aa18bc9502eb57daaf5e7c1e94da7 wizard_of_wor.bin -ec3beb6d8b5689e867bafb5d5f507491 word_zapper.bin -c5930d0e8cdae3e037349bfa08e871be yars_revenge.bin -eea0da9b987d661264cce69a7c13c3bd zaxxon.bin From df4eb0956b365bf6369e88e2d1dbf49320005e0c Mon Sep 17 00:00:00 2001 From: Jet Date: Wed, 7 Feb 2024 17:25:06 +0000 Subject: [PATCH 12/16] nuke everything --- src/python/__init__.py | 4 + src/python/env/__init__.py | 0 src/python/env/gym.py | 425 ------------------------------------ src/python/env/gymnasium.py | 384 -------------------------------- src/python/gym.py | 185 ---------------- 5 files changed, 4 insertions(+), 994 deletions(-) delete mode 100644 src/python/env/__init__.py delete mode 100644 src/python/env/gym.py delete mode 100644 src/python/env/gymnasium.py delete mode 100644 src/python/gym.py diff --git a/src/python/__init__.py b/src/python/__init__.py index d7e4001f9..67f6ea483 100644 --- a/src/python/__init__.py +++ b/src/python/__init__.py @@ -48,3 +48,7 @@ from ale_py._ale_py import SDL_SUPPORT, Action, ALEInterface, ALEState, LoggerMode __all__ = ["Action", "ALEInterface", "ALEState", "LoggerMode", "SDL_SUPPORT"] + + +from .register_gymnasium import register_gymnasium_envs +register_gymnasium_envs() diff --git a/src/python/env/__init__.py b/src/python/env/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/python/env/gym.py b/src/python/env/gym.py deleted file mode 100644 index 44353418c..000000000 --- a/src/python/env/gym.py +++ /dev/null @@ -1,425 +0,0 @@ -import sys -from typing import Any, Dict, List, Optional, Sequence, Tuple, Union - -import ale_py -import ale_py.roms as roms -import ale_py.roms.utils as rom_utils -import numpy as np - -import gym -import gym.logger as logger -from gym import error, spaces, utils - -if sys.version_info < (3, 11): - from typing_extensions import NotRequired, TypedDict -else: - from typing import NotRequired, TypedDict - - -class AtariEnvStepMetadata(TypedDict): - lives: int - episode_frame_number: int - frame_number: int - seeds: NotRequired[Sequence[int]] - - -class AtariEnv(gym.Env, utils.EzPickle): - """ - (A)rcade (L)earning (Gym) (Env)ironment. - A Gym wrapper around the Arcade Learning Environment (ALE). - """ - - # No render modes - metadata = {"render_modes": ["human", "rgb_array"]} - - def __init__( - self, - game: str = "pong", - mode: Optional[int] = None, - difficulty: Optional[int] = None, - obs_type: str = "rgb", - frameskip: Union[Tuple[int, int], int] = 4, - repeat_action_probability: float = 0.25, - full_action_space: bool = False, - max_num_frames_per_episode: Optional[int] = None, - render_mode: Optional[str] = None, - ) -> None: - """ - Initialize the ALE for Gym. - Default parameters are taken from Machado et al., 2018. - - Args: - game: str => Game to initialize env with. - mode: Optional[int] => Game mode, see Machado et al., 2018 - difficulty: Optional[int] => Game difficulty,see Machado et al., 2018 - obs_type: str => Observation type in { 'rgb', 'grayscale', 'ram' } - frameskip: Union[Tuple[int, int], int] => - Stochastic frameskip as tuple or fixed. - repeat_action_probability: int => - Probability to repeat actions, see Machado et al., 2018 - full_action_space: bool => Use full action space? - max_num_frames_per_episode: int => Max number of frame per epsiode. - Once `max_num_frames_per_episode` is reached the episode is - truncated. - render_mode: str => One of { 'human', 'rgb_array' }. - If `human` we'll interactively display the screen and enable - game sounds. This will lock emulation to the ROMs specified FPS - If `rgb_array` we'll return the `rgb` key in step metadata with - the current environment RGB frame. - - Note: - - The game must be installed, see ale-import-roms, or ale-py-roms. - - Frameskip values of (low, high) will enable stochastic frame skip - which will sample a random frameskip uniformly each action. - - It is recommended to enable full action space. - See Machado et al., 2018 for more details. - - References: - `Revisiting the Arcade Learning Environment: Evaluation Protocols - and Open Problems for General Agents`, Machado et al., 2018, JAIR - URL: https://jair.org/index.php/jair/article/view/11182 - """ - if obs_type == "image": - logger.warn( - 'obs_type "image" should be replaced with the image type, one of: rgb, grayscale' - ) - obs_type = "rgb" - if obs_type not in {"rgb", "grayscale", "ram"}: - raise error.Error( - f"Invalid observation type: {obs_type}. Expecting: rgb, grayscale, ram." - ) - - if type(frameskip) not in (int, tuple): - raise error.Error(f"Invalid frameskip type: {type(frameskip)}.") - if isinstance(frameskip, int) and frameskip <= 0: - raise error.Error( - f"Invalid frameskip of {frameskip}, frameskip must be positive." - ) - elif isinstance(frameskip, tuple) and len(frameskip) != 2: - raise error.Error( - f"Invalid stochastic frameskip length of {len(frameskip)}, expected length 2." - ) - elif isinstance(frameskip, tuple) and frameskip[0] > frameskip[1]: - raise error.Error( - f"Invalid stochastic frameskip, lower bound is greater than upper bound." - ) - elif isinstance(frameskip, tuple) and frameskip[0] <= 0: - raise error.Error( - f"Invalid stochastic frameskip lower bound is greater than upper bound." - ) - - if render_mode is not None and render_mode not in {"rgb_array", "human"}: - raise error.Error( - f"Render mode {render_mode} not supported (rgb_array, human)." - ) - - utils.EzPickle.__init__( - self, - game, - mode, - difficulty, - obs_type, - frameskip, - repeat_action_probability, - full_action_space, - max_num_frames_per_episode, - render_mode, - ) - - # Initialize ALE - self.ale = ale_py.ALEInterface() - - self._game = rom_utils.rom_id_to_name(game) - - self._game_mode = mode - self._game_difficulty = difficulty - - self._frameskip = frameskip - self._obs_type = obs_type - self._render_mode = render_mode - - # Set logger mode to error only - self.ale.setLoggerMode(ale_py.LoggerMode.Error) - # Config sticky action prob. - self.ale.setFloat("repeat_action_probability", repeat_action_probability) - - if max_num_frames_per_episode is not None: - self.ale.setInt("max_num_frames_per_episode", max_num_frames_per_episode) - - # If render mode is human we can display screen and sound - if render_mode == "human": - self.ale.setBool("display_screen", True) - self.ale.setBool("sound", True) - - # Seed + Load - self.seed() - - self._action_set = ( - self.ale.getLegalActionSet() - if full_action_space - else self.ale.getMinimalActionSet() - ) - self._action_space = spaces.Discrete(len(self._action_set)) - - # Initialize observation type - if self._obs_type == "ram": - self._obs_space = spaces.Box( - low=0, high=255, dtype=np.uint8, shape=(self.ale.getRAMSize(),) - ) - elif self._obs_type == "rgb" or self._obs_type == "grayscale": - (screen_height, screen_width) = self.ale.getScreenDims() - image_shape = ( - screen_height, - screen_width, - ) - if self._obs_type == "rgb": - image_shape += (3,) - self._obs_space = spaces.Box( - low=0, high=255, dtype=np.uint8, shape=image_shape - ) - else: - raise error.Error(f"Unrecognized observation type: {self._obs_type}") - - def seed(self, seed: Optional[int] = None) -> Tuple[int, int]: - """ - Seeds both the internal numpy rng for stochastic frame skip - as well as the ALE RNG. - - This function must also initialize the ROM and set the corresponding - mode and difficulty. `seed` may be called to initialize the environment - during deserialization by Gym so these side-effects must reside here. - - Args: - seed: int => Manually set the seed for RNG. - Returns: - tuple[int, int] => (np seed, ALE seed) - """ - ss = np.random.SeedSequence(seed) - seed1, seed2 = ss.generate_state(n_words=2) - - self.np_random = np.random.default_rng(seed1) - # ALE only takes signed integers for `setInt`, it'll get converted back - # to unsigned in StellaEnvironment. - self.ale.setInt("random_seed", seed2.astype(np.int32)) - - if not hasattr(roms, self._game): - raise error.Error( - f'We\'re Unable to find the game "{self._game}". Note: Gym no longer distributes ROMs. ' - f"If you own a license to use the necessary ROMs for research purposes you can download them " - f'via `pip install gym[accept-rom-license]`. Otherwise, you should try importing "{self._game}" ' - f'via the command `ale-import-roms`. If you believe this is a mistake perhaps your copy of "{self._game}" ' - "is unsupported. To check if this is the case try providing the environment variable " - "`PYTHONWARNINGS=default::ImportWarning:ale_py.roms`. For more information see: " - "https://github.com/mgbellemare/Arcade-Learning-Environment#rom-management" - ) - self.ale.loadROM(getattr(roms, self._game)) - - if self._game_mode is not None: - self.ale.setMode(self._game_mode) - if self._game_difficulty is not None: - self.ale.setDifficulty(self._game_difficulty) - - return seed1, seed2 - - def step( - self, - action_ind: int, - ) -> Tuple[np.ndarray, float, bool, bool, AtariEnvStepMetadata]: - """ - Perform one agent step, i.e., repeats `action` frameskip # of steps. - - Args: - action_ind: int => Action index to execute - - Returns: - Tuple[np.ndarray, float, bool, Dict[str, Any]] => - observation, reward, terminal, metadata - - Note: `metadata` contains the keys "lives" and "rgb" if - render_mode == 'rgb_array'. - """ - # Get action enum, terminal bool, metadata - action = self._action_set[action_ind] - - # If frameskip is a length 2 tuple then it's stochastic - # frameskip between [frameskip[0], frameskip[1]] uniformly. - if isinstance(self._frameskip, int): - frameskip = self._frameskip - elif isinstance(self._frameskip, tuple): - frameskip = self.np_random.integers(*self._frameskip) - else: - raise error.Error(f"Invalid frameskip type: {self._frameskip}") - - # Frameskip - reward = 0.0 - for _ in range(frameskip): - reward += self.ale.act(action) - is_terminal = self.ale.game_over(with_truncation=False) - is_truncated = self.ale.game_truncated() - - return self._get_obs(), reward, is_terminal, is_truncated, self._get_info() - - def reset( - self, - *, - seed: Optional[int] = None, - options: Optional[Dict[str, Any]] = None, - ) -> Tuple[np.ndarray, AtariEnvStepMetadata]: - """ - Resets environment and returns initial observation. - """ - del options - # Gym's new seeding API seeds on reset. - # This will cause the console to be recreated - # and loose all previous state, e.g., statistics, etc. - seeded_with = None - if seed is not None: - seeded_with = self.seed(seed) - - self.ale.reset_game() - obs = self._get_obs() - - info = self._get_info() - if seeded_with is not None: - info["seeds"] = seeded_with - return obs, info - - def render(self) -> Any: - """ - Render is not supported by ALE. We use a paradigm similar to - Gym3 which allows you to specify `render_mode` during construction. - - For example, - gym.make("ale-py:Pong-v0", render_mode="human") - will display the ALE and maintain the proper interval to match the - FPS target set by the ROM. - """ - if self._render_mode == "rgb_array": - return self.ale.getScreenRGB() - elif self._render_mode == "human": - pass - else: - raise error.Error( - f"Invalid render mode `{self._render_mode}`. " - "Supported modes: `human`, `rgb_array`." - ) - - def _get_obs(self) -> np.ndarray: - """ - Retreives the current observation. - This is dependent on `self._obs_type`. - """ - if self._obs_type == "ram": - return self.ale.getRAM() - elif self._obs_type == "rgb": - return self.ale.getScreenRGB() - elif self._obs_type == "grayscale": - return self.ale.getScreenGrayscale() - else: - raise error.Error(f"Unrecognized observation type: {self._obs_type}") - - def _get_info(self) -> AtariEnvStepMetadata: - return { - "lives": self.ale.lives(), - "episode_frame_number": self.ale.getEpisodeFrameNumber(), - "frame_number": self.ale.getFrameNumber(), - } - - def get_keys_to_action(self) -> Dict[Tuple[int], ale_py.Action]: - """ - Return keymapping -> actions for human play. - """ - UP = ord("w") - LEFT = ord("a") - RIGHT = ord("d") - DOWN = ord("s") - FIRE = ord(" ") - - mapping = { - ale_py.Action.NOOP: (None,), - ale_py.Action.UP: (UP,), - ale_py.Action.FIRE: (FIRE,), - ale_py.Action.DOWN: (DOWN,), - ale_py.Action.LEFT: (LEFT,), - ale_py.Action.RIGHT: (RIGHT,), - ale_py.Action.UPFIRE: (UP, FIRE), - ale_py.Action.DOWNFIRE: (DOWN, FIRE), - ale_py.Action.LEFTFIRE: (LEFT, FIRE), - ale_py.Action.RIGHTFIRE: (RIGHT, FIRE), - ale_py.Action.UPLEFT: (UP, LEFT), - ale_py.Action.UPRIGHT: (UP, RIGHT), - ale_py.Action.DOWNLEFT: (DOWN, LEFT), - ale_py.Action.DOWNRIGHT: (DOWN, RIGHT), - ale_py.Action.UPLEFTFIRE: (UP, LEFT, FIRE), - ale_py.Action.UPRIGHTFIRE: (UP, RIGHT, FIRE), - ale_py.Action.DOWNLEFTFIRE: (DOWN, LEFT, FIRE), - ale_py.Action.DOWNRIGHTFIRE: (DOWN, RIGHT, FIRE), - } - - # Map - # (key, key, ...) -> action_idx - # where action_idx is the integer value of the action enum - # - actions = self._action_set - return dict( - zip( - map(lambda action: tuple(sorted(mapping[action])), actions), - range(len(actions)), - ) - ) - - def get_action_meanings(self) -> List[str]: - """ - Return the meaning of each integer action. - """ - keys = ale_py.Action.__members__.values() - values = ale_py.Action.__members__.keys() - mapping = dict(zip(keys, values)) - return [mapping[action] for action in self._action_set] - - def clone_state(self, include_rng=False) -> ale_py.ALEState: - """Clone emulator state w/o system state. Restoring this state will - *not* give an identical environment. For complete cloning and restoring - of the full state, see `{clone,restore}_full_state()`.""" - return self.ale.cloneState(include_rng=include_rng) - - def restore_state(self, state: ale_py.ALEState) -> None: - """Restore emulator state w/o system state.""" - self.ale.restoreState(state) - - def clone_full_state(self) -> ale_py.ALEState: - """Deprecated method which would clone the emulator and system state.""" - logger.warn( - "`clone_full_state()` is deprecated and will be removed in a future release of `ale-py`. " - "Please use `clone_state(include_rng=True)` which is equivalent to `clone_full_state`. " - ) - return self.ale.cloneSystemState() - - def restore_full_state(self, state: ale_py.ALEState) -> None: - """Restore emulator state w/ system state including pseudorandomness.""" - logger.warn( - "restore_full_state() is deprecated and will be removed in a future release of `ale-py`. " - "Please use `restore_state(state)` which will restore the state regardless of being a full or partial state. " - ) - self.ale.restoreSystemState(state) - - @property - def action_space(self) -> spaces.Discrete: - """ - Return Gym's action space. - """ - return self._action_space - - @property - def observation_space(self) -> spaces.Box: - """ - Return Gym's observation space. - """ - return self._obs_space - - @property - def render_mode(self) -> str: - """ - Attribute render_mode to comply Gym API. - """ - return self._render_mode diff --git a/src/python/env/gymnasium.py b/src/python/env/gymnasium.py deleted file mode 100644 index 7d6dff262..000000000 --- a/src/python/env/gymnasium.py +++ /dev/null @@ -1,384 +0,0 @@ -from __future__ import annotations - -import sys -from typing import Any, Literal, Optional, Sequence, Union - -import ale_py -import ale_py.roms as roms -import ale_py.roms.utils as rom_utils -from gymnasium.utils import seeding -import numpy as np - -import gymnasium -import gymnasium.logger as logger -from gymnasium import error, spaces, utils - -if sys.version_info < (3, 11): - from typing_extensions import NotRequired, TypedDict -else: - from typing import NotRequired, TypedDict - - -class AtariEnvStepMetadata(TypedDict): - lives: int - episode_frame_number: int - frame_number: int - seeds: NotRequired[Sequence[int]] - - -class AtariEnv(gymnasium.Env, utils.EzPickle): - """ - (A)rcade (L)earning (Gym) (Env)ironment. - A Gym wrapper around the Arcade Learning Environment (ALE). - """ - - # No render modes - metadata = {"render_modes": ["human", "rgb_array"]} - - def __init__( - self, - game: str = "pong", - mode: Optional[int] = None, - difficulty: Optional[int] = None, - obs_type: Literal["rgb", "grayscale", "ram"] = "rgb", - frameskip: Union[tuple[int, int], int] = 4, - repeat_action_probability: float = 0.25, - full_action_space: bool = False, - max_num_frames_per_episode: Optional[int] = None, - render_mode: Optional[Literal["human", "rgb_array"]] = None, - ) -> None: - """ - Initialize the ALE for Gymnasium. - Default parameters are taken from Machado et al., 2018. - - Args: - game: str => Game to initialize env with. - mode: Optional[int] => Game mode, see Machado et al., 2018 - difficulty: Optional[int] => Game difficulty,see Machado et al., 2018 - obs_type: str => Observation type in { 'rgb', 'grayscale', 'ram' } - frameskip: Union[tuple[int, int], int] => - Stochastic frameskip as tuple or fixed. - repeat_action_probability: int => - Probability to repeat actions, see Machado et al., 2018 - full_action_space: bool => Use full action space? - max_num_frames_per_episode: int => Max number of frame per epsiode. - Once `max_num_frames_per_episode` is reached the episode is - truncated. - render_mode: str => One of { 'human', 'rgb_array' }. - If `human` we'll interactively display the screen and enable - game sounds. This will lock emulation to the ROMs specified FPS - If `rgb_array` we'll return the `rgb` key in step metadata with - the current environment RGB frame. - - Note: - - The game must be installed, see ale-import-roms, or ale-py-roms. - - Frameskip values of (low, high) will enable stochastic frame skip - which will sample a random frameskip uniformly each action. - - It is recommended to enable full action space. - See Machado et al., 2018 for more details. - - References: - `Revisiting the Arcade Learning Environment: Evaluation Protocols - and Open Problems for General Agents`, Machado et al., 2018, JAIR - URL: https://jair.org/index.php/jair/article/view/11182 - """ - if obs_type not in {"rgb", "grayscale", "ram"}: - raise error.Error( - f"Invalid observation type: {obs_type}. Expecting: rgb, grayscale, ram." - ) - - if type(frameskip) not in (int, tuple): - raise error.Error(f"Invalid frameskip type: {type(frameskip)}.") - if isinstance(frameskip, int) and frameskip <= 0: - raise error.Error( - f"Invalid frameskip of {frameskip}, frameskip must be positive." - ) - elif isinstance(frameskip, tuple) and len(frameskip) != 2: - raise error.Error( - f"Invalid stochastic frameskip length of {len(frameskip)}, expected length 2." - ) - elif isinstance(frameskip, tuple) and frameskip[0] > frameskip[1]: - raise error.Error( - f"Invalid stochastic frameskip, lower bound is greater than upper bound." - ) - elif isinstance(frameskip, tuple) and frameskip[0] <= 0: - raise error.Error( - f"Invalid stochastic frameskip lower bound is greater than upper bound." - ) - - if render_mode is not None and render_mode not in {"rgb_array", "human"}: - raise error.Error( - f"Render mode {render_mode} not supported (rgb_array, human)." - ) - - utils.EzPickle.__init__( - self, - game, - mode, - difficulty, - obs_type, - frameskip, - repeat_action_probability, - full_action_space, - max_num_frames_per_episode, - render_mode, - ) - - # Initialize ALE - self.ale = ale_py.ALEInterface() - - self._game = rom_utils.rom_id_to_name(game) - - self._game_mode = mode - self._game_difficulty = difficulty - - self._frameskip = frameskip - self._obs_type = obs_type - self.render_mode = render_mode - - # Set logger mode to error only - self.ale.setLoggerMode(ale_py.LoggerMode.Error) - # Config sticky action prob. - self.ale.setFloat("repeat_action_probability", repeat_action_probability) - - if max_num_frames_per_episode is not None: - self.ale.setInt("max_num_frames_per_episode", max_num_frames_per_episode) - - # If render mode is human we can display screen and sound - if render_mode == "human": - self.ale.setBool("display_screen", True) - self.ale.setBool("sound", True) - - # seed + load - self.seed_game() - self.load_game() - - # initialize action space - self._action_set = ( - self.ale.getLegalActionSet() - if full_action_space - else self.ale.getMinimalActionSet() - ) - self.action_space = spaces.Discrete(len(self._action_set)) - - # initialize observation space - if self._obs_type == "ram": - self.observation_space = spaces.Box( - low=0, high=255, dtype=np.uint8, shape=(self.ale.getRAMSize(),) - ) - elif self._obs_type == "rgb" or self._obs_type == "grayscale": - (screen_height, screen_width) = self.ale.getScreenDims() - image_shape = ( - screen_height, - screen_width, - ) - if self._obs_type == "rgb": - image_shape += (3,) - self.observation_space = spaces.Box( - low=0, high=255, dtype=np.uint8, shape=image_shape - ) - else: - raise error.Error(f"Unrecognized observation type: {self._obs_type}") - - def seed_game(self, seed: Optional[int] = None) -> tuple[int, int]: - """Seeds the internal and ALE RNG.""" - ss = np.random.SeedSequence(seed) - np_seed, ale_seed = ss.generate_state(n_words=2) - self._np_random, seed = seeding.np_random(np_seed) - self.ale.setInt("random_seed", int(ale_seed)) - return np_seed, ale_seed - - def load_game(self) -> None: - """This function initializes the ROM and sets the corresponding mode and difficulty.""" - if not hasattr(roms, self._game): - raise error.Error( - f'We\'re Unable to find the game "{self._game}". Note: Gym no longer distributes ROMs. ' - f"If you own a license to use the necessary ROMs for research purposes you can download them " - f'via `pip install gym[accept-rom-license]`. Otherwise, you should try importing "{self._game}" ' - f'via the command `ale-import-roms`. If you believe this is a mistake perhaps your copy of "{self._game}" ' - "is unsupported. To check if this is the case try providing the environment variable " - "`PYTHONWARNINGS=default::ImportWarning:ale_py.roms`. For more information see: " - "https://github.com/mgbellemare/Arcade-Learning-Environment#rom-management" - ) - self.ale.loadROM(getattr(roms, self._game)) - - if self._game_mode is not None: - self.ale.setMode(self._game_mode) - if self._game_difficulty is not None: - self.ale.setDifficulty(self._game_difficulty) - - def step( # pyright: ignore[reportIncompatibleMethodOverride] - self, - action: int, - ) -> tuple[np.ndarray, float, bool, bool, AtariEnvStepMetadata]: - """ - Perform one agent step, i.e., repeats `action` frameskip # of steps. - - Args: - action_ind: int => Action index to execute - - Returns: - tuple[np.ndarray, float, bool, bool, Dict[str, Any]] => - observation, reward, terminal, truncation, metadata - - Note: `metadata` contains the keys "lives" and "rgb" if - render_mode == 'rgb_array'. - """ - # If frameskip is a length 2 tuple then it's stochastic - # frameskip between [frameskip[0], frameskip[1]] uniformly. - if isinstance(self._frameskip, int): - frameskip = self._frameskip - elif isinstance(self._frameskip, tuple): - frameskip = self.np_random.integers(*self._frameskip) - else: - raise error.Error(f"Invalid frameskip type: {self._frameskip}") - - # Frameskip - reward = 0.0 - for _ in range(frameskip): - reward += self.ale.act(self._action_set[action]) - is_terminal = self.ale.game_over(with_truncation=False) - is_truncated = self.ale.game_truncated() - - return self._get_obs(), reward, is_terminal, is_truncated, self._get_info() - - def reset( # pyright: ignore[reportIncompatibleMethodOverride] - self, - *, - seed: Optional[int] = None, - options: Optional[dict[str, Any]] = None, - ) -> tuple[np.ndarray, AtariEnvStepMetadata]: - """Resets environment and returns initial observation.""" - # sets the seeds if it's specified for both ALE and frameskip np - # we only want to do this when commanded to so we don't reset all previous states, statistics, etc. - seeded_with = None - if seed is not None: - seeded_with = self.seed_game(seed) - self.load_game() - - self.ale.reset_game() - - obs = self._get_obs() - info = self._get_info() - if seeded_with is not None: - info["seeds"] = seeded_with - - return obs, info - - def render(self) -> Optional[np.ndarray]: - """ - Render is not supported by ALE. We use a paradigm similar to - Gym3 which allows you to specify `render_mode` during construction. - - For example, - gym.make("ale-py:Pong-v0", render_mode="human") - will display the ALE and maintain the proper interval to match the - FPS target set by the ROM. - """ - if self.render_mode == "rgb_array": - return self.ale.getScreenRGB() - elif self.render_mode == "human": - return - else: - raise error.Error( - f"Invalid render mode `{self.render_mode}`. " - "Supported modes: `human`, `rgb_array`." - ) - - def _get_obs(self) -> np.ndarray: - """ - Retreives the current observation. - This is dependent on `self._obs_type`. - """ - if self._obs_type == "ram": - return self.ale.getRAM() - elif self._obs_type == "rgb": - return self.ale.getScreenRGB() - elif self._obs_type == "grayscale": - return self.ale.getScreenGrayscale() - else: - raise error.Error(f"Unrecognized observation type: {self._obs_type}") - - def _get_info(self) -> AtariEnvStepMetadata: - return { - "lives": self.ale.lives(), - "episode_frame_number": self.ale.getEpisodeFrameNumber(), - "frame_number": self.ale.getFrameNumber(), - } - - def get_keys_to_action(self) -> dict[tuple[int], ale_py.Action]: - """ - Return keymapping -> actions for human play. - """ - UP = ord("w") - LEFT = ord("a") - RIGHT = ord("d") - DOWN = ord("s") - FIRE = ord(" ") - - mapping = { - ale_py.Action.NOOP: (None,), - ale_py.Action.UP: (UP,), - ale_py.Action.FIRE: (FIRE,), - ale_py.Action.DOWN: (DOWN,), - ale_py.Action.LEFT: (LEFT,), - ale_py.Action.RIGHT: (RIGHT,), - ale_py.Action.UPFIRE: (UP, FIRE), - ale_py.Action.DOWNFIRE: (DOWN, FIRE), - ale_py.Action.LEFTFIRE: (LEFT, FIRE), - ale_py.Action.RIGHTFIRE: (RIGHT, FIRE), - ale_py.Action.UPLEFT: (UP, LEFT), - ale_py.Action.UPRIGHT: (UP, RIGHT), - ale_py.Action.DOWNLEFT: (DOWN, LEFT), - ale_py.Action.DOWNRIGHT: (DOWN, RIGHT), - ale_py.Action.UPLEFTFIRE: (UP, LEFT, FIRE), - ale_py.Action.UPRIGHTFIRE: (UP, RIGHT, FIRE), - ale_py.Action.DOWNLEFTFIRE: (DOWN, LEFT, FIRE), - ale_py.Action.DOWNRIGHTFIRE: (DOWN, RIGHT, FIRE), - } - - # Map - # (key, key, ...) -> action_idx - # where action_idx is the integer value of the action enum - # - return dict( - zip( - map(lambda action: tuple(sorted(mapping[action])), self._action_set), - range(len(self._action_set)), - ) - ) - - def get_action_meanings(self) -> list[str]: - """ - Return the meaning of each integer action. - """ - keys = ale_py.Action.__members__.values() - values = ale_py.Action.__members__.keys() - mapping = dict(zip(keys, values)) - return [mapping[action] for action in self._action_set] - - def clone_state(self, include_rng=False) -> ale_py.ALEState: - """Clone emulator state w/o system state. Restoring this state will - *not* give an identical environment. For complete cloning and restoring - of the full state, see `{clone,restore}_full_state()`.""" - return self.ale.cloneState(include_rng=include_rng) - - def restore_state(self, state: ale_py.ALEState) -> None: - """Restore emulator state w/o system state.""" - self.ale.restoreState(state) - - def clone_full_state(self) -> ale_py.ALEState: - """Deprecated method which would clone the emulator and system state.""" - logger.warn( - "`clone_full_state()` is deprecated and will be removed in a future release of `ale-py`. " - "Please use `clone_state(include_rng=True)` which is equivalent to `clone_full_state`. " - ) - return self.ale.cloneSystemState() - - def restore_full_state(self, state: ale_py.ALEState) -> None: - """Restore emulator state w/ system state including pseudorandomness.""" - logger.warn( - "restore_full_state() is deprecated and will be removed in a future release of `ale-py`. " - "Please use `restore_state(state)` which will restore the state regardless of being a full or partial state. " - ) - self.ale.restoreSystemState(state) diff --git a/src/python/gym.py b/src/python/gym.py deleted file mode 100644 index 62aec69cf..000000000 --- a/src/python/gym.py +++ /dev/null @@ -1,185 +0,0 @@ -from collections import defaultdict -from typing import Any, Callable, Mapping, NamedTuple, Sequence, Text, Union - -import ale_py.roms as roms -from ale_py.roms import utils as rom_utils - -from gym.envs.registration import register - - -class GymFlavour(NamedTuple): - suffix: str - kwargs: Union[Mapping[Text, Any], Callable[[str], Mapping[Text, Any]]] - - -class GymConfig(NamedTuple): - version: str - kwargs: Mapping[Text, Any] - flavours: Sequence[GymFlavour] - - -def _register_gym_configs( - roms: Sequence[str], - obs_types: Sequence[str], - configs: Sequence[GymConfig], - prefix: str = "", -) -> None: - if len(prefix) > 0 and prefix[-1] != "/": - prefix += "/" - - for rom in roms: - for obs_type in obs_types: - for config in configs: - for flavour in config.flavours: - name = rom_utils.rom_id_to_name(rom) - name = f"{name}-ram" if obs_type == "ram" else name - - # Parse config kwargs - config_kwargs = ( - config.kwargs(rom) if callable(config.kwargs) else config.kwargs - ) - # Parse flavour kwargs - flavour_kwargs = ( - flavour.kwargs(rom) - if callable(flavour.kwargs) - else flavour.kwargs - ) - - # Register the environment - register( - id=f"{prefix}{name}{flavour.suffix}-{config.version}", - entry_point="ale_py.env.gym:AtariEnv", - kwargs=dict( - game=rom, - obs_type=obs_type, - **config_kwargs, - **flavour_kwargs, - ), - ) - - -def register_legacy_gym_envs() -> None: - legacy_games = [ - "adventure", - "air_raid", - "alien", - "amidar", - "assault", - "asterix", - "asteroids", - "atlantis", - "bank_heist", - "battle_zone", - "beam_rider", - "berzerk", - "bowling", - "boxing", - "breakout", - "carnival", - "centipede", - "chopper_command", - "crazy_climber", - "defender", - "demon_attack", - "double_dunk", - "elevator_action", - "enduro", - "fishing_derby", - "freeway", - "frostbite", - "gopher", - "gravitar", - "hero", - "ice_hockey", - "jamesbond", - "journey_escape", - "kangaroo", - "krull", - "kung_fu_master", - "montezuma_revenge", - "ms_pacman", - "name_this_game", - "phoenix", - "pitfall", - "pong", - "pooyan", - "private_eye", - "qbert", - "riverraid", - "road_runner", - "robotank", - "seaquest", - "skiing", - "solaris", - "space_invaders", - "star_gunner", - "tennis", - "time_pilot", - "tutankham", - "up_n_down", - "venture", - "video_pinball", - "wizard_of_wor", - "yars_revenge", - "zaxxon", - ] - obs_types = ["rgb", "ram"] - frameskip = defaultdict(lambda: 4, [("space_invaders", 3)]) - - versions = [ - GymConfig( - version="v0", - kwargs={ - "repeat_action_probability": 0.25, - "full_action_space": False, - "max_num_frames_per_episode": 108_000, - }, - flavours=[ - # Default for v0 has 10k steps, no idea why... - GymFlavour("", {"frameskip": (2, 5)}), - # Deterministic has 100k steps, close to the standard of 108k (30 mins gameplay) - GymFlavour("Deterministic", lambda rom: {"frameskip": frameskip[rom]}), - # NoFrameSkip imposes a max episode steps of frameskip * 100k, weird... - GymFlavour("NoFrameskip", {"frameskip": 1}), - ], - ), - GymConfig( - version="v4", - kwargs={ - "repeat_action_probability": 0.0, - "full_action_space": False, - "max_num_frames_per_episode": 108_000, - }, - flavours=[ - # Unlike v0, v4 has 100k max episode steps - GymFlavour("", {"frameskip": (2, 5)}), - GymFlavour("Deterministic", lambda rom: {"frameskip": frameskip[rom]}), - # Same weird frameskip * 100k max steps for v4? - GymFlavour("NoFrameskip", {"frameskip": 1}), - ], - ), - ] - - _register_gym_configs(legacy_games, obs_types, versions) - - -def register_gym_envs(): - all_games = list(map(rom_utils.rom_name_to_id, dir(roms))) - obs_types = ["rgb", "ram"] - - # max_episode_steps is 108k frames which is 30 mins of gameplay. - # This corresponds to 108k / 4 = 27,000 steps - versions = [ - GymConfig( - version="v5", - kwargs={ - "repeat_action_probability": 0.25, - "full_action_space": False, - "frameskip": 4, - "max_num_frames_per_episode": 108_000, - }, - flavours=[GymFlavour("", {})], - ) - ] - - _register_gym_configs(all_games, obs_types, versions) From e4552e59abcae67dfdd6d713855726284f634023 Mon Sep 17 00:00:00 2001 From: Jet Date: Wed, 7 Feb 2024 19:28:28 +0000 Subject: [PATCH 13/16] hacky gymnasium register --- pyproject.toml | 24 +- src/python/__init__.py | 8 +- src/python/atari_env.py | 384 ++++++++++++++++++++++++++++++ src/python/register_gymnasium.py | 288 ++++++++++++++++++++++ src/python/roms/__init__.py | 25 +- src/python/roms/md5.txt | 107 ++++++++- src/python/roms/tetris.bin | Bin 2048 -> 0 bytes src/python/scripts/__init__.py | 0 src/python/scripts/import_roms.py | 102 -------- 9 files changed, 798 insertions(+), 140 deletions(-) create mode 100644 src/python/atari_env.py create mode 100644 src/python/register_gymnasium.py mode change 120000 => 100644 src/python/roms/md5.txt delete mode 100644 src/python/roms/tetris.bin delete mode 100644 src/python/scripts/__init__.py delete mode 100644 src/python/scripts/import_roms.py diff --git a/pyproject.toml b/pyproject.toml index aa2443541..ee11f6895 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,7 @@ dynamic = ["version"] [project.optional-dependencies] test = [ "pytest>=7.0", - "gym~=0.23", + "gymnasium", ] [project.urls] @@ -50,21 +50,9 @@ homepage = "https://github.com/mgbellemare/Arcade-Learning-Environment" documentation = "https://github.com/mgbellemare/Arcade-Learning-Environment/tree/master/docs" changelog = "https://github.com/mgbellemare/Arcade-Learning-Environment/blob/master/CHANGELOG.md" -[project.scripts] -ale-import-roms = "ale_py.scripts.import_roms:main" - -[project.entry-points."gym.envs"] -ALE = "ale_py.gym:register_gym_envs" -__internal__ = "ale_py.gym:register_legacy_gym_envs" - [tool.setuptools] -packages = [ - "ale_py", - "ale_py.roms", - "ale_py.env", - "ale_py.scripts" -] -package-dir = {ale_py = "src/python", gym = "src/gym"} +packages = ["ale_py", "ale_py.roms"] +package-dir = {ale_py = "src/python"} package-data = {"ale_py" = ["py.typed", "*.pyi", "**/*.pyi"], "ale_py.roms" = ["*.bin", "md5.txt"]} [tool.pytest.ini_options] @@ -79,12 +67,6 @@ skip = ["*-win32", "*i686", "pp*", "*-musllinux*"] build-frontend = "build" -# Test configuration -# test-extras = ["test"] -# TODO(jfarebro): Temporarily use upstream Gym until v26 release. -test-requires = ["pytest", "git+https://github.com/openai/gym#egg=gym"] -test-command = "pytest {project}" - # vcpkg manylinux images manylinux-x86_64-image = "ghcr.io/jessefarebro/manylinux2014_x86_64-vcpkg" diff --git a/src/python/__init__.py b/src/python/__init__.py index 67f6ea483..c117f4937 100644 --- a/src/python/__init__.py +++ b/src/python/__init__.py @@ -16,8 +16,8 @@ ctypes.CDLL("msvcp140.dll") except OSError: raise OSError( - """Microsoft Visual C++ Redistribution Pack is not installed. -It can be downloaded from https://aka.ms/vs/16/release/vc_redist.x64.exe.""" + "Microsoft Visual C++ Redistribution Pack is not installed. \n" + "It can be downloaded from https://aka.ms/vs/16/release/vc_redist.x64.exe." ) # Loading DLLs on Windows is kind of a disaster @@ -50,5 +50,5 @@ __all__ = ["Action", "ALEInterface", "ALEState", "LoggerMode", "SDL_SUPPORT"] -from .register_gymnasium import register_gymnasium_envs -register_gymnasium_envs() +from .register_gymnasium import register_gymnasium +register_gymnasium() diff --git a/src/python/atari_env.py b/src/python/atari_env.py new file mode 100644 index 000000000..18c9bb827 --- /dev/null +++ b/src/python/atari_env.py @@ -0,0 +1,384 @@ +from __future__ import annotations + +import sys +from typing import Any, Literal, Optional, Sequence, Union + +import ale_py +from ale_py import roms +from ale_py.roms.utils import rom_id_to_name, rom_name_to_id +from gymnasium.utils import seeding +import numpy as np + +import gymnasium +import gymnasium.logger as logger +from gymnasium import error, spaces, utils + +if sys.version_info < (3, 11): + from typing_extensions import NotRequired, TypedDict +else: + from typing import NotRequired, TypedDict + + +class AtariEnvStepMetadata(TypedDict): + lives: int + episode_frame_number: int + frame_number: int + seeds: NotRequired[Sequence[int]] + + +class AtariEnv(gymnasium.Env, utils.EzPickle): + """ + (A)rcade (L)earning (Gym) (Env)ironment. + A Gym wrapper around the Arcade Learning Environment (ALE). + """ + + # No render modes + metadata = {"render_modes": ["human", "rgb_array"]} + + def __init__( + self, + game: str = "pong", + mode: Optional[int] = None, + difficulty: Optional[int] = None, + obs_type: Literal["rgb", "grayscale", "ram"] = "rgb", + frameskip: Union[tuple[int, int], int] = 4, + repeat_action_probability: float = 0.25, + full_action_space: bool = False, + max_num_frames_per_episode: Optional[int] = None, + render_mode: Optional[Literal["human", "rgb_array"]] = None, + ) -> None: + """ + Initialize the ALE for Gymnasium. + Default parameters are taken from Machado et al., 2018. + + Args: + game: str => Game to initialize env with. + mode: Optional[int] => Game mode, see Machado et al., 2018 + difficulty: Optional[int] => Game difficulty,see Machado et al., 2018 + obs_type: str => Observation type in { 'rgb', 'grayscale', 'ram' } + frameskip: Union[tuple[int, int], int] => + Stochastic frameskip as tuple or fixed. + repeat_action_probability: int => + Probability to repeat actions, see Machado et al., 2018 + full_action_space: bool => Use full action space? + max_num_frames_per_episode: int => Max number of frame per epsiode. + Once `max_num_frames_per_episode` is reached the episode is + truncated. + render_mode: str => One of { 'human', 'rgb_array' }. + If `human` we'll interactively display the screen and enable + game sounds. This will lock emulation to the ROMs specified FPS + If `rgb_array` we'll return the `rgb` key in step metadata with + the current environment RGB frame. + + Note: + - The game must be installed, see ale-import-roms, or ale-py-roms. + - Frameskip values of (low, high) will enable stochastic frame skip + which will sample a random frameskip uniformly each action. + - It is recommended to enable full action space. + See Machado et al., 2018 for more details. + + References: + `Revisiting the Arcade Learning Environment: Evaluation Protocols + and Open Problems for General Agents`, Machado et al., 2018, JAIR + URL: https://jair.org/index.php/jair/article/view/11182 + """ + if obs_type not in {"rgb", "grayscale", "ram"}: + raise error.Error( + f"Invalid observation type: {obs_type}. Expecting: rgb, grayscale, ram." + ) + + if type(frameskip) not in (int, tuple): + raise error.Error(f"Invalid frameskip type: {type(frameskip)}.") + if isinstance(frameskip, int) and frameskip <= 0: + raise error.Error( + f"Invalid frameskip of {frameskip}, frameskip must be positive." + ) + elif isinstance(frameskip, tuple) and len(frameskip) != 2: + raise error.Error( + f"Invalid stochastic frameskip length of {len(frameskip)}, expected length 2." + ) + elif isinstance(frameskip, tuple) and frameskip[0] > frameskip[1]: + raise error.Error( + f"Invalid stochastic frameskip, lower bound is greater than upper bound." + ) + elif isinstance(frameskip, tuple) and frameskip[0] <= 0: + raise error.Error( + f"Invalid stochastic frameskip lower bound is greater than upper bound." + ) + + if render_mode is not None and render_mode not in {"rgb_array", "human"}: + raise error.Error( + f"Render mode {render_mode} not supported (rgb_array, human)." + ) + + utils.EzPickle.__init__( + self, + game, + mode, + difficulty, + obs_type, + frameskip, + repeat_action_probability, + full_action_space, + max_num_frames_per_episode, + render_mode, + ) + + # Initialize ALE + self.ale = ale_py.ALEInterface() + + self._game = rom_id_to_name(game) + + self._game_mode = mode + self._game_difficulty = difficulty + + self._frameskip = frameskip + self._obs_type = obs_type + self.render_mode = render_mode + + # Set logger mode to error only + self.ale.setLoggerMode(ale_py.LoggerMode.Error) + # Config sticky action prob. + self.ale.setFloat("repeat_action_probability", repeat_action_probability) + + if max_num_frames_per_episode is not None: + self.ale.setInt("max_num_frames_per_episode", max_num_frames_per_episode) + + # If render mode is human we can display screen and sound + if render_mode == "human": + self.ale.setBool("display_screen", True) + self.ale.setBool("sound", True) + + # seed + load + self.seed_game() + self.load_game() + + # initialize action space + self._action_set = ( + self.ale.getLegalActionSet() + if full_action_space + else self.ale.getMinimalActionSet() + ) + self.action_space = spaces.Discrete(len(self._action_set)) + + # initialize observation space + if self._obs_type == "ram": + self.observation_space = spaces.Box( + low=0, high=255, dtype=np.uint8, shape=(self.ale.getRAMSize(),) + ) + elif self._obs_type == "rgb" or self._obs_type == "grayscale": + (screen_height, screen_width) = self.ale.getScreenDims() + image_shape = ( + screen_height, + screen_width, + ) + if self._obs_type == "rgb": + image_shape += (3,) + self.observation_space = spaces.Box( + low=0, high=255, dtype=np.uint8, shape=image_shape + ) + else: + raise error.Error(f"Unrecognized observation type: {self._obs_type}") + + def seed_game(self, seed: Optional[int] = None) -> tuple[int, int]: + """Seeds the internal and ALE RNG.""" + ss = np.random.SeedSequence(seed) + np_seed, ale_seed = ss.generate_state(n_words=2) + self._np_random, seed = seeding.np_random(int(np_seed)) + self.ale.setInt("random_seed", np.int32(ale_seed)) + return np_seed, ale_seed + + def load_game(self) -> None: + """This function initializes the ROM and sets the corresponding mode and difficulty.""" + if not hasattr(roms, self._game): + raise error.Error( + f'We\'re Unable to find the game "{self._game}". Note: Gym no longer distributes ROMs. ' + f"If you own a license to use the necessary ROMs for research purposes you can download them " + f'via `pip install gym[accept-rom-license]`. Otherwise, you should try importing "{self._game}" ' + f'via the command `ale-import-roms`. If you believe this is a mistake perhaps your copy of "{self._game}" ' + "is unsupported. To check if this is the case try providing the environment variable " + "`PYTHONWARNINGS=default::ImportWarning:ale_py.roms`. For more information see: " + "https://github.com/mgbellemare/Arcade-Learning-Environment#rom-management" + ) + self.ale.loadROM(getattr(roms, self._game)) + + if self._game_mode is not None: + self.ale.setMode(self._game_mode) + if self._game_difficulty is not None: + self.ale.setDifficulty(self._game_difficulty) + + def step( # pyright: ignore[reportIncompatibleMethodOverride] + self, + action: int, + ) -> tuple[np.ndarray, float, bool, bool, AtariEnvStepMetadata]: + """ + Perform one agent step, i.e., repeats `action` frameskip # of steps. + + Args: + action_ind: int => Action index to execute + + Returns: + tuple[np.ndarray, float, bool, bool, Dict[str, Any]] => + observation, reward, terminal, truncation, metadata + + Note: `metadata` contains the keys "lives" and "rgb" if + render_mode == 'rgb_array'. + """ + # If frameskip is a length 2 tuple then it's stochastic + # frameskip between [frameskip[0], frameskip[1]] uniformly. + if isinstance(self._frameskip, int): + frameskip = self._frameskip + elif isinstance(self._frameskip, tuple): + frameskip = self.np_random.integers(*self._frameskip) + else: + raise error.Error(f"Invalid frameskip type: {self._frameskip}") + + # Frameskip + reward = 0.0 + for _ in range(frameskip): + reward += self.ale.act(self._action_set[action]) + is_terminal = self.ale.game_over(with_truncation=False) + is_truncated = self.ale.game_truncated() + + return self._get_obs(), reward, is_terminal, is_truncated, self._get_info() + + def reset( # pyright: ignore[reportIncompatibleMethodOverride] + self, + *, + seed: Optional[int] = None, + options: Optional[dict[str, Any]] = None, + ) -> tuple[np.ndarray, AtariEnvStepMetadata]: + """Resets environment and returns initial observation.""" + # sets the seeds if it's specified for both ALE and frameskip np + # we only want to do this when commanded to so we don't reset all previous states, statistics, etc. + seeded_with = None + if seed is not None: + seeded_with = self.seed_game(seed) + self.load_game() + + self.ale.reset_game() + + obs = self._get_obs() + info = self._get_info() + if seeded_with is not None: + info["seeds"] = seeded_with + + return obs, info + + def render(self) -> Optional[np.ndarray]: + """ + Render is not supported by ALE. We use a paradigm similar to + Gym3 which allows you to specify `render_mode` during construction. + + For example, + gym.make("ale-py:Pong-v0", render_mode="human") + will display the ALE and maintain the proper interval to match the + FPS target set by the ROM. + """ + if self.render_mode == "rgb_array": + return self.ale.getScreenRGB() + elif self.render_mode == "human": + return + else: + raise error.Error( + f"Invalid render mode `{self.render_mode}`. " + "Supported modes: `human`, `rgb_array`." + ) + + def _get_obs(self) -> np.ndarray: + """ + Retreives the current observation. + This is dependent on `self._obs_type`. + """ + if self._obs_type == "ram": + return self.ale.getRAM() + elif self._obs_type == "rgb": + return self.ale.getScreenRGB() + elif self._obs_type == "grayscale": + return self.ale.getScreenGrayscale() + else: + raise error.Error(f"Unrecognized observation type: {self._obs_type}") + + def _get_info(self) -> AtariEnvStepMetadata: + return { + "lives": self.ale.lives(), + "episode_frame_number": self.ale.getEpisodeFrameNumber(), + "frame_number": self.ale.getFrameNumber(), + } + + def get_keys_to_action(self) -> dict[tuple[int], ale_py.Action]: + """ + Return keymapping -> actions for human play. + """ + UP = ord("w") + LEFT = ord("a") + RIGHT = ord("d") + DOWN = ord("s") + FIRE = ord(" ") + + mapping = { + ale_py.Action.NOOP: (None,), + ale_py.Action.UP: (UP,), + ale_py.Action.FIRE: (FIRE,), + ale_py.Action.DOWN: (DOWN,), + ale_py.Action.LEFT: (LEFT,), + ale_py.Action.RIGHT: (RIGHT,), + ale_py.Action.UPFIRE: (UP, FIRE), + ale_py.Action.DOWNFIRE: (DOWN, FIRE), + ale_py.Action.LEFTFIRE: (LEFT, FIRE), + ale_py.Action.RIGHTFIRE: (RIGHT, FIRE), + ale_py.Action.UPLEFT: (UP, LEFT), + ale_py.Action.UPRIGHT: (UP, RIGHT), + ale_py.Action.DOWNLEFT: (DOWN, LEFT), + ale_py.Action.DOWNRIGHT: (DOWN, RIGHT), + ale_py.Action.UPLEFTFIRE: (UP, LEFT, FIRE), + ale_py.Action.UPRIGHTFIRE: (UP, RIGHT, FIRE), + ale_py.Action.DOWNLEFTFIRE: (DOWN, LEFT, FIRE), + ale_py.Action.DOWNRIGHTFIRE: (DOWN, RIGHT, FIRE), + } + + # Map + # (key, key, ...) -> action_idx + # where action_idx is the integer value of the action enum + # + return dict( + zip( + map(lambda action: tuple(sorted(mapping[action])), self._action_set), + range(len(self._action_set)), + ) + ) + + def get_action_meanings(self) -> list[str]: + """ + Return the meaning of each integer action. + """ + keys = ale_py.Action.__members__.values() + values = ale_py.Action.__members__.keys() + mapping = dict(zip(keys, values)) + return [mapping[action] for action in self._action_set] + + def clone_state(self, include_rng=False) -> ale_py.ALEState: + """Clone emulator state w/o system state. Restoring this state will + *not* give an identical environment. For complete cloning and restoring + of the full state, see `{clone,restore}_full_state()`.""" + return self.ale.cloneState(include_rng=include_rng) + + def restore_state(self, state: ale_py.ALEState) -> None: + """Restore emulator state w/o system state.""" + self.ale.restoreState(state) + + def clone_full_state(self) -> ale_py.ALEState: + """Deprecated method which would clone the emulator and system state.""" + logger.warn( + "`clone_full_state()` is deprecated and will be removed in a future release of `ale-py`. " + "Please use `clone_state(include_rng=True)` which is equivalent to `clone_full_state`. " + ) + return self.ale.cloneSystemState() + + def restore_full_state(self, state: ale_py.ALEState) -> None: + """Restore emulator state w/ system state including pseudorandomness.""" + logger.warn( + "restore_full_state() is deprecated and will be removed in a future release of `ale-py`. " + "Please use `restore_state(state)` which will restore the state regardless of being a full or partial state. " + ) + self.ale.restoreSystemState(state) diff --git a/src/python/register_gymnasium.py b/src/python/register_gymnasium.py new file mode 100644 index 000000000..b39f16a4f --- /dev/null +++ b/src/python/register_gymnasium.py @@ -0,0 +1,288 @@ +from typing import Any, Callable, Mapping, NamedTuple, Sequence +from collections import defaultdict +from ale_py.roms.utils import rom_id_to_name +from gymnasium.envs.registration import register + +ALL_ATARI_GAMES = ( + "adventure", + "air_raid", + "alien", + "amidar", + "assault", + "asterix", + "asteroids", + "atlantis", + "atlantis2", + "backgammon", + "bank_heist", + "basic_math", + "battle_zone", + "beam_rider", + "berzerk", + "blackjack", + "bowling", + "boxing", + "breakout", + "carnival", + "casino", + "centipede", + "chopper_command", + "crazy_climber", + "crossbow", + "darkchambers", + "defender", + "demon_attack", + "donkey_kong", + "double_dunk", + "earthworld", + "elevator_action", + "enduro", + "entombed", + "et", + "fishing_derby", + "flag_capture", + "freeway", + "frogger", + "frostbite", + "galaxian", + "gopher", + "gravitar", + "hangman", + "haunted_house", + "hero", + "human_cannonball", + "ice_hockey", + "jamesbond", + "journey_escape", + "kaboom", + "kangaroo", + "keystone_kapers", + "king_kong", + "klax", + "koolaid", + "krull", + "kung_fu_master", + "laser_gates", + "lost_luggage", + "mario_bros", + "miniature_golf", + "montezuma_revenge", + "mr_do", + "ms_pacman", + "name_this_game", + "othello", + "pacman", + "phoenix", + "pitfall", + "pitfall2", + "pong", + "pooyan", + "private_eye", + "qbert", + "riverraid", + "road_runner", + "robotank", + "seaquest", + "sir_lancelot", + "skiing", + "solaris", + "space_invaders", + "space_war", + "star_gunner", + "superman", + "surround", + "tennis", + "tetris", + "tic_tac_toe_3d", + "time_pilot", + "trondead", + "turmoil", + "tutankham", + "up_n_down", + "venture", + "video_checkers", + "video_chess", + "video_cube", + "video_pinball", + "wizard_of_wor", + "word_zapper", + "yars_revenge", + "zaxxon", +) +LEGACY_ATARI_GAMES = ( + "adventure", + "air_raid", + "alien", + "amidar", + "assault", + "asterix", + "asteroids", + "atlantis", + "bank_heist", + "battle_zone", + "beam_rider", + "berzerk", + "bowling", + "boxing", + "breakout", + "carnival", + "centipede", + "chopper_command", + "crazy_climber", + "defender", + "demon_attack", + "double_dunk", + "elevator_action", + "enduro", + "fishing_derby", + "freeway", + "frostbite", + "gopher", + "gravitar", + "hero", + "ice_hockey", + "jamesbond", + "journey_escape", + "kangaroo", + "krull", + "kung_fu_master", + "montezuma_revenge", + "ms_pacman", + "name_this_game", + "phoenix", + "pitfall", + "pong", + "pooyan", + "private_eye", + "qbert", + "riverraid", + "road_runner", + "robotank", + "seaquest", + "skiing", + "solaris", + "space_invaders", + "star_gunner", + "tennis", + "time_pilot", + "tutankham", + "up_n_down", + "venture", + "video_pinball", + "wizard_of_wor", + "yars_revenge", + "zaxxon", +) + +class GymFlavour(NamedTuple): + """A Gymnasium Flavour.""" + + suffix: str + kwargs: Mapping[str, Any] | Callable[[str], Mapping[str, Any]] + + +class GymConfig(NamedTuple): + """A Gymnasium Configuration.""" + + version: str + kwargs: Mapping[str, Any] + flavours: Sequence[GymFlavour] + + +def _register_configs( + roms: Sequence[str], + obs_types: Sequence[str], + configs: Sequence[GymConfig], + prefix: str = "", +): + """Registers all possible configurations of the atari games given a list of roms.""" + for rom in roms: + for obs_type in obs_types: + for config in configs: + for flavour in config.flavours: + name = rom_id_to_name(rom) + if obs_type == "ram": + name = f"{name}-ram" + + # Parse config kwargs + if callable(config.kwargs): + config_kwargs = config.kwargs(rom) + else: + config_kwargs = config.kwargs + + # Parse flavour kwargs + if callable(flavour.kwargs): + flavour_kwargs = flavour.kwargs(rom) + else: + flavour_kwargs = flavour.kwargs + + # Register the environment + register( + id=f"{prefix}{name}{flavour.suffix}-{config.version}", + entry_point="ale_py.atari_env:AtariEnv", + kwargs={ + "game": rom, + "obs_type": obs_type, + **config_kwargs, + **flavour_kwargs, + }, + ) + + +def register_gymnasium(): + frameskip: dict[str, int] = defaultdict(lambda: 4, [("space_invaders", 3)]) + + configs = [ + GymConfig( + version="v0", + kwargs={ + "repeat_action_probability": 0.25, + "full_action_space": False, + "max_num_frames_per_episode": 108_000, + }, + flavours=[ + # Default for v0 has 10k steps, no idea why... + GymFlavour("", {"frameskip": (2, 5)}), + # Deterministic has 100k steps, close to the standard of 108k (30 mins gameplay) + GymFlavour("Deterministic", lambda rom: {"frameskip": frameskip[rom]}), + # NoFrameSkip imposes a max episode steps of frameskip * 100k, weird... + GymFlavour("NoFrameskip", {"frameskip": 1}), + ], + ), + GymConfig( + version="v4", + kwargs={ + "repeat_action_probability": 0.0, + "full_action_space": False, + "max_num_frames_per_episode": 108_000, + }, + flavours=[ + # Unlike v0, v4 has 100k max episode steps + GymFlavour("", {"frameskip": (2, 5)}), + GymFlavour("Deterministic", lambda rom: {"frameskip": frameskip[rom]}), + # Same weird frameskip * 100k max steps for v4? + GymFlavour("NoFrameskip", {"frameskip": 1}), + ], + ), + ] + _register_configs( + LEGACY_ATARI_GAMES, obs_types=("rgb", "ram"), configs=configs + ) + + # max_episode_steps is 108k frames which is 30 mins of gameplay. + # This corresponds to 108k / 4 = 27,000 steps + configs = [ + GymConfig( + version="v5", + kwargs={ + "repeat_action_probability": 0.25, + "full_action_space": False, + "frameskip": 4, + "max_num_frames_per_episode": 108_000, + }, + flavours=[GymFlavour("", {})], + ) + ] + _register_configs( + ALL_ATARI_GAMES, obs_types=("rgb", "ram"), configs=configs, prefix="ALE/" + ) diff --git a/src/python/roms/__init__.py b/src/python/roms/__init__.py index cf2e8b1ee..4fe89466f 100644 --- a/src/python/roms/__init__.py +++ b/src/python/roms/__init__.py @@ -7,7 +7,8 @@ from typing import List, Optional from ale_py import ALEInterface -from ale_py.roms import plugins, utils +from ale_py.roms.utils import rom_id_to_name, rom_name_to_id +from ale_py.roms.plugins import Package, EntryPoint, Directory, Plugin # pylint: disable=g-import-not-at-top if sys.version_info >= (3, 9): @@ -21,11 +22,11 @@ # 2. External ROMs # 3. ROMs from atari-py.roms # 4. ROMs from atari-py-roms.roms -_ROM_PLUGIN_REGISTRY: List[plugins.Plugin] = [ - plugins.Package("ale_py.roms"), - plugins.EntryPoint("ale_py.roms"), - plugins.Package("atari_py.atari_roms"), - plugins.Package("atari_py_roms.atari_roms"), +_ROM_PLUGIN_REGISTRY: List[Plugin] = [ + Package("ale_py.roms"), + EntryPoint("ale_py.roms"), + Package("atari_py.atari_roms"), + Package("atari_py_roms.atari_roms"), ] # Environment variable for ROM discovery. @@ -35,13 +36,13 @@ _ROM_DIRECTORY_ENV_VALUE = os.environ.get(_ROM_DIRECTORY_ENV_KEY, None) if _ROM_DIRECTORY_ENV_VALUE is not None: - _ROM_PLUGIN_REGISTRY.append(plugins.Directory(_ROM_DIRECTORY_ENV_VALUE)) + _ROM_PLUGIN_REGISTRY.append(Directory(_ROM_DIRECTORY_ENV_VALUE)) def _resolve_rom(name: str) -> Optional[pathlib.Path]: """Resolve a ROM path from the ROM registry.""" for package in _ROM_PLUGIN_REGISTRY: - rom_id = utils.rom_name_to_id(name) + rom_id = rom_name_to_id(name) # Resolve ROM from package try: @@ -73,7 +74,7 @@ def _resolve_rom(name: str) -> Optional[pathlib.Path]: continue # Deprecation warning for atari-py - if isinstance(package, plugins.Package) and package.package.startswith( + if isinstance(package, Package) and package.package.startswith( "atari_py" ): warnings.warn( @@ -105,7 +106,7 @@ def __dir__() -> List[str]: lambda line: line.strip() and not line.startswith("#"), fp.readlines() ) roms = [pathlib.Path(rom) for _, rom in map(str.split, lines)] - return [utils.rom_id_to_name(rom.stem) for rom in roms] + return [rom_id_to_name(rom.stem) for rom in roms] @functools.lru_cache(maxsize=None) @@ -130,9 +131,9 @@ def __getattr__(name: str) -> pathlib.Path: return path -def register_plugin(plugin: plugins.Plugin, *, index: int = 0) -> None: +def register_plugin(plugin: Plugin, *, index: int = 0) -> None: """Register a ROM plugin.""" - if not issubclass(type(plugin), plugins.Plugin): + if not issubclass(type(plugin), Plugin): raise ValueError(f"{repr(plugin)} is not a valid ROM plugin.") _ROM_PLUGIN_REGISTRY.insert(index, plugin) __getattr__.cache_clear() diff --git a/src/python/roms/md5.txt b/src/python/roms/md5.txt deleted file mode 120000 index b9bf6d10a..000000000 --- a/src/python/roms/md5.txt +++ /dev/null @@ -1 +0,0 @@ -../../../md5.txt \ No newline at end of file diff --git a/src/python/roms/md5.txt b/src/python/roms/md5.txt new file mode 100644 index 000000000..63b23408c --- /dev/null +++ b/src/python/roms/md5.txt @@ -0,0 +1,106 @@ +# The following is a list of the md5 checksums for the various roms supported by ALE. + +4b27f5397c442d25f0c418ccdacf1926 adventure.bin +35be55426c1fec32dfb503b4f0651572 air_raid.bin +f1a0a23e6464d954e3a9579c4ccd01c8 alien.bin +acb7750b4d0c4bd34969802a7deb2990 amidar.bin +de78b3a064d374390ac0710f95edde92 assault.bin +89a68746eff7f266bbf08de2483abe55 asterix.bin +ccbd36746ed4525821a8083b0d6d2c2c asteroids.bin +826481f6fc53ea47c9f272f7050eedf7 atlantis2.bin +9ad36e699ef6f45d9eb6c4cf90475c9f atlantis.bin +8556b42aa05f94bc29ff39c39b11bff4 backgammon.bin +00ce0bdd43aed84a983bef38fe7f5ee3 bank_heist.bin +819aeeb9a2e11deb54e6de334f843894 basic_math.bin +41f252a66c6301f1e8ab3612c19bc5d4 battle_zone.bin +79ab4123a83dc11d468fb2108ea09e2e beam_rider.bin +136f75c4dd02c29283752b7e5799f978 berzerk.bin +0a981c03204ac2b278ba392674682560 blackjack.bin +c9b7afad3bfd922e006a6bfc1d4f3fe7 bowling.bin +c3ef5c4653212088eda54dc91d787870 boxing.bin +f34f08e5eb96e500e851a80be3277a56 breakout.bin +028024fb8e5e5f18ea586652f9799c96 carnival.bin +b816296311019ab69a21cb9e9e235d12 casino.bin +91c2098e88a6b13f977af8c003e0bca5 centipede.bin +c1cb228470a87beb5f36e90ac745da26 chopper_command.bin +55ef7b65066428367844342ed59f956c crazy_climber.bin +8cd26dcf249456fe4aeb8db42d49df74 crossbow.bin +106855474c69d08c8ffa308d47337269 darkchambers.bin +0f643c34e40e3f1daafd9c524d3ffe64 defender.bin +f0e0addc07971561ab80d9abe1b8d333 demon_attack.bin +36b20c427975760cb9cf4a47e41369e4 donkey_kong.bin +368d88a6c071caba60b4f778615aae94 double_dunk.bin +5aea9974b975a6a844e6df10d2b861c4 earthworld.bin +71f8bacfbdca019113f3f0801849057e elevator_action.bin +94b92a882f6dbaa6993a46e2dcc58402 enduro.bin +6b683be69f92958abe0e2a9945157ad5 entombed.bin +615a3bf251a38eb6638cdc7ffbde5480 et.bin +b8865f05676e64f3bec72b9defdacfa7 fishing_derby.bin +30512e0e83903fc05541d2f6a6a62654 flag_capture.bin +8e0ab801b1705a740b476b7f588c6d16 freeway.bin +081e2c114c9c20b61acf25fc95c71bf4 frogger.bin +4ca73eb959299471788f0b685c3ba0b5 frostbite.bin +211774f4c5739042618be8ff67351177 galaxian.bin +c16c79aad6272baffb8aae9a7fff0864 gopher.bin +8ac18076d01a6b63acf6e2cab4968940 gravitar.bin +f16c709df0a6c52f47ff52b9d95b7d8d hangman.bin +f0a6e99f5875891246c3dbecbf2d2cea haunted_house.bin +fca4a5be1251927027f2c24774a02160 hero.bin +7972e5101fa548b952d852db24ad6060 human_cannonball.bin +a4c08c4994eb9d24fb78be1793e82e26 ice_hockey.bin +e51030251e440cffaab1ac63438b44ae jamesbond.bin +718ae62c70af4e5fd8e932fee216948a journey_escape.bin +5428cdfada281c569c74c7308c7f2c26 kaboom.bin +4326edb70ff20d0ee5ba58fa5cb09d60 kangaroo.bin +6c1f3f2e359dbf55df462ccbcdd2f6bf keystone_kapers.bin +0dd4c69b5f9a7ae96a7a08329496779a king_kong.bin +eed9eaf1a0b6a2b9bc4c8032cb43e3fb klax.bin +534e23210dd1993c828d944c6ac4d9fb koolaid.bin +4baada22435320d185c95b7dd2bcdb24 krull.bin +5b92a93b23523ff16e2789b820e2a4c5 kung_fu_master.bin +8e4cd60d93fcde8065c1a2b972a26377 laser_gates.bin +2d76c5d1aad506442b9e9fb67765e051 lost_luggage.bin +e908611d99890733be31733a979c62d8 mario_bros.bin +df62a658496ac98a3aa4a6ee5719c251 miniature_golf.bin +3347a6dd59049b15a38394aa2dafa585 montezuma_revenge.bin +aa7bb54d2c189a31bb1fa20099e42859 mr_do.bin +87e79cd41ce136fd4f72cc6e2c161bee ms_pacman.bin +36306070f0c90a72461551a7a4f3a209 name_this_game.bin +113cd09c9771ac278544b7e90efe7df2 othello.bin +fc2233fc116faef0d3c31541717ca2db pacman.bin +7e52a95074a66640fcfde124fffd491a phoenix.bin +6d842c96d5a01967be9680080dd5be54 pitfall2.bin +3e90cf23106f2e08b2781e41299de556 pitfall.bin +60e0ea3cbe0913d39803477945e9e5ec pong.bin +4799a40b6e889370b7ee55c17ba65141 pooyan.bin +ef3a4f64b6494ba770862768caf04b86 private_eye.bin +484b0076816a104875e00467d431c2d2 qbert.bin +393948436d1f4cc3192410bb918f9724 riverraid.bin +ce5cc62608be2cd3ed8abd844efb8919 road_runner.bin +4f618c2429138e0280969193ed6c107e robotank.bin +240bfbac5163af4df5ae713985386f92 seaquest.bin +dd0cbe5351551a538414fb9e37fc56e8 sir_lancelot.bin +b76fbadc8ffb1f83e2ca08b6fb4d6c9f skiing.bin +e72eb8d4410152bdcb69e7fba327b420 solaris.bin +72ffbef6504b75e69ee1045af9075f66 space_invaders.bin +b702641d698c60bcdc922dbd8c9dd49c space_war.bin +a3c1c70024d7aabb41381adbfb6d3b25 star_gunner.bin +a9531c763077464307086ec9a1fd057d superman.bin +4d7517ae69f95cfbc053be01312b7dba surround.bin +42cdd6a9e42a3639e190722b8ea3fc51 tennis.bin +b0e1ee07fbc73493eac5651a52f90f00 tetris.bin +0db4f4150fecf77e4ce72ca4d04c052f tic_tac_toe_3d.bin +fc2104dd2dadf9a6176c1c1c8f87ced9 time_pilot.bin +fb27afe896e7c928089307b32e5642ee trondead.bin +7a5463545dfb2dcfdafa6074b2f2c15e turmoil.bin +085322bae40d904f53bdcc56df0593fc tutankham.bin +a499d720e7ee35c62424de882a3351b6 up_n_down.bin +3e899eba0ca8cd2972da1ae5479b4f0d venture.bin +539d26b6e9df0da8e7465f0f5ad863b7 video_checkers.bin +f0b7db930ca0e548c41a97160b9f6275 video_chess.bin +3f540a30fdee0b20aed7288e4a5ea528 video_cube.bin +107cc025334211e6d29da0b6be46aec7 video_pinball.bin +7e8aa18bc9502eb57daaf5e7c1e94da7 wizard_of_wor.bin +ec3beb6d8b5689e867bafb5d5f507491 word_zapper.bin +c5930d0e8cdae3e037349bfa08e871be yars_revenge.bin +eea0da9b987d661264cce69a7c13c3bd zaxxon.bin diff --git a/src/python/roms/tetris.bin b/src/python/roms/tetris.bin deleted file mode 100644 index e3647abc165c2101c62674849d7e13546af3d175..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2048 zcmcIkT}UHW6rLF;YTPw5fmQ@f3}Lr4?W!rQ;DdC9=A~_aW*7Pg?H%Tv zd%ydgd%kn$-uZYfUYQ|`RLLmElcOAI1dTimIU+&707vp(Jnj0rT%CHIP6;?wz^VQCB2LLgue~6oi^`cekLhkps~PbK`Pqdr zPB{jad=FN#3XPbChYsijPTuvfg$94)Z@|U4h*H*&-2vib_JktK&@myhvl6>2e(tdWkpapxZT-ICjv4Iwz4E_zTnoce>dDADEvPE4>G3qvlu>U@ zX@AsUWP5VEA~eeUBxdQRMV>+b+fQO@td>$uMX|a%%p6x~CYD-LS#`VqB8ORx$*xvi z6-KeT9HtKB;4Ya*(L`|DjXF$$^y<(P4c1sI;88v%PS_wk9(Ep@NCWVM1q*O-OGYW< z(Y42TJnqB}9mEZtqz%^bQgC1e8wm17`-nKP!OmsX7(ZrB-u&|0I5TNw!8Rs5$dV>l zi}84MHlTTKJ$VgmNeP6x?59@tlo@y4Xg1jCHRZ?jx-v`eI7`mq&6bOzCB5rc9;J7c z_hr2QF1ak{U^|_2y;ELnc1@NS8~pbk$=hH6I60>;-8+H2I_|j$dhOR?Od(UC_dF|Y zN?RG_Vd2Ag%TixON-hmnq`Mihc%$gOgg3llg;RnHVb0?!Oc~kouq|t_b0N%$jaM7f zn#euS&JB3&a^6#byu95$!g~Zu9`c4F3+DOk49dyNJD{18ZW_Onh3g#scQ{IB@5s-T zaP>G7W0K*Oi;6WFT@jQ9RXLjsPluUgbUMo1QGaezej!X^xvkKGAcd6u@j~wkuW&>x zwFmcLPu?G4V1I2K)RvZ@Iv?j#XDia}l&d1GMl#|)e%K-=!Yz#Rv-#A!C)PrLFz0lc}l}&9jePN}T+G6^m%63HA zX|KV7uoOl(5S5|`56^o9Q%3Lr4n8+l2Nl;>gNJY!<8K|DFM)dN@cfZQm6%LK)Q2no zPjiXQJjXKFaDBru49oG&I1svQ&ic2j*XHbU_H@_0fP-FVkE7<1-h+?>odf%Z@9uK8 zIs^mnrQ=rQI&c}}Il9rkjJ~tx8SyFCIqysT1B1RdFAaad{M)gfNF=n}t3yL?d^4Jq zXn;_!kL{(U7h0KA-pGn9y6H7v46ab>9y@pRT+0 z2%5`ogmkwtaO-Fih+#vIJ)F=(fj}VSe$nj?g?u49n#(?fLOx^Q3!w=hh7CRTa3b`d KenF= (3, 9): - import importlib.resources as resources -else: - import importlib_resources as resources - - -def import_roms( - romdir: pathlib.Path, - datadir: pathlib.Path, - pkg: Optional[str] = None, - dry_run: bool = False, -) -> None: - """ - Recursively copies all compatible ROMs in romdir - to datadir using the proper filename for the ALE. - """ - - supported = {} - unsupported = [] - for path in romdir.glob("**/*.bin"): - rom = ale_py.ALEInterface.isSupportedROM(path) - if rom is not None: - supported[rom] = path - else: - unsupported.append(path) - - # Copy over supported files - for rom, path in supported.items(): - identifier = str(path) if pkg is None else f"{pkg}/{path.name}" - if not dry_run: - shutil.copyfile(path, datadir / f"{rom}.bin") - print(f"\033[92m{'[SUPPORTED]': <15}\033[0m {rom: >20} {identifier: >30}") - - print("\n") - # Print unsuported - for path in unsupported: - identifier = str(path) if pkg is None else f"{pkg}/{path.name}" - print(f"\033[91m{'[NOT SUPPORTED]': <15}\033[0m {'': >20} {identifier: >30}") - - # Print summary - if not dry_run: - print(f"\nImported {len(supported)} / {len(supported)} ROMs") - - -def main() -> None: - """ - CLI for ale-import-roms - """ - parser = argparse.ArgumentParser() - parser.add_argument("--version", action="version", version=ale_py.__version__) - parser.add_argument("--dry-run", action="store_true") - - group = parser.add_mutually_exclusive_group(required=True) - group.add_argument("--import-from-pkg") - group.add_argument("romdir", help="Directory containing ROMs", nargs="?") - - args = parser.parse_args() - - if args.romdir: - romdir = pathlib.Path(args.romdir) - - if not romdir.exists(): - print(f"Path {romdir} doesn't exist.") - sys.exit(1) - elif args.import_from_pkg: - if "." in args.import_from_pkg: - root, subpackage = args.import_from_pkg.split(".", maxsplit=1) - else: - root, subpackage = args.import_from_pkg, None - try: - with resources.path(root, subpackage) as path: - romdir = path.resolve() - if not romdir.exists(): - print(f"Unable to find path {subpackage} in module {root}.") - sys.exit(1) - except ModuleNotFoundError: - print(f"Unable to find module {root}.") - sys.exit(1) - except Exception as e: - print(f"Unknown error {str(e)}.") - sys.exit(1) - - with warnings.catch_warnings(): - warnings.filterwarnings( - "ignore", category=DeprecationWarning, module="ale_py.roms" - ) - - datadir = resources.files("ale_py.roms") - import_roms(romdir, datadir, pkg=args.import_from_pkg, dry_run=args.dry_run) - - -if __name__ == "__main__": - main() From 6c750e84c2f13e79e6134272847744f38cd2ae76 Mon Sep 17 00:00:00 2001 From: Jet Date: Wed, 7 Feb 2024 20:09:28 +0000 Subject: [PATCH 14/16] black isort some other things and add the roms --- pyproject.toml | 4 +- src/python/__init__.py | 4 +- src/python/__init__.pyi | 2 + src/python/atari_env.py | 23 +--- src/python/register_gymnasium.py | 10 +- src/python/roms/__init__.py | 163 ++++++-------------------- src/python/roms/adventure.bin | Bin 0 -> 4096 bytes src/python/roms/air_raid.bin | Bin 0 -> 4096 bytes src/python/roms/alien.bin | Bin 0 -> 4096 bytes src/python/roms/amidar.bin | Bin 0 -> 4096 bytes src/python/roms/assault.bin | Bin 0 -> 4096 bytes src/python/roms/asterix.bin | Bin 0 -> 8192 bytes src/python/roms/asteroids.bin | Bin 0 -> 8192 bytes src/python/roms/atlantis.bin | Bin 0 -> 4096 bytes src/python/roms/atlantis2.bin | Bin 0 -> 4096 bytes src/python/roms/backgammon.bin | Bin 0 -> 4096 bytes src/python/roms/bank_heist.bin | Bin 0 -> 4096 bytes src/python/roms/basic_math.bin | Bin 0 -> 2048 bytes src/python/roms/battle_zone.bin | Bin 0 -> 8192 bytes src/python/roms/beam_rider.bin | Bin 0 -> 8192 bytes src/python/roms/berzerk.bin | Bin 0 -> 4096 bytes src/python/roms/blackjack.bin | Bin 0 -> 2048 bytes src/python/roms/bowling.bin | Bin 0 -> 2048 bytes src/python/roms/boxing.bin | Bin 0 -> 2048 bytes src/python/roms/breakout.bin | Bin 0 -> 2048 bytes src/python/roms/carnival.bin | Bin 0 -> 4096 bytes src/python/roms/casino.bin | Bin 0 -> 4096 bytes src/python/roms/centipede.bin | Bin 0 -> 8192 bytes src/python/roms/chopper_command.bin | Bin 0 -> 4096 bytes src/python/roms/combat.bin | Bin 0 -> 2048 bytes src/python/roms/crazy_climber.bin | Bin 0 -> 8192 bytes src/python/roms/crossbow.bin | Bin 0 -> 16384 bytes src/python/roms/darkchambers.bin | Bin 0 -> 16384 bytes src/python/roms/defender.bin | Bin 0 -> 4096 bytes src/python/roms/demon_attack.bin | Bin 0 -> 4096 bytes src/python/roms/donkey_kong.bin | Bin 0 -> 4096 bytes src/python/roms/double_dunk.bin | Bin 0 -> 16384 bytes src/python/roms/earthworld.bin | Bin 0 -> 8192 bytes src/python/roms/elevator_action.bin | Bin 0 -> 8192 bytes src/python/roms/enduro.bin | Bin 0 -> 4096 bytes src/python/roms/entombed.bin | Bin 0 -> 4096 bytes src/python/roms/et.bin | Bin 0 -> 8192 bytes src/python/roms/fishing_derby.bin | Bin 0 -> 2048 bytes src/python/roms/flag_capture.bin | Bin 0 -> 2048 bytes src/python/roms/freeway.bin | Bin 0 -> 2048 bytes src/python/roms/frogger.bin | Bin 0 -> 4096 bytes src/python/roms/frostbite.bin | Bin 0 -> 4096 bytes src/python/roms/galaxian.bin | Bin 0 -> 8192 bytes src/python/roms/gopher.bin | Bin 0 -> 4096 bytes src/python/roms/gravitar.bin | Bin 0 -> 8192 bytes src/python/roms/hangman.bin | Bin 0 -> 4096 bytes src/python/roms/haunted_house.bin | Bin 0 -> 4096 bytes src/python/roms/hero.bin | Bin 0 -> 8192 bytes src/python/roms/human_cannonball.bin | Bin 0 -> 2048 bytes src/python/roms/ice_hockey.bin | Bin 0 -> 4096 bytes src/python/roms/jamesbond.bin | Bin 0 -> 8192 bytes src/python/roms/journey_escape.bin | Bin 0 -> 4096 bytes src/python/roms/joust.bin | Bin 0 -> 8192 bytes src/python/roms/kaboom.bin | Bin 0 -> 2048 bytes src/python/roms/kangaroo.bin | Bin 0 -> 8192 bytes src/python/roms/keystone_kapers.bin | Bin 0 -> 4096 bytes src/python/roms/king_kong.bin | Bin 0 -> 4096 bytes src/python/roms/klax.bin | Bin 0 -> 16384 bytes src/python/roms/koolaid.bin | Bin 0 -> 4096 bytes src/python/roms/krull.bin | Bin 0 -> 8192 bytes src/python/roms/kung_fu_master.bin | Bin 0 -> 8192 bytes src/python/roms/laser_gates.bin | Bin 0 -> 4096 bytes src/python/roms/lost_luggage.bin | Bin 0 -> 4096 bytes src/python/roms/mario_bros.bin | Bin 0 -> 8192 bytes src/python/roms/maze_craze.bin | Bin 0 -> 4096 bytes src/python/roms/md5.json | 106 +++++++++++++++++ src/python/roms/md5.txt | 106 ----------------- src/python/roms/miniature_golf.bin | Bin 0 -> 2048 bytes src/python/roms/montezuma_revenge.bin | Bin 0 -> 8192 bytes src/python/roms/mr_do.bin | Bin 0 -> 8192 bytes src/python/roms/ms_pacman.bin | Bin 0 -> 8192 bytes src/python/roms/name_this_game.bin | Bin 0 -> 4096 bytes src/python/roms/othello.bin | Bin 0 -> 2048 bytes src/python/roms/pacman.bin | Bin 0 -> 4096 bytes src/python/roms/phoenix.bin | Bin 0 -> 8192 bytes src/python/roms/pitfall.bin | Bin 0 -> 4096 bytes src/python/roms/pitfall2.bin | Bin 0 -> 10495 bytes src/python/roms/plugins.py | 108 ----------------- src/python/roms/pong.bin | Bin 0 -> 2048 bytes src/python/roms/pooyan.bin | Bin 0 -> 4096 bytes src/python/roms/private_eye.bin | Bin 0 -> 8192 bytes src/python/roms/qbert.bin | Bin 0 -> 4096 bytes src/python/roms/riverraid.bin | Bin 0 -> 4096 bytes src/python/roms/road_runner.bin | Bin 0 -> 16384 bytes src/python/roms/robotank.bin | Bin 0 -> 8192 bytes src/python/roms/seaquest.bin | Bin 0 -> 4096 bytes src/python/roms/sir_lancelot.bin | Bin 0 -> 8192 bytes src/python/roms/skiing.bin | Bin 0 -> 2048 bytes src/python/roms/solaris.bin | Bin 0 -> 16384 bytes src/python/roms/space_invaders.bin | Bin 0 -> 4096 bytes src/python/roms/space_war.bin | Bin 0 -> 2048 bytes src/python/roms/star_gunner.bin | Bin 0 -> 4096 bytes src/python/roms/superman.bin | Bin 0 -> 4096 bytes src/python/roms/surround.bin | Bin 0 -> 2048 bytes src/python/roms/tennis.bin | Bin 0 -> 2048 bytes src/python/roms/tetris.bin | Bin 0 -> 2048 bytes src/python/roms/tic_tac_toe_3d.bin | Bin 0 -> 2048 bytes src/python/roms/time_pilot.bin | Bin 0 -> 8192 bytes src/python/roms/trondead.bin | Bin 0 -> 4096 bytes src/python/roms/turmoil.bin | Bin 0 -> 4096 bytes src/python/roms/tutankham.bin | Bin 0 -> 8192 bytes src/python/roms/up_n_down.bin | Bin 0 -> 8192 bytes src/python/roms/utils.py | 29 ----- src/python/roms/venture.bin | Bin 0 -> 4096 bytes src/python/roms/video_checkers.bin | Bin 0 -> 4096 bytes src/python/roms/video_chess.bin | Bin 0 -> 4096 bytes src/python/roms/video_cube.bin | Bin 0 -> 4096 bytes src/python/roms/video_pinball.bin | Bin 0 -> 4096 bytes src/python/roms/warlords.bin | Bin 0 -> 4096 bytes src/python/roms/wizard_of_wor.bin | Bin 0 -> 4096 bytes src/python/roms/word_zapper.bin | Bin 0 -> 4096 bytes src/python/roms/yars_revenge.bin | Bin 0 -> 4096 bytes src/python/roms/zaxxon.bin | Bin 0 -> 8192 bytes 118 files changed, 162 insertions(+), 393 deletions(-) create mode 100644 src/python/roms/adventure.bin create mode 100644 src/python/roms/air_raid.bin create mode 100644 src/python/roms/alien.bin create mode 100644 src/python/roms/amidar.bin create mode 100644 src/python/roms/assault.bin create mode 100644 src/python/roms/asterix.bin create mode 100644 src/python/roms/asteroids.bin create mode 100644 src/python/roms/atlantis.bin create mode 100644 src/python/roms/atlantis2.bin create mode 100644 src/python/roms/backgammon.bin create mode 100644 src/python/roms/bank_heist.bin create mode 100644 src/python/roms/basic_math.bin create mode 100644 src/python/roms/battle_zone.bin create mode 100644 src/python/roms/beam_rider.bin create mode 100644 src/python/roms/berzerk.bin create mode 100644 src/python/roms/blackjack.bin create mode 100644 src/python/roms/bowling.bin create mode 100644 src/python/roms/boxing.bin create mode 100644 src/python/roms/breakout.bin create mode 100644 src/python/roms/carnival.bin create mode 100644 src/python/roms/casino.bin create mode 100644 src/python/roms/centipede.bin create mode 100644 src/python/roms/chopper_command.bin create mode 100644 src/python/roms/combat.bin create mode 100644 src/python/roms/crazy_climber.bin create mode 100644 src/python/roms/crossbow.bin create mode 100644 src/python/roms/darkchambers.bin create mode 100644 src/python/roms/defender.bin create mode 100644 src/python/roms/demon_attack.bin create mode 100644 src/python/roms/donkey_kong.bin create mode 100644 src/python/roms/double_dunk.bin create mode 100644 src/python/roms/earthworld.bin create mode 100644 src/python/roms/elevator_action.bin create mode 100644 src/python/roms/enduro.bin create mode 100644 src/python/roms/entombed.bin create mode 100644 src/python/roms/et.bin create mode 100644 src/python/roms/fishing_derby.bin create mode 100644 src/python/roms/flag_capture.bin create mode 100644 src/python/roms/freeway.bin create mode 100644 src/python/roms/frogger.bin create mode 100644 src/python/roms/frostbite.bin create mode 100644 src/python/roms/galaxian.bin create mode 100644 src/python/roms/gopher.bin create mode 100644 src/python/roms/gravitar.bin create mode 100644 src/python/roms/hangman.bin create mode 100644 src/python/roms/haunted_house.bin create mode 100644 src/python/roms/hero.bin create mode 100644 src/python/roms/human_cannonball.bin create mode 100644 src/python/roms/ice_hockey.bin create mode 100644 src/python/roms/jamesbond.bin create mode 100644 src/python/roms/journey_escape.bin create mode 100644 src/python/roms/joust.bin create mode 100644 src/python/roms/kaboom.bin create mode 100644 src/python/roms/kangaroo.bin create mode 100644 src/python/roms/keystone_kapers.bin create mode 100644 src/python/roms/king_kong.bin create mode 100644 src/python/roms/klax.bin create mode 100644 src/python/roms/koolaid.bin create mode 100644 src/python/roms/krull.bin create mode 100644 src/python/roms/kung_fu_master.bin create mode 100644 src/python/roms/laser_gates.bin create mode 100644 src/python/roms/lost_luggage.bin create mode 100644 src/python/roms/mario_bros.bin create mode 100644 src/python/roms/maze_craze.bin create mode 100644 src/python/roms/md5.json delete mode 100644 src/python/roms/md5.txt create mode 100644 src/python/roms/miniature_golf.bin create mode 100644 src/python/roms/montezuma_revenge.bin create mode 100644 src/python/roms/mr_do.bin create mode 100644 src/python/roms/ms_pacman.bin create mode 100644 src/python/roms/name_this_game.bin create mode 100644 src/python/roms/othello.bin create mode 100644 src/python/roms/pacman.bin create mode 100644 src/python/roms/phoenix.bin create mode 100644 src/python/roms/pitfall.bin create mode 100644 src/python/roms/pitfall2.bin delete mode 100644 src/python/roms/plugins.py create mode 100644 src/python/roms/pong.bin create mode 100644 src/python/roms/pooyan.bin create mode 100644 src/python/roms/private_eye.bin create mode 100644 src/python/roms/qbert.bin create mode 100644 src/python/roms/riverraid.bin create mode 100644 src/python/roms/road_runner.bin create mode 100644 src/python/roms/robotank.bin create mode 100644 src/python/roms/seaquest.bin create mode 100644 src/python/roms/sir_lancelot.bin create mode 100644 src/python/roms/skiing.bin create mode 100644 src/python/roms/solaris.bin create mode 100644 src/python/roms/space_invaders.bin create mode 100644 src/python/roms/space_war.bin create mode 100644 src/python/roms/star_gunner.bin create mode 100644 src/python/roms/superman.bin create mode 100644 src/python/roms/surround.bin create mode 100644 src/python/roms/tennis.bin create mode 100644 src/python/roms/tetris.bin create mode 100644 src/python/roms/tic_tac_toe_3d.bin create mode 100644 src/python/roms/time_pilot.bin create mode 100644 src/python/roms/trondead.bin create mode 100644 src/python/roms/turmoil.bin create mode 100644 src/python/roms/tutankham.bin create mode 100644 src/python/roms/up_n_down.bin delete mode 100644 src/python/roms/utils.py create mode 100644 src/python/roms/venture.bin create mode 100644 src/python/roms/video_checkers.bin create mode 100644 src/python/roms/video_chess.bin create mode 100644 src/python/roms/video_cube.bin create mode 100644 src/python/roms/video_pinball.bin create mode 100644 src/python/roms/warlords.bin create mode 100644 src/python/roms/wizard_of_wor.bin create mode 100644 src/python/roms/word_zapper.bin create mode 100644 src/python/roms/yars_revenge.bin create mode 100644 src/python/roms/zaxxon.bin diff --git a/pyproject.toml b/pyproject.toml index ee11f6895..f41256452 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,16 +33,16 @@ classifiers = [ ] dependencies = [ "numpy", + "gymnasium", "importlib-metadata>=4.10.0; python_version < '3.10'", "importlib-resources; python_version < '3.9'", - "typing-extensions; python_version < '3.11'" + "typing-extensions; python_version < '3.11'", ] dynamic = ["version"] [project.optional-dependencies] test = [ "pytest>=7.0", - "gymnasium", ] [project.urls] diff --git a/src/python/__init__.py b/src/python/__init__.py index c117f4937..1f93433d6 100644 --- a/src/python/__init__.py +++ b/src/python/__init__.py @@ -45,10 +45,12 @@ __version__ = "unknown" # Import native shared library -from ale_py._ale_py import SDL_SUPPORT, Action, ALEInterface, ALEState, LoggerMode +from ale_py._ale_py import (SDL_SUPPORT, Action, ALEInterface, ALEState, + LoggerMode) __all__ = ["Action", "ALEInterface", "ALEState", "LoggerMode", "SDL_SUPPORT"] from .register_gymnasium import register_gymnasium + register_gymnasium() diff --git a/src/python/__init__.pyi b/src/python/__init__.pyi index d1c619727..6904abea1 100644 --- a/src/python/__init__.pyi +++ b/src/python/__init__.pyi @@ -28,6 +28,7 @@ class LoggerMode: """ :type: str """ + @property def value(self) -> int: """ @@ -54,6 +55,7 @@ class Action: """ :type: str """ + @property def value(self) -> int: """ diff --git a/src/python/atari_env.py b/src/python/atari_env.py index 18c9bb827..264d49df6 100644 --- a/src/python/atari_env.py +++ b/src/python/atari_env.py @@ -4,14 +4,13 @@ from typing import Any, Literal, Optional, Sequence, Union import ale_py -from ale_py import roms -from ale_py.roms.utils import rom_id_to_name, rom_name_to_id -from gymnasium.utils import seeding -import numpy as np - import gymnasium import gymnasium.logger as logger +import numpy as np +from ale_py import roms +from ale_py.roms import rom_id_to_name, rom_name_to_id from gymnasium import error, spaces, utils +from gymnasium.utils import seeding if sys.version_info < (3, 11): from typing_extensions import NotRequired, TypedDict @@ -190,16 +189,6 @@ def seed_game(self, seed: Optional[int] = None) -> tuple[int, int]: def load_game(self) -> None: """This function initializes the ROM and sets the corresponding mode and difficulty.""" - if not hasattr(roms, self._game): - raise error.Error( - f'We\'re Unable to find the game "{self._game}". Note: Gym no longer distributes ROMs. ' - f"If you own a license to use the necessary ROMs for research purposes you can download them " - f'via `pip install gym[accept-rom-license]`. Otherwise, you should try importing "{self._game}" ' - f'via the command `ale-import-roms`. If you believe this is a mistake perhaps your copy of "{self._game}" ' - "is unsupported. To check if this is the case try providing the environment variable " - "`PYTHONWARNINGS=default::ImportWarning:ale_py.roms`. For more information see: " - "https://github.com/mgbellemare/Arcade-Learning-Environment#rom-management" - ) self.ale.loadROM(getattr(roms, self._game)) if self._game_mode is not None: @@ -207,7 +196,7 @@ def load_game(self) -> None: if self._game_difficulty is not None: self.ale.setDifficulty(self._game_difficulty) - def step( # pyright: ignore[reportIncompatibleMethodOverride] + def step( # pyright: ignore[reportIncompatibleMethodOverride] self, action: int, ) -> tuple[np.ndarray, float, bool, bool, AtariEnvStepMetadata]: @@ -242,7 +231,7 @@ def step( # pyright: ignore[reportIncompatibleMethodOverride] return self._get_obs(), reward, is_terminal, is_truncated, self._get_info() - def reset( # pyright: ignore[reportIncompatibleMethodOverride] + def reset( # pyright: ignore[reportIncompatibleMethodOverride] self, *, seed: Optional[int] = None, diff --git a/src/python/register_gymnasium.py b/src/python/register_gymnasium.py index b39f16a4f..53b40831f 100644 --- a/src/python/register_gymnasium.py +++ b/src/python/register_gymnasium.py @@ -1,6 +1,7 @@ -from typing import Any, Callable, Mapping, NamedTuple, Sequence from collections import defaultdict -from ale_py.roms.utils import rom_id_to_name +from typing import Any, Callable, Mapping, NamedTuple, Sequence + +from ale_py.roms import rom_id_to_name from gymnasium.envs.registration import register ALL_ATARI_GAMES = ( @@ -174,6 +175,7 @@ "zaxxon", ) + class GymFlavour(NamedTuple): """A Gymnasium Flavour.""" @@ -265,9 +267,7 @@ def register_gymnasium(): ], ), ] - _register_configs( - LEGACY_ATARI_GAMES, obs_types=("rgb", "ram"), configs=configs - ) + _register_configs(LEGACY_ATARI_GAMES, obs_types=("rgb", "ram"), configs=configs) # max_episode_steps is 108k frames which is 30 mins of gameplay. # This corresponds to 108k / 4 = 27,000 steps diff --git a/src/python/roms/__init__.py b/src/python/roms/__init__.py index 4fe89466f..8bd58231a 100644 --- a/src/python/roms/__init__.py +++ b/src/python/roms/__init__.py @@ -1,142 +1,55 @@ import functools -import logging -import os +import json import pathlib -import sys -import warnings -from typing import List, Optional +import re +from os import path from ale_py import ALEInterface -from ale_py.roms.utils import rom_id_to_name, rom_name_to_id -from ale_py.roms.plugins import Package, EntryPoint, Directory, Plugin -# pylint: disable=g-import-not-at-top -if sys.version_info >= (3, 9): - import importlib.resources as resources -else: - import importlib_resources as resources -# pylint: enable=g-import-not-at-top -# Precedence is as follows: -# 1. Internal ROMs -# 2. External ROMs -# 3. ROMs from atari-py.roms -# 4. ROMs from atari-py-roms.roms -_ROM_PLUGIN_REGISTRY: List[Plugin] = [ - Package("ale_py.roms"), - EntryPoint("ale_py.roms"), - Package("atari_py.atari_roms"), - Package("atari_py_roms.atari_roms"), -] +def rom_id_to_name(rom: str) -> str: + """ + Let the ROM ID be the ROM identifier in snakecase. + For example, `space_invaders` + The ROM name is the ROM ID in camelcase. + For example, `SpaceInvaders` -# Environment variable for ROM discovery. -# ale-py will search for supported ROMs in: -# ${ALE_PY_ROM_DIR}/*.bin -_ROM_DIRECTORY_ENV_KEY = "ALE_PY_ROM_DIR" -_ROM_DIRECTORY_ENV_VALUE = os.environ.get(_ROM_DIRECTORY_ENV_KEY, None) + This function converts the ROM ID to the ROM name. + i.e., snakecase -> camelcase + """ + return rom.title().replace("_", "") -if _ROM_DIRECTORY_ENV_VALUE is not None: - _ROM_PLUGIN_REGISTRY.append(Directory(_ROM_DIRECTORY_ENV_VALUE)) +def rom_name_to_id(rom: str) -> str: + """ + Let the ROM ID be the ROM identifier in snakecase. + For example, `space_invaders` + The ROM name is the ROM ID in camelcase. + For example, `SpaceInvaders` -def _resolve_rom(name: str) -> Optional[pathlib.Path]: - """Resolve a ROM path from the ROM registry.""" - for package in _ROM_PLUGIN_REGISTRY: - rom_id = rom_name_to_id(name) - - # Resolve ROM from package - try: - rom_path = package.resolve(rom_id) - except ModuleNotFoundError: - continue - - # Failed to resolve ROM - if rom_path is None: - logging.debug(f"{package} did not resolve {rom_id}.") - continue - - # ROM isn't supported - resolved_id = ALEInterface.isSupportedROM(rom_path) - if resolved_id is None: - warnings.warn( - f"{package} contains unsupported ROM: {rom_path}", - category=ImportWarning, - stacklevel=2, - ) - continue - - # If the ROMs resolved differently - if resolved_id != rom_id: - warnings.warn( - f"{package} contains ROM {rom_id} which doesn't resolve to {resolved_id}. " - "This is most likely caused by a filename mismatch.", - ) - continue - - # Deprecation warning for atari-py - if isinstance(package, Package) and package.package.startswith( - "atari_py" - ): - warnings.warn( - "Automatic importing of atari-py roms won't be supported in future releases of ale-py. " - "Please migrate over to using `ale-import-roms` OR an ALE-supported ROM package. " - "To make this warning disappear you can run " - f"`ale-import-roms --import-from-pkg {package.package}`. " - "For more information see: https://github.com/mgbellemare/Arcade-Learning-Environment#rom-management", - category=DeprecationWarning, - stacklevel=2, - ) - - return rom_path - # Return None if we couldn't resolve the ROM from any package - return None + This function converts the ROM name to the ROM ID. + i.e., camelcase -> snakecase + """ + name_to_id_re = re.compile(r"([0-9]*[A-Z][a-z]*(\d*$)?)") + return name_to_id_re.sub(r"\1_", rom).lower().rstrip("_") @functools.lru_cache(maxsize=None) -def __dir__() -> List[str]: - """Return the ROM directory, i.e., a list of all supported ROMs.""" - md5s = resources.files(__name__).joinpath("md5.txt") - if not md5s.exists(): - raise FileNotFoundError( - f"ROM md5 resource couldn't be found. " - "Are you running from a development environment? " - ) - with md5s.open() as fp: - lines = filter( - lambda line: line.strip() and not line.startswith("#"), fp.readlines() - ) - roms = [pathlib.Path(rom) for _, rom in map(str.split, lines)] - return [rom_id_to_name(rom.stem) for rom in roms] - - -@functools.lru_cache(maxsize=None) -def __getattr__(name: str) -> pathlib.Path: +def __getattr__(rom_name: str) -> pathlib.Path: """Return the path to a ROM.""" - roms = __dir__() - if name not in roms: - raise AttributeError(f"No ROM named {name}. Supported ROMs: {', '.join(roms)}") - - path = _resolve_rom(name) - if path is None: + # get list of available ROMs from the md5 file + # realistically we don't need the MD5s since PyPI will perform hash checks for us + base_path = path.dirname(__file__) + rom_names = [ + rom_id_to_name(n.split(".")[0]) + for n in json.load(open(path.join(base_path, "md5.json"))).keys() + ] + + # check that the rom is valid + if rom_name not in rom_names: raise AttributeError( - f"Failed to resolve ROM `{name}` from plugins " - f"{', '.join(map(lambda plugin: f'`{repr(plugin)}`', _ROM_PLUGIN_REGISTRY))}. " - "If you own a license to use the necessary ROMs for research purposes you can download them " - f"via `pip install autorom[accept-rom-license]`. Otherwise, you should try importing `{name}` " - f"via the command `ale-import-roms`. If you believe this is a mistake perhaps your copy of `{name}` " - "is unsupported. To check if this is the case try providing the environment variable " - "`PYTHONWARNINGS=default::ImportWarning:ale_py.roms`. For more information see: " - "https://github.com/mgbellemare/Arcade-Learning-Environment#rom-management" + f"No ROM named {rom_name}. Supported ROMs: {', '.join(rom_names)}" ) - return path - - -def register_plugin(plugin: Plugin, *, index: int = 0) -> None: - """Register a ROM plugin.""" - if not issubclass(type(plugin), Plugin): - raise ValueError(f"{repr(plugin)} is not a valid ROM plugin.") - _ROM_PLUGIN_REGISTRY.insert(index, plugin) - __getattr__.cache_clear() - -__all__ = ["register_plugin"] + __dir__() + # return it as a pathlib object + return pathlib.Path(path.join(base_path, f"{rom_name_to_id(rom_name)}.bin")) diff --git a/src/python/roms/adventure.bin b/src/python/roms/adventure.bin new file mode 100644 index 0000000000000000000000000000000000000000..a092248593d105dc0f79afbb60163bfdc6925a84 GIT binary patch literal 4096 zcmai0dr(x@89(>257uROK~WwHT#O-Nlg=8Ngr+oXGfAs-otkFaX3QVGX-6kyI+;l) zow1!(SHnR>qpP^65Ov+zgIrn}kiqHBNMs-++$;Z}}%qma;~$q|s(?sEH`T{F>4 zW_s@4bARW1f8ROh1JBnJn}&q657{RiJg=m z+ZRNPxWkbW^0jSm28u6`puAla)A#^S_7vH*Ld;j@SLJ+<<@fiHJ*wGp0<6Ce(J?7c zD+O;(oTL2o?K)l#@FjiY_SWcHecWh)QOQ?3;(mu;l9TcCkP`5A> zzlx|u-JqqDIzU$9$3vtJ`^gZlYxH+Mr{mhr=XHD_;iZb1$2&TI&Zxg&R2IRt&NXbk z7S_`7i9VogXVY8BkQQ~8>&(5;^U2cm7}dmCQbV(7ws@ z*u*{irlfs?9C}9yVn41slv$CY27OS$rZCGW%rXk1+_Ha zd2CcOkBw=f;>BYpy}YVxDJ)_^6{rY?$K-OK*cA4n(3nGfeyk{Jk-x2mym=_NQbB(4 zLr1jUk3Muor8g#}3zJgir1a3dbZUZJ*M3dHYPx1obbJF}2ZJL4By~?o7w<}6QptWt zGE?cdcLN-weKyBvWL7GjW8}z;)H(x{0MD5I8L6F-;c_u4;M?y2R#JmJTr8z7DBQbW zGK9ZYe|UcpM+?byRj1D=X}AV|s;tMm@GV8cyYX#^qY0nHr|=D^{E=@Icj#)CSioO&neJU9kh+e;E7Vnn> zd1N$kluI^_;?bh~D^E}P{(I#oQrM@4S!5%NTl zn;PN9?a*?_;|UD+P_vU_z{Tt)YJw;KZWpgTNsg!+sEgF}p3zp3T_vNPs>N5JE^Eyt z@0N`2R%5NAn$_xnQ*f9-H3{qlfqHf!%>aiEh@avbatrSwxAAUrIP1l`xFH}0Yb5=y zjJjNfrJF93%SBzPTXQJE-Xqk4k0>G@f%gWOJd}vd1U=!Pr}l0lI1@Anw(wlI+jvmA zJwXntFZiBdeE{y#W8@&%Fe`?n1#%D{X{-$x$zkbV)2xA%jPAlm*&T<-@iDpa$ViZ` z03&dBYWCFzII=5q<52)S*`2xZ7yzW`jkPRRW`M7(5BDL}NtcJ}JJ6Mrb6QphuckT{ zhg;|TNLlaalt=x%!X55Ia;U!Ap#k?bSBjGA=K}`MvFQ#@6?`U@whEp|f5A}=-H2G;F5YE*(UsgsFcmR8O*4w43q?-2Y#=pri= z+>x81n{DTt``XSoL6s2FtnXa{-Ah!Iem5hJ-5g8swW}c6Aj2j%gb^Ae?`QNIxPgH734C6e!584TTns)b{bxo#9SYTi!l7=Y zbl+nMz$#0yl(ULbqlA$%d>?B~g4NPe(-Lku)6(A3i>d<92b()YxC1Y-hUtZkLLPBe zIgKw>4N4g^Bp{7XQweyhR;?=H48u*~W3zsd-12keHah@t`W*O1RefVXT9|IES6|Sq z3g;IUA<6I@omDyNQF$$=Bhi97xXcQH^{@`UWZc5q5r|BS5JJrNaMD|z!h-=mX+UWh z3-zri55C?qNp{5?em8?&-vq@{*s=ZlV*32j*@XR?to%kQm!Ubh676)s3E{XMMwwa zfIH-~?cMFY?Wfz%w1?ZF53v7hphKu;Y68tqJj>^yuU&!8H*UjMp)XX}@F(P|N_{5S z=&Qh1xBEn88@?)&8=3)U;cWT+8)SrLz#iwTJbfJg-u_G#X}M~kzX2I+#u?=}3;qLI z2CH&KXyp|k0Rw266;2>4Mz&sS$IC!~1(_wo2~UWu%}=PU74S0Xa#>6gtayC$RM*ji zgX;DN2?%7qk7x(F3!(deASSMenwLkdQq++bUE+*pW=EZwQ7JpR!Wmte7kyNUx|T=v z714#}2On@^zuyvIpfI;qE47wVD=f>9%SDUJk|EQWPEi*v8$hmpItf23a#?Aaoq{5~ zu0+(upi)ZpQY4hp;!;#>1u+n2pg4w#5uIe&Cmi)7)K7&0BmfauAkYt%5!gpvsH_LM zy6%+$EW32Joa{mZ5c}yuhD*qI2~Z69l_eujTesT2Znb0GYNQ)t`u`a$)?^$AS*dQN zLO(4Ypx~)kXHh1$C=n$fa0xMifKGu`W~EHU&JqUCENxb`1pIbuSm?}(Ky?8exym2` zC{tqlzQqDi5|JcbSgBn{_Ab{zKkDjdE)tcxH|w9odSE>Joxx~Gw2Cln2#X|`Rr@t(rie`j_b?g+qU)e=o>a6&dYgAH*D(Jwrv|iPsh1UasKJJ{;RmrKDl5M z#k{huw|aU$&`ZsGZq(8K-&HI|66KYRlRBu{XA`uw}FK`mxMOsO*D!kG=8h3=BtSX$h}-Q3G~ylMBniG8$5l5qHO(ft z-jTgK?KR`xJ(jNmpI3in*)tLfiC(kKV)5E6HnYtuLU>|wMtSQikX{`}`Xozi%a+p2 zOcn&UfY5q3Hx(1axNZe@`aaK%>Uc+iZeBL(FgOJEywMPY}Pkphb#rD4!h%4)M_oT*;DPeqKGfxoTBqq zbd{yRDDd31c>w7!cE-R?#*&PstpHXm%~-m)fq}(K7cc2%tR;(cG74jxECpGaS()n? z$X=Gctb_shX8)c6;A9m=0qlg4$UWDz+x#YD?d2Shdlfk4jDo{%H>9QIS}e|`OLMGN z!D_vNxELQ>V>Qi2`_P?vs8}?rCc#4V!zB1T{zd%DcmyGSVHg<}UP35@n$Qp9n`}ry zCjqQ7q84-t0I*J@&GEl7${BPyHWaJkO=nRo7LNg?I{|Dq)SCd{D}n~lk~o0lXnXvv gxY>-t=<7I*M~tW)*%s^zh}nt97nscbgCcnT2e5Eq0ssI2 literal 0 HcmV?d00001 diff --git a/src/python/roms/air_raid.bin b/src/python/roms/air_raid.bin new file mode 100644 index 0000000000000000000000000000000000000000..6adc39c12978e202594ae980be7deb4c01769ae5 GIT binary patch literal 4096 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 YJfD;RYpda$hL*v8wbp%q!=V2E4^Y{P%K!iX literal 0 HcmV?d00001 diff --git a/src/python/roms/alien.bin b/src/python/roms/alien.bin new file mode 100644 index 0000000000000000000000000000000000000000..209d3a9fe4bfe8cff0f87e84c5dfd7c0cd3dcf77 GIT binary patch literal 4096 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 literal 0 HcmV?d00001 diff --git a/src/python/roms/amidar.bin b/src/python/roms/amidar.bin new file mode 100644 index 0000000000000000000000000000000000000000..1a75863babcff0139b0f2f7d23759c7b394c05f0 GIT binary patch literal 4096 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 Ja*5KJ{2$2-_J05X literal 0 HcmV?d00001 diff --git a/src/python/roms/assault.bin b/src/python/roms/assault.bin new file mode 100644 index 0000000000000000000000000000000000000000..da2ea47f12c5cedf8270b127d3b91f418afa1272 GIT binary patch literal 4096 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 Ijg!y+096T#l>h($ literal 0 HcmV?d00001 diff --git a/src/python/roms/atlantis.bin b/src/python/roms/atlantis.bin new file mode 100644 index 0000000000000000000000000000000000000000..ebf1b761cb876ae40efa3b715e1831fcb9f57b3a GIT binary patch literal 4096 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 EAHEUN&j0`b literal 0 HcmV?d00001 diff --git a/src/python/roms/atlantis2.bin b/src/python/roms/atlantis2.bin new file mode 100644 index 0000000000000000000000000000000000000000..387958ae250314d0a04ea6220f91c65093d2c28a GIT binary patch literal 4096 zcma)9eQ*=k5r4WnNhcxDVT>b;kOYI>*dakf+eD=)aT1e&qa>a*A5%{hvLWR#7As`I<>GJ6F^2HQ!cS5r7>j0m$AXXg%4w56Ls%o znU zE!wn|*}I7H$l4r5oiMVK)7y%n2I@HqlLKooq$^P2M_zZd{IJ z<(&5|K~vrfcVq+3-GN@o3^zeR>TPu~0@vUw48sWAf}IeB51>D4i1cNIG1}|qbhK%x*-?jxvxSm+?wEslHhQ#D|m!vWsX{etx>b&jJzYg9`R=~!cBTju_EQoh~Ku_ zjD*|nmGq8mp_^>0b*@1N_~oCWg61eA@`;^Q*pP^NnibB zhDb!7)8TQ@Z+`KVLuNZ3a|9Y1Bgn}~JnYB_acLfGhDJ213Gtgnf8w;leQ{c>Z`^_q zE=hVYOKzBsZsTDFSS1#uT~eaOGsLHFhjFu_#I@c*C3BpHB5@jy>{e%^x_iX^Nc<=z z32EA;aI^)wqFf^B`ox*ZGOZ)H;pO;yK!aqVN6QQ{BQYyQ5Jf_>3SXvE7}1)_222Cr z$o#+;g^T!cZKv&sTJr!c8= z^jxex>4fue0TG;)7to#GeWb@=2yRFr$iihjrdOnA;7r=z$_$*PXVRbJ`%Dy%ZE!k0 z`$HUOVr3YZZE={J$^J$j_P#PpGj<+?;eDwa4y%C_b!=B7=RiEMJ9T*1VPMW5mYI8_U5Q1jfBTOqZ9+eRG z3GaUjwV@t|!-e$N)Qo&@MpkFiQ!}64oB2$g$q2JF6%P72G}~755E6NLrbe2Eq>xH# z7D-@Qz=9xm9Ttsyoes1SL!~VQbK9E1K8dRRK1ZfrI5)YGljPvxl;mc;g@wR;*9VdS zW|5)2c$B(iT?}_php&KB4NiK#WE}X^VMLC&@eh|qI-UxWPgYT&9Kk`u!=ky zhr)uo{!FA|*y8o#lB!`ojX(q43R~1AMdQ9l9Qkz&!Ksx{dDmh`y)cBKDuM~cG4km} zL_P>sDz4;20}7ioY*ZBSq~^%HJG!&^~bH=4W+8uI4mCBfcK&2s5NHgm1d1LNzIi zDz@KK(apA6Bn8>6$eshMY7DC6CscklsS<7B?kRGiA~z~}4{`07bl*KB?ZSlkFk1Qm zs-2PdTmHttu}Sq>y)cGDv$PByVnlbaYS`o_?q0&9sP5H#w9SQSv;<03(9$QG^Gwjt z($Ka6EoIv3!QK|cs0hH8w)OEge~e;$1m_0qfw`#$;pD`^C@(oN!P8#M-@n9r`+3Zz z92}9C&?CY_QJu&HjvU#f*!TJwHzyLsT2`S1NHUR3cZ7M&1j$H;LNdaaqAto}D!bD` z1pUe!X6dvk%zBs1>0Isb+(}Lmxz@FTu*kOekg}&CbUq~4w4Wg9YI&g~xccpqANqr* z)QUiIK{n9>-3T)Y2VvMFJthk%WvbZLLnIyY+N8xW)Dmt#L==;x!}O^t$P$?ec1dJg zk|_Fmp{XTY?>2e2+A{9j-XWay0QeXfIh{R=#vXEt0^OW z>RpXym~ij1GMf>OqbNsv1=d?vEV4)qx3>_5^)4*l6;AY&u@E{EQjDpvpm-g{NZ^zk z-K_kAcDE?XMZ8*^@&!!E{@Fc|o|J+k*0$zHZVLY!-K0tL(50qzIrT-4#Hj|#9f+jM{SOCw^@(*6c1j1M*9_^4B4@QBT(LyEboDjrJ>VQQNlye}{ZIW8%}N@tv2 zs;Y{mE=dL`l3%2k6(Mz*UM{(!@Dc|_QM^Uzuu@Eiq_7 zhNX@J{aB1UP=p7j2xe;>&M6K@&=x30YZ&=g^m_Vv*-1y0Sy6%&o*JKhsZm8gm^I4R zqzcTtJY2z&^fk2{)j==4XN_wIztcC_jD^+Do^qqPUpoL zQh{7Zcd4EBLl$j>FTqt&%iG-5Yjmm^YR9oGYcLbS^So)AqGYpKo)-kcYIQoDSTzzj z181Qt(xL_$oXZGTg5y#W&Pyq{fS3OOUb+|Y1|O^)UD)fMXLUJR0eR6j<35A|~a{f%-XquaQrfi@%s_|_q;0jbTRXhaSI5uVkhTXv6EjOU;1p*0t8qRGgrFa;Yq>c3w4#cLYK}@))Vsh zqmPb{*VKIasl~G4&u`RJEzN9rqo%z4x#wPQ@qM}UWn0s?nM~8mwxvsp%a>NwVH-cT z;ds??-|_I8+BJfWgne~Bd~qDZVapCGqfKIKx7-x_=dqJbufFuc_Gk2W3G1qGRj;kz z(W)0_yGqra+Dosg6Q`=H zPgS16K|J3Y3E9%v7`fTl_&Y*MrC99#`=wGsY`HZ#8*jZP^uz@7eeN`41S? UgAtcngoPqPt#6~ahX_RYFEFg{8UO$Q literal 0 HcmV?d00001 diff --git a/src/python/roms/backgammon.bin b/src/python/roms/backgammon.bin new file mode 100644 index 0000000000000000000000000000000000000000..7f5c28c715fe74ba96cc5b6906ca4d14b2856b73 GIT binary patch literal 4096 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 Fe*s;;=GOoK literal 0 HcmV?d00001 diff --git a/src/python/roms/basic_math.bin b/src/python/roms/basic_math.bin new file mode 100644 index 0000000000000000000000000000000000000000..2ad8007a313e6a17655d0a8bb97d912b29ad6ee5 GIT binary patch literal 2048 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 literal 0 HcmV?d00001 diff --git a/src/python/roms/beam_rider.bin b/src/python/roms/beam_rider.bin new file mode 100644 index 0000000000000000000000000000000000000000..d8292a128270297c9a0c4e5903c2b7efc09f79d1 GIT binary patch literal 8192 zcma($3sh5Awl^R0Gmwj*2Eh`LQUX>}U$s1kr7 zk`p6}URz?J_05Eo%Wz|vcR|uMw6zagp(J&6ZWS1-qO7Sv!uKklf>87Jxe4|&)3x4# zoa}SX{-3?~IR}Gotw$E(d zkMZ}JcL?ZeSq>g&37_?{)v%N`>`oiDyN8EwR9mf9_zt+={mZFSBg4eqmX$RNCcKuO z5P}u4$+3zMn6q_%iZ(o(5DS1r9nt)* zIh!iX%XemG=dpwVMrLHr&ntX6&+s4%e6kUhtt)KKZ+=h*BOPJ|&LM3dPF|Ui5UUFf z(rjB(p3Ede18vs+;{91+A8503igWG`_OIHO8Ae*YB#aT8pa{E8G3!HemLMU#BG6zM zMRyP=-6Xw$NEgwM(oupfzKQq(-9(9eM9cK9n=l#MI?_Tk1v!*q=eMJj`aX9HQ6I3k zOlR&IB7!KE{R%I}ve$zs14AixKC`|LCjNeRoCZYoftG2Q{PgZ*4CaKV8iXl3Kz}lw z|CVSJCSL!R=M8~}y@T))uuyWB&G~}d{?HO$0F{s!#iDduJW8O)P$Y^%8ctO+>KRAl zOpufHjuzxH1RAsRZ=j40VwMt{62$g%^GsXX0b;}`@!TNwyK@8V3X`E74{hy$^q)34#M6BJJ7GChWqXH0q%DK)&TnwJpfn(rd1bh z@joKvh3`rUJC0HPON9W{Meq-SMneU;1-Y#wpq?q~B5cM`>&UDEZ~XJVV0JarrGgz* zV23NhM}E^X%?{+2R(Cb;wGh?UaF1r3YR}>Iqf~?nnorLgEt1Nw2!^Y?LD=owjXJBC zoTi%s8k7vtm7d8gt~qN^@nT;}Kk!3y@uXFR?nSBAcxwWZq6m~kzaJp2$!IYm%c6;z zk1>j24lnLc@e4FDPC1@(sW5|#`VkagKU9`ZpoIDX6mK1^??KZ-Y*tVA+x@ztYt zvXj|_MwqA25c3=wKatk`6zEZsgEU-vW5A#Ax00poNuH1UBHPtG$Mp&n< zJ=Wfui)Z@2L<7tYq@ddk5|g!)zKN0oB+}_MwTuFjAT@mp1u_G2)Mp3%NEGc<+wlTJ z@G|@j;SLD~X)Z%@7h%&BUU0qORZ&vcoPf$EC?B$>k7>t}7z0)XC53#4 zbpQ<#Lh`jJ19QU)*vW1Pg(hr|l0$wFs=>8Q(Rh}Z*hx`8nA8u9Kw?NkA(L@Ncc3A^ zkOp075C+321Ka^}fmf1VgVKRp|96;xR|+wgGw*O{kqX@dxhSQlc`+yz@3+lhvsxgB z?BCA2A)`Ym0cW*U5h_z?5xR#SLNa<7EX1yPl_8+)02@fbbAtc~p1>8YW`-deuQtD$ z-gpH%_ZgN$L*U6DqJtmlV@QXUcGE}oli+%6ENP8quES=ior?NCA1CZ9-bT6@FWVw* zQ8tZAzqF>F7c*i@w%~Cd=@=3vVTAfV6~?xsjL=9RJ*t-zu}fX5+7PrZvhzX?zJd(^~1>_Bt`7%WPC@0mb`lQ^v9#w z3jkaWU_{2w3pO5adhr?>YI?H^jWnI;Lc@BwndZ?DN+e@#IIZB6LmA<~k7jrGL*@^J z5wt%1}6qiE_0jMNuKf}-_(E*PnXhh&VVmSM!NLWl-=&9o!Bl$Sw#N7wh&%=9B3 z#Ae{&oh9j==|4BlO8fE2N?ZsFP}$-^MId26$3)S+Xo60U@e#7BaxD))dk-ak^Gfi&`xZPpgrk}mj{32V_l#R#U{_+7V1+tsc-xRIk&J_)9Jp&L6hLsHACad0S20#E z?$lw_(SL>7J45p>0tl>E!)UQTL_;K-hxrVI5fXlgfJKJ5gf}{bC~Pzd8;ueWSs(az zqr+NalJ9SHSbz%GJxmhXXcA7an#6-L+p7;A|eyx;^GKRx>mc8 zh|AE%Z6uE3`U)a)BLJyMN$_Pz!9OcfU;?Vjz(cz55<|Eh#9MyucIK}bMu)7Zv9%qJ%!{pvKH`;fQYMJn zbuqV$rh48b^K=-gM>{(_*Lk_N$LAeoGo1w7DWjZXW^YRuCu?iD#);aje%j~gqAxf; zqpvzT=w?R~-FCyzssisjgKdEbcDllG)+O?B^|nDLe;z_nY%A$4=3;tFIE^jNwy^Gi z*hod-g>lm-d~nHRFkzc}OZ5#iKv4v-oY-N6f@t7P={IdXbg?nD=k9NB4}Abm{&fdb zsRvv^oo-B@h$;krYv>~uxo-QkEBF@8WCk`NzcJshduJxXkB+(C0$U^4NEhW5adNs? zui!;~+fiM`C_RXjV7V!g*X!VxBu7QqU&VfMy7vH_`H1-~Du=pkMRukHl`$IBZZx8c zbkU@RxWtN4k?zx*wWJvCC#mdhMA<96a!Sm|HpoFoWUKIMVYI+9A05=OH|a8!K}xst zEZmunFhz(#_0FKx*;}GZ{8~5Xj%gguQkUG}_L0-o?FOaen&V@~c|ZTPnJ$96YG7TF z5ljM`@&jgT;R}EG!|~&9zFA)0`^8%?-|!tf^v@66&GanvtfX-#9v&F5MjO za(ve0pNR-SJo+7Hdq;K+60RUU3@|_m1_4{%Lk6L-E!(C-A+D2H{#cru%SD3435&9 z^l38MCD&S>V0D%hqtSgvImX4g2nJHI(L2Q|=*GY`?-V7m$lRrtv*?yfMpr1iGXuKX zrja}PJC3mmA9u?(=1pV8tz(REeMh5C(yB6d4Rj4m;UC%xZ?*(Z4ZVVs2_wSh*XH}( zbU?3Z=~1i;7@=)Y1mtPED;}19=HuL2Gu4OlM0CxVbMTF`$*ofiLUU9Yfkek4RcygbL)Cfvk#jdxyj1A z|3OKN$l80Fd!Y3+=%nHF%uZD96zS@}Zbf7Q-GGqk(VM+ht{A#Ow{Xm>prqbwl$3c0 zoyH*!!u4ykmidC1QC;%{)WAyb=TrjCSR%%&yh?Vi#|^Qj_QvpQy)sJaRkQFnhbx8+ z-4a&b+U}HsAiAukos%lFIT;@G7%K}toJ za5AO>mG>So&AHQRZMD+Y4Mj#sNtye@M&3H;HFCYcs2lP`N}uKu>x#rKsftvv^B9*) z!HJn?S{PmlWz$v`QHILsGQFD5^eSkEo$rjWe>wFvb(^0INDza;SbnbADQW|dy$ydy z5$5JJO?{0Rpc_SHW=ziMU5lL}T20OaS$dLN=xTO8L0{yfS}(d%o$F}^)iR$Wr>)-V zFoRYyrn%-cr=simkG=hsk z(6yP6%n58aV@Ic%C^X2Fp>iA{5HU{4BweJnkZ@*H3V-DYG)onbo0PaWVCN>%c!g7{ zN(Jz?HvbShM3?3VP$_-bLZHKx0!R*_Qgrx|8SVjD&%9K0+;tAF!cItUoKAc=ch9x6 zTBgWR1*a%PVx>jwaMK6$ntYq1!Cb-3xO*{BJ(c>=?AJSFaQcQ$Z56X9rZilIK^)R#9^65yXJc5;}ma8x>_ zo+^W=t=wHrHGMKuYuY*G^Z9hjX}{lZ#zlfl^SF1CYF14TL8f*zu+e6Aj3F|6LPK86 zj(8dzRi-&p>|&Tp1{8iVm1L&jt!w1(XA)U4)4jU^EWu%F1}+$dx(-B@jmPN`z~bbd zO5M6K>O53$qf^pUNAMALa?+3mN&hLk(rc!cTjZ4|2~U+bi@(o%j$d3^O<4U-$vHo- z@>FW4P7>Z*Y?PfGVeUl^MqKT$tRotax0N3U1H4k!X5_NL2ty1T|ajcSQND} zT5c{o1XJQRnBpbuq`i&NS^bU2*A444q2lKI1CrXNiLbHpI&23#xk4+oAU5Cc?PhP- z$qB=CsBxmrb&fjvAop|Te$GfAG+JV~2u7{VKL{1H7V6nda{&Y#9AeF^g>clF8|w^; zwhHfdPQ}aMhH{k?Zzyuqc|33k%rT{o=^na0QDCl*gk0^fZGE=a=04AvAR7Hn}T22>v;~)>>;2ssw z0Q&u&YRLKfJRZkMynpxLA+rQ9+8rKG-G*jt=1JYy6d0rHyUpmdyH7eiW`n`7{K1t( zLZgS5cY2zLX*Ae*vhyTVEl-_=fZ8dBB&ai&uEU4fWOW%CQQdCmrH~QwTI8S1_g`u$ zqQu-axV5QB6_lcOy2`*o+#LMIQCCshH18YaZ+h$-6xih_zyuvwG*_^p`E$s~&mAY3 zEYO0Nbb8E;!t}sxrcB3AHS4Hn;Zzl3N(m7NrB}lL%ue#<9>~gcy~rXaHB5Fyg+myp z>m?RZt>99p3=)c$LQ3iEr%p)eOU`bGr@UGafPD$~7PzlapL^=O+c;Tk8IuXQ9#(G-s0TSA^*%@a1t;+V zmA;v)W*&5|gcJ0<^Amwl5eJckZw);G%csa+>f7*|K~kzLf?y(eNxpyQ`T#Y>%Z>1L zi<{E=4U!GPOpD3;7Q5Io$E#t*0hJq*1F2qvLtR5jd;I~8aEVr#37Il-?t=K_v}G%P z^69j0?h}p$`b5Wxw(@eot<@jnqx2f!f}FG<|89(daL`)wq9!= z+({+x;{SXgG5kfdRD}r73AjD7e{aO(2Ah$sHL_MCi;UDV7G z9hvXud|`cEq%9}JYE99f(oT|lt2HIZ6gIiglY19u?VB@aj(Q*70U~E{?f$DlV*kF_ z!3D8b7nCRyvf|>%{riq-iWG0~{O`9+{w8m4qFk|2`jT9+MS5MXcv>Cvigr7(e{zo~ zGtVk2*iP(S^85GpO)gVB8VrugW^RtYW8Sl8&#SoYhW2}Czr*df&|Z1vm6xHt1ntEa zU&PzLYuBz9UU*?AG>gSjQ1I(t{~G@Jou8lo{PWK}_uP&h&p!LC*}Q%G_Gg}X`sr=k zo_cEQ)?fbe$tQpDi!EC=Z+>FarpF)ud0yVne)ibLji#Ue)VLux_tEw1*R6ddC+Fep zHLL$4>nE%J{ogYmTKV9=Eq}nUZ0Y?=()DTg-J6n}q`PNPJcP%*m}oLety0LOVmQjb O)8IJ&|F+Qk|Nj6h!tIp+ literal 0 HcmV?d00001 diff --git a/src/python/roms/berzerk.bin b/src/python/roms/berzerk.bin new file mode 100644 index 0000000000000000000000000000000000000000..48854dfae6f8e9462a5fb1ecb6a3d06fae83b23f GIT binary patch literal 4096 zcmb_eeQZXx>qpIPO0v@073G==bRvZ{N7kT0Bg=h*}T zs=7b6`()p9&pjXa{_Z{JT>g`3vP=2+7v!-q{3B1M+0vf1R#{e+(t4FQ3N+5nLP7g} zcwkHsJiEv9x3hbL1MRzDwzsK-tN5rjLd}6GLd}9PLfuOH1~Q2Rg@HL6M|SnGvyj!^ z1WN<)(8<=wlL?UcTKQf^U@m4uDWKuu`#o(&PrvJDD&UJ{U&~bP$^fAl{6#B`M zJ^96>dYk54hliHp6DQ|a05rc}?)-HyTcG$Ga&lLEi;Nhw$hh6xn?=uR#WT>tFk}h| zFbdkhyTFAWdt&T)v|o_) zj+RmP_J^KSf@NOI)Z4ekO7=z6Pw5UQHi6&aWIN zSEC78Q~c&tr2UkSAx8)I#^t;kc8VO6v!k!dM$5BX4VG0~H8!>7X_h6;QIkx&o83+B zeBl-xLXAd6qd?I(C@)407Bw;~wyipw#`4rwn$36Txf|U3QQBj%m1d9{D=TXR8YW;R zsf#imGpqG6JwyC&rldMQWu;}ce+6Ql>z);ikjJ06K_*bjN?col&yzN;QrlhcDIoO< z(dNk}gIr~LcdbWHYPrhA-Gjjl%PO1B@+>a*2{sdPtrK*R1C#vFb%g$!upMHbR?#Kc z944zt%t2-y-k1Z3Pr9^I)RHQG>2=r_sEeG8!z{7eo5SA6 z*yr}*Dz=Tqp^?;gDX-=z(CSk0P~E+LP8BZ4wzolVn zkCD%!+r@|QJZ$pvz{D^UT!tWU(gcD(ed2O>ECGYiU&%smSV}+}e`B0XAB_hng)dT8Q!DZUl6R~}>QNZVg}KYB>5PN61N`9$AH|C|J9Xj{N=%BQ=8H2Uqv9ADVOifIykO@#jXOS?jTOaDc7&{jZ?wnABLVYqVNfQI1foOpz z(|meHC^hHJNEi51H)F#(nZcg?8L`zQa|^?_Pm-@nT}obCoos z^iLhbUqfeb&Aflk-e>917@hBFG z;8CtJ1M5Yjk4|ox_Nh=^?@R$9j$_5qu1ql#EBF*sK!BPe$+*Mlgq2 zYDv;yu2m)Lq@$^?=V;GSC6Jq-i5yjKFQbIrl+6hF82Twi9W3!{PYr}s3Gc~1cEYbs zZayc}h4S9xHBHepk7iWuNVN1eeB8%g#v2}YNv>H}forMj0apzUrNA7 z=HJaI+Vh1vFEAO_4?Bz zQ`4BN*r`BOONINl?Z;Ee;za4)+zCG@a!-^8v!&PR&UO_0nx`(TngPBhi&<$<-HWYh&4^VCqve#+%nMtRv*2&x+`aSyp(Htc z@3h(c_~VbKSocfbFL-zSgOc-;iFxAx-7k5-^2ifEUj6fRWt*JuD9u@zY0D@3xNwHWfBynEy;hAKwz+u*&iLDvH)pX=$mSR+kjkXf&%;*Im>ymNsN@ zxAl{q?oKnA?vN>^g<{`N|If#A*W*I!t}~9K>>D>Cn1vLDbnyl|r+ee&6wTSW@f{3B z*)10P8A7fVEq|<75n77k^9uIoo|~uX!g>B(^Y>7R@7;}hVY^2j2ik>%r7qdFfgo#=R=~s7LK%MjtKi9W@1S zITE89r394&JvQbw6LSqa>t)L+N}pX|dGwi|tX;o;+?CsOKfrR;EA zF=bCpJ(D7ogS+kP4b5$$sN9~ijGPovggc-t$$Px`rD_XB{nA1y{Y&=hmniDtSDh+~ bp&brQt!`gQN%88^^7lPKF@+N)Q`CO|V^@7! literal 0 HcmV?d00001 diff --git a/src/python/roms/blackjack.bin b/src/python/roms/blackjack.bin new file mode 100644 index 0000000000000000000000000000000000000000..924e8043637b9488de54e5ae8f68a966c4ac6c34 GIT binary patch literal 2048 zcmahJZ%iB4{oNn?%!R;_0&#$(7iMQTYBEQfwoFY3Tf-JCrjn_A*f(okq!4wQSBbHG zm}aVbCxcLJA>YK7Nx2;y<(#ECqa01z5h0W^pWC5FurneNjY^JfP?ep5V5T%jMl;_OygLh?>rj3-m^kJ8*fg+u(KEq;Ad z8-%qQp*Ud;WJOwZKr)@?p2IrGX?}QA>wr8Y;c<Y8EHv|$rgM=Iw zQX0YMO6q3`{`J!2=lr>n`gubBHYPL@&T9@VgY#RiD{|vK(&q{wQmz&BDr!oh z)J^Y}SO4*sWhos=b0wt_r_DX<{*fioMzce)0cGC3|{F9|9%D(Bayjk zJ#PSC?Mqpe&5uMd%L)YGbRb|Gyin!-E~aEUdAn}rLUI9;G3F)OlANo%3!9Vo63M^R zeJr+MbRM=`hRy2C|5b@_cTz&L&=Q3BNcC^fsqJCA&!9`8WspqyzLNBdsi`|@AkC!n zsWi7CNA9I`GN0OoWbzhP{TMnYZyKk&_SmGJNKRwUZRk{Ipi^v){8vStzhuJsq&_$J zKRWvDm4yXA)txWNKxoDRR6&vvKwO=iqE$$$ySS-#a%djC3 z2&_?-bIpldVuSXox6oG|fCncF){(SWKnK*4Ng-BUcNDSZeK;tVh6Ax%-LuAa`Yk{BNP>jUkMs3qrlLlbMs+S3qu-KI8 z)g8tnB2w|uxP&FD)kO3)V+g&9yG}|Ew0-g?u}UZ7 zQE3DR=g7jWU33}W@;{9sX0n8+lu+;%SlGA{yz};s>GV;teThmQ7L`I@3Yj4Pji+JkS`UL#-$j(2-@a9}9Cavp6a#d|?=iF5}iT8lzp53svp9jc9*Vimqr5nP}7NtOIJ!f5`Z=Ibgk_ zIc4C+&L%REF?S#B`wC|8RMhMHagH&U-8QR$NQK7vcl`@41vMvZTv%4Gv-zvIBSOiZ z+$++y5tYi?JFu{DE!cF(4UQacAzF_eKY7&aJHdO}UL#0~b^yGB*4Ebc*47r#&hzag z*$(hhWA8Lgw|hLiP1_=Xl`1<~ffqDpRpj=5^0*Br!5I&>q-y|q<~5JUa|X0}+Wc*v zRuJTl?w$MXxxZ$!yL&Go+~ErK^z?*y8$ul+E)?SYT!`aBJvPx13ia4r3^vw-rFc2m z(9m!Y*bt9z$EBPnCp>wQ_%QK=ao}&Dx4*alXT6=Bodfv)ffqwR5ByvRziPP8q*i4D zM9Xaa(dqt&(!&nM;czgOlo*;J7>1w-h9DS+P0$QJ5F;3D%z>qNX+LHC`(nj^7{lDe zMDD|lCy|lJ`=K6=JN@o~cfU7qzWc48o*$_H<@td*5C(JM4xV$-B;l$7&Tmzt2R#FO MM|!YNVD**%1JVGnod5s; literal 0 HcmV?d00001 diff --git a/src/python/roms/bowling.bin b/src/python/roms/bowling.bin new file mode 100644 index 0000000000000000000000000000000000000000..18b397cb8f4c870c83a5d7f5d29139811692cbe6 GIT binary patch literal 2048 zcmZWqS!^4}8J@j(tSE9NyDC-7Hl<1q=v;IWv~=qxLQn^lKomWIv?==F7aLWd{Nkc1 z@<7QNnxUnVu(LrNB{IFbMSf(@@DGIh(5mfDsQJ_ z-}>k3JkRa{i^GrMKH9|{5HN8Ix3Pu8ID%!nLptnx8mJKyhl}DzS>-d*!r@Gt=nbXd zzy6dPjIod`pVUvb%f?e~Jc4=MoDXA@64diu}B+`5K%f zq@KM4*4sD?9wK_qv*F;jp6wZv+PF;s3;2xa=O64@`pXaYENnHM-*~EerpK8s8_O;P z5Yf+hO8>));Fg`SbM`s={6kmY{IQ+2Uw&vj^?Rq`D+s9%?rd{%c~j$zizqJ0vYPRT zkec;AiRtM^fN`>#%Qlt2K%g1p=im>po^mC~50E(^A#tr9s`C#PmVVBYYXfeO^CALS zFCu_+&I4hew{tyM7LJfiTwpq62&$!ExsRk3yx?`tXqOoLJAg?(Pr5C-&7XqnYH{wLL8Xp>orwCB`JQ@LmB<2MrD zrxmQR?~pX}*_RoQXX6K9E`oTRcSKxdUmY(dz6^RrdyagFbq!@^GYgqCnS+#z89g(n z39*%3{9$SC`dco(5l=fjQmp_Y$|tF)BIHTT@11)a~b2ffaeLGh5^)BvcR4I5DO63BEEpHYQgI{S6#dQimMv5 z8~fcZMe2G}`3UpHzAXFfl7jTby5tHcqD2N-h z%G*@WMPElV{NQC&}bu@_k}Xc!1k3?_{=^vPOuhKF8}YU`KQQC9g!Q{lDX zhN--sxz@W7QhOXSs~j)o$s#*K($J5iwX@ECIIN+KG864G8`z+%A2I;p?5~QrY}4AY z#9oNI;Dj2I(_cLdY7xfUi>XI!+P$^^ee^&yx#|SpRO}9dfmyu;V1{)fa9S!j7)qcHgZqVRaiSLdsL%n@s?qQ7s55 ziYBC9io8P7u4-gVjfci6nN8)d0Hn5rTVC#tRuy@yt- zfy5CIdxMRFjaj_tO!RDM2rL8=L!JfZgBwC;Av&q9)Rq$b&`Kr`VUFK8Es_HB)y_gW z_CVF3$yi^M&|bB8Of{}{1+r<%a`dFBW?%u0gU#t_Z<(!?fPbc>=9-G73As2|Hr_;A z0u!Kli(UNaW^y~Ce2V$#jDM;t!%#=zr=SkNRdKkfyiZYSaZ6%B8o{4Cj6YA(c$g4J z-XK#E9Hk*Vguifta{r2+xuRYv@Vh1J1n#_TP?S8JRsM~wx7UoD=y+$sasnPQ&yrHkkU`oW=jD~o)x z4~2O_5O@>@^x-jY?2PSXcXq~3jqOw_JFv_G1Evb_yxodN!Nv~qMi?Pp3?NyQQ9$H- zEVPACY%-0ClSQQL=^s+$;XVC+za)Vl4h9l=A14X^$bo~;JgF$7M+OEtl%A}|iphjG zlF6r^Ihs_G$CCR72M34tzVYWDy>x6OFmhmIeQxfZQ;#K+UwiskZ~wQ}=B5=0DSM;- Q!2SDyK0gl#oXPZm00YFj`~Uy| literal 0 HcmV?d00001 diff --git a/src/python/roms/boxing.bin b/src/python/roms/boxing.bin new file mode 100644 index 0000000000000000000000000000000000000000..973812c40c9253fe9df0863d0a761480cac852ca GIT binary patch literal 2048 zcmXX`e@q+K9e+Ns4aRVnCbZY$z@;l44(aCoQ04^}=sJ^Np(^dPE|C_^ng$J%#`t3h zq&N;u@41$hbeDt!Q&sE5@e!YklS7NeTxp84)&yJCb|ElG2q`TB#YCHK0|Qbsd*6e6 zrFZW>-}imr_x*m~=Y9L;LWK_>1$gvG`DSH-nOJN4{eL23VO%vff*E@xu$)X&Y%$)i zet%VdD>4?rma!<-<9+tPqve}vio2`Sh4Bhb{0-roi44MFJhHt9?^phD5+_gM2##9G z^A;rkT|&4DSFKW4LkQPn0`H$>DJ!gr*MGz%^LHA4PS+?#8>MpWB$Cx zytgL3cz%!&XVv$bV=tNttHpch!`i!DNjL%AS_$4hBAJSsv)sl$^BM%h9 z$K%R?3qLR3cgcDvDxP)8`zGe7n()2JpjC(O;UPT8UUcChDT*&)4qx!ihW7gALb`y- z6fxX~Y%Kxjlu|6-QFbTB(envpO)8}~KH!XEze^?rHF!h7qfBCr`EZ?CTN@C3)1P1F zh?VIZAy6iZz@#X7j3b0!41~1xP=*qjbL;6B1iSoP`WaZRW4!BWBS)q!oFV;P&M4S} zbSD*IuZ4`O0auADQYW7fLvLS~oy%8FypgM1GD#(zS-hhfq^-}V6hg|=J>xdROpv40 zium|UP~Gi{k7sqXt(+g{sQ6<_&q(X>CH^t<-*tW|9!yK;r$;8usYD41{ zSR}=Hm6(`^uzn~`MWJ?5$;2H94h{bWN!H`u_6qZzjW}l|hkYo{L69!RutK(aLZ0$bbm2Ng_@1cfHHLMX5#fbhFaAs1-X zMZ;%LwiR-AsTUL3U6HW>t`cXk0UO7B@XXHnMx$eXUlq29XEo)>Xi%Aj7i5`ai&?$f zFv`Yc-K1eu*7@C`Jwhq_9Atd~@q+>WPtrP%6UR6Bk24uR^+>A6oEtxIwVJJF%)8;@ zWh0P5Cc-hd!|QdJJr3I8KxiU%^wVqy?QSu*bkH3wi2g!47Wf1d2Co4W21L(gfBEV4 z4%%UE^LWgTMub%9?NF?PHW&`L-G+`1u=v-{4rM#~T3RgbU;ji?eDT_@e&n!w9A>Y> zyL}3&=8?w5y>hu-3@u-gi(3nwy0p!07lq}^Pf>+CSq&_$PIdu_e>Whwncj8z_0C-S z8F&&6@IHPd74g?45QqC+ z!Gk1;NQ>pwCQS}CZWaKez9)`+0c2_EGg`ylTYc3IkEg5aqZ$WER#bao&ExUF*IQjd zlHR;GpYQAI%fsv?N%G*qhK5t8;Dn}zMhik@VYBhjw*OZx7Vt*WpDiuH-a_8-qz?gh z)O^&{9sW7)SMb?HA0EqJpMbuTKG@YzVox9dG-G z-I}?k(WFwJf(^~>l{?reB2@ra?LEEV{P5xTMZ|H0Yqhpt*#ksq!e5FZ{Iw|Huf&Je z8oVejn4AkH)5{NEUJ$=Zw?N}Mls!PVFA8MpvT6{Fz|Au8YkQWV+rLaFWn=1!TI}a^ z{$O}MNpZ&Tl%fl^7P(P3YTpSU2U8LfdD-C8vYkVSQ&IanhYv%H@9<6; zGJ+(~&-HrkSA}7Nyuqbj0Hi7iIiYgf3Fkn?Y37E?RR0g7>uYNO literal 0 HcmV?d00001 diff --git a/src/python/roms/breakout.bin b/src/python/roms/breakout.bin new file mode 100644 index 0000000000000000000000000000000000000000..abab5a8c0a1890461a11b78d4265f1b794327793 GIT binary patch literal 2048 zcmYjSUu+Y}8Q&eR9orZ@0S?>)!V(N+xmLH$wUAR=C8`p1vGQ1#OV>y^s;Wp; z>-ME*kY{B@RI*OEvUsShRmRG?Ldw782(}MPSIpX&V~+v_0!ff@Vkew$lT++d$({SH zudVv+dguEyGvCbj`^|Xx^k#w!qiJ1mAFGpH)As~7Nl)Jw3~QRWrymMN+xrN-?&`R} z>h~k~vv7KRGMV*| zdp6i@N^l_984Py5)fw#U1TPo^!Qk28z(6O6seuauLAogP8G+P9b6#(fOi^%8s-feV zsD=Na0S!LM|E4K?6Gaa8WJKRIc8!|$2VjRr!G<2l=Yy|xbaYh6#X|X8x=pc>TluDx zfthpG3^C_t{j%lwbP>PuCl|@$xX`bI=ctlPSos?pN-MG7Z2A(`Sg07m|MK_N-b^5^ z=AL>r)T-HFC1Di7U&nBa@0S^=Cap?qQdO!*&!u($SDukOI0_K zyX%p9JyMF)BN6a|(Os{n>fPND5K{ve1nP__e|RLfE?386rKqna>bR*qf;}_dCBq{+$GGG&cF7#Jk8!dcBN^qF67KMKr2UGE zyhfiv5lA}WkXu(SAi=J4i9ND=+&d>C0Z@}2%bd8^JNxcoI)@K3bB$*td+t z9aJ?s=2Oj~sBVE~Fnuf&%S4K}MC`-{jLH&+#7WjGVWhv3ki<$R>CA^wx?!^`PxRMT z7L4~+p3%9%y5XSL4XZN24>e&SzNs##iZmYy&xPw+_?c|ER05F0Pr?iQ^NrzOZu#U^ zl#`xL9j8x4JAESX)&=AB8hxC6$ddousU=WHpME z7O5#?q@uh-mb{DD#`l;Dqnfv8%ti~|x85D|u8;kC%bdTT-v!pwfo||`cPRWr)?#`h-xjIkpgS{+#-D|B_FLMlTn`EDAJb;L;aFPRQvGlo?WqdjcFO%N(;tRqr)y1Nf0uC0Avuf?{6)7u&QZ-vc_IB&_V< zf7KvwkbH`_7%~C_*wMa|$sy7uW63Go_>|GUcH3@w2blbi4dCh_sY^O69UT6}CWPBT zEsTo#rXNnG`6uIaj->t00Q0}sFRuZ~t11K7#Td0U)Cw@MmVc<#8({o^!cROSpHI@7 zovzwtH2<{uj$nzco)flXRjpXW^%J56a5>TDpUDjWxO4ETfDD_ah12=~%?I`lwSK3K zKEBYLvdmv-YM?~hN;nptnb!lr$GhEoVQeG+RT)vzytC}Cq zh)QP!3%PDyXsN8xB(wbAHk21+QD)~#>Zh^NrB91^i>iQys@O>pIC-lmnTPhaU&?YsM5?dZS-Zs6y}at@;BS@!&S6herF$CUTc`wX`C QhoX@jFP`w9-pp_O8(sa^HUIzs literal 0 HcmV?d00001 diff --git a/src/python/roms/carnival.bin b/src/python/roms/carnival.bin new file mode 100644 index 0000000000000000000000000000000000000000..391d4b30335e8d7be44cc13d16c725bb9bf4fae0 GIT binary patch literal 4096 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 Q^Ie@~U9Fw@Ra8R#AB9rl+W-In literal 0 HcmV?d00001 diff --git a/src/python/roms/centipede.bin b/src/python/roms/centipede.bin new file mode 100644 index 0000000000000000000000000000000000000000..6befff16ba4b71ea9a83d287e0360ae2ad4fb74f GIT binary patch literal 8192 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 PW5OT9@uw;)r6Be{A5(o- literal 0 HcmV?d00001 diff --git a/src/python/roms/chopper_command.bin b/src/python/roms/chopper_command.bin new file mode 100644 index 0000000000000000000000000000000000000000..f8358d3dbf8bb4d93d4569e95305b4f068b79bec GIT binary patch literal 4096 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 literal 0 HcmV?d00001 diff --git a/src/python/roms/crazy_climber.bin b/src/python/roms/crazy_climber.bin new file mode 100644 index 0000000000000000000000000000000000000000..46841a59d47d025dd9642d96f32c75aad55839af GIT binary patch literal 8192 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 XltlcWdI1VzefB@Szs3GXp56Zf&{;s% literal 0 HcmV?d00001 diff --git a/src/python/roms/darkchambers.bin b/src/python/roms/darkchambers.bin new file mode 100644 index 0000000000000000000000000000000000000000..f7277e85c15ff40f3e6292268ed46fe925a235ee GIT binary patch literal 16384 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 Z)%)ia2vvbC&W%+S8_Oz6QP3W=|1XD!_Y434 literal 0 HcmV?d00001 diff --git a/src/python/roms/donkey_kong.bin b/src/python/roms/donkey_kong.bin new file mode 100644 index 0000000000000000000000000000000000000000..fbe04314b72ddc4928e1a6079f11157a876ac105 GIT binary patch literal 4096 zcmZ`*4{#Gz8sBV^O`4|77Ayvn(om4Ih?sgFMy3ktQKeT-9AzA5jb%3=#)^NA;d-Yo{I*kDQ9cF^zx|!MSOk z7WSWQ3K+aiUN3A2+Vyx;UK`QNYeWNX@;4K$wMr9pS}#r|$rRY*^~Z=_HXI|mp;khN zTPuFJh6jrs7!t{2Gd5p&?SDl=<|ED0=$FuX2e=1tZr#`m7%!vv%VT`z3|vEwF4FpXP_wS<-Q zC|}A(%b@}d=rF0kNPbMzMWK34uJP{nb6wiN7!+XK6VV0Y_^LBId=<=992gtsuHuG> zhGa=*l#fw4S{_DZ3vRVm8Bi-!$1A&VslML19x}BSH?vt#(7;NyZ2mYYIOlB$$D!ir zJGHv%ib8~7e;okg;9a(;SMi$RT9S>u#Z0-{8j`LZw~G46`s>Xw=^aIbuTv@Clr)Vl z<3a1|l!9W8@o7BhiWl&ABghb69Gr^VeUmumirZ04u!TpR#*Wx=Ox3VIN(>s6D3uS7 zvQyxv8JDoXf((>MTEt0N&=j8S}ub6=7`$hUm{ER^-*@x$>7$Ag{%U6ooJHuj;;ixUN0|^G^h^y9Umny zzg2^H$LK7asb_EYgJZHD=`aHu)C)TPO_%C2lfLqjPtpPZbEwzgPC-29HAyLdvwFa+yHAKZz|VCkr{S_W^DuWiw+7lm$LU7|-s=z;#EP{j57(J^ zTKtM*I0H2C+D>W~4I&yE-PZ99vUgmnw^1DzPl4JyP!uLhzl*+lAkPEl?e*pG5L`T%-%i1dO@i;p2XpH0@` z_mK_U&r!i`gYx0ipE0FyY7KmHTg4!;SiO#$P#24#~Q6i;gfpo-fWFQT?bUl7B z*Wb|((IX4#uSiZhx9{Tvgb+pXkKk7Le98v+6w9=$(prgmI6vA`f;RL+;`sMew1%A! zO#%hCBrVuG4^hQ(c@^dBlV*KiMWd>$p166gsAG zHG8miX*t(&_lgzmDwVajuSk<%oBNPWRZ=C!YNcM$zR+@4EqaR5 z&|00|VuA;@rEea|9myRED2+D{R?f-r$|TiB5)_MUh^7#g@Vyg-?8;fm?!DTQb{vq-CcufBx?5|bm)TUfUj)>cUb)&0Y`OtrFZuq%PZD9^Y!`b z9(yytWLp)=2Py&D22=$kO4Y;~0b7~PkF!22I&+EJ#K3G}lR`CZbca>63r`60noR&eA#y_aOJ^VczHf+47M7ek4M$e+4 zG8LsS&wQG^bf7d-w}^Ki54{;W?p~x{pm4br<&{&XURj{LS*ZN=9cBqGRG|ny$4*`I z6gyc_n>coM+K~FYVoqL|rl!q6kX@tEm~F~38W_EzuwW*0+)_IH)CaG1-E5qqx?cNe z$H)VQOsvY!<2M68KY*PR{?DS>z5L$UH0q|GN8WN*EACRO+jYYI>Bd3>OuzI*$ulo) zDS2tzw%reHePb0o#Si>pf9626EAwmQV-f9hRPL|Z`NWouchwXX6|Gvmz34VOyKeLE z_BUEA6qE7H@2|0#^x3)dQublLAM#kz?r8{jjzyB>e>ZE zyJGy*(`N=UQwWgj`IqMwtpxd}F}J`2I(5vG2N>n!tskowlFBa_in1A0>PVBY?b&Uw z9y+=WQ1W(x?91GIp5I4=?Xu@a{WinO&2#v)k<6cYaHfuuiG#=sG;@Odvsy(_U025( zsR|hCYo0P#^=}>!bAt>jK^q&5=^46UL&1i}V=o>OKKbGmo&DaeyUesoluXJ01Lk8p Ai~s-t literal 0 HcmV?d00001 diff --git a/src/python/roms/double_dunk.bin b/src/python/roms/double_dunk.bin new file mode 100644 index 0000000000000000000000000000000000000000..b4b0ad62470f33b5c8b5905ee9917c1ce50b4311 GIT binary patch literal 16384 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 Brq%!e literal 0 HcmV?d00001 diff --git a/src/python/roms/earthworld.bin b/src/python/roms/earthworld.bin new file mode 100644 index 0000000000000000000000000000000000000000..7fdf48f39ce3963a4403f63d8f748bcfdf03f0e2 GIT binary patch literal 8192 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 vG+n!R`O%*1+isj2;>oRNMT|7 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 R`8F_m|Hoo)@5uZAe*?W+gTep+ literal 0 HcmV?d00001 diff --git a/src/python/roms/enduro.bin b/src/python/roms/enduro.bin new file mode 100644 index 0000000000000000000000000000000000000000..e975c0652ffa9c9de763ad16885be63ce3de84ab GIT binary patch literal 4096 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 literal 0 HcmV?d00001 diff --git a/src/python/roms/et.bin b/src/python/roms/et.bin new file mode 100644 index 0000000000000000000000000000000000000000..4892f9b3f6a00fc8eb783f36f817af3e3555520d GIT binary patch literal 8192 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 literal 0 HcmV?d00001 diff --git a/src/python/roms/fishing_derby.bin b/src/python/roms/fishing_derby.bin new file mode 100644 index 0000000000000000000000000000000000000000..1d25bc42b3c8f4d1b79b5bb2e37b5be0c275eb5a GIT binary patch literal 2048 zcmZ`(e@q+K9e;P6?~F0{m`ZVrmpEGJb|h<=Hf?MoO%{!Gwk!>)(zZXQU{y*q{>!9w z%A|-Hb6AWy?5_@zu(XI_d9tvO*EpA;j-~WbAcz=VbQqZPBV#kG19Y<7IL51Z9R>$0wpfX zDtu9VNax`_`Yzn0LuoE}!=i&lXT1Q!^iObJ`$n21s_;TRmwkERpI|+eau?(!{1}`EsBm#(0))Eee%fWDjrmou100G)F z=gP97aH9-72z5&q5772R@C3-LNYWpabBee}ozYC%F1l9~!|z z(@=OUcWno7NMgqP`ta))lwsJHt(pE`X;VJKwp6>DtL}>>Z@=~{{Vt&&BXP|MJIZn^@~KUp19_y z#)u;G#B`&LvD*j1TQ@}xz2EJp=Z|5Qk{rDRdwc&6?|VDEyZm(c7-kiDF9OD6wAfq` zIsXXlJPnnYqMAfi!dp&o2^WY^tBJ5DjVXerT*-olT-k!aC%G*vlQZ!s2Vh@OT-Ad8 zZ?b7#U&%;9W4CM0I65S`4bIA2`K7$tuIDAzVAT_{O=rz#M=qE9ipwT3ywnB_{my8I zz^1mpQ=D88+0H}Cs!H^H;hAKO>ehNnyuoU}ypzv*WLsa6Y?rO96vYABEeYncV?}+X zS)}ncJfT{&Ammn7kj9KrOQ|#2Tb90V?!gNd$F z=gH_f$qb{a_S98^|2~h$lX}}}p|i@lNyT)x{O0U;&-EHxy5jMmsU^k`z}D23`?fOZ z7-RTUmc~)4E$(>%6aO#twW&qxxIY`lk)gAa&woryl?e)`)(G8{IMbssut7^9H&u!#2M_8g1g&bEk#!f*q<6#5Rc%}8xl=6Bjr zjmxae$OKoMKpu1(!h;#3qUHFE`uTXAb%0ghkV^K>h@51P%*Z_;Yt6u75_cF5+^~`3 zKLc-#$JmTi0cVCJuczQkvXz|Vb)CVp;}Y~Qm~3JO0mE5WdM3h|DhX$UG--0TlH&6Z zVl*Z(5QCXYHM1b+v@y7$?vm%^B%L!WBJEb6@ogG2UQ)?ao)p@%!onP%+)x?%?(({= zRwr4t2J5A}vf0tqXpcNHS*;4rOq!-RwM-;;$nF2_O`@ogCmZ_U9o2LvIZL(FB~=ym z!In)G9*cr%hCcUqvI7^vR?>cbzOxp#lo8D71>RO;E!4ih)r?sjs=MIa423hsKN0_{ z`bf)Wx*&?r(8p42$JH%IrR~~EEtAp9lV&g7Fst!Jv$S^j6dp#ho78kYGG9YaU=;KI zb=@o0_L*+7`|JxW(}PUXL+n#Okd=~P5)3xkhcA=uPf_llnEO!i--@W zZd<7pGWEWd%9FkjGln(^iOW-WEtth7%Lgx6$_E3BDl=sgtm@FzJo)}x1nk_pjeFiU zzNhCqE0$GayolY=q^}m*&AsI}%_1_S<_VoO#N0As$(Qv!jB4&w9iedv`=(bMFN&Aw zC@O#}@)+$lDrr0{`PxcQQvXUzJB4#ko{sD`(|qoIOUdVMTgr+SRaVjLkCk$X&5D|! zD9UrWZCg=uk8P#CDBej|YqoN=SQMW`@$q-wlBmD)#f;>`&^zJH_>b9GUKMM;g^++9 zol*#?i5L%8dOu z8a)x+Qe($AHtsFnEdBjZjI{iy<9AQ#CrdGgIc?j2v2EdJzuLnLGR(*I^{>>|3-5eu dmHF`SVe*C?CxLIiu-jkjsSv+Hh`ImQ{{?3=q3!?x literal 0 HcmV?d00001 diff --git a/src/python/roms/flag_capture.bin b/src/python/roms/flag_capture.bin new file mode 100644 index 0000000000000000000000000000000000000000..a8464b887185e696a1efb372770f66d22816ee74 GIT binary patch literal 2048 zcmY*aQEc1B89q{!L@}<#)N`XC!Nj^wQMG8Hm$bqlEZN!wupsQI-HJYO57`i4uNejm zFjUoNXr!Gou`X~EBv5Fbpi^qc1;(O!C~6GwVEduCAd6$CapE+U>^X_b#EIl=*tDOr zgLWt3?*IPd{r`90{YU<3iJ=NXXNT8@mxi;$AFcC8Ou!p>5npG$c$sCfhy{$G7n#>1 z_QvQLQG4&<#ycoF&7)fOP&$-8`z*G%uoIQ zyjlEod)~w*UGr9Ab0Oa2g{ohwPu4%JPkGDIY1f;<-9S(2klBa##Ht* zN_onX*a>_Ol&b)#sV8nQ#oCr1F9AEeMwjA&Bj;mL{tF5{Ucz}y>Wf4LFII8K&ap`IGJ9`V;{P2d{^PJ4&vq0Q=2I(T7N(v%t!ei@I0XR zwi30KrT86uj|bSH19_Zx*4@P7y8D)Mh4hlWPHA3r1I|^lhX)q#I(Nadez446Xcm86 zv;rXARah4ySDg~M@Mban=S@gk^Z+b-n?=bUvey%97rp21(|*&m1^yBYv&*XLNxf$k zxjpoIH=O?FG3DyhRm7iwEZ+}TKvFR7>u@l8jRB!t&7u{z#pP-StvqVE%9n17WWRjW zA{lMD=_KPXl3aRjQ~U0*Gw%8p$8{&K#04EDeCf^2gE>{uy?n29a>rkUJV7r{`t42T zBNyl?lBE{O0QVY%12QZ_a!l*z?h7Oj=__oN0&~w@rL`_8jDbB;txlt>=*EW@o? zCs!15mnY^!{7vmIPZG3HvsM2t%k#J8CrufnG9;BRf8bCMVf>Qn85Ctw@oB-o`fkM23W!$TwMEC^ge z=(XG^RhhV7hY#R6YYLclM2MLu47>VFCQ@WJVllgT9it!&sOLx>ANpYgE5z zVPnqgO-~w6BFGN<38dFc;ACeZ!2+@1qm5 z7u}yuZ{r|;zJrw=EW@kD52YfJUY!~-!Bwe|+O9?aY1jY++rh5i1PtUB_@e&T41TS; z(jrd^eJ%WTYL5^#Afhw9_Jyhp(oYlkzL!`kkU-xo!47&-6&@~^E0Lnibz@4DrOInZ3PMXrm>v?LU|J@qgqqMnMS!A*9EbuItn|`e ziWD#$iRPo}KV{>1EV+l4COiVihES^Q22l_JOoA9B(IAT){8PoKjFz-WE5Rm+mx>(8 zeX~l`!pI|bXWqQ`zM1*nw|wTzbdv%#j^`8I*TIN%d`2fog zf(r(-EjSxkxDDM4cc4dm%wG5xPX7%(8MVe^uwUerQr)Q@-U@B32)EcjU^(lB0TEzl zz{^eoLa?(%mnjK<;TFOK?S*B8Fl$cMY4CC{P!VYe6R|mJyal(6_Yo|>iAaN85P(*Z zWPAkYjjQ1RAW8<^nQ#lpWsfQIlIiA44Y=WC%|v?V>=pOIaz)()wcyq|Y|KXBtDvzn zqvKyR8Zh{+YY)|xD-IevIL}xzl(fY3`(ecS1Z!ibM|-|xr?&aQi|!vy9MEPRQrr zQr?E{rGkz9T`Jo7rMJ;d^}s+yy~FpOy&$ovuS5|f3&%^EsiFO{z%ICYv1QkGNV?HT8ztyy4uF&bx zr@t~9w2S*`@YIAtJDtkFb{@vq@p3Dd3I|xtQn0kky2l!xduEs=55>wW`hJ!io(lie z>SD>T+bcuoP~8O~?t&n6Ko{&a-!-P=X!)W~m$KN1qDPl7{9tj0z=nvRGS|xz*K~zH zga^n=le=kfagD&&{59BQ4`7RJgj;+*uRD*99a{Wn^YGW@tS;5F@XeCl9IAd(kPkhq z*&l8e6fCe|D>EB*o1d`F51V>`2WXJ*qn+GEyYN2On#0X_oOWH_DlM)zLxAz1G|Elj=H||aldiN%Ooi@z2c)@>t+L$gY1xE&sTsjM_ zh8~gm%d}BZ7js1Woqf?D#WqU`O{f>&BCBHIy`6>k2~zi4^LadwY(0nbz?BtW;VENg zyRUgRHJps=o{TDGNnCF!6&2KgXBK((fsOx3uX}7dgD1vH7fj_`LXJXX74?3uoj2)? zN=4nkPPpi$CEKPSg>WNx{?s3?H}i`nEaeG$m5)(_F47Mw>di!G;wizX?J$}T9k$B> z-CYlq4xxH{zJ#`ZpK2gw)J>`;u3u6IzB?#tR`nWdp~DyG8t#9|nU+f^)ISTI;nJG1 zhSgwaGMSEmo-XGA##J3tZrp4~aemj1KUT2YK^)_ZBA3T9TlS5TIfkKJD zlIZvMx088Z=byb0AY;FJ`Ij&4w#YlD-pT!Va*Ncj{$;*S$XG0*sj3!uJX0#r^+5nZU(Y>P(;cuvD+j{k5&+NCQ?9DGQy_lq5$ zw#z9oPNJ*$kgnB|i2^IDkabmGADKwlaBNl)jbPbQSkE1EhzWHNas7}b+dN~8k!@M~ z?lW1^Js-XMeeZtv`*pux{->b;I_g6wJwu*!hTV63wm{e8U(-b#!|&rh{Ga#({2#cN zZowC+52t7oPT~PtizC#GAJ76GgWpH^W4Zx%Q62si596!&e;n=wM{a>inBo7%BLP$F zR>9X28!!0YPFbh1Km{yd9sC4xgRM{!B$1SuL=jGO#0m=_i0>o558nWyC;$1Fpxhvz zCavsG$qtv93^zt3@{~)gdG0>2*uZg_DAhWwkS^euQDxfFmtlH((s6&G#y6AbvgVUI zW=g!r)+f5yTD3Z9R1L`%Hd3?fF1e*jiCb2QTnC1xdt@N_Q}!&dBud1_S%MP%c$99$ zXUa$L2w2rcl#Xs@eG+KA8>tn43f}1gs!`ZmvYz&UTyQ1ZeUx(mYS+h(>kHgS-w1ez zw!on#aO4J@rr*c&w1~r+Q@7x5?pV^9G^i$$ckaS@U_1wE;fX!)R2HQC&2_ zqo1@jyjd4g9@u{H8{cmz`oWcLQ-f*=YyQHP8+V+FDya-%UK)~ufnhOJ`FnrXPv$+%wq+ujz0v(cW8*vu5p#R^k+ zJf^>kCrG3(8(H08fcJiqJ&;SD_O2iE_eCPlc~_CF?LZE|YLZPFBXYy>zKHj!#JFnF zHbN7Q_63|}vBV?@S`PAh-{vANSpO}0DOs!jn0RdsWjL!z`1=N9U)Fn&OsZSrCiY}B zp6pTokQ@y-G#(&BtjXLBm`DV0AY2XnsN5HZ7%1rrGedjyVzN|q2E%bVqIbNspl+p= zAw?BvOZiRkaU}U}WGpFpM`?@xHoncw8sW$ENI771SNRR*NTk&@^mxT++R;7*cP00w znVJ5B5Fk4iyW_X%kvE4VKg@TB?cn%ohqE9#yQ*gt#?q4ONsuN=HAQ^F1 zsxld=jxNKw7e%*1KchWr?QqEm%3^?F)tu=w+~EbtuyN=LMI6 z>cCY@P90?e_2UTl+!XE(cE$~?Od}MOD2eiq=YnVAMz)6ZJ1g08ZL3r{tDGfg`Q~|& zcNxHtuh#tb9w@ns?ICkXfkeIfF=NfXdksb$4b5fJ5)%T*GiQB~yajpwH-3Z0r=H}i z9XRiFsNVzIqj(PT;T$#KP8!E~_6!Lp4eW8Ai|SV)3Zh=)n9SdiwGDiI1RVQ-9>JaE zdB~81{4Q3IX!5w%436$3`4R_DosdGMkFI(}r~uU1cZMB?cz8nN`8cgC@5kfhY+oeu zNNAj`hj4=kT|H)m4_0q!Xk0Z_Xov_`OwJ~cNB)Q(VQa~|4J(tC-oNfU8~Gls)c50l z`U{)_38ZyQ>O1TG7JVN$_w!=O#kN4z094zD3bfg8)U4k?`m0-TzjZa(eikf05B8kV z3aoaT-zCl_b?VdL*Gg5Q?Qm0FI8H^#wXefBE0mHXe7lG*X;@jn$L~lL0>AL2YCf9@ z?0bmr_oRZ?T~D&j(QE40{vx1rmbLf(tl(SYlBgcen|-TNF8QEiaKU<8x#llxb+2_l z>b`FMgT(d3kZW^r$hC=Whz{blstss5V$T=wssX^)0JaAYDTB&jVrZ#qIA=TG5b#Z> zj_nH#Q!y>7a#~Uas3A}jApjK5i05~nyl2icn(~CT%FHX_Qj_TVa`pSXD&kJ?39pjd zaK@3F;u(NgV;|)!#B|KiO?HItkEZB8Q-{2SdFxT$4+6wTn0 zPPgEir)2cs8fl!q$%9Xw1AAmm*?pp@64CqqHpzC4g?ncd41}fk@!>A?q|SIZ*2_GAZzp zzu=j6-?MG)&`!h(1$>8O8ylVS?~%;7?{@%ad^fgi5#&pFHl%=6I&g>(`%VE0dA^7z zW6u_FHh>)63nZ6ue6Zjbz(N@tz!8#1?=b}oC!tNlG4+X%;z#GMILn~voXBxV-E$&~ zGMRyaxWp6;Nq#P?3d~Mxec4P}WK-&LEck2PIrq4Gf(*kkoYh>1h2UhXwGhnq>WX+> zFvk~ra2%G7-~_xq>#$wPDmi6bnNTJ#q)u7~sUDjGI-+7dxeL1bC4LXs7@S?wHbw4W ziA-_*OZg6U;bn9Ow%{_rVdB8+qBGdu%BJ0dd!5_vwz$juCb*7qJn7Cthcod}q!9{5 zDj&Uo_0dasqW3fk=s;8u_SskbB4_K@Uk`fWdFTLNo`8OlXQ=O_5->)wLYFXY#|Kqe+lzP+k)@8$hHRIFVUg zvLq1#Ng@NGuuFho%6-nSmCbTqHBZf5?o>-JpHZK>99EyHvF1TFys&N6}Pp7KZ<|KJ_2-I9cwgL!bYK9nNo{T4RxWwA@fftp z#tyf+F2!a%>pXV&uAnHCGj;zWT{sgGqDHl7k3mqJ)R4_sDLl`nhH4HyRv{U)p@3E2c}`d6vq5@J_och zEI^=dhJwKdwZAmoH}*tV)7yfk;QBjM6}F;Op?WHL6$A{LGOx#t2Z66vZ&wf7RN zz0rn4o1d}$JNo&*{>#rEU9<9$_Y{;a9#rTV>X{< zv3vhk)3d0>-`>%N+RzVCoA5kplYS_)$=lI(WJlYDgT>E^6Genh3I@wEoux#UOXU9p zO%FknESEk6OJ(^>UrS`sU=R)PZ-uDSiSR}o!tkXAB5F z0UbY`82qpIZ`4M6dZImFfNfuZ9UAESf?%Hp>SQG9^`c0h9nlNI(F}017&t z?jeZmo9gT9H`$S7wwul7BWR~6?yRdjf|_9!#bzYPmere&$f&ulPQzidxxRiUI`Wbz zn{6(%6A+HWYZn|rzxNo|UwlPC)}GgtO7x)kQA<3n_Ux12cxSQV@P&6S9IjY==Z(pV z!_9{)CVyn#*}T*Kqt~mNcRW`0dh7Ft%Vkkm{^-tNUA*#Z+ZGm@zqxA5*B6S-6)Sf% z7mLN_9fg}m=8JcV2~_MaqC`C^jucV-%ZvR=|KiJqSBoc#uNGbe!ea47l=f>%wZ!Wr Ui+$hR?A$)PMY620A{D9s3;BEjOaK4? literal 0 HcmV?d00001 diff --git a/src/python/roms/frostbite.bin b/src/python/roms/frostbite.bin new file mode 100644 index 0000000000000000000000000000000000000000..d5be210d822573c103c13b71a07715a43f4d96e0 GIT binary patch literal 4096 zcmaJ^e{d7oeP8WLtCi5%YXe4SJ6K$VFM>Kra9wCL1~Z_-QCNpew=feCHX zq0_m8PB|ePeHUYnB4{X8!+`?fB$*5t1{Vatm&(;$lzx2;>Ti>wtv-`{!4eR~3Vcf{LzS zuy5#ROcp9&tN79R33-jwD4987c(w%f$Ut{hsGsQ^S*v;nj{`q=4C;s(4ECo-QYP*a z-*0kG;|Qf@3SG!$ZKzlI$GlPn83w1~00&)F9fQaE2ah*XE~yT9QkY(KsS2K3b&4_x zR0$REiBFs4>du(i)i-#Q8g<@W)$>G)k1N5}^JZ536>*9z+<9aO+V6V81kcG%=@QcF zsglPQQU~jlIJmmdr*m;O{Q;Qio0yob$k_;~6r}ao1 zQ|9pdEAUy^lrhC$!&bDJ3beAd@rfd8jKhZJG;wHXp@&Xhqm&bX zD~mo?NjV4=%I|^O%9aTJq%+nq)LHHy`d%68=+2>$G6+Na%TPab5J|+G9*W`wp+~VT zbY>RCpDPV=2e2njv62mGxPspbXdoG*hGYYkjv2iIPyJzCAkko)^BpM3b}UjOS#ilf zDsrK{4~XHoWo-2J?-O7Hc+t!n#RlsYd#+kXPMCwIq1Hdw){vPabAbjtl5^mdJyMO# z1USQ~h`z*55kWNy3ts(n{l z@sd@mlN~WO#^4t}z~tNYwfRvp8n_0d$mPsm)w$-qSCu9!VgD9ot<7G{)w*OklhG?O z88X@=xS}vBf1uZsYr$TX^D!l?=CLt@X5;F+9c)l_$X7#lBgq8SKOjd8d*+Dv1sXuO z1qRG1Ct)@pCD8x}QMd}Du4La>Y)t6~4YKHFCb=9cLmVS@O~4TaZTWYVbi{reK8a*8An z2+FH4tN#M#F~2kD<&-{3W&%Fr8Pww?*B$TcJj`S=O&1V`Uvc@{MK{{d=UDXG4Ke>6aR02M#9l1*=D!;!xc`pg0%Vx#}QSnFRBYf?i^ z)#>itt%+`nfkmX??$i*u*z{6AHCD6f&*bMx-n&m|evm_W_oaXH!hbI(euF+MUq?kE zOo?ly`|0jrpmldE-e=u`lMXY{jT+t{dF&ic!2-s^?VDcKTv%mX8b(Fwfjo(!bk2As zsBW_SS*sjwp3G~4x}BP$zkt3KOs#Zps2LM48r|^?zQ}@mN~`q;eg@Jit{p;7{J1cIXU+`RwmA2 zi9+jN^%q56`6tNhFO%N$SauK%Hsp*2Jg}g+&rgihVXUiQk>qzR(2u$%DU2@0t0v9R z-!9^G9bP-9RCbRE`It#=2SkSnw`G=02P(1NR>=$%rFje|3btdHBi&6=ByZQ(;weR_ zi!dXLBtKeEseh&g_vcZLK;;C)eo{b}T?mxg$)3>7MeQxMYEKUo&fg$bbO)`md ziSF20oYF$=OO>+Xf67mxHFzR(4wW0WuOue2;$7)}*hE2_y?e5djc+8{J_a;0hQDJ( z3zQ8z_Qe9ZPC=Bveltua&E}#FCh@H!;KaO)Yfc1~bJSGr*gY6z9`AfNF)awQd5X&s z44jGF(a^t^%Gep<`?(=KTOQp z#>Ozb_`-`gyxz}!uDSWozV^L?yu;zJbF7UDEtg6h`}lQbd1+~Bk#q1IXLHzj-mW@{ zxo06Xzf8=@1>e`p;c1)0;XIjbuC3eI+WHn#UHkn*zyHjR*O-xT)4^pY%P_6@+%{3Z z_1DAgKbzX|*zDCgX@5L#iF_E14n$fW&uutZ92{r2w?xVx4wRp)o%wlW`fCt0a@+{ESjsB^SXC=FY z?TgYnjemNoU|q}&e#u9dW1C}*ow2%DeXI^l?EM!8Pl7pkqJI$RD*k=Ie+sLABhH(& z;frBgmst=$#tP4?yl3@?eSs49% zps3vW_VY~J*S_)9uT&Y#rLIfzi_sEO9JugqaTN&jiKB6Y`O`l>@apSG;Fy<#f5Qk& zG}_;P>DgzHQdPBK{RVsFxRO d6b9vVn5*1g9x;~7^tnIM((=!jrWu1V{s&ynLBjw5 literal 0 HcmV?d00001 diff --git a/src/python/roms/galaxian.bin b/src/python/roms/galaxian.bin new file mode 100644 index 0000000000000000000000000000000000000000..44078febbc071836a23d47b0df2fc27ffa5e1acd GIT binary patch literal 8192 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 AS^xk5 literal 0 HcmV?d00001 diff --git a/src/python/roms/gopher.bin b/src/python/roms/gopher.bin new file mode 100644 index 0000000000000000000000000000000000000000..00e1ad4f66b29628d7d2632b93c7c466e48b4b53 GIT binary patch literal 4096 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 literal 0 HcmV?d00001 diff --git a/src/python/roms/gravitar.bin b/src/python/roms/gravitar.bin new file mode 100644 index 0000000000000000000000000000000000000000..bee28da5470d53dedf477ea32e6ec48684c08326 GIT binary patch literal 8192 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 MhF4d2K9qj{11|bR{Qv*} literal 0 HcmV?d00001 diff --git a/src/python/roms/haunted_house.bin b/src/python/roms/haunted_house.bin new file mode 100644 index 0000000000000000000000000000000000000000..0a4610e4197bb786506af33a57781caa1cadefa0 GIT binary patch literal 4096 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 Yx#(c~v;Vft6dR}4@!g6yZ`_I literal 0 HcmV?d00001 diff --git a/src/python/roms/hero.bin b/src/python/roms/hero.bin new file mode 100644 index 0000000000000000000000000000000000000000..4242bd9adebcddb55d67894fca15174fef13b037 GIT binary patch literal 8192 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 Zek_OW-MJUTTdg+3jjN-t9ePDC_up6x^8Nq- literal 0 HcmV?d00001 diff --git a/src/python/roms/ice_hockey.bin b/src/python/roms/ice_hockey.bin new file mode 100644 index 0000000000000000000000000000000000000000..5864e0959d326089465426dfcf42ec67de470f77 GIT binary patch literal 4096 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 A-T(jq literal 0 HcmV?d00001 diff --git a/src/python/roms/jamesbond.bin b/src/python/roms/jamesbond.bin new file mode 100644 index 0000000000000000000000000000000000000000..a9aa5fc39aa91e3e1c4392fdfc38b766743a2359 GIT binary patch literal 8192 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

a8j5%-xzSp&mN(s%J) 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 literal 0 HcmV?d00001 diff --git a/src/python/roms/joust.bin b/src/python/roms/joust.bin new file mode 100644 index 0000000000000000000000000000000000000000..9b0972aa81b02ee84ba74b9334754cbfcaf25e02 GIT binary patch literal 8192 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 AE&u=k literal 0 HcmV?d00001 diff --git a/src/python/roms/kaboom.bin b/src/python/roms/kaboom.bin new file mode 100644 index 0000000000000000000000000000000000000000..ae35480ff15892e3825f421e57cdf65f30507541 GIT binary patch literal 2048 zcmah}e`p)m9e;W{oz99nJ-cou*J`VP5-g{5A}~0N_N&BZ*1XVl*~rHJFk=aL_>Zx% zl0Ss2DEE~QLqRLKVCRQ(I_n9aZONy!f+VW8j5Wd*?KoKILLtnI*4SYix5hrl&Svl3 zB`GQWW8WveyYJ`w`}=+GPT$ecgn}-tnL9&&ysvdMer*>kH>3GLalsT;xI9O*{gwW+ z2PFksprIL`&^TO z5qLR|^+0%J6gEeA*wTLl<8tD_O_KKmZjQ_5Ci8@$+qG$q*MtOaZpq{x;8nY5U%!eV z3j5;zS@}`X45}m8xko3RvR%|IS`9HbkDNSq0f(u5Yr*uDb!cedGX^|qwp&*1 zPi)96y_Pj7lRr2yWBvqY@tjgV4+?npiJ{-Ia4T+{Wv*C%dRtbA1#tyxsAXOQoa@d- zs5{fQ+R&iPr0n434(Imef_gh};DZC~n6zuG&(Pbb+0fl+u+tX(f!+Afo^9M>06S8L zTjX#0fw#`%N(Qf4Seh2;*1C^)CdE%Lhlxl0r zYW!nMd0)A$d{Qh}f^7L^i#PdmpPajW?tOM0ikE)BwnY~M@$jtfhwbaamh!>UT63Fh zy4OR^N?7tpi0y0vT0LX>SEo&{cxqbRW9KR^m{<)>swg&1xcF9aS~|<01xZf;X6dR( zl9^JzW)SH!|>CMuDj+DB77%G}T=Plfv&n?vI=3iT6 zOOHaOSusDRhKZX6TC&14lgdA)#Y}hmkr_&)nXTk zcW^I!^1`x}=QX7JOkw#A_O_?Lk-G>NoWV@R{NzOGfs?$7SNJ?0Th=qB!rBUF;9Nof78oVF zR=0>Iu$|LSD|J@rkaxr7+nX{=y#Gwx8(=iz3fHLSlzp-aRdKx||9z-x{@PXLomyRa zkD0O$M7^ZN%CCi}>{~Qn;ma$2!@HK#CQ=4R-!orWou~76@7sBSY0m3!LYZdcf^Q10 z>ZhU3qQ$*)Dx$I_Or%(IyHtTLS|L?vE-r`ioErR8o0GcOh#8~_Cl{8ZhBu|X1YOjy zf=y#CmCAeO)S%Rr`>OOD>r5apcYOxMt<*@5vf;dioI-jOTi;SP74})WtD)M)V2>#% z8*6JTf&6DfU$Jzg9aN@Uk{+MJ&Fwudq9 z>+1{RrFvs&8MgpT_$BuP$baw9FFYlKh)Bod9Szp)x`JE7Y z?ZnT1{KIw&-I%zc^<{R^`ut*f9igMy?2V7ywd+t?6vffw>Cu1few0q9#kS5DdyahX z7w7l(Ec7LM8;K;_gAgG{-zMlNLTo2lgp#LRN+!b&iQ%!~;Uv!wB$EStFvy<4U~p{c zI-PcTXJhsM E4>2WdssI20 literal 0 HcmV?d00001 diff --git a/src/python/roms/kangaroo.bin b/src/python/roms/kangaroo.bin new file mode 100644 index 0000000000000000000000000000000000000000..4d82d4dd05799aa9827ea346faa8582858d00927 GIT binary patch literal 8192 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 QgpYq0un>3dK$%SD<}kUCt9s0o zxtoA96BGsXDZ;^FZEJ&VioDhAx?0!itxKFna7x4xE0!;Xy$gkeP9~<82-_2SYdaVd z+tR&N(w5#_@9nI1zqjvu-}}Dze!r*o3&~RdR0oHqlqKHcXu6RkqeN6EJE0GCdH8J4fLzMn~o?%t|M8x7A913D%yAK zWx_94@s{KR65{@PLRo^Kaxst5@%ged<{<;VdEs!DQtgE~g(#@ffviCxBoe%sAtn!@ zkbyhr9q$(LK-R#L0q?vs%mi9psm8`eZ(}3w8s+r_)psV&5%R?I?BwA4PvvSyi*Tw|t$YX`#; zEhXDaH4_iyM9uq8o)A0YQ`L&Sy{qiiNc)F zsopIf&XbD1*N@lqdZ(#8B~#@bSYnaRtcfKHT}`8DEOLrd7=Ao`<32!k#Ba4>Esqd>D>nE0p11Woen# z!iUASWzB3u*23DeR(4A?5&u#mxZxqG)p2rVFu|B|hYB&ZMxvS|eUoIshj}^@jYVq2 zC|(2p1y1wQQo;$_zKJ4tMY<#i54246T98Ev4l#q zRpPl8S~g2az-M|`0#I`6e{secmFs0265JN%(_i$R4Q~ zT*~WV?_`sNnt(_XfCPiQ96H}@qF)dcTmDB1Gb-dsmdMSG4&&KT8Lz+^`h}AU z4cx5PdQ6h+Fq>1O{chFYmqGBfn$70E(`bVEe2U*Ono;O@W@>8E%w7k~jq*Z|J#Ks;TIJv;)6CI1#0yM#s(-+<*E87eTiD4dCT z7SmS{9!pOloQs@EZiqUiZ(*qDXg#jRjkpfm@oG$;NKev`Kowh03};Xgzn}S_gfp4T zCH!9cYlJQ0cl%yO>5B+fUk|Xdp7?NrSvheYS7IW431LV2auJ(AHG>%~KyY|6eGTEW znGHp3&D0k0>P%(v#PJzitq{0+QZEzYJU)##OY6(#=1jJP>(Vm9dR&qD%`&dXjx4D< zW~3~paMO%<9Xn5?_}Y=P*f=9SiA}h1ci(Nn$&{c4C@LBw!_+5MulBD2|@aDl^l_ULBvSOq7$qXDHuELw~hO!@uZ-|&? z20zfWw}}@(zp<>RrAllrmwtL-318Gm3%rBt^E?BNS4UTYDFo7ytl%;e6U+^50K`Qr zTVqKiHE2U@iq=KfM%Ot?MTv!sIeOjDvSEZ7rcN`6*5Ue*^LX9Jhj?xJzaX`EbKa$% z9=Ukx+DeSMbjzh_9Co{kH;Nt5qpD?~tLX|>mx>JZwitnVagz3GmfY!Sn#)Yn6Cli7 zc`gEvw8OO`wd2)VIYRkv_G2e+jMSexSxWapF=%$_&Jw26dUBpG{Y)ZeuF_yJhan2}Tur+$2um zllUE5kico+^B#5cufJ4`9d2E4HRI^8J(~y`^A#*Htrrpsl{d-ID^Z>+=ARZ2$8$yPWeurTn>Hv+qZQ3Xl%;m{Zl(zYisCspVtSIJinjoc<|v}d$yL9 zf6#rN`8-n+f3tIs??D?ve;grn(7ES;sW_xvrCZ|kH6PHV*-qQK>3m1UJC zI#Q?U>FncMwj6wE-%HPUpGk#NIuc~nt?MyOVBl(*#T`e^y{muI!(eQfV2c{=c`&^}_!tq0q;-8tBdeVbhtc!x z#f9Z|uf=X(XaCD}>(*JC?{01my#UwUHVqyjpU-DQ2M#>_^v+t%`P;>NPhAhK*|zPz zZRJnlS4yGKy`dLh`i432%JQ=B?myMpS^<#F%|72Z>>S(oheEL2H+33Q!(SGizfE6J zU7^44xgFtn>b|DOVd)Br)FMErp`l^C!R37Nk2g)zi{e|}X&p=8w=Mww_h{lnJ{ySF zhC*#^K59FRw^J7FX|aR?O?C^~zn$9O))vz2_qXlxA+WYk50=NED%&?}PwiyZS#GWl zzh7~OSQS0;z>DAC3nLOe=Kn`H&~SKY=$$(^fTptYj+#5*`QrEf`Fk%Of9k2m?b~xrGCWCs<<+q)(AZ4{27uowCE{sFzQ_fNSB+SGO~_6DW2xA5eB&4%2#9?UeN z1)y9E=Y{i&eenyM$OsaEFh8_=Q>Y=dLF?c04vXW6LsKxZ?7@>)gX^xQHbghHNcuC) z%qWOXZGL_fd7HTm^2K)Vi#6ws1S&TiS1p&3Jr+D|QIh|q7$UKTef!7x2_lS<_oF0e zelBRg0vitg`ATs4wQ&Q3cN=IhT9GG8nzxMnEpH^61oL@e2Vf{4v>|8UO_>kqY#RYb zvwK?-6pyQm-9z1EuZionvxto*Cu!DQZPd}GF-Px=#N%I~dD2bCb;3RKuhMLDt@h zCLxc%xf1-y0%OTk!?>~M?lv|lo0H-aYcQI!#vZzDEcafRQ7tT3#x$R@pd zDw5f*F&zp+hQ+4qKw}ZJr-qGfCtLtlu@9Xx%1~FthFGKD;!gAni}t0OU(Hw^GebPh zuVs`}aNjj$hUHae&;!b>cHk=aXW#wpb*T>FK!2DGGujep7x5oyaphpvyvSNw31Wem zsu3R_Onr(!Ew(4D*&WvO4vQCk*TuXcYlbM^62CnhbY2%f9-?nRTgJ`@e|Bw8EsMyq z%d|^YWacJOGN2a*bODoqJ|9#mcKn zy{Q3h9*TlFjmk=x_Q)T8mp6LDnQ2IYpi@DvFjLLS88gV0m7Bri~2 zZe$fcS?nQfF7Qp++2wzK7lm#c({TAuSZgyJ9=h z&hRb?u8|wpA+$Dj5?}pu6whiLrCL`RkmWE z+wHFU1ih_&S;Zh(iZCi;)D~NT`pryxJ3y45h5Nhhqz~e&Xr~d;hEdYk?5`xs=6;Q- zOu~jqRRw~f1!|JrJaKCaK$PyO!G%E7V+?Kq)YIc+$R*+GP6HUA}p z?%(sz4C>c{C^Z&M2ktHYJL-iPRX=9LUAz(>zoI-tp991qBVK$XOQe_8eW@LIN3k;? zJL1h}GAIRzJ>L9_3|!aTYw`Xnp@OaQ`6V-9xk}F$RNMU&gMSdjXe_snLXY!TUiSW^gQ_Vn$IAsoM&=w}4WQ-UI!5(b5 z={Rb%%G?TGX)XDkyToNQK`!(k%T55^O6wI$laYX0DNG71lx$NM81{7?7`5D8?W0ys zmpMvacr$J!N5h-z+JbXv3G>Fc^dxd9k z!kj5rL<)?A@_vVKGs0+Y7_SKd+TM6yN%VJ%VoRd*(9V(rEO3-aVfS-Wgvof%WXm(s~(dlFXJNu z8#d_&|V=P%QvJ#$Gt+FFhf$6g4kv~9%(E~8vXQK0XtG|jvTj|$eYib~v88W93_}anuPmEo%X5)(iIY<0pm~d^E zEU);MNwoZk>cHIO*fSXIl5q1 z$8nGYx|hxpx0m5O94s&3te5AryMqNfWwI>GP$I>>ZXS+g`9KCbAY(Yl0o|EHmy==b zllyHpNz z%7j2Llt}UF3Za_HgB)G-{5TGBK)2`eot>*!uU=mCKLP%~GUI@&6HfT%Ev+>1M3uXTGKb)1%Nb>;r`0CX4~k0?>Bp_18b7 zYjpJW(xsPHl`@l8&7{?mjmX%m1?p$wnP5!NC^WI|po4)Wq9~QOP~{m$y8UD(na(gZ zx1W)F(C;Nr>GPPLb@W)zx_R*KT|19?{sP6Hzd-S(_?>w4uM;?xVweg0ldX*0_NfLW zCf0=Xo%G>*$}^13eV-fNcK6IViynPq<(hRbXPAWrJmX(Di)Vy|(|Cqo2&v-%%CI57 zCsg1G37*h&0Ju;-7xHtV>0HRog#?bVWxn$(_Y}`muTRLGcHi_FGwne7esCHK+tWd> u4YuckZo2)vV$F}%Z1Ou+=R1lD986iT;6a8D7WjhG%YeIqS$S>cwf_K=nJv!% literal 0 HcmV?d00001 diff --git a/src/python/roms/klax.bin b/src/python/roms/klax.bin new file mode 100644 index 0000000000000000000000000000000000000000..220a168653f42f838a92975b148c51b287ec049f GIT binary patch literal 16384 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 Y8{qJhQfo9uc=lfnKV$#jeFVS%1R>y1zyJUM literal 0 HcmV?d00001 diff --git a/src/python/roms/koolaid.bin b/src/python/roms/koolaid.bin new file mode 100644 index 0000000000000000000000000000000000000000..1f12dcbd100c6819d2ff0d4e8e03bab35af7eb21 GIT binary patch literal 4096 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 M+wnW{>x1se6ri!=f2mv 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 UL(Ym9>T)gnv47tGYb5#pUtUBfb^rhX literal 0 HcmV?d00001 diff --git a/src/python/roms/kung_fu_master.bin b/src/python/roms/kung_fu_master.bin new file mode 100644 index 0000000000000000000000000000000000000000..3875821560eed07d9dc17eb9f8e4e4ad191954a0 GIT binary patch literal 8192 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 EfAma2X8-^I literal 0 HcmV?d00001 diff --git a/src/python/roms/laser_gates.bin b/src/python/roms/laser_gates.bin new file mode 100644 index 0000000000000000000000000000000000000000..587744d415b70265bd8b74c4ed4559eaf5db1f7c GIT binary patch literal 4096 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 literal 0 HcmV?d00001 diff --git a/src/python/roms/mario_bros.bin b/src/python/roms/mario_bros.bin new file mode 100644 index 0000000000000000000000000000000000000000..a992fddab38f454e7fb9b8b30fe9002ef5b5fd05 GIT binary patch literal 8192 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 literal 0 HcmV?d00001 diff --git a/src/python/roms/maze_craze.bin b/src/python/roms/maze_craze.bin new file mode 100644 index 0000000000000000000000000000000000000000..c3f142e3e08c176833389c19b942c712562ebac2 GIT binary patch literal 4096 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 literal 0 HcmV?d00001 diff --git a/src/python/roms/montezuma_revenge.bin b/src/python/roms/montezuma_revenge.bin new file mode 100644 index 0000000000000000000000000000000000000000..858309a4ea9da80c50fc8e9e4a9b8d4663d45146 GIT binary patch literal 8192 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 KB>y);{{Mf=i=n6h literal 0 HcmV?d00001 diff --git a/src/python/roms/ms_pacman.bin b/src/python/roms/ms_pacman.bin new file mode 100644 index 0000000000000000000000000000000000000000..4b10657b80af8fec7a3e4853c0a5375e8cfca92c GIT binary patch literal 8192 zcmd5hYj_jam3Kzc=wVqJ8w>(FARI#wwRIr3!8;n=RLx3i*vTHUoKZR z);mV=aJ@rs+sMcoC70YJ><^XStx-PID~ekf4)ga_Q;KRzk&;tHuN%h0?j1@{Bh>O* zQKPWp_B$$YTV$ulS6W@Tz;(OgAVioj`q>Fq`uqxd`-epN1-l?dxX~D*N)F%R@m<_5M7XU z_bS0Ov20f!lTgO-Owo$_@jxi(%#FN=?G7vcG;#!!NCcmc9D-hdzMShDfL_}x}M8pY-ZSP|}4AW1v@qT`p7{mQ9f69=X5+pA{79_~L1j$Yi zTY_XINM?dq6U3Y##suLKM3W#0^T0a*yaOulz{{Wg1H2iS#Y-@o*I%Lq>)0{-awV%I zn;7Wi0g4G)KzJ0V=MC_44`0AJf`lzX`whSO09(5bUkC%)0(bfw@cA6G%gS^G(f!}Q z|NAkEViJb%d=nmWFL@)jN72(BG|$14*I#x!|8?NZ_T_*P@A%)0xQk=D+?8(}CX}Iz ze%#yWz}X_$x@)ox7?{j}b1~H@)E1ds!^msuTJ}MouIH=$j)BW}vbWUAgObAm1 z!o`@CW5gWqpU}3;eaD+q)DX*Ye>^B6fMV8{Lzl1XT*rZeg{3wTdT_eX3(Io`)&>xI)kc9gsj-DVp$}Vyvw%HE z7t^T&eOswq7FKQwBe32)!0;SMcfa*!5KS1!WMCTQI9m|2jKUmuv{g{J$NLz_Q#u2&${2gXs7HSm^=x zic=G+g4+#%A6@GQQG8&chp7(?fDe53_xpevhoJhRDXN28PUa-=Uo|Ji1EBy};C|zE zn9>(uHGl;LCv%^WbZA;6=@jG$t#s@Z=7btqLvo5W;f|xHXco(8(qo;tNXbHo(J;tn zvk6I>lv*T6TNzj{^}maIq>Jz3-meXb@8Uju4u6V2!vtSwVk@N}@^dvH!~6tm6IYu#o^8bx$G#UPqG+X!`n zHc00im)4z0B>oz)!}51(lf+Axe# z2~HW0@~6Q7*%;a&XCYohhk>}t+qIV!4uUCfksok6K#2oDw!{9?JFvmJTn-J6n(|Ad z;STn_{2o6(BL3+t!V!hDuYM0`4$+K`S@(sHgdNH(9K@2~w{7I;8fU#{EW?M~yH5w1 z$mg)Sh6GQ9hWws=}ISAWo>x@7ck##VJ|Ge^v13_w1hRET?);dRSgH-$^m_($==ICgRN&DXhUg0 zoA&15t5T#o|EyUy``L{w-H@~1!)+8O+bBd{<7CgSk)7~%$~Ji0WHY?Y`DeAosfp4e z@FMDFlsOZ6+cui1p2F3WhEMR5L7?sV#!l1u2b|woN01MF~Vu6 z(I>%R@GiunWbzXHPFL-HJO0ZPalL-!%9VG%_~MI(6`Rlh?sw-muNWB_8NPV&NXAF- zG`oy zEUVFIv|6oBr{g$Iuh$!>hDNY)T3Q;+Ha$JvVzGp3B0DF1lltLB-+^GdFRG@k3?Uk- zIn_WOOz@W=RL(nR3DM4%$jZ(a$(^11$gQ0XWD&s6H8DZ--v`K?h-d@90$+eys|_kH z0!5A(YWE3vQDr`{G#Q2URoW@fP%B8NoukeHMC>^LW000dD68vx#nJetC zA{kXg>(KDLV^z4L>V21s=Nt?VDXTN!WaH6zwB^+!5#JP7qXCB7Bfh`a(b{_tG#?23 z3PmcM7Dt&}NX)XA7{q^5IPpKgw?57v+8OvQV0nu$=vby`#X`j>mMFSv>ZVx@P~W=O zzPBj4?rY|~NNe~%{b;sPz64RHms!Oj43dTMK^Sxu;UR42xbSS){^p43ILnI%gPKT% zV=Jw{N4z|X_>aeUgvxF_kL;*yRYoD=;Z57Qf8bdy|8b1}W{tQ1`VKG^TZ?BK~^&&ilmekw<6<|8@NT zM<~lq)2d*L{JxxmCwv{4Lf;pz1EbY~AgrE7`GuXYe(!rfUHj8xk4_`V1>j#mb5?GJ z=Q~@c(>@ygewo)>_H2I6R0)qB`_Z#r@3UK0UI%@eir6|Gde`R9&V3e-Z=1@GkO~Fx zz0<&VZN75(3Ot+gr-JXgVU~sCEc0@vLpT3q9>>kwavkmFVJhG{3EujdnTsBLdYE{p z%0;N)Cy)L7M+M8LIZnZrEveM;_^Lg1pJc6JA*o&Z=TP<@a|s1;xeOtHmcb zh7T-$E{I~!<@Vj+D-Rc!d3suCxmdC0xk~rNU6)esG$k{4?h8oiW#GK#H#2@2r;=kbSkgS}z61ex^K`1hY z9ff!}p7e?=6l4f9aE_%p?Up>u-zeY~hcjI7IEiufMytcA!_c5*X!x}`7e#AO2iX?+ zvMOg|HgvE%rErk+SUg89{E@f>sxi?IRW7YojUe?~N&%D~R^_%(jc+7W>)VZofp{)X z;_@&Y1!~25ywlg{t4a6{ieXSZh<&~F?&asj8}UK=gXGXQ*5zO266)X#U&r6tdb7%` zT8Ng?!U(SEtr2R#JT*b9uP(3F$(G#&wdK>sLdBL>Tauku;GBJx65v*KTSH*+8%ghe zC`tALGObB#FS=O8Uv^(e*}pNUR+IVi3aBXWCJyN=q-r0Op@3&m#{@6^J*w{2cT- z@^$-vUIKvg9(6mr3!1K0pe&6#SmovzB1?hF5@SeCj3pKs5u0ovGi5X3l?<6BCfP(d zMK5!T2IRojUgNy4Nn06(j(}aQbO;v8GWwnc%W5sripQO-xN0FRr~8Eql#Jpu_O-)# z%St%=Z9{J1g8lH2{p>|yBvk%k)>{4zesF9R>M2ua!N%n^o-;5P~&$l;{kqZ@*Yjti+tkog**VekHid2?qY z@S@yNTj?kst^QjzuNU0PAi_qgeB%#|$~}?~#k7QjbulVhc^$dk{aRAmiQetGNHp$W zB}0vU-CDx>YJ81iu+9fC`wbUqf$yEg zY7Gto1&*E~nbR$k9P{TR zlntWQuWfd=EqgxvvTgI{XJ^l!f4upi?U*ofdJ# zX;RyN20#92A^4M65~OBQYb;t4&I1e0mCML#sJS%7Y+Xgf_sq^BNPSrq#&TMNy)*R= zG;bJU!T|N-*8HBND^z3K+%kj*dq=#n@W+$VvqN1Yro%Kv zgSx^Fa*xpHm=C8-tR8sV;ilMe06$X?zcL}gaFi*WGe zlx1}6vh=tlNZ_`4z_7#HE7@Ne>6BV$35~|`Yl6caE61_qo;x~S&_k1gTp_8iI}=VW z^w>M0N|?$n$?3Rtyio^ta+h7!?M1eFVk;2@t*Q zE(qE%eEeL;cgG!8FTBB?F0UYgn?Zo?im8= zEUiMIn8g7hN?8p!>i+WRbV1>c@Kh$)L;^1VVwZo0n6j1bkkb^JDt1V(jJtn4Dl!0R zo(cgirFM#vem@>+>_$#5Bz1!dw$j~rH+b|#jHz;TTZj2}!_7n^&3;+1|AZY{$Zuc2 zRPESG=jV=%g2J@`+J6bQPEAidgEiXZ@W z!ytPYL|MZf^T3n5FteEPHe4&z!f6=SI_$(swSh>QN&$EZbZB(7P_tC1+L3S29V64P zm4EF|m*$Qa!Eh_)juXa9b)g9<4HD8qxlGA(S0*R50*-Zxy1y(_(ka~wMla~>ej*7S zm;Xj8a-AOzx2-&63#8#$@2teeJtM-u8B`myJZQC}%) z2NdnQmnP$TA&y`5QH~#v!9RGSU{gJ$0F8S_(%vvi$ER^1@o@PAE#L)xSyB`Vbi6gb zunoj4O@`X9w(m$zxbEjb1!BNC$s1P5lzXqy`~}J}ftn=DzN+%%V2WvSBqdEX;FvA| zhEY{?`R(D3?J3*Od?*0+F}HH!uc6_Uf78l%MKf)NJ1SFdQQf3hD+QE`hifiL80py$ z%dS!V2DazPRRFX>DW!Y+YBEPIA$byreTZnq6znaG?PohtU?=iY;5xuRMEU_?q}|8J z2NbXBp=t@2e5#69{~@RP=j#A6Qn!Q}gbuPy2~i?&YM~l3D!3v<6U`M6eNxBP)P#0- zB;ih&dS0=W6v#T~QYiRt0v5P4YmxqIyk()JWYz1WVhzzl!Z5oZ9iw>zJ}pI3xa0IV z

_n*6yA7;DDFj6-{n1kjSb*bP%lPR3mU!ka78QCz4gUsYUuJB$qZUbwOhzgmx1; z3~57I_+ z`cX>jT;(xm@r7BT+SsZwG`8|J^zFJ@R8sOtA-Oz;E~m`skrG{@=Lg3p&Vq0=hpwbj zj8>y%pk7HKc#n;bW$xdfxsGM`>{({evP=rGDGgi^FpPoKLJ{-#-{)Cr#l>f2WMpP$ z&X_S{=FFK{Sy`y`Tb##(noK5;b>cf4p79`PHX+Y?vu6m}2sJ&8EFMp5SZ$y5tmoFg zLun|(Q|nC@%4=Q^WF|_`xxur+gGxPXJ!?S-1^qj>p4%`>do~j5`I<%hZ*+hAps8G~Ng`(1D*3!8HGr&)*SLIXeiS?i-=nOJ- z5372@L_p@ZOx)*l-@G1lpuB(roi~8Y4G*K6uKeQ4$tyRez`Zm~ius4G{tC1kb?K*@A&bOiQhHIRf1eN|Bt5NP@kD=+=j?J=6?VJOe}=} literal 0 HcmV?d00001 diff --git a/src/python/roms/name_this_game.bin b/src/python/roms/name_this_game.bin new file mode 100644 index 0000000000000000000000000000000000000000..2ccfe68d26197b016c7b0a4a406155dfdecf43e2 GIT binary patch literal 4096 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 G#s39kAd1WY literal 0 HcmV?d00001 diff --git a/src/python/roms/pitfall.bin b/src/python/roms/pitfall.bin new file mode 100644 index 0000000000000000000000000000000000000000..3a667676af4db11ead4f74b22c811cc554e551fc GIT binary patch literal 4096 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 literal 0 HcmV?d00001 diff --git a/src/python/roms/pitfall2.bin b/src/python/roms/pitfall2.bin new file mode 100644 index 0000000000000000000000000000000000000000..9d0c0dd6e35969ec11b2747dbf99f76cff8878c9 GIT binary patch literal 10495 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 (3, 9): - import importlib.resources as resources -else: - import importlib_resources as resources -# pylint: enable=g-import-not-at-top - -from ale_py import ALEInterface - - -class Plugin(abc.ABC): - @abc.abstractmethod - def resolve(self, id: str) -> Optional[pathlib.Path]: - """ - Resolve the ROMs supported by this plugin. - """ - pass - - -class Package(Plugin): - def __init__(self, package: str): - self.package = package - - def resolve(self, id: str) -> Optional[pathlib.Path]: - rom = resources.files(self.package).joinpath(f"{id}.bin") - if not rom.exists(): - return None - return rom - - def __str__(self) -> str: - return f"{self.package}" - - def __repr__(self) -> str: - return f"Package[{self.package}]" - - -class EntryPoint(Plugin): - def __init__(self, group: str): - self.group = group - - def resolve(self, id: str) -> Optional[pathlib.Path]: - # Iterate over all entrypoints in this group - for external in metadata.entry_points(group=self.group): - # We load the external load ROM function and - # update the ROM dict with the result - external_fn = external.load() - sig = inspect.signature(external_fn) - - # Check signature of entry-point for backwards compatibility - if not sig.parameters: - rom = None - for path in external_fn(): - if path.stem == id: - rom = path - break - elif len(sig.parameters) == 1: - rom = external_fn(id) - else: - warnings.warn( - f"Entry point {external_fn} has an invalid signature " - f"with parameters {sig.parameters}" - ) - continue - - if rom is None: - continue - # We'll manually check if the ROM is supported because we don't - # want to exit early in case one plugin provided an invalid ROM. - if ALEInterface.isSupportedROM(rom): - return rom - return None - - def __str__(self) -> str: - return f"{self.group}" - - def __repr__(self) -> str: - return f"EntryPoint[{self.group}]" - - -class Directory(Plugin): - def __init__(self, directory: Union[str, pathlib.Path]): - self.directory = pathlib.Path(directory) - - def resolve(self, id: str) -> Optional[pathlib.Path]: - rom = self.directory.joinpath(f"{id}.bin") - if not rom.exists(): - return None - return rom - - def __str__(self) -> str: - return f"{self.directory}" - - def __repr__(self) -> str: - return f"Directory[{self.directory}]" diff --git a/src/python/roms/pong.bin b/src/python/roms/pong.bin new file mode 100644 index 0000000000000000000000000000000000000000..14a5bdfc72548613c059938bdf712efdbb5d3806 GIT binary patch literal 2048 zcmZ8hZ)_Up89!eB`hv|lOB%8y^VK@iuw~iR(;60~GwIrDP_Ofc#8taZ^!K6I+8eLz#Nus=+f#U_=KiM`lL zRPDLD_kDiP``q*Ip6A~EJkg1@f)H!U9UDmcl_Y6O9v!rfOR!Yb5n0a3tyG zJayrA^a)$5*Qw8>;W)vB6f0|z=w`Tip@t|Vp$ugd%dDCn+9XMjIm3h`%>%BXfDe;4VQag4T))ek4CksCmQ_@T~<7J-^gekW>+F z-kp^ZtlqtZ*IQ|sWTKPxGr%2O=;lorD?l z;@?u!$`^?}cf;(vcQ3GSLohX?ya0teGxA$&GZMwj%Q|t@a*KUKB47qCLP`1&gd`5K zAV5J>_9S-0unIIZ7)L=l(#@ zZ*J13x5Ow3{)9SzV@YCnF|kpVQ^@5PWV54Fvn?XYRx3o1RABt08s~2-ZDE0aX|qoK z9alh~wyJ>Z2C3{3g$kjNw+o7H5qY%h2#1#wXEP22mspC4QAX*YB_QjBTVX&} zHh@cXB*Rf3l2O9MdlTxo-dws$FkV)@5^cLVi&xMEu=0j5)_LLL4J*WiQH zhm9Jp6#;SVP?gEgTZe^tF=`zk)1}*Oh?Q=&;fl3FY&bJ%iC|(AxKn_`&L~xNRMo=n zRP@vL>q#$O#d)U_j(}CX0up3M_y|6h)ERBeI-Jf+Wa(e+MUAi!K|&4(^KV`iSC168 zO4r)jD}tt!Y->3|EPdSFuDh55<*w-2SMUe=d1F9+ohegqus;)L*`HwUl~lH|6w%>m zM?SibEALQcN<&2Q(*7ZWq>i!2E4}QI#%`srqLFpjkcAy+^ioeTS#}?_9~(4QjIyCY zu^QYm8!2w&gL?@=!Bg1|^Rv=0%t$5(;WouuM;iSsvsUP-$R{gC_Ujlu)!0Mr#Mwwggv)1T^~m*ml$thdF}_hX zJ-8|Pv7gL6v4qDMN@CH`@eojSIXX5E;UYc6liO}v|4tlL*G-EL9i4R@vEfGn>c0Xd z&@SApLnN!OQ(w0D%PwYq^x36cUZT*_0v>psUyx2iD1Xyox6H$3WpC8h6#RmDNuspw zEM#5o0u)@*EeNe$EONlY7IxSvQ_J4o5E$>k@`I~ZIvP@H3{&KR|-HLgMeN{4i zPXx|kaC+3g&NU0xVP}nspq>WKBgx6?8g~|oOw7ijpi?YIkb|RCF0tIrvh1P^fy$#r z6o2HZR9GH&=ovBY6%FOa;S zhhSW>HUDTyDJV_^Tejws<+{OtFxdUSWUoSR_I>Q&r@lQFC^2U;!2Y3Rhx!`TfQJ7?!QYQ4$qI^d>NjE?SMdg!4d}`%6{`i!A*>mw)<^4a|mj%zu@BH$$m*=KlJDm>y>hxP@JhqB`Ih~F~ zip6L&$^|$~zJQnWPXEs9KLrl{)KoAKoOT9%ffr_e7z%~`;ct&y#y>eWKAt-Ei*ZF^ zyF@&pZP@8N!CzrIo$bT_TOR99*tcn7=Y_}89mEkk*JG*j6{`f%>FBjut$wgell_w~ hPEPiJw}0}?naTbxz|V`5XRr(t-Z9rbAQao?{{YB7UaSBB literal 0 HcmV?d00001 diff --git a/src/python/roms/pooyan.bin b/src/python/roms/pooyan.bin new file mode 100644 index 0000000000000000000000000000000000000000..2578e2f1ac3098479a533e167d7da8ee453752c8 GIT binary patch literal 4096 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 literal 0 HcmV?d00001 diff --git a/src/python/roms/private_eye.bin b/src/python/roms/private_eye.bin new file mode 100644 index 0000000000000000000000000000000000000000..0d15e45f94f31518eb35fbde5a946cc21ea63483 GIT binary patch literal 8192 zcmcIJ4OA0Xx;MjQGK7$fh!}-Jx&_xz+s2;V%I?ELUA4ldt^Gk;U)POxb;0HAvfH}0 zuRa8tJ5pM^*{;}F+il*MiJ8=vU`yK+0udw<+_yU(`BfBbs|8Mf=2?Hxh0OaV5r6u6 z_Uw7*>~}MB|Gs;_pZneKesi0PR;`AWXi!*|#w;SJnh{x&vzQ$UtX=HH*CU9;o zbJRMwzsZ9?d|!Z>jhheP*Oj^7fVuG={3cMpZ~%W~J$7>3sLbxjVYGFg4@4cC;qw;> zXh#O}7g(P=)?Ihz!_JSq{=vrAaYo}Cn5*+1>gBl|xd=w3`xC6M9v$a7p!;V`=S?HO ze;v8ewL^~%^#XD(PUkro5WF_PFk%+p>dqUFlW=JHkFho;DRq7P^w4fU^$v%u7mi*f zX85d6Ot8vF#n4s$@4A8i2jl!Nwf^}SknP=(j;#MJPBZtn>ytO`l!gI;YJ64U?OQ@L z+<>PD0Z1T1<4#OXvjFV-uFm@1%y$EJuJq_l1Qf=P#3crKO<3zMjuUeCb??xCni_Wj zGyO2eiAih%y&A4*X#TiyJib`C_Zr93ny@AvjjLkZaaI4`y6zDMR?4pNDwD=0Oo~tG ze@9^A3%e>#-RCki?gB;OTvl1jLzL2#{~QcO@8DIj-1nHYA+Mx1U>w(FHuu^YNa2*=BD5(2srT|xFrJzhnBjV`Q* z{B=bDyhl-Stx44nKPYmw;(}{P%9?{xltqtWLH%{8yb;5L${wh^+4QalW@6JlZ?Key zW9c!2f3oP3=S@5#COPHf_ki+&q%uXF769Ms`Jjo%A2z*>)mT@1sMr6?>(&KWbWR4DyY(e84@xk6%%BLUh*4DWrU}(EN~4V*a##ImY-r9`LwE>skra%g+C3CNr(oIL zRa$5>Z)3j611bDpA|(Ie>p!&pL6$eKT3xaQK|SrUm5;2FE-6`E0`qMKGAk<(YOZVMnxS6xQQgPf$J^0MbpYOh+6tzk5V}#}{l8uP z7%+77kG*@2LLHTqY+k(?q0E$&G=^a^(YchAb2Zz~DP0N-F&U{t@nY=VTT)We%cL^X zO}7Fp&d$!~JF2tS@Jo!@YqDcqvci8HxWzVpu!v@)} zY>uV|PGu}JxCg8+^`L^eY0?CMeC3(DZ{}E+IX5lSR&lkDz6ox|L`avJHupO2O}$ms z)pB*UN>%_?<2(EKU~I)y3M{;&(P(DPQeU`U`{7jbEmeeS&A(e8$jn+>k~Vwo&#cM% znJEviS%az`s(fJUPyeH+2&GlpV zWh^V5smx>Lmh#7#y8-%QFgFH@Z7Pf*lm&p0Qoe4S@e^8p1_^z4v`MGb1F87NBE90ggXYTWPh%F@7z>)6o{#_;~K=2f8&c%K=C(mSpN)r zGpG`>Dl0}pS2dcSpJ)8R?FBzy^@Du0t!4gAO9++J#nA)pf+G5$RGv!NNP{EV}2O>$BZ3SK*f5|Nb8$d?%4#|N1N98j-DEg)fFrEuEjo z%qR6(dSmv|nwq6~)7uB~t2<6{2jCPcD}%e&aIH_4*Pb}K z>gb8JV63^usJ!($xA`})3%0pjap}dyVE5CY?A9;F$G)%T$`sSn!S;)jSPs;_yCY56BV>V0DmjWRYV4)g!@9z*Q+`1n{Qq^xhobK0l&?ce zX-H^kDkQXYJ|v{uSa(wb90kzQ??FOKNl4#;L($Wc+?1P;3F_cK;TwG%hyPqUIBAuW3WE7E==&nVD525)nWQ$C6Vqd zw58eBmul$Jj>4n&ht_xKYRKEjnxtf_j&wcsfN9+bRZ6K#P35{j-qsQBphi~EHj0YQ zUSgx?p9dZaO(De~H55(J9fdFGs!Fdyxs|k*Lz;hR$>IA#iMLu06@GpH;T8QW+LyG$ zQv*g`Rb{nW$r_zb-A;uI+fCt6dnk19;KB2uPh?~nWZaMl+r7rNa<#KQ7DH8=!n*F zJ{D9d5jh{u6|!=3vC4G!Bz#S2A$V7Fi0?2y$R<+ zBb)Ani-_b#4nBI^*wFY&Et`SU*$k#XHn};$7aWzbA$WXB`SJ^XCS!eYbPB#VlQGTa zu=7~J4Sa73@8$j$VjQm41ye#Jd@J9I$-r$>c)J@5Bn(uCGE7Khb!?JL!?&BWBB}mF z7mDZ{31d31eXp}f16&{C=sZPQSG2x%=Zj->J-c)J7~KsZYGdulo@l3;5z{@BLLN52 z4a7pM7HSk~E)C8SQgF6{7T6i}1)hoG^uS-Dm=0`>;*5Yj3QA26ybNVF9cR?SlF?x| z^B3VZt{oGsYR4`DL;_LZF|b+6R~dYCg+E{}P|Xev+{>(;f;$Y?H(rRJ5==)Y10_)@ zt#jBx;9)P5$bVu{gS)|>2!u#Gjb()UQ@{#O@@D?zH=^Q)9Enf|ws`)6DKP173523KpNR`0Q-6j(bGqBbyIcf}r)wid ze?n~}RZuy=CxSaP;Gj@6u_1yZPSaBox;xc!O zbRT~~THHA#GWLJ1>b4-{^0sBaL{ zgemtWh7eR6+Hf0xn4$RNMKV4#RlPaA}G=Lvgyi6>rc;nE=%h06;b75F9**>aazl# zQM-}9Qd~Sj_*KO|zFnHjhb=SIhs_3cwU`J(sc=w>+Xdv4ZsCtxh$7jXUtH`hOTe=Q z0!|*%J|ZOGSptFQuyak{oIG1C&OCdVKWsjX4}&T+XTxGb-o~(S2PVJ<_g4{odxUUg zx@5i@y3>H?c5_tJ+uM0?G>KLCFn2_}(~0prRX|G8M;HgCU??2m@H;g`v45tgDrla< z9bnJFQMR2sWYtJ6RP4{=?GhQbGk2e@;;W#uGop5=>Tnefds>l5=V^TPd1zeb3^!aC+{bC9&=XvMV<$f^bAN(F)FP?T~{RgM)_tj8@5); zh(*+!`cImMPkKdlcem-#=rMLk)sjPf0=E= zC+x@hwn&P52)t5RRpeE0=~Z30&F5S;WFHbKejpMOXS0_;Y*3h1(Bx`yf3Z0Wx0fDqHdE&+wKelkS)b=<=26AZ~cC+OLajn6g3x}8El-i==qW-1!&bGLG- ziW`&&PR9n|EQRAvVBwfMFJt9c|8wB{q!&!eoFEa2PhywTtVtZeZ#iKZVx|9refHmtORUd&W&-S~EXgfMIxM&^ixlh{ zHNLDcEhaACr|7(R-ahsNtMjt;^3`oaXt37#&8ugRe0Y-db2vWp)M&&J7LV40VwjKZzS0i2_(KD7?n&G}CxL zFTkl(;BN4o>=xC0b+JE@-(Ra3Lsu7gw*yXwWCa&m)?jZiPk_ceARXV}RaTBJg5iP* z3t}JNls87iHhB|xpIgMA@J-U7`(%i4P-BGUZ@3BYBNT8?6&dh>@ByyGl@l`$H0d%>~sFal0z-b~f+hhh$I-ey!za2P(^xK||LaH+wk z%!UncZQi)oybW4P@C%{WL>k)m;eD)^BOqRiY3-6_H9u7Bf6%mdQlcO_5u|@}(Mk}r zuyaVfBSecSbz%}L2+M%}9K3H-$PlvwJrFucS;{&Ahx2hPzG0GUnZpo}1tzTne^(}Q zw}y2jx=^x2 zyAYQkq=+OqRF!?dNON=elM(?F39QE^Xz*So5CyM9VGZ{UOR{R0N+=wM&|ZtPSrca9 z4~J9*#hzoIS8GpKu2C_EZ+Gu)bU+Rrjjt$Sc{&8^5W6ceyqi-(S3AV!;k`_MY3oNaEfB`-vPIlQg&f<(D5*dYS52}I{*iE^3nW&0J|gfZ|)bVE1p z4m=uF0-?ZT(G6W@STS&T-8h1~25fuwynztB_UzfCdcs$}(y8Of0P`cMKvGA8A!fqliYVr|cZtMte)yWsxcY@XVe)b7lfkZk=L)2qJw~{m!4PC|b5_XVagWhPEiT z8H0ndXK-+^tu@%!KQPeN0Z|X*d*_`>OH0cWrB9T$v^1Z93(K}+NA&k> zYR_2kyISVQzwkM1@L2~}&wU7TGC_IMG}{N6EUQ#X2H&=j+1CFqe`5^t;d&1s&sb@* znFs$k1(FPElKJwn_zmt8IM@T}LfZ>iMx}Idv~|UAw7P?cTjx zg?8_DAtyYq!Lth<2RyddbW}p@mh%qtnDFJgyU@zVpIr6m3<-S!7IvJ8l()oMU1)l&@8X(@_sK5=5W>(quELei>*M5sVv_B7H6k>KvY^?E zlJeG3aIH^LmPLMlj4@@loY<8#+{en%$q8vBR0VA@h+kFzl8EQcGvsXrSq4p9)iRPK V8DxI1dHC27xS;Nn5a8`SEtkd&}nb-gE)HkgPrTk z*cpo{-DgedX}LM@>~(IMEW3e4?FHK)FL|TjLQ+~+S}h!)ITeK1%Gz2)Xyv{ew4>hr z2lqTl_IbYheV)(v{drPe+JZ_psI3H*(9j$-+EZ3mLaOr(q(yyF)SgW%ER;GYcbDx^ z`FEGuZOCr7(-fJS1E~bPOp&GV6d2qoRO7gzh=Gr#B95y?Q~`rd%!p{Lx`M%s6CsrL z-;LO(Ir;RyJNr|3(;njSDW3{r<9iC|qAYP2> zmBm=BMZN}mO;|Pt4%^R$>pE)V<+^76M4J?pCozEup)0nwW*diCBU>QSxKPw!5*LlL zTK+=YvM|4aHVUdBO$@;nircYHbYMG2x#^#9HcQ0eL9+zA zl1<6we}Kzl2@zovE)-Zy@!sZR*d}?a8$+*03b`M$n9E~`YD>j%V;h1E zlaiPBippcJ0)tQmoaw>aoCCLEgpjX~|AtXvz(7C4@Lm;1>I{9TuKfg4NudQ(tbHD5 z$fD|1T`@9`!+*+9pb*aDt3vhR;+qVGO)7L!=*86hic`KSmQraj1?7ZqvCj9o5WWjF zX#s}Q!j~ZEk|53s4S}?*J@j!<-H-p8xn;gM-9L|h_7=J~wA+ufQVOb?20B9r{Hg%+ z$ftQPL#RW^>%Ikk1?%`Pgox_1yHrjlh4TP@Tm=sTxNuR`drZ|^7{9Ga3Y@PJ6yU@W zoEhQ)9Mr}p1{ADrY);^Uc|Y?r3xS{Xl(- zrNY4=n-v}kvL+uE-UjftkAo~IFYLpV@JdUoS(cVX4LQHU;f0sz=VL zHrZc*_$r19o`^76!B@kKiJxS2!2^C6cyF7f9}{Xw@toiRMIeMSReR6t%}@dlV1iHm zY2=$Q75L!K6vDP-oc>~*y%$*ZM#}o_e%NJ0_?9{5jUajBbYh&%WV2WkYx9udUEu&y zs53|=#bh0F(^u@z1bqVvAr1IO6}>bnm)iTnzEg@$Iwe1-Jml+eJgQU(SLsYK9nW&v zBH9}KQBGaG8pHXh@n9%z(#=6J4 zU}vdY>QcY*V~J6vAaTvFhn>31{}52Q#{geCoS3QU_YVQcap7UQm#>F1g%8`Of}Vwg zFN75+6Vprw>=e!O@ACC4+nNGslLs;V{Dmm#*F(LkLvl%!@|y;ig_Q>e9bx%$TWMIH zh#LI{m?L;#jmPO3 zvDQA>jukwEPk}kbC-4#c5%!CZ;G_7TxK+%sw}nNh#n96*;z+@3*}1l{`B>dScbuM{ zpWD>=2kIOOvo(yX#-X4g@uB?(3BeU36-~MOxX_^FKv^_sPjJ-8KO%||xE*FNUyPMA z0T}!zYPmE}3QmeWLc6?M-0EvhWcf9rbHUg;IhUWNO`gTfb&r;rb~8zjo|)pC9V8Z& zEcHB*H}MVX=~9O{{l!-KQBhgm#p&XX2S=hod73|Qv7shQF?QGc$*~xn1z#E@GQ(Ua z#m0JQgA|i`Xj06=J;H_JC7U-lg`yEGf%qDklv+WBzqASxZWWppv#-HH!?6_)&a}57 zYD2ZaS?Of~M|hoF3ibAvgO8 zk4_DuBb+91x$c`deFK&`o{T+Lv&6GOXrPT8DQ-V~Ss~#~qnN$Aig7ULMP< zF-n)@d@&ne@^s62(uADBOo)r@r-EXU{B3N+9~Sj^n7Pc6fnmtT?7HDN{R2#nsON03 z_Om#BO=#FSx$#`gE!#MQ;2)8KiUwT5_Hf%-kqtn0#W+2CPKe z@kfpl72Fy8HYjvHHHSNYh0wB2n%oSnvF$g(R9KxC28yrZbY-z`nznM8EP%69vF$b5 zanF-7$q}9x#C&^KSdf*q9&`AGa7h47Je)F}B+FYM9>yV;FO0Q8_GoJ|qUIXXLo*lb z;~jRT0}=&7R%ngs$`eB7|;auHzfMOj0!BDY!07$LVt*dK!qXYxgjM zEI;I1OYh}$YIwvQucv}T=o+aG*QAXHZ?p%Jv2M8#yc#P~jA9AI{{S8k(_})_q+(Ol zzmJgocp^>Kt4!I-8evpBpl#Q7$SWb93~Ee>b9I-eK9k&{4|v5q+^%}147VEaD5qhs z*+(KCt=q9Q6)&+O`C8i%#AU?60rE_><&Dl@x~mD&$N}|2aDm((wWn@7DJz{ua+aH( z679GZ%i=;vO~LITW28R^Do&0_o@nzM$J|^R+a_NSN$eJmYdf_4T3I`)JtaSP)I}64 zv6q#?m|rQ5C6pyGSuT_6p`v}oo`K(EtPp39v;B_y#_Bmro*1hap{hvr;v7B$e*TJE zz#ikQz#*{xoJK^RG^p017P};uc=Lo?&cj`}8@KvO=>jP(>&GtOI5^VRB?t=P>t?-2 ztBB2hO?hccs%)tRMa8hpTyC(j7=9(}oYeM&T;DsU7f%8MZ>%e)k0a$c!rCo?;g=t5jbc41@ z)0T?sO?;BMA^ZRfv_WhQz21Q>!W3TM1cq5)x8fi0l#|$Zavz~L=?S{z%@W%2jML?z z7ZpG9#Fm$KzV_CBh{-9>GRSymRnKHk)w*?Cw{G9Qee2eB&<4@Jpbw!TH7V0wmWp~O zTCv7#HrsxGHZr&O@l~rVXKv`=^ z>lv%{dCp+1t+g5s)oy#o)zC8W)`HN%Y zZ@rE8z2o6{Ut<&dX|FO<)zdY3`O=+zXT^#Y1#6$(^=?BT$p>naI3Uh#Y{tz_x@1oY zU1E9jrP4e5mem_Ks-+(8y;GbPPczrqBzwaN5z-E2TCbi#vuuoQa9Vc$va#5NI}fFf z4EP5cGu3D&OubL()f#2_=+mm+YBVE zuq-Ppd$ICQH~XS}H(xm$jaOE#F&mKWJ|I^1MSt8oH++kPfcRqN&Az>JKmJTn5p}w| za9ysurfF#!eU7ED*zxFZ5s65WB+}Ahf!Q23hihwH@b@|}?QJlU$;QSoO}ipif{Yae9Tv;f?E>XTOK}C4_$(JYYsHBj zOV-|7IfgjXXYcpt`+na0zVG|K_tmo>34}<*h*OiwqQ|a@WnqoStqEH^FD~LE3O#WI zvm$yeF77xjM;`Ia}K+qIsTOI>VAm8Ih@o|6WYGu=WGCQK7w_s!L+*+MqB2z=CktbHR zsY8D~`)V2Gvv+FfZBWp$tZ~DnnxATsHr*t0MIMM&ABm6ahcJ^{I|aL@C8`GNT!EFB z)O*vXP-$QZ)c&V0FnD4agKbQoRM#d87O81eSwi*l(!i4P)j-#ksVOmt>d_s_bZRI8 zc2kN}QlC`+ca#A>eKn}KEX=VYOy9$A`C?~uYC?{8msJmH2tJlbw)4f(ttQT$jJ3?H#33T|ifyZWbMU=C( z@{dzfQVo_TSU1~q#jR-t=ab@jsEf%IQ$Bd)kSsW`-!=e(Y3{T1=Ap(vq%l1AxBB1=-k|`m^0VQF+PE1 z-1S2^|ayD*#-o`cXM4wt$G)QYg&L~6iHqmstEc?I44&gWRdja-64d?QE6_e+GG^IgU4{^ND z8b&AZ1ysbZp{c+V^YKalKc{(!lm0x-Q3^BB!#9Zr&*NHrQX~VfUBl-?27!Xet)W*0 zBRYR^K{ZDfAPZcNU-VmV@G9rlnX!Bq=PGu^ z#zcB_lzCU|5^RtP#l0vOxc6FYOc+$|#uLl}emO5lOvs!B=Ny<*47LR0@UN{M-d_Y~ zq%73ov#yW{U5&iVyYcn-g6bVrY2H~NC7P!@lOnBHRFZE>ij;g^d88dK?81KLA@ItF z@cDe3)I4wIigNK`hBYB=Tvn8C>uPubk8QzqB zG1fa7RTd`Yk}}Ko;}bp#orn)dqVhS_70-++b&(#pxnfu%MHZd#$cuq*T$eM7#q-{x zz(g`IpmUic$3(mGeVx-HeFqWS(K3WY6JfvgOh+DoG6!r2-uvmbMkF zoC91q%6L^$E)}ej!$WAE)FL_!pefabhx4>#7M+)dL3Il{4YMuHFb^VVd^(;&smN*l z#Q}5uJS}by9A4yYIOE!PF5e*4iEWp%wl}A^fAki+wnyW`7o!{*kBTPL4}0WvjhjMr zek1q2(BsN?vvL_wA1n~+x5NjP?V=@~Ef_(nVe?dSIH%<-3RPP<{AN_S3srQ4E#g!0 zew7q|bZG{a0Bw8d?tL@M8}=6cyO|$IB#xqrPzS)IV7pk6=)R&^F{1t`uGnm91B^Ss zKX-eJ%m%1=#T;T_rNtE@_suA772;c=Q?QoDqxS^aD=9NIcZ}G(i(xdmex6k$RN~hm*;nuiixt z^$ZFB8mh1G^6pq`~NX{oU6S87}Dg{j8Tw3=?6cdn+@t1YYPW@O`P`ckH0HLd82&GsAyUrB=2c;(YAZ; zzaOgQ5rU){qK&@e+YAL9Fa7M$(~o}hPK(Grv*$inI9E^?DplRu{%k8PrV1TF^G_9Vf*bVCvd&+td{$Vv#?bH$@ zeLg3e?$Z-Q!nJSu#*3xH0vE*=ymt1!-8;9Ri|wEewLRS4{@wQWhuhi$fxsbc#jLej zBaCnSo4@h%UEg}Wou)M+4DK0*8U51?hYuhA^zh+XgJI*wjT%9IK`==~{eD-1XseEo z>;E5oF#daad|dv$EPnv~gFC*o_6}gKXnsatMED<;^~%w!IV^gE()uaAaj^RAGDC|N zUKw{oLub@$g7LJ~7$}ma^uC9Dz(eXS3f^O3eTr&wP!y4@9)zX?+C(*3ZGrI~l@;vRvHeFFJ^5$Z9$4t6v@+5GLcwvMiC|HAwkh_KrU`0VZN?aQSkg{9`^ zU;W~R#MeRex`uTPF8J1Y*z0Hk_wIx&rh0LYYJ~vrhD@bw@ScDNLw2-qk3tImNEZJM z0@UZ|HSw=#2+86gdN~MhuWscEgemwH0KX+zH}8OFuSM?%DGL0W*4>?^D2vHrg2Lp? zf1E$sC%yF3*(Y#VZQ3~p8QYl74!#k4<79S}xhmMwV}qZ>J~^rHX)Z{(&9}3JN)Z1K DZS6^A literal 0 HcmV?d00001 diff --git a/src/python/roms/road_runner.bin b/src/python/roms/road_runner.bin new file mode 100644 index 0000000000000000000000000000000000000000..1451b61968705fef87d5acb881a1f88f5a0a9859 GIT binary patch literal 16384 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 literal 0 HcmV?d00001 diff --git a/src/python/roms/robotank.bin b/src/python/roms/robotank.bin new file mode 100644 index 0000000000000000000000000000000000000000..4471fa1e8a4c1f7e671c2b8834c6480133f2789c GIT binary patch literal 8192 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 ZZ~bg=5O=yOspj{iv&xN!nDJiKzX7>!z&ii{ literal 0 HcmV?d00001 diff --git a/src/python/roms/solaris.bin b/src/python/roms/solaris.bin new file mode 100644 index 0000000000000000000000000000000000000000..0ed3d8d3d8cc113648742e17407ac148136de739 GIT binary patch literal 16384 zcmbt*3tUvy+V`Fd!=B+X8$=y-gb`60?9^zMQ;{Me0ZvBVj+xbDVPbWTR-UwDfI4d$ zL2yJy0ZpfAHnB-MV=~yJ&=hJ8-gc6TdCI8}(X=)w_sjmCwSn@y@B6*K@Avzj&FsDQ zT5CV+S!+G(^8D8WDG`fABH|AUCId!{2oH~ro;K&1m)7QRp>oxzh?t2FPETC%=H?wc zT9rX#RFfyonDNBRZ*49tbhIheV`B9)ljkmad&|y`KKiUpIewg0pYr6A3{>Q-I#hQ@ z89DXQM^cu)`VX7US#_-8jxy@LM^m3&wf_BGW$xqOHQiNaKR@qLtu{7x^ypC|M~;+6 zDI#%=6$uft0Hrc2I7p$8Z?#w~IAZ_y?fDQP-wF{p zhf=8w-IkYIDu+)087)Sr5^xTaI=yt!TH( zBkFy97jeAY5Y>7-&m-=&PIvt~!&g!BQ!io+e{DU%P-k=+=eG=jzU5T(WnX}6MDt~< zvVtKvNrjsjBkr{kCr_R6GE%)5+@%df1edR9FdEVRV5;Lbzi9Ntn>iLLJ zP~sD%7PdVmKRwkUp-Z$oqvPWuCh6q zCAMn6lnby+4UKKgO7yeS$F&<8+T0)AnBWU^jm|Bv*R1WlA+c3kCoo?0GZSHR8p7`k zu^)f*P%jtiaT3~WU#u&D@!On6Mtr=~+K$T|EQ^&q@9T$xO8in+sGHVimwO%=(9TZf z*BwTs4B;1BrEYg-AhLnDwHc+WZPm^^&#SFk<$HC#$89bda1-0A=@MTdt9Mr)cUwz* zTU|C(&5)?lR*PzxYsk+O+bS8t){0u00NX8ei=mK{2|?F!^T{bT!&hz{ZI7kTXGT$C zyQ-*`&P&UrGS00w+-gfpOVb9H(>^Xp*60eL^Ddw4UD-%CY9-~Ko?aW|e1z}au&+PO zDo*7y&emcMQPTb7WdCq)q0xPo{iYi&DRxbD2 z>WqX~+|%3B%hUN<9jlJXU-K4S&;4B+SP@gNTeK$FU9G!~u_7ph%TXxy_?-M{%LtCSgQ`^`=;A`phO-GWZbEpK5| zwp)g^opg;)0~vBq4HUkG-B0h+D%tTM!YJoqHryfByvMf;{g~g;7e>RLyFLlqO&{Xo z=r4Wgt{A89@;iOj@lIMd2JHqt8wwsxWXnGfFa}eg+^to%Fx+JN53v`BE znJGTL+aN zJ^p@F%(R2@HfVJm2|W>xwVIi7!AUn#Ay%n28#NZsy{MH?q`OWVsM>I+5A9}%*NX}C zfnia*yOxz`-nvWsG9^`&$IfA`L3`=*zMR$2z2)9ctIRXCAK8jEVZ6Il^8#;c)w?UN zozpM_s*JswxxD*2j=T<+7-xyDNR^j2%V~ zVXM5S0W$Uq>?-@vE)Uy>S$ZR3uFBJDT=F!iHOYw?{#mxq6_A+@RmwPbz$Er6Tq2{s zy;3uUPu>O12Swf6d>J1=U+z*2%BOFyhHUOz&a++-&Uj0h8lQDKsj*%^D=|o0>1tnz zbvew4kHYW)RW&qpcvD=>gM)6J+coESwd--1$;SOTlVwz2IBX4ae*{BuB7EdB!a%!~ zTkasZB}ZF$(5g_V^E<9}FiraWT%yrk{X=!HRrA9az1ElfA}#{dPnZ)ag?4tPXdNEd zsYh#j2eXgRW}8bZJcoMOhiYJSCAjV{D|I_doCMZNcWX&2;l5sSop9eOxkb3^O6mxA zWl1IBt}Ur0+|?!3gt2UUO%8`HlpV#7rm`CRxaJVSL_l#-Rc$b(z@UREFqPM@yA30P zu-&$B;-a>C>Sv?*dxtb8zsUy602cvM)UDL03%9Ha+byhCa*3@DHy`(+HoK}8BqNU% zTBW8!^Y`BMt~piJ-V)biRh3?;^&#(G*EAc9F|-p#86IP17-OCmPQh%9gVm`voh!Y>#bt@JCM&-Z8+6UL!0?htfQC|3wO|NP1VM zR6F2X;)?JETPbveZcL+(xFnfN($K9Fx6C$rw>J#LAygYq_d_>IaYGsG-LMGla*5F{ z;Gq?bsqTlU;()U(XAd|1!f4Q0W;Ux623rJk0{sNJAe&t3rL6yui(PM;ZO-bVYM2`; zU8@;CEK`K-I-_D=@HqPYA{Ut{wcAa#tWcI#S>^ODvWhc;c;lp1%(mq|F<@i}H zj17a99|;2KZ>Uu3s%_QHI1yGdx!&f4nO)`iiBF$owgFphV8BismmW*+vQ-1i1kd$0 zmmDT=Z4M`9`c&&GeRf<=TW7saV)(JsSf0!Ef?}j!bysJK3}buTwV9MJ+@%IhU2ms- zDl5qb$26`X?Jpi4^hDT&M_p)}jAi(`LoG=%rrI=D(5-d`o9E@AtXeI&P$u&wWH*BP7WCFoxXM2U&KMF zjYM8vo;c{k$%^rs>C64o62w89AQ{5kG=n(k<2`kCs;djVb#=gCegjn}esO-I_~TDh zS2N4S&!y%kq&AM6tlSUp8>X%Q1k?O{(mTIl&isaS@ruI2!kLh|yhU|&_q1K!*AL>z2 z)Dgj?)R>^t5E2;~tkT5l^k9aF?jK8zxO?|b=jh1~_dVX$cBfM#lgR>ONL9cXrF<-Z z>{wG%@~n8h7)blbV>2Ja--O;D85%lu@!Z9+dd1R|x2xW+x-w4HH@>?mW_*8tY|H&W z@-f~A&hd&L$4t6C{&s&zzmyCP8%ahIV{^PY30}LOSVNL>(jOE=1c?L4pipYW$kAiQ zjgQhq$4r_MtDSn^U+#ZEH*NZZGah<)rv8yf9(^o6{_$C}W+x=fnUk3K#1lzLbLS=} zKl$X7DJiL`scC8R=FKx03{L^gpFjU;pmZQ;q-UV*7A$}UTexr$ro{p+5orJA4{Q;a z_aFFRPw6EI_iE|B&kpY2Zx7;U-+)!JZsI*E3{hAo;iK0eg}{hsG*_Z+i%Zp z%e`=+p@BVnIv281RA{gw{ps|l=RakbM}PQ1iEU@$j?p7Wgi*?n;0aOVMn{amS`wtV zH~fcq!Bm7wt%;$?R9NV7lY9TtI2Lhv2lz02_Y7WSUm)DH)hsXtWciBHtRT z_(aj%qT@TQVtVJ?qRB-WCBibKf%yogKOO01T@lOxOAbx(17MkTxWqR^dDMYZl*RlnMP zExP5tUe#=CF1qe^R()jqsL1KAtNPmZby1y>|F4wa&1)XGli$gM9%%M;=6CUYUw=;z zyurAaXp(wxQWTN&^x`Kq^Uv|;cp?((%7c-_Y~1hhYGSrd7b%%Ii-?>kkx3>-5;EEB z$)iGL#H@*uSvuWpoPU;XS|kCvWs(WPJ48j|{89Kx5Ttq*)duQ7cN5hQ�X~y_Q&i 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 literal 0 HcmV?d00001 diff --git a/src/python/roms/space_war.bin b/src/python/roms/space_war.bin new file mode 100644 index 0000000000000000000000000000000000000000..b7b2674234ec5bfa9569a745882cfbd7e6cd2322 GIT binary patch literal 2048 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 TNt>NjOadWkkKKl^E9Soe9^HUA literal 0 HcmV?d00001 diff --git a/src/python/roms/superman.bin b/src/python/roms/superman.bin new file mode 100644 index 0000000000000000000000000000000000000000..60b04faebd4dd68d8b1d938119929070c066ccad GIT binary patch literal 4096 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 literal 0 HcmV?d00001 diff --git a/src/python/roms/tennis.bin b/src/python/roms/tennis.bin new file mode 100644 index 0000000000000000000000000000000000000000..faef0f0d959ee1acd22de59a0c598870fc515e05 GIT binary patch literal 2048 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 KenFyi} zG}?1NX6DQ}zwz*6g3i*Rx~f_UxZYX~Xf(+#*K^CQr_MP17c(V#gtXZY#HH}Y28c89u=EFyN*jNX(CfmDgON6NH*`rO zcPGO^p+?UL@C}>-0oWpslN02)D~M@f$14Le$N|zMwc&%U)eg}fJtf}$GJ_H+bcgTV zp&HdQ5qg-0G8#RU*{3aO^Yu!mmEc9bN1I(t{Gh531b^&2O)y1+@8k78j@Q4*S*(~! z=X%&uD$O?eAIvkAsUDoW$=kvm+{>25GtuvM$W60YnYUVDS!e#U64AmP!1{ePgXl6H zxw#iig7nE0K}uI4sVnp*Ei)?4gfV|H zJMhV0t01%N?vrh{ZRmyV=-MtIhXiKJa3HxYaiFlxJ)pcL^htqpeY6!)oGrt_-W12! zSwnM>cokHR@!M>vv$J!8m4z}tcf1Z z<^PfvJ_n;`qJ*Mf?7}QCaY+fQWNkEjR$rT>wOr(^Y!M9j1oZAmnl1CD?2=q_PqsYQ zUY*GGjHI2{tx~4y_}>4oZS!y0rZ{XDHhGiuQUq9;``8NCq^}=YnD0S{DN$8GX|alMD!B-Wsx6q2M9W9(O;3 zgyvRr-y|+vAztFS5Fm9i&2E|WF_8M zzh9`fu@i&RxH9xdoKZm22TAa9oYCpKvd-LB$v`b|M+d1J$rq;}Bkk9LB{Q1n40{Yc z;Rhmhg)7|S=!JJhle-US<~vo^?e~Ay&vi$!XbcL{vw-=pL<6w9x`D5`nAfwozbg)l zpsNzf2FRd`%zG7T6$XqxHb@4pXDReyXmu~CIhsXfZ04=XsyC^*`>FoeSG~!-Jmzn# z?oIFJEa=d2C_dJE9xk$8^C`GHdVvEFSmN17~!2uvhgy;J{kb6kP`y z9avuPhdtOmN#d@Lbm#0llTczt#L`anZtP%5e3 c`k>^?b>!>|NDTzfH6Lneed3qlCy_wt2R4PRzyJUM literal 0 HcmV?d00001 diff --git a/src/python/roms/time_pilot.bin b/src/python/roms/time_pilot.bin new file mode 100644 index 0000000000000000000000000000000000000000..37347ed1ab4dc355f7400ad26bc16de07904f95a GIT binary patch literal 8192 zcmc&Ye{@q-nl~>$Thitgi=+_RIu?f1>ck)GiB6VwQB*t`ggvgFRMsjRFVI_Bv1+qFd#3m3575sV9bAPNFg};Vu1<)fw*YB zf7DZfbMQPDw*qGByXkR3hwB9=4hR|eqF}`Ti@$dlE%A5 zyq(`H&Mw$u%k0}ir}J(}Cm`&``Zf0w!(yEtMo~;Zp;phZMP5|fO zK*K%Q*XIUiYdN#kS|Z)eGv@WGh}KUD2>hCkIaq1Za!h65O-0KBpDk?!)W#lI1P>oy zseQ|;7Z(7&59DnD;hfa1C@D~24jHqytfbkSlv50m#V!MFHuHf1ED!)1$yQ zpHqhfVh-?=F`^9`Ld~FlvrXMiUvhD(1GS>@z;5Z$@M@Wv(wJ_r88b_2_4WMO7R10XyHDy)@=AkhJb zHPR$+_ei#aJ+>l*q+u92{P~<~M6rKeb}T>XlEFEGcD;iy|dt)9{JRfv{CU?SRm3{cgghl^%5d@+yZym!&-OdQHRvWW-#KWQ(+* z0jFx1eo==xU?1p@ruln$PNMJtSz>@C3+$IQAeAgIzyfpB5Y#YN{K*BEyXo@^`(+nj z(ZGCNebujaz5^a5nW%1JQHYPM^sBl;LgWcQC?0AC#kp|J)c|L|$VqqDbdY1ZAeiTd zSvYV~vsHpzYwo76y0YmKvrDB{YXe;k_52CxKKv;Ut}$;qCP&^0nj@4y8%OvP=1nK% z$ab(W<=5a7K_e*Bg(qFesY1O{(Sfs*YvuvZ?M!dzoMfK5ia8fFfR<;INp#Z1A`jeRbq8H6t!MpSIZ$MNue` zNF+x@A+=hKCMG75BT^%+R*S~R$CD${LJaKu`qgW}#c)$tRZXE@F&C7!<%D@8WkcvO zz3^()GiSpBa=PNoITu~xb`vAh_cWT<(^i`_3v)Hr&x!YK^RJ)G&hL__%PPDJjwq%> zP3^tbO`ov+R#q%)F@z)mEYKc7$Vo zYCWpmj(U&9q+BRR^TL(LD&ZJ;=&zT3jW$+WiCiv@1jP~NAajyA4{>>oMCEr-2td0N z((Fc(?oBmTNO*yUh-OYEN#|0K&a-OJ9&->mR!uq{Z0(&W;8s#XX8}i3LIWNKD~W37 zH!(Z{=20*r9nK{n*kG(77HZZGGR^!5bC4glUmRnO@M96R-`Wr(X-h$ZA0dYrGp`*6 z5^yE?Av&S3G{l&LK5(ro2A6ul7T7V_zkCaAN%LzHI4$4~mrh_qxMBk9uwE#^G|s~L z!b*IHpvD@k1xqf(i(pLmn!JVKO|V3mz(e>dzJ^EfIGzYsjp9VuKZ<3=23A#Ls26fQ zg796Oj4AtNQ!q}FW4W~6Uo{)ypYh!E+1n7l6I!K0uW3U9Re$xu=;#=~4=04%v3zWw zpBxb@r^8er5oY6fe)zLEwuDgvu(||n_DJIJ#zcU_7Vmryd^!`IviODb?Qy>PNddhS zaOV&eg*2Q{B)m5P>l!9NLXcpeO@M?$6Nft{rb?jCV5ok0KDLn0lp_c);6WURgk3oX zzI%EM+_q*6y!G4|9)m4p=ZoV+-Rw00D1Gr`D-l3$I$z!N(v6cKJci?;?ZnTG;o@;T z7}{<%DcJCpP}Pm;kx z!gqj*h2X|_Ahg1}C$K(T1)Af#n)?wR5_(90!zi~F%edd%FtBZHACB=MY*ey_KaL0a z9z4u{i%0kXPVn_4w`XlzdkK$iTicK0Vc&!kNvNeV|7=kgxqaXEQ^MQMf4fHBm09QO zMO|H8HRiRI_)7)OPD1kS%Obe>5liKd74fgVX0sI+-+OO)IYKHGi6^Af>k%rgIK&;Q zC`B5LUZY3l?X`7vwe973c{-iW>-E;(J7r=;(huu(;5Nw!PY@TaApK zUB+x*$5Q0$Mdq4Ca+`Z^UEN-Hn=D6Nn?^@BxuUZC;<{+`(j}KmmL0D>lv7)4cF1z3 zt}l_eq&La(rcEla|6Brt z0O-{S9Y5aLc{Y*w>+$0Vty*>Jn^mh;px2lF`y;O-**H3yGisEjm}2If(E)^ti_P)yew57z3Mp>&5xoe zN|YBD|MK4dOcXhs&unt1 zludakGv%Y?OiH8EXfz&;Y@jKF8L17FUuD!Fqe11PG%62;APY1o9*-wSq~K&O9#2w^ zc6PEKgUdVFAn|+$8Q3|}9O>thbC#%l^@nf$MKSU}^Si(FXHLzUa9T?YXF%1pw2a|Q zsAeV~E-Juuk@M-(n3_Qov(2IJ8cE@(A%&ra6b7iElpVR9| z3p^aUfJZ|Ag^i()rRE>1T0ZA1BHs=b=oD0-C>{|sIGt1TE!fB(k}N;GYW}I2hirjM z)F<9<=KnI|pzw9zrlTsX^q*K0e-!o>VefCC`UPRv@Q>|UnPOKTF-`xB9dF)m(lm2J zN5C{E!8GT=G|ga|gHp5eQIOjFvmTtrA#aA);?4BVhX!Sq*9a{Pv@ca~17zVl;Kp0j z1Ko|ahjcgLJ9|jchYsdOQ`5k-E;@~;v@^-Bhm+e>!Iv@RH6<@z-l)x>f1z{_=nV7> z>1UvO$R^l*XP|q?J#%>RnVat3GtNJMk!&`4NVCyHnhk<|)}yGBQ|cTkJ_tGC^D6+r2q-vtQ{Kq~ zX4qooBF_N%CTOByKcDy=YvOd!CQ4}g32tUG5B&>u?tn+n?gPbP$&JRAu`KdGm zPJ`LMfRp|T`32cWf;GwEqpz_gAWAJ}5kb(H3olOoT=p)eeDHf6w1S=mo?K6!XQ5}2 zXO_q4(Rj4ZgQt{R=A!><1(sm!-@5gfT9c-9sNX))ey4vWO|RT>N87+qd)p`N0H0hK zy8r%xf&1?tx&W9h8npoym23pFNzoSe7|XV>e^kmz%OT|fgZlss*T`G=V|G8w_K?D( zz=tgVIm>>I2mm<120n+4 zCwYhfILb@`rxa!);6ri}0#RiI0W6VpO!3&10lxQ517io$EnQq=Sc(u`YNuBx5~Uj! z+wC+>_?(}{9f`!N^YzKr1fhJ}YYsUP3+5-Ks7=LVg9%6J2IbvguvoGmTvA?MXs|3X z0JEoL!3PgNTlDbiyO+}L~3sq{HU?f4Y&8(+qPY4x&jwDf);tjlM2?~*!V%? z2W0L|y!pcB3xKJfUiJLORU1|ELG}}(Xb_6MV*I3c!r&b;eT~c!hR~GlZlG>}8!tB@ zZa3V%ZK+ObNgTYetR%$%2z5G56k{>C)#9<(E3Zta{qA@1IDAb{ zl$P4<(`gSpP*x_(<>gD3SS-_N#3YlE@D_y8!Rd7JKG|ocAgNSpb!sZ1==G_ogkmzK zrs{MiQ(j(@I(@^8jJ&+!;v{uCZQi`%;&Nzv^71k>r_+Ay{b({>Nn8ZsZ$|thWOg{r zW_b-nRVsL4Fl^E3=cZ|9lTgeWvB_{`rL`N>b6umS(bfD&j3N z1sRRVn0rJnmyhHcQ>-UmSHKPB1pqoA_;&*~lbMFprW1)|e$OPkhU0rPP1nqb{uG-%YPXj@YNw?XB$(3AGd+~3sZ3>Mpys8^wR;Ueu=Eyde{A1d>5ncgg~03i z?{A-JOLgpIE!?j(zU|<5B?#R6;hBPa@*JiVB$!I-?=!nnl9T%Xz|vc+{jq(2vi^^! zJQv)K$)X1qfXlFhb;ET@AVvKvv>29`FUdB*{gete#Q$V*EP=0jsljG1Pka=ken1`@{D>5QX3{tSz%@t!OwyiWGe=pE zT$Z{F0r%gAdQ1J`=Zl{Alb4$>*4+Pl|35VpJaELufjM8zvN-DD?Ol0;{n045o@27+d&@|Blj6ylTIuBFQXg%#OV?{-si9d#Le5aQtiNcYc0%Jg4zO!X5*G~FyEvtk zRjT&cclW*by}$4K-uvGBqGrLQS)wI^EtqVJaSC^uBm6CTV`wBeD$yy6o0^~?r3ALd z10)>S)fpgd&Q|(xq?L{hrkL^Wk*-nhXp%XW_$;Ztf%g49TduE2%^@t0HKo zSUjw!h;EK|>YiyX97`}~k+L8L^t>p0<=kMe*y~!PO&(^&eAMC-^Mc&3ef-II#!Q(U z<>bkq|I$gE8yZ^PJ!mGj65^<-sdGA=Pj1~xJFdYsch%pzhGob#`b{f-bx(hQ7>&X< z-zmN*L|g;A(+UsO(3pjl(j=VoRZh)g5U#!f11qygQGFdvk>Ciq0@YdH;swTQNX67% zP1Dg?y{|WvR-`&U?`H+J~{=D)j%KRM8e=Bd%1g&&1Es^YQe<%4z*c=Do6nX*zVaNYYVbU;r~9J5LmpwTrtpiTl4Ke8;^bVd79o90h z*Tpui09m@N{V$Yw)GODrKi1jhGwk>*>zx&k=}XwVE;q>AE7(`2eNv9SfBLn9l$Zb0 zYe5Pq=8Gs5B0!<0Oo&Jx6d|#zWW7o}>Z(v3vi&go_scH5lnxK4j~c=q>|0mZKhClJ zdEZoy?U-Syi|lh(7G$yHs_&_s#5Blmv#;%Zfmhg1u5pE~Vs!r7;fcPFH0;l!Y-)uvB);XR4jB7>YN(W!KzYe=WL z;81+yH1y`3;ye4@)9{XXWdHZ4;fUDRN}%r(mgv3@XA|1X4P<)-&U=w4H*E$P%@Feq zCgzT4iJ_5G?Tky!yH}-$x{BiZt?mj@c3s7lBoG0C9hqhSJ&Q~}o1REnH8o0bBFvrf zA50%M)<~-|W|Pre$}qlHO8an5QXQP^+fhi-ZmQLa8zrD3i1n?XjS}CKi#_KX1dhA$Z9FE5ffz=`<<<&t_tYK6O||d{We8l zm*wq{a_;3+UACx{Ogmdm>6Gu0=`&AU;cXM9!IV55=9HLcEESF^5hQo+4XAeBapu`8 zyk){9&!QhCnAiPLOPjMf02z^k2{GfD4nRtrzylRV*tfq>AC#A>8{~R*jeLhKWgAhe zfosc=LbvA2wf zBVoZW9v3X9Uzp*|oR&vla+!7iVW|v~=*uZ3;@sOypZ;}$DLK8r;HJWn6wNpk*)_X{ zW+?POd5sbnNXj-P!Yp>%2U2{w5^>vgS|Z`dbZ@vZF$eAA%_>wc*3Nkg5(r=Q=;U-aQn)7-+6E;}dv@px3nWF&WwP6wld#k;Y?4d^r+7L+VC;i@Hs|+mA>q(hH17AbQ!E;R27L;BM4i0*#{T`0uJ~`Pw1%c7BO^7r%H& zeSj}hAL17csV%%k{XI_!j(&&#Aja8qp-@PACd>(>&4tS!=2BLk>X17k9YO?ybnqmz z16Su{m#W{7TZ^Zw`SgoAe=%lEaAU)9_o6+e;&FTH_JINZ)@Vt?Tg;+F!<+7vVnU_d zwdg~*oGF{O1jfY|JQGg0Wnf$`X>|{b^CrJI9_E})bFE*5WAoLIWM+@gNkSM;9M9Ns zV|Bcc7EGviAk9-^x?zB!n&vt)gkvVffU`Mc#HzD-0w&nErj-arjW8dp`exDKcDB|* z+6!Z164^04?E4~$VBaU)o7Bz?jLGzp$P&Sp)KX|(mH7PF3%Kjup0Ss=BpEZ-NO;VN zDMKHkM~p8O6G4U6^k7z_vJ{m1Cw$`k8Vh> zH8h8^m{PwQHBd-l$FyDfJnFIJLRq9m`xeRp!1O-VK6$+bNdJe0G+&PVl|UD5wCB){ zO2`&%sJ%`La%@}RxVPtmVaxIbTgtDaTCk<+OW9Zp%-iI^5`ei-w5v9kWuD72Z+^9U zvzH7fd&DdMLUe2?v-3MKbx|N=I=yYrgV+y%jBbG88pI>}kfdYcgjXdN-snEo_W?%R zi+!KsyUX|PEE4x4@vEK*23{3|m?0o2G4O`ZKRYj4wqcN7!Kf4m?AV;m#s}Zd*6^02 zFz^jqjXUqaLfPY~^LX5A+%!uF!W%sg)j2&Ic8_kR=F4J24zyr9Z~*IR=aUEg2W;54 z*`BfC%NRM(VnZbM@XflN4x7>6V9*#iVslhE>l)U&*KZ(}h99z{jN<~qhvmu9$YmD zgZAdZ7-ri*eEbTJ2SK=3;Rd*Pt_buGCA3k$#bPNG3dBNLjGdvbUcEYhE4n%-a$B3` zJgrZ_jCg3Fmyc3P*ihe?3!hc~Z1n1L;belD2yvl9KxIN@q4H3L^Z}GgRB;0cnWV1CcLrK;8P2Wz#&*M_glaUjXf6+_hb!%;3zEbIR`aH z363F{?wRUJ2azFE6Izb#z(_ zS$QR9%u*xao5I3J$+$q6%6X_$AXwkv96FOE!D+|E5siYVAIrvY0FxxG1lH(gqsB-X eegh+7$~ZKvBhKcOOK%=&+7ssS#8{Uze*XmsnV>cR literal 0 HcmV?d00001 diff --git a/src/python/roms/turmoil.bin b/src/python/roms/turmoil.bin new file mode 100644 index 0000000000000000000000000000000000000000..d44aaa08079d6539c6fe14880866230497864d05 GIT binary patch literal 4096 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 BRhj?* literal 0 HcmV?d00001 diff --git a/src/python/roms/tutankham.bin b/src/python/roms/tutankham.bin new file mode 100644 index 0000000000000000000000000000000000000000..cbe3f285f6b3afd500c9dec7f7817b59e780509e GIT binary patch literal 8192 zcmd5=3tUv!nLqc=9T-N0D?y!9gd3}tG1<+;nCvuV1t~T{CWiQkE;ZfRMpJ3s{mpih z4QUMyIA=s_l3C>%qy#6^xx);ILSzC%MF)A!P_0*7Qy<-QY!ngW=K2iSk=yUw0rP0u z-}cvEf8B3j<~!dx=R42;_c#c}=*7~~qSCA^9Vxvi@7@i(QepXxEN6}L#)n@kDJgkv zA*oQQ7$(olDs2pgoE#(b(sokVvBEfJT(LuuvqrXLqcJm+6vlbYXLxR$|8eOP_u3GC zq7C7dHVc2S>G8CAshZSTN)Il_YR%zT4MS5D&1hICYc>Rv5j!{Yslp$w9aAxgVoTdP zr_4&JV4nL&*2%0~^8LN;W#5@r$w)u_N;lJ0|NS*?v#b!&Na(Mz{!K3|df}Be9+{m)*#uY185P5z57?g~JMT{mfQIp6duHRr;kJh)XKQL|W+7!EPy%Cj{DTKcA z*z)Dy+p}k6L|OlyAB+ojZ5#0@@8!Q&R(_m;(5Axm6DnxJ8OM|-dgR@-$H$aVbqahGdgU;wHKJHgp(r%s)+*+6>wb9C<9 zxeq@0;QaaXL?tNe4fmFo!Y?V3*?yipjU$1p13_6-``!9T!OJy#jSszC%~zi`+qZ6i z6XjUZ(XplvFJHENr6N-{n0$42zmc{5neD$hwmloof9&zXe_xqfkeQncH6s}r z(L2$RI~i!fKV%y&K9D?!lIis?8S}V@G^uOW6f)$qy$UIZWwmxvh(bXWLFoL4NR|;w zLy7`4hF%yAuFUj&HvU;Gu4gTW@{#VT43R3^IL?-x8byN;V4AzRy8{c@T z+uf7TZe$j%vFvqUf8}xKoDUs~+Mbx(U|M5Y3tJwezsCB%i2u@3z<%kArA0twRBJ{4 zytJ$%6yg6k{!8!Ki>%zTj)UQP9OH(7*nx2( z&1!1e+o9E-J$qu-pqWs^z6SEs(%RD8(a|w(J%Yg?6u=tv_V$Lu;iI=l6^cSXVi3R{ zoKg(Hxev~NaN*LWVIY?aRA0S%Rgxs4ueq7D1tV1R=~TzLKO6rmbLLpNoX5Tl{}oB| zer=UPKdt5~`RbqU`F7FxN7>}he%-_WPqNXYk5AyAc&BmvU&}y`8UGai5#V03S`jiB zGGuC|ikhlYqv>jtL7`13BOdGkz#g!TPz1~YN=rhiNmLY3Hzg{9)I&%;f~Ytv6%A66 zAe9}YK1`+}V^n0R#gZo7$hIJ(Wz#n-n~avyQcLNNETttD`7RpyvE^gcHCaWWcq;Ns zDv|_tN7kgyeQ_=L6h@=d=~xcwvhpj1q&HQgVGHp3*_6}P#LRl7TFfkSAZ1D(XQp2CN*w!q{#>uwFGL`hXu%s?|5czYV20exobOvPu% z?V$@vjEfS{+Z^KA!h(q+&+(J=@gT{XkdOu-^6EgS{{vyOk7pgzCd51&>+jX!UZ{y% z&$n(f%{Y6nT2byD){#2s7x_ZPy)&og?)Gicq`j>OPr)>v=SXq4VOHTXr0H(hH9QaY4bM<^(LNKm z!p=bxMSKFMO^Dk-Y)UkQp7o!hKsMN?>9{Gt>z{1 z6HE&Y`ZR5^2H(K6I6oEq}X2HBVAGc^Z7s66iX|RZg%cOSO9ih!z99A z$wpLS4V7#>U@>i&S7I_j_EA<2DF8w;Z2i>!RCk`GwFo^*az7!$>B8OnW z0b-IA>>}=Xk82M^<~s#JfPz<{21pB<_mRpLLXaA?wzjvoLj=�d)`zKqS!hS>?b( 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 literal 0 HcmV?d00001 diff --git a/src/python/roms/up_n_down.bin b/src/python/roms/up_n_down.bin new file mode 100644 index 0000000000000000000000000000000000000000..290a1e6ed68b9e35011e0a7ebd9f30d58ad8bd75 GIT binary patch literal 8192 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 Qxc+UFW*r>7FfQ}|1OD6VfB*mh literal 0 HcmV?d00001 diff --git a/src/python/roms/utils.py b/src/python/roms/utils.py deleted file mode 100644 index d492607f3..000000000 --- a/src/python/roms/utils.py +++ /dev/null @@ -1,29 +0,0 @@ -import re - -_ROM_NAME_TO_ID_RE = re.compile(r"([0-9]*[A-Z][a-z]*(\d*$)?)") - - -def rom_id_to_name(rom: str) -> str: - """ - Let the ROM ID be the ROM identifier in snakecase. - For example, `space_invaders` - The ROM name is the ROM ID in camelcase. - For example, `SpaceInvaders` - - This function converts the ROM ID to the ROM name. - i.e., snakecase -> camelcase - """ - return rom.title().replace("_", "") - - -def rom_name_to_id(rom: str) -> str: - """ - Let the ROM ID be the ROM identifier in snakecase. - For example, `space_invaders` - The ROM name is the ROM ID in camelcase. - For example, `SpaceInvaders` - - This function converts the ROM name to the ROM ID. - i.e., camelcase -> snakecase - """ - return _ROM_NAME_TO_ID_RE.sub(r"\1_", rom).lower().rstrip("_") diff --git a/src/python/roms/venture.bin b/src/python/roms/venture.bin new file mode 100644 index 0000000000000000000000000000000000000000..8b168aa5e5faf3450bbb98c64a1767f24ebf070c GIT binary patch literal 4096 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 We-#QsxA2ucd;ir@SblvLKmP+9^lZrh literal 0 HcmV?d00001 diff --git a/src/python/roms/video_checkers.bin b/src/python/roms/video_checkers.bin new file mode 100644 index 0000000000000000000000000000000000000000..0af80cefb355378ccc64ae4490627011ab68c797 GIT binary patch literal 4096 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 b2@Z~qqvR=tQ>)M(d9SoPTHePncNpehe)|~b literal 0 HcmV?d00001 diff --git a/src/python/roms/video_chess.bin b/src/python/roms/video_chess.bin new file mode 100644 index 0000000000000000000000000000000000000000..aff226ed33a9b0903c0f542e68a0d4ae6815eca1 GIT binary patch literal 4096 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%0DB<__254DLfG3?ky3d|g-y}9+_6|U%vS^?%Ayva8 z=o0cmi$>#UOge;GoDd<>W_h5IUHTV zP!z>*wt`&F1i#z@Te9Y{-FtRDY=0~Hiv5{g`%8B3Nj~8yc{;iO(OtWrf3)O8vZmz6 z&(}P*r!;A`I`$+9wd}Lo?T>ESv}Ma?D38}%YU%0eIoH})mm2)PtJ2a}U$s~)D%!bo z$;Y#}oXuNGHgD#37jewDfEkUZZ1BKJl{bqy{Oq%#UC$o=D@DCoUhZ!Do!9dx4}0C`^L>|~rVbx&al7Ic z%YQ6?xL&@tpx~Vwd)EexMwihT*#ExU?SB8Y>%X@wwOE#ZxOZ>u-n}2z^w+)kq~i^! z%N+;*A?z*RbO7qoO|So*|5<6lub{RTlxPUB$X;AhoS$C;btzj$lIAkPY|hNfw-n?T zKfd(-NhYyz)GYxy)=X0f!W4n#)!f7nc%Si?7U|AMko(gZuTwUl1+(0n;2UcoVEh$>Qkx+(Q{{=xENl5?z literal 0 HcmV?d00001 diff --git a/src/python/roms/video_pinball.bin b/src/python/roms/video_pinball.bin new file mode 100644 index 0000000000000000000000000000000000000000..b68b0eec98ef39db522d9660c458d63d7ba13e25 GIT binary patch literal 4096 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 literal 0 HcmV?d00001 diff --git a/src/python/roms/warlords.bin b/src/python/roms/warlords.bin new file mode 100644 index 0000000000000000000000000000000000000000..4bf5fb96f08e465e3f88415d785811ed35d435d0 GIT binary patch literal 4096 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 literal 0 HcmV?d00001 diff --git a/src/python/roms/wizard_of_wor.bin b/src/python/roms/wizard_of_wor.bin new file mode 100644 index 0000000000000000000000000000000000000000..d02165a9f403162d0e0385c9af6ad12984ccf51f GIT binary patch literal 4096 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 QolPVXmvhB!2x+?hcPnPlu>b%7 literal 0 HcmV?d00001 diff --git a/src/python/roms/word_zapper.bin b/src/python/roms/word_zapper.bin new file mode 100644 index 0000000000000000000000000000000000000000..5e5f3cdd2bc99c8ab7c66b9556949b0d43dbc4b6 GIT binary patch literal 4096 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 WYEGo*?Lvf!d^E{;nTLf$$NvM0F8G`P literal 0 HcmV?d00001 diff --git a/src/python/roms/yars_revenge.bin b/src/python/roms/yars_revenge.bin new file mode 100644 index 0000000000000000000000000000000000000000..9545161e460a2dbbe18330f492e7ca47d7a2f878 GIT binary patch literal 4096 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 literal 0 HcmV?d00001 From 0b63d15b8c22541cef50f33bf267e1ec311274e5 Mon Sep 17 00:00:00 2001 From: Jet Date: Wed, 7 Feb 2024 20:31:09 +0000 Subject: [PATCH 15/16] remove redundant functions --- examples/python-interface/python_example.py | 3 +- .../python_example_with_modes.py | 3 +- examples/python-rom-package/roms/__init__.py | 2 +- src/python/__init__.py | 3 +- src/python/atari_env.py | 4 +-- src/python/register_gymnasium.py | 4 +-- src/python/roms/__init__.py | 34 ++----------------- tests/conftest.py | 1 + tests/python/gym/test_gym_interface.py | 8 ++--- 9 files changed, 14 insertions(+), 48 deletions(-) diff --git a/examples/python-interface/python_example.py b/examples/python-interface/python_example.py index c11146ed3..b2fb928e2 100755 --- a/examples/python-interface/python_example.py +++ b/examples/python-interface/python_example.py @@ -6,7 +6,8 @@ # ALE provided in examples/sharedLibraryInterfaceExample.cpp import sys from random import randrange -from ale_py import ALEInterface, SDL_SUPPORT + +from ale_py import SDL_SUPPORT, ALEInterface if len(sys.argv) < 2: print(f"Usage: {sys.argv[0]} rom_file") diff --git a/examples/python-interface/python_example_with_modes.py b/examples/python-interface/python_example_with_modes.py index d9b656c40..eb528ff26 100755 --- a/examples/python-interface/python_example_with_modes.py +++ b/examples/python-interface/python_example_with_modes.py @@ -6,7 +6,8 @@ # ALE provided in doc/examples/sharedLibraryInterfaceWithModesExample.cpp import sys from random import randrange -from ale_py import ALEInterface, SDL_SUPPORT + +from ale_py import SDL_SUPPORT, ALEInterface if len(sys.argv) < 2: print(f"Usage: {sys.argv[0]} rom_file") diff --git a/examples/python-rom-package/roms/__init__.py b/examples/python-rom-package/roms/__init__.py index 95436eefd..c6231af0c 100644 --- a/examples/python-rom-package/roms/__init__.py +++ b/examples/python-rom-package/roms/__init__.py @@ -1,5 +1,5 @@ -import sys import pathlib +import sys if sys.version_info >= (3, 9): import importlib.resources as resources diff --git a/src/python/__init__.py b/src/python/__init__.py index 1f93433d6..7343e6457 100644 --- a/src/python/__init__.py +++ b/src/python/__init__.py @@ -3,8 +3,6 @@ import sys import warnings -packagedir = os.path.abspath(os.path.dirname(__file__)) - # Make sure to adjust the filter to show DeprecationWarning warnings.filterwarnings("default", category=DeprecationWarning, module=__name__) @@ -25,6 +23,7 @@ # with user defined search paths. This kind of acts like # $ORIGIN or @loader_path on Unix / macOS. # This way we guarantee we load OUR DLLs. + packagedir = os.path.abspath(os.path.dirname(__file__)) if sys.version_info.major == 3 and sys.version_info.minor >= 8: os.add_dll_directory(packagedir) else: diff --git a/src/python/atari_env.py b/src/python/atari_env.py index 264d49df6..69453e00d 100644 --- a/src/python/atari_env.py +++ b/src/python/atari_env.py @@ -8,7 +8,6 @@ import gymnasium.logger as logger import numpy as np from ale_py import roms -from ale_py.roms import rom_id_to_name, rom_name_to_id from gymnasium import error, spaces, utils from gymnasium.utils import seeding @@ -126,8 +125,7 @@ def __init__( # Initialize ALE self.ale = ale_py.ALEInterface() - self._game = rom_id_to_name(game) - + self._game = game self._game_mode = mode self._game_difficulty = difficulty diff --git a/src/python/register_gymnasium.py b/src/python/register_gymnasium.py index 53b40831f..ddd21f2ba 100644 --- a/src/python/register_gymnasium.py +++ b/src/python/register_gymnasium.py @@ -1,7 +1,6 @@ from collections import defaultdict from typing import Any, Callable, Mapping, NamedTuple, Sequence -from ale_py.roms import rom_id_to_name from gymnasium.envs.registration import register ALL_ATARI_GAMES = ( @@ -202,7 +201,8 @@ def _register_configs( for obs_type in obs_types: for config in configs: for flavour in config.flavours: - name = rom_id_to_name(rom) + # convert snake to pascal, ie: space_invaders -> SpaceInvaders + name = rom.title().replace("_", "") if obs_type == "ram": name = f"{name}-ram" diff --git a/src/python/roms/__init__.py b/src/python/roms/__init__.py index 8bd58231a..582df52cb 100644 --- a/src/python/roms/__init__.py +++ b/src/python/roms/__init__.py @@ -1,38 +1,8 @@ import functools import json import pathlib -import re from os import path -from ale_py import ALEInterface - - -def rom_id_to_name(rom: str) -> str: - """ - Let the ROM ID be the ROM identifier in snakecase. - For example, `space_invaders` - The ROM name is the ROM ID in camelcase. - For example, `SpaceInvaders` - - This function converts the ROM ID to the ROM name. - i.e., snakecase -> camelcase - """ - return rom.title().replace("_", "") - - -def rom_name_to_id(rom: str) -> str: - """ - Let the ROM ID be the ROM identifier in snakecase. - For example, `space_invaders` - The ROM name is the ROM ID in camelcase. - For example, `SpaceInvaders` - - This function converts the ROM name to the ROM ID. - i.e., camelcase -> snakecase - """ - name_to_id_re = re.compile(r"([0-9]*[A-Z][a-z]*(\d*$)?)") - return name_to_id_re.sub(r"\1_", rom).lower().rstrip("_") - @functools.lru_cache(maxsize=None) def __getattr__(rom_name: str) -> pathlib.Path: @@ -41,7 +11,7 @@ def __getattr__(rom_name: str) -> pathlib.Path: # realistically we don't need the MD5s since PyPI will perform hash checks for us base_path = path.dirname(__file__) rom_names = [ - rom_id_to_name(n.split(".")[0]) + n.split(".")[0] for n in json.load(open(path.join(base_path, "md5.json"))).keys() ] @@ -52,4 +22,4 @@ def __getattr__(rom_name: str) -> pathlib.Path: ) # return it as a pathlib object - return pathlib.Path(path.join(base_path, f"{rom_name_to_id(rom_name)}.bin")) + return pathlib.Path(path.join(base_path, f"{rom_name}.bin")) diff --git a/tests/conftest.py b/tests/conftest.py index f57228a2c..5fa194d0b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,5 @@ import os + import pytest diff --git a/tests/python/gym/test_gym_interface.py b/tests/python/gym/test_gym_interface.py index cbc370834..7028008f5 100644 --- a/tests/python/gym/test_gym_interface.py +++ b/tests/python/gym/test_gym_interface.py @@ -8,12 +8,8 @@ import numpy as np from ale_py.env.gym import AtariEnv -from ale_py.gym import ( - _register_gym_configs, - register_gym_envs, - register_legacy_gym_envs, -) - +from ale_py.gym import (_register_gym_configs, register_gym_envs, + register_legacy_gym_envs) from gym import error, spaces from gym.core import Env from gym.envs.registration import registry From 7ca7df93d6ff30255eee4be5c7d8de69461e6947 Mon Sep 17 00:00:00 2001 From: Jet Date: Wed, 7 Feb 2024 20:43:47 +0000 Subject: [PATCH 16/16] fix json missing on install --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f41256452..7dcb5c9dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,7 +53,7 @@ changelog = "https://github.com/mgbellemare/Arcade-Learning-Environment/blob/mas [tool.setuptools] packages = ["ale_py", "ale_py.roms"] package-dir = {ale_py = "src/python"} -package-data = {"ale_py" = ["py.typed", "*.pyi", "**/*.pyi"], "ale_py.roms" = ["*.bin", "md5.txt"]} +package-data = {"ale_py" = ["py.typed", "*.pyi", "**/*.pyi"], "ale_py.roms" = ["*.bin", "md5.json"]} [tool.pytest.ini_options] minversion = "7.0"