Skip to content

Commit

Permalink
Remove gym implementation (only gymnasium) (#511)
Browse files Browse the repository at this point in the history
  • Loading branch information
pseudo-rnd-thoughts authored Feb 16, 2024
1 parent 687351c commit 382bb3d
Show file tree
Hide file tree
Showing 13 changed files with 134 additions and 820 deletions.
15 changes: 2 additions & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@ jobs:
fail-fast: false
matrix:
include:
- runs-on: ubuntu-latest
python: '3.7'
triplet: x64-linux-mixed
- runs-on: ubuntu-latest
python: '3.8'
triplet: x64-linux-mixed
Expand All @@ -52,9 +49,6 @@ jobs:
python: '3.11'
triplet: x64-linux-mixed

- runs-on: macos-latest
python: '3.7'
triplet: x64-osx-mixed
- runs-on: macos-latest
python: '3.8'
triplet: x64-osx-mixed
Expand All @@ -68,9 +62,6 @@ jobs:
python: '3.11'
triplet: x64-osx-mixed

# - runs-on: windows-latest
# python: '3.7'
# triplet: x64-windows
# - runs-on: windows-latest
# python: '3.8'
# triplet: x64-windows
Expand All @@ -97,9 +88,7 @@ jobs:
- name: Install test dependencies
# TODO(jfarebro): There's a bug with Windows cmake and PEP517 builds via pip install.
# As a temporary workaround installing cmake outside of the isolated env seems to work.
run: |
python -m pip install --user --upgrade -r tests/requirements.txt
python -m pip install --user cmake
run: python -m pip install --user cmake

- uses: microsoft/[email protected]
if: runner.os == 'Windows'
Expand All @@ -116,7 +105,7 @@ jobs:
doNotCache: true

- name: Build
run: python -m pip install --user --verbose .
run: python -m pip install --user --verbose .[test]

- name: Test
run: python -m pytest
Expand Down
16 changes: 3 additions & 13 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ build-backend = "setuptools.build_meta"
name = "ale-py"
description = "The Arcade Learning Environment (ALE) - a platform for AI research."
readme = "README.md"
requires-python = ">=3.7"
requires-python = ">=3.8"
license = {text = "GPLv2"}
keywords = ["reinforcement-learning", "arcade-learning-environment", "atari"]
authors = [
Expand Down Expand Up @@ -42,7 +42,7 @@ dynamic = ["version"]
[project.optional-dependencies]
test = [
"pytest>=7.0",
"gym~=0.23",
"gymnasium==1.0.0a1",
]

[project.urls]
Expand All @@ -53,17 +53,13 @@ changelog = "https://github.com/mgbellemare/Arcade-Learning-Environment/blob/mas
[project.scripts]
ale-import-roms = "ale_py.scripts.import_roms:main"

[project.entry-points."gym.envs"]
ALE = "ale_py.gym_registration:register_gym_envs"
__internal__ = "ale_py.gym_registration:register_legacy_gym_envs"

[tool.setuptools]
packages = [
"ale_py",
"ale_py.roms",
"ale_py.scripts"
]
package-dir = {ale_py = "src/python", gym = "src/gym"}
package-dir = {ale_py = "src/python"}
package-data = {"ale_py" = ["py.typed", "*.pyi", "**/*.pyi"], "ale_py.roms" = ["*.bin", "md5.txt"]}

[tool.pytest.ini_options]
Expand All @@ -78,12 +74,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"

Expand Down
15 changes: 15 additions & 0 deletions src/python/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,18 @@
from ale_py._ale_py import SDL_SUPPORT, Action, ALEInterface, ALEState, LoggerMode

__all__ = ["Action", "ALEInterface", "ALEState", "LoggerMode", "SDL_SUPPORT"]


try:
import gymnasium

from ale_py.env import AtariEnv, AtariEnvStepMetadata

__all__ += ["AtariEnv", "AtariEnvStepMetadata"]

from ale_py.registration import register_v0_v4_envs, register_v5_envs

register_v0_v4_envs()
register_v5_envs()
except ImportError:
pass
99 changes: 42 additions & 57 deletions src/python/gymnasium_env.py → src/python/env.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from __future__ import annotations

import sys
from typing import Any, Literal, Optional, Sequence, Union
from typing import Any, Literal, Optional, Union

import ale_py
import gymnasium
import gymnasium.logger as logger
import numpy as np
from ale_py import roms
from gymnasium import error, spaces, utils
Expand All @@ -21,7 +20,7 @@ class AtariEnvStepMetadata(TypedDict):
lives: int
episode_frame_number: int
frame_number: int
seeds: NotRequired[Sequence[int]]
seeds: NotRequired[tuple[int, int]]


class AtariEnv(gymnasium.Env, utils.EzPickle):
Expand All @@ -30,12 +29,12 @@ class AtariEnv(gymnasium.Env, utils.EzPickle):
A Gym wrapper around the Arcade Learning Environment (ALE).
"""

# No render modes
metadata = {"render_modes": ["human", "rgb_array"]}
# FPS can differ per ROM, therefore, dynamically collect the fps once the game is loaded
metadata = {"render_modes": ["human", "rgb_array"], "render_fps": 30}

def __init__(
self,
game: str = "pong",
game: str,
mode: Optional[int] = None,
difficulty: Optional[int] = None,
obs_type: Literal["rgb", "grayscale", "ram"] = "rgb",
Expand All @@ -44,7 +43,7 @@ def __init__(
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.
Expand Down Expand Up @@ -111,15 +110,15 @@ def __init__(

utils.EzPickle.__init__(
self,
game,
mode,
difficulty,
obs_type,
frameskip,
repeat_action_probability,
full_action_space,
max_num_frames_per_episode,
render_mode,
game=game,
mode=mode,
difficulty=difficulty,
obs_type=obs_type,
frameskip=frameskip,
repeat_action_probability=repeat_action_probability,
full_action_space=full_action_space,
max_num_frames_per_episode=max_num_frames_per_episode,
render_mode=render_mode,
)

# Initialize ALE
Expand Down Expand Up @@ -194,6 +193,31 @@ 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."""
super().reset(seed=seed, options=options)

# 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 step( # pyright: ignore[reportIncompatibleMethodOverride]
self,
action: int,
Expand Down Expand Up @@ -229,29 +253,6 @@ 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 = 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
Expand All @@ -274,7 +275,7 @@ def render(self) -> Optional[np.ndarray]:

def _get_obs(self) -> np.ndarray:
"""
Retreives the current observation.
Retrieves the current observation.
This is dependent on `self._obs_type`.
"""
if self._obs_type == "ram":
Expand Down Expand Up @@ -344,7 +345,7 @@ def get_action_meanings(self) -> list[str]:
mapping = dict(zip(keys, values))
return [mapping[action] for action in self._action_set]

def clone_state(self, include_rng=False) -> ale_py.ALEState:
def clone_state(self, include_rng: bool = 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()`."""
Expand All @@ -353,19 +354,3 @@ def clone_state(self, include_rng=False) -> ale_py.ALEState:
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)
Loading

0 comments on commit 382bb3d

Please sign in to comment.