diff --git a/gym/core.py b/gym/core.py index dea7dfa7b52..96e5870ce4c 100644 --- a/gym/core.py +++ b/gym/core.py @@ -160,11 +160,11 @@ def render(self) -> Optional[Union[RenderFrame, List[RenderFrame]]]: - None (default): no render is computed. - human: render return None. The environment is continuously rendered in the current display or terminal. Usually for human consumption. - - single_rgb_array: return a single frame representing the current state of the environment. + - rgb_array: return a single frame representing the current state of the environment. A frame is a numpy.ndarray with shape (x, y, 3) representing RGB values for an x-by-y pixel image. - - rgb_array: return a list of frames representing the states of the environment since the last reset. - Each frame is a numpy.ndarray with shape (x, y, 3), as with single_rgb_array. - - ansi: Return a list of strings (str) or StringIO.StringIO containing a + - rgb_array_list: return a list of frames representing the states of the environment since the last reset. + Each frame is a numpy.ndarray with shape (x, y, 3), as with `rgb_array`. + - ansi: Return a strings (str) or StringIO.StringIO containing a terminal-style text representation for each time step. The text can include newlines and ANSI escape sequences (e.g. for colors). diff --git a/gym/envs/box2d/bipedal_walker.py b/gym/envs/box2d/bipedal_walker.py index 0a94fb377c7..ee4ae5687b2 100644 --- a/gym/envs/box2d/bipedal_walker.py +++ b/gym/envs/box2d/bipedal_walker.py @@ -164,7 +164,7 @@ class BipedalWalker(gym.Env, EzPickle): """ metadata = { - "render_modes": ["human", "rgb_array", "single_rgb_array"], + "render_modes": ["human", "rgb_array", "rgb_array_list"], "render_fps": FPS, } @@ -742,7 +742,7 @@ def _render(self, mode: str = "human"): pygame.event.pump() self.clock.tick(self.metadata["render_fps"]) pygame.display.flip() - elif mode in {"rgb_array", "single_rgb_array"}: + elif mode in {"rgb_array", "rgb_array_list"}: return np.transpose( np.array(pygame.surfarray.pixels3d(self.surf)), axes=(1, 0, 2) )[:, -VIEWPORT_W:] diff --git a/gym/envs/box2d/car_racing.py b/gym/envs/box2d/car_racing.py index 342f6bc28a4..993f80ebf0f 100644 --- a/gym/envs/box2d/car_racing.py +++ b/gym/envs/box2d/car_racing.py @@ -184,10 +184,10 @@ class CarRacing(gym.Env, EzPickle): metadata = { "render_modes": [ "human", + "rgb_array_list", + "state_pixels_list", "rgb_array", "state_pixels", - "single_rgb_array", - "single_state_pixels", ], "render_fps": FPS, } @@ -541,7 +541,7 @@ def step(self, action: Union[np.ndarray, int]): self.world.Step(1.0 / FPS, 6 * 30, 2 * 30) self.t += 1.0 / FPS - self.state = self._render("single_state_pixels") + self.state = self._render("state_pixels") step_reward = 0 terminated = False @@ -601,7 +601,7 @@ def _render(self, mode: str = "human"): zoom, trans, angle, - mode not in ["state_pixels", "single_state_pixels"], + mode not in ["state_pixels_list", "state_pixels"], ) self.surf = pygame.transform.flip(self.surf, False, True) @@ -623,9 +623,9 @@ def _render(self, mode: str = "human"): self.screen.blit(self.surf, (0, 0)) pygame.display.flip() - if mode in {"rgb_array", "single_rgb_array"}: + if mode in {"rgb_array", "rgb_array_list"}: return self._create_image_array(self.surf, (VIDEO_W, VIDEO_H)) - elif mode in {"state_pixels", "single_state_pixels"}: + elif mode in {"state_pixels_list", "state_pixels"}: return self._create_image_array(self.surf, (STATE_W, STATE_H)) else: return self.isopen diff --git a/gym/envs/box2d/lunar_lander.py b/gym/envs/box2d/lunar_lander.py index e06a342b5d9..b48bc7ada38 100644 --- a/gym/envs/box2d/lunar_lander.py +++ b/gym/envs/box2d/lunar_lander.py @@ -179,7 +179,7 @@ class LunarLander(gym.Env, EzPickle): """ metadata = { - "render_modes": ["human", "rgb_array", "single_rgb_array"], + "render_modes": ["human", "rgb_array", "rgb_array_list"], "render_fps": FPS, } @@ -698,7 +698,7 @@ def _render(self, mode="human"): pygame.event.pump() self.clock.tick(self.metadata["render_fps"]) pygame.display.flip() - elif mode in {"rgb_array", "single_rgb_array"}: + elif mode in {"rgb_array", "rgb_array_list"}: return np.transpose( np.array(pygame.surfarray.pixels3d(self.surf)), axes=(1, 0, 2) ) diff --git a/gym/envs/classic_control/acrobot.py b/gym/envs/classic_control/acrobot.py index fb8aa4abef4..bc9ffcbef86 100644 --- a/gym/envs/classic_control/acrobot.py +++ b/gym/envs/classic_control/acrobot.py @@ -137,7 +137,7 @@ class AcrobotEnv(core.Env): """ metadata = { - "render_modes": ["human", "rgb_array", "single_rgb_array"], + "render_modes": ["human", "rgb_array", "rgb_array_list"], "render_fps": 15, } @@ -297,7 +297,7 @@ def _render(self, mode="human"): self.screen = pygame.display.set_mode( (self.SCREEN_DIM, self.SCREEN_DIM) ) - else: # mode in {"rgb_array", "single_rgb_array"} + else: # mode in {"rgb_array", "rgb_array_list"} self.screen = pygame.Surface((self.SCREEN_DIM, self.SCREEN_DIM)) if self.clock is None: self.clock = pygame.time.Clock() @@ -358,7 +358,7 @@ def _render(self, mode="human"): self.clock.tick(self.metadata["render_fps"]) pygame.display.flip() - elif mode in {"rgb_array", "single_rgb_array"}: + elif mode in {"rgb_array", "rgb_array_list"}: return np.transpose( np.array(pygame.surfarray.pixels3d(self.screen)), axes=(1, 0, 2) ) diff --git a/gym/envs/classic_control/cartpole.py b/gym/envs/classic_control/cartpole.py index 776c7147b6b..c56c0d2421c 100644 --- a/gym/envs/classic_control/cartpole.py +++ b/gym/envs/classic_control/cartpole.py @@ -83,7 +83,7 @@ class CartPoleEnv(gym.Env[np.ndarray, Union[int, np.ndarray]]): """ metadata = { - "render_modes": ["human", "rgb_array", "single_rgb_array"], + "render_modes": ["human", "rgb_array", "rgb_array_list"], "render_fps": 50, } @@ -226,7 +226,7 @@ def _render(self, mode="human"): self.screen = pygame.display.set_mode( (self.screen_width, self.screen_height) ) - else: # mode in {"rgb_array", "single_rgb_array"} + else: # mode in {"rgb_array", "rgb_array_list"} self.screen = pygame.Surface((self.screen_width, self.screen_height)) if self.clock is None: self.clock = pygame.time.Clock() @@ -294,7 +294,7 @@ def _render(self, mode="human"): self.clock.tick(self.metadata["render_fps"]) pygame.display.flip() - elif mode in {"rgb_array", "single_rgb_array"}: + elif mode in {"rgb_array", "rgb_array_list"}: return np.transpose( np.array(pygame.surfarray.pixels3d(self.screen)), axes=(1, 0, 2) ) diff --git a/gym/envs/classic_control/continuous_mountain_car.py b/gym/envs/classic_control/continuous_mountain_car.py index 2a0cc8227f8..6db6a8a96ed 100644 --- a/gym/envs/classic_control/continuous_mountain_car.py +++ b/gym/envs/classic_control/continuous_mountain_car.py @@ -102,7 +102,7 @@ class Continuous_MountainCarEnv(gym.Env): """ metadata = { - "render_modes": ["human", "rgb_array", "single_rgb_array"], + "render_modes": ["human", "rgb_array", "rgb_array_list"], "render_fps": 30, } @@ -208,7 +208,7 @@ def _render(self, mode="human"): self.screen = pygame.display.set_mode( (self.screen_width, self.screen_height) ) - else: # mode in {"rgb_array", "single_rgb_array"} + else: # mode in {"rgb_array", "rgb_array_list"} self.screen = pygame.Surface((self.screen_width, self.screen_height)) if self.clock is None: self.clock = pygame.time.Clock() @@ -282,7 +282,7 @@ def _render(self, mode="human"): self.clock.tick(self.metadata["render_fps"]) pygame.display.flip() - elif mode in {"rgb_array", "single_rgb_array"}: + elif mode in {"rgb_array", "rgb_array_list"}: return np.transpose( np.array(pygame.surfarray.pixels3d(self.screen)), axes=(1, 0, 2) ) diff --git a/gym/envs/classic_control/mountain_car.py b/gym/envs/classic_control/mountain_car.py index 419202a7640..34c21294147 100644 --- a/gym/envs/classic_control/mountain_car.py +++ b/gym/envs/classic_control/mountain_car.py @@ -97,7 +97,7 @@ class MountainCarEnv(gym.Env): """ metadata = { - "render_modes": ["human", "rgb_array", "single_rgb_array"], + "render_modes": ["human", "rgb_array", "rgb_array_list"], "render_fps": 30, } @@ -186,7 +186,7 @@ def _render(self, mode="human"): self.screen = pygame.display.set_mode( (self.screen_width, self.screen_height) ) - else: # mode in {"rgb_array", "single_rgb_array"} + else: # mode in {"rgb_array", "rgb_array_list"} self.screen = pygame.Surface((self.screen_width, self.screen_height)) if self.clock is None: self.clock = pygame.time.Clock() @@ -260,7 +260,7 @@ def _render(self, mode="human"): self.clock.tick(self.metadata["render_fps"]) pygame.display.flip() - elif mode in {"rgb_array", "single_rgb_array"}: + elif mode in {"rgb_array", "rgb_array_list"}: return np.transpose( np.array(pygame.surfarray.pixels3d(self.screen)), axes=(1, 0, 2) ) diff --git a/gym/envs/classic_control/pendulum.py b/gym/envs/classic_control/pendulum.py index 37cc2c38a09..7aa3d2edbcb 100644 --- a/gym/envs/classic_control/pendulum.py +++ b/gym/envs/classic_control/pendulum.py @@ -89,7 +89,7 @@ class PendulumEnv(gym.Env): """ metadata = { - "render_modes": ["human", "rgb_array", "single_rgb_array"], + "render_modes": ["human", "rgb_array", "rgb_array_list"], "render_fps": 30, } @@ -182,7 +182,7 @@ def _render(self, mode="human"): self.screen = pygame.display.set_mode( (self.screen_dim, self.screen_dim) ) - else: # mode in {"rgb_array", "single_rgb_array"} + else: # mode in {"rgb_array", "rgb_array_list"} self.screen = pygame.Surface((self.screen_dim, self.screen_dim)) if self.clock is None: self.clock = pygame.time.Clock() @@ -249,7 +249,7 @@ def _render(self, mode="human"): self.clock.tick(self.metadata["render_fps"]) pygame.display.flip() - else: # mode == "rgb_array": + else: # mode == "rgb_array_list": return np.transpose( np.array(pygame.surfarray.pixels3d(self.screen)), axes=(1, 0, 2) ) diff --git a/gym/envs/mujoco/ant.py b/gym/envs/mujoco/ant.py index bb7c5875f9a..46c8da13228 100644 --- a/gym/envs/mujoco/ant.py +++ b/gym/envs/mujoco/ant.py @@ -10,9 +10,9 @@ class AntEnv(MuJocoPyEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 20, } diff --git a/gym/envs/mujoco/ant_v3.py b/gym/envs/mujoco/ant_v3.py index 5902340dfda..cb4115d7b91 100644 --- a/gym/envs/mujoco/ant_v3.py +++ b/gym/envs/mujoco/ant_v3.py @@ -14,9 +14,9 @@ class AntEnv(MuJocoPyEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 20, } diff --git a/gym/envs/mujoco/ant_v4.py b/gym/envs/mujoco/ant_v4.py index 45a1ee42d7d..9f12822f649 100644 --- a/gym/envs/mujoco/ant_v4.py +++ b/gym/envs/mujoco/ant_v4.py @@ -176,9 +176,9 @@ class AntEnv(MujocoEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 20, } diff --git a/gym/envs/mujoco/half_cheetah.py b/gym/envs/mujoco/half_cheetah.py index 5645b54a8ea..7744942a421 100644 --- a/gym/envs/mujoco/half_cheetah.py +++ b/gym/envs/mujoco/half_cheetah.py @@ -10,9 +10,9 @@ class HalfCheetahEnv(MuJocoPyEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 20, } diff --git a/gym/envs/mujoco/half_cheetah_v3.py b/gym/envs/mujoco/half_cheetah_v3.py index d57c8f9faa2..882bfd73926 100644 --- a/gym/envs/mujoco/half_cheetah_v3.py +++ b/gym/envs/mujoco/half_cheetah_v3.py @@ -16,9 +16,9 @@ class HalfCheetahEnv(MuJocoPyEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 20, } diff --git a/gym/envs/mujoco/half_cheetah_v4.py b/gym/envs/mujoco/half_cheetah_v4.py index 9a1e1fc9115..fb352ce2fb9 100644 --- a/gym/envs/mujoco/half_cheetah_v4.py +++ b/gym/envs/mujoco/half_cheetah_v4.py @@ -136,9 +136,9 @@ class HalfCheetahEnv(MujocoEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 20, } diff --git a/gym/envs/mujoco/hopper.py b/gym/envs/mujoco/hopper.py index 30c45c81924..dc3ec049d6b 100644 --- a/gym/envs/mujoco/hopper.py +++ b/gym/envs/mujoco/hopper.py @@ -10,9 +10,9 @@ class HopperEnv(MuJocoPyEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 125, } diff --git a/gym/envs/mujoco/hopper_v3.py b/gym/envs/mujoco/hopper_v3.py index 522a8d8f3fe..1c31c75791d 100644 --- a/gym/envs/mujoco/hopper_v3.py +++ b/gym/envs/mujoco/hopper_v3.py @@ -19,9 +19,9 @@ class HopperEnv(MuJocoPyEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 125, } diff --git a/gym/envs/mujoco/hopper_v4.py b/gym/envs/mujoco/hopper_v4.py index 34571acccc5..d1d125cb7b7 100644 --- a/gym/envs/mujoco/hopper_v4.py +++ b/gym/envs/mujoco/hopper_v4.py @@ -142,9 +142,9 @@ class HopperEnv(MujocoEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 125, } diff --git a/gym/envs/mujoco/humanoid.py b/gym/envs/mujoco/humanoid.py index dfc5297113c..c83f1bfbe93 100644 --- a/gym/envs/mujoco/humanoid.py +++ b/gym/envs/mujoco/humanoid.py @@ -16,9 +16,9 @@ class HumanoidEnv(MuJocoPyEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 67, } diff --git a/gym/envs/mujoco/humanoid_v3.py b/gym/envs/mujoco/humanoid_v3.py index d58c6542f4e..1b90997eee7 100644 --- a/gym/envs/mujoco/humanoid_v3.py +++ b/gym/envs/mujoco/humanoid_v3.py @@ -23,9 +23,9 @@ class HumanoidEnv(MuJocoPyEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 67, } diff --git a/gym/envs/mujoco/humanoid_v4.py b/gym/envs/mujoco/humanoid_v4.py index 95987b8e195..d384432f511 100644 --- a/gym/envs/mujoco/humanoid_v4.py +++ b/gym/envs/mujoco/humanoid_v4.py @@ -216,9 +216,9 @@ class HumanoidEnv(MujocoEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 67, } diff --git a/gym/envs/mujoco/humanoidstandup.py b/gym/envs/mujoco/humanoidstandup.py index 48e31c2973f..3bf8f104952 100644 --- a/gym/envs/mujoco/humanoidstandup.py +++ b/gym/envs/mujoco/humanoidstandup.py @@ -10,9 +10,9 @@ class HumanoidStandupEnv(MuJocoPyEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 67, } diff --git a/gym/envs/mujoco/humanoidstandup_v4.py b/gym/envs/mujoco/humanoidstandup_v4.py index 4376421d83e..410ba51e8c2 100644 --- a/gym/envs/mujoco/humanoidstandup_v4.py +++ b/gym/envs/mujoco/humanoidstandup_v4.py @@ -182,9 +182,9 @@ class HumanoidStandupEnv(MujocoEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 67, } diff --git a/gym/envs/mujoco/inverted_double_pendulum.py b/gym/envs/mujoco/inverted_double_pendulum.py index 99f3931d29e..b273a562485 100644 --- a/gym/envs/mujoco/inverted_double_pendulum.py +++ b/gym/envs/mujoco/inverted_double_pendulum.py @@ -10,9 +10,9 @@ class InvertedDoublePendulumEnv(MuJocoPyEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 20, } diff --git a/gym/envs/mujoco/inverted_double_pendulum_v4.py b/gym/envs/mujoco/inverted_double_pendulum_v4.py index ece33579448..c438bb1454f 100644 --- a/gym/envs/mujoco/inverted_double_pendulum_v4.py +++ b/gym/envs/mujoco/inverted_double_pendulum_v4.py @@ -116,9 +116,9 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 20, } diff --git a/gym/envs/mujoco/inverted_pendulum.py b/gym/envs/mujoco/inverted_pendulum.py index 6c359250565..57abf50b295 100644 --- a/gym/envs/mujoco/inverted_pendulum.py +++ b/gym/envs/mujoco/inverted_pendulum.py @@ -10,9 +10,9 @@ class InvertedPendulumEnv(MuJocoPyEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 25, } diff --git a/gym/envs/mujoco/inverted_pendulum_v4.py b/gym/envs/mujoco/inverted_pendulum_v4.py index 7cb27fc4509..e0855a2bcc0 100644 --- a/gym/envs/mujoco/inverted_pendulum_v4.py +++ b/gym/envs/mujoco/inverted_pendulum_v4.py @@ -87,9 +87,9 @@ class InvertedPendulumEnv(MujocoEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 25, } diff --git a/gym/envs/mujoco/mujoco_env.py b/gym/envs/mujoco/mujoco_env.py index 1b7408d09cd..e009b9422ea 100644 --- a/gym/envs/mujoco/mujoco_env.py +++ b/gym/envs/mujoco/mujoco_env.py @@ -64,10 +64,10 @@ def __init__( assert self.metadata["render_modes"] == [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", - ] + "depth_array_list", + ], self.metadata["render_modes"] assert ( int(np.round(1.0 / self.dt)) == self.metadata["render_fps"] ), f'Expected value: {int(np.round(1.0 / self.dt))}, Actual value: {self.metadata["render_fps"]}' @@ -254,9 +254,9 @@ def _render( assert mode in self.metadata["render_modes"] if mode in { "rgb_array", - "single_rgb_array", + "rgb_array_list", "depth_array", - "single_depth_array", + "depth_array_list", }: if camera_id is not None and camera_name is not None: raise ValueError( @@ -274,11 +274,11 @@ def _render( self._get_viewer(mode).render(width, height, camera_id=camera_id) - if mode in {"rgb_array", "single_rgb_array"}: + if mode in {"rgb_array", "rgb_array_list"}: data = self._get_viewer(mode).read_pixels(width, height, depth=False) # original image is upside-down, so flip it return data[::-1, :, :] - elif mode in {"depth_array", "single_depth_array"}: + elif mode in {"depth_array_list", "depth_array"}: self._get_viewer(mode).render(width, height) # Extract depth part of the read_pixels() tuple data = self._get_viewer(mode).read_pixels(width, height, depth=True)[1] @@ -298,8 +298,8 @@ def _get_viewer( elif mode in { "rgb_array", "depth_array", - "single_rgb_array", - "single_depth_array", + "rgb_array_list", + "depth_array_list", }: self.viewer = mujoco_py.MjRenderContextOffscreen(self.sim, -1) else: @@ -383,9 +383,9 @@ def _render( if mode in { "rgb_array", - "single_rgb_array", + "rgb_array_list", "depth_array", - "single_depth_array", + "depth_array_list", }: if camera_id is not None and camera_name is not None: raise ValueError( @@ -406,11 +406,11 @@ def _render( self._get_viewer(mode).render(camera_id=camera_id) - if mode in {"rgb_array", "single_rgb_array"}: + if mode in {"rgb_array", "rgb_array_list"}: data = self._get_viewer(mode).read_pixels(depth=False) # original image is upside-down, so flip it return data[::-1, :, :] - elif mode in {"depth_array", "single_depth_array"}: + elif mode in {"depth_array", "depth_array_list"}: self._get_viewer(mode).render() # Extract depth part of the read_pixels() tuple data = self._get_viewer(mode).read_pixels(depth=True)[1] @@ -436,8 +436,8 @@ def _get_viewer( elif mode in { "rgb_array", "depth_array", - "single_rgb_array", - "single_depth_array", + "rgb_array_list", + "depth_array_list", }: from gym.envs.mujoco import RenderContextOffscreen diff --git a/gym/envs/mujoco/pusher.py b/gym/envs/mujoco/pusher.py index 57d2f70d757..49dca52bb12 100644 --- a/gym/envs/mujoco/pusher.py +++ b/gym/envs/mujoco/pusher.py @@ -10,9 +10,9 @@ class PusherEnv(MuJocoPyEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 20, } diff --git a/gym/envs/mujoco/pusher_v4.py b/gym/envs/mujoco/pusher_v4.py index c89a750d550..84862e6169a 100644 --- a/gym/envs/mujoco/pusher_v4.py +++ b/gym/envs/mujoco/pusher_v4.py @@ -132,9 +132,9 @@ class PusherEnv(MujocoEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 20, } diff --git a/gym/envs/mujoco/reacher.py b/gym/envs/mujoco/reacher.py index c852934ed21..a083942345d 100644 --- a/gym/envs/mujoco/reacher.py +++ b/gym/envs/mujoco/reacher.py @@ -10,9 +10,9 @@ class ReacherEnv(MuJocoPyEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 50, } diff --git a/gym/envs/mujoco/reacher_v4.py b/gym/envs/mujoco/reacher_v4.py index e3ede8d94a3..44a783a788a 100644 --- a/gym/envs/mujoco/reacher_v4.py +++ b/gym/envs/mujoco/reacher_v4.py @@ -122,9 +122,9 @@ class ReacherEnv(MujocoEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 50, } diff --git a/gym/envs/mujoco/swimmer.py b/gym/envs/mujoco/swimmer.py index 79a7dcf1a24..9c2c07f63f3 100644 --- a/gym/envs/mujoco/swimmer.py +++ b/gym/envs/mujoco/swimmer.py @@ -10,9 +10,9 @@ class SwimmerEnv(MuJocoPyEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 25, } diff --git a/gym/envs/mujoco/swimmer_v3.py b/gym/envs/mujoco/swimmer_v3.py index 955c52accd6..65506f4790e 100644 --- a/gym/envs/mujoco/swimmer_v3.py +++ b/gym/envs/mujoco/swimmer_v3.py @@ -14,9 +14,9 @@ class SwimmerEnv(MuJocoPyEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 25, } diff --git a/gym/envs/mujoco/swimmer_v4.py b/gym/envs/mujoco/swimmer_v4.py index 8f795d8556b..ace08d7413a 100644 --- a/gym/envs/mujoco/swimmer_v4.py +++ b/gym/envs/mujoco/swimmer_v4.py @@ -128,9 +128,9 @@ class SwimmerEnv(MujocoEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 25, } diff --git a/gym/envs/mujoco/walker2d.py b/gym/envs/mujoco/walker2d.py index f3e74f2a3e4..4f13593ed9d 100644 --- a/gym/envs/mujoco/walker2d.py +++ b/gym/envs/mujoco/walker2d.py @@ -10,9 +10,9 @@ class Walker2dEnv(MuJocoPyEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 125, } diff --git a/gym/envs/mujoco/walker2d_v3.py b/gym/envs/mujoco/walker2d_v3.py index afd99f867ac..216c50fba34 100644 --- a/gym/envs/mujoco/walker2d_v3.py +++ b/gym/envs/mujoco/walker2d_v3.py @@ -17,9 +17,9 @@ class Walker2dEnv(MuJocoPyEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 125, } diff --git a/gym/envs/mujoco/walker2d_v4.py b/gym/envs/mujoco/walker2d_v4.py index a01f25dbb83..d70e39486b5 100644 --- a/gym/envs/mujoco/walker2d_v4.py +++ b/gym/envs/mujoco/walker2d_v4.py @@ -147,9 +147,9 @@ class Walker2dEnv(MujocoEnv, utils.EzPickle): "render_modes": [ "human", "rgb_array", + "rgb_array_list", "depth_array", - "single_rgb_array", - "single_depth_array", + "depth_array_list", ], "render_fps": 125, } diff --git a/gym/envs/registration.py b/gym/envs/registration.py index 0e4878bc266..8e5f1d05101 100644 --- a/gym/envs/registration.py +++ b/gym/envs/registration.py @@ -638,17 +638,17 @@ def make( if ( mode == "human" and "human" not in render_modes - and ("single_rgb_array" in render_modes or "rgb_array" in render_modes) + and ("rgb_array" in render_modes or "rgb_array_list" in render_modes) ): logger.warn( "You are trying to use 'human' rendering for an environment that doesn't natively support it. " "The HumanRendering wrapper is being applied to your environment." ) apply_human_rendering = True - if "single_rgb_array" in render_modes: - _kwargs["render_mode"] = "single_rgb_array" - else: + if "rgb_array" in render_modes: _kwargs["render_mode"] = "rgb_array" + else: + _kwargs["render_mode"] = "rgb_array_list" elif mode not in render_modes: logger.warn( f"The environment is being initialised with mode ({mode}) that is not in the possible render_modes ({render_modes})." diff --git a/gym/envs/toy_text/blackjack.py b/gym/envs/toy_text/blackjack.py index 6c00ef69974..9fe525c554b 100644 --- a/gym/envs/toy_text/blackjack.py +++ b/gym/envs/toy_text/blackjack.py @@ -112,7 +112,7 @@ class BlackjackEnv(gym.Env): """ metadata = { - "render_modes": ["human", "rgb_array", "single_rgb_array"], + "render_modes": ["human", "rgb_array", "rgb_array_list"], "render_fps": 4, } diff --git a/gym/envs/toy_text/cliffwalking.py b/gym/envs/toy_text/cliffwalking.py index 676acdef3a7..745389b91c0 100644 --- a/gym/envs/toy_text/cliffwalking.py +++ b/gym/envs/toy_text/cliffwalking.py @@ -62,7 +62,7 @@ class CliffWalkingEnv(Env): - v0: Initial version release """ - metadata = {"render_modes": ["human", "rgb_array", "ansi"], "render_fps": 4} + metadata = {"render_modes": ["human", "rgb_array_list", "ansi"], "render_fps": 4} def __init__(self, render_mode: Optional[str] = None): self.shape = (4, 12) diff --git a/gym/envs/toy_text/frozen_lake.py b/gym/envs/toy_text/frozen_lake.py index f3aedeb7629..d02f01edce4 100644 --- a/gym/envs/toy_text/frozen_lake.py +++ b/gym/envs/toy_text/frozen_lake.py @@ -156,7 +156,7 @@ class FrozenLakeEnv(Env): """ metadata = { - "render_modes": ["human", "ansi", "rgb_array", "single_rgb_array"], + "render_modes": ["human", "ansi", "rgb_array", "rgb_array_list"], "render_fps": 4, } @@ -274,7 +274,7 @@ def _render(self, mode="human"): assert mode in self.metadata["render_modes"] if mode == "ansi": return self._render_text() - elif mode in {"human", "rgb_array", "single_rgb_array"}: + elif mode in {"human", "rgb_array", "rgb_array_list"}: return self._render_gui(mode) def _render_gui(self, mode): @@ -292,7 +292,7 @@ def _render_gui(self, mode): pygame.display.init() pygame.display.set_caption("Frozen Lake") self.window_surface = pygame.display.set_mode(self.window_size) - elif mode in {"rgb_array", "single_rgb_array"}: + elif mode in {"rgb_array", "rgb_array_list"}: self.window_surface = pygame.Surface(self.window_size) assert ( @@ -370,7 +370,7 @@ def _render_gui(self, mode): pygame.event.pump() pygame.display.update() self.clock.tick(self.metadata["render_fps"]) - elif mode in {"rgb_array", "single_rgb_array"}: + elif mode in {"rgb_array", "rgb_array_list"}: return np.transpose( np.array(pygame.surfarray.pixels3d(self.window_surface)), axes=(1, 0, 2) ) diff --git a/gym/envs/toy_text/taxi.py b/gym/envs/toy_text/taxi.py index 40c4de2ccf6..19d9d57e483 100644 --- a/gym/envs/toy_text/taxi.py +++ b/gym/envs/toy_text/taxi.py @@ -122,7 +122,7 @@ class TaxiEnv(Env): """ metadata = { - "render_modes": ["human", "ansi", "rgb_array", "single_rgb_array"], + "render_modes": ["human", "ansi", "rgb_array", "rgb_array_list"], "render_fps": 4, } @@ -284,7 +284,7 @@ def _render(self, mode): assert mode in self.metadata["render_modes"] if mode == "ansi": return self._render_text() - elif mode in {"human", "rgb_array", "single_rgb_array"}: + elif mode in {"human", "rgb_array", "rgb_array_list"}: return self._render_gui(mode) def _render_gui(self, mode): @@ -300,7 +300,7 @@ def _render_gui(self, mode): pygame.display.set_caption("Taxi") if mode == "human": self.window = pygame.display.set_mode(WINDOW_SIZE) - elif mode in {"rgb_array", "single_rgb_array"}: + elif mode in {"rgb_array", "rgb_array_list"}: self.window = pygame.Surface(WINDOW_SIZE) assert ( @@ -412,7 +412,7 @@ def _render_gui(self, mode): if mode == "human": pygame.display.update() self.clock.tick(self.metadata["render_fps"]) - elif mode in {"rgb_array", "single_rgb_array"}: + elif mode in {"rgb_array", "rgb_array_list"}: return np.transpose( np.array(pygame.surfarray.pixels3d(self.window)), axes=(1, 0, 2) ) diff --git a/gym/utils/play.py b/gym/utils/play.py index f4fbdbf49d8..796c2e4085a 100644 --- a/gym/utils/play.py +++ b/gym/utils/play.py @@ -27,7 +27,7 @@ import matplotlib.pyplot as plt except ImportError: logger.warn("Matplotlib is not installed, run `pip install gym[other]`") - plt = None + matplotlib, plt = None, None class MissingKeysToAction(Exception): @@ -50,9 +50,9 @@ def __init__( keys_to_action: The dictionary of keyboard tuples and action value zoom: If to zoom in on the environment render """ - if env.render_mode not in {"rgb_array", "single_rgb_array"}: + if env.render_mode not in {"rgb_array", "rgb_array_list"}: logger.error( - "PlayableGame wrapper works only with rgb_array and single_rgb_array render modes, " + "PlayableGame wrapper works only with rgb_array and rgb_array_list render modes, " f"but your environment render_mode = {env.render_mode}." ) @@ -85,10 +85,10 @@ def _get_video_size(self, zoom: Optional[float] = None) -> Tuple[int, int]: if isinstance(rendered, List): rendered = rendered[-1] assert rendered is not None and isinstance(rendered, np.ndarray) - video_size = [rendered.shape[1], rendered.shape[0]] + video_size = (rendered.shape[1], rendered.shape[0]) if zoom is not None: - video_size = int(video_size[0] * zoom), int(video_size[1] * zoom) + video_size = (int(video_size[0] * zoom), int(video_size[1] * zoom)) return video_size @@ -150,7 +150,7 @@ def play( >>> import gym >>> from gym.utils.play import play - >>> play(gym.make("CarRacing-v1", render_mode="single_rgb_array"), keys_to_action={ + >>> play(gym.make("CarRacing-v1", render_mode="rgb_array"), keys_to_action={ ... "w": np.array([0, 0.7, 0]), ... "a": np.array([-1, 0, 0]), ... "s": np.array([0, 0, 1]), @@ -217,10 +217,6 @@ def play( seed: Random seed used when resetting the environment. If None, no seed is used. noop: The action used when no key input has been entered, or the entered key combination is unknown. """ - deprecation( - "`play.py` currently supports only the old step API which returns one boolean, however this will soon be updated to support only the new step api that returns two bools." - ) - env.reset(seed=seed) if keys_to_action is None: diff --git a/gym/utils/renderer.py b/gym/utils/renderer.py index d0a31a4dc69..ab10cc72609 100644 --- a/gym/utils/renderer.py +++ b/gym/utils/renderer.py @@ -5,7 +5,7 @@ NO_RETURNS_RENDER = {"human"} # list of modes with which render returns just a single frame of the current state -SINGLE_RENDER = {"single_rgb_array", "single_depth_array", "single_state_pixels"} +SINGLE_RENDER = {"rgb_array", "depth_array", "state_pixels", "ansi"} class Renderer: @@ -36,7 +36,7 @@ def __init__( no_returns_render (Optional[Set[str]]): Set of render modes that don't return any value. The default value is the set {"human"}. single_render (Optional[Set[str]]): Set of render modes that should return a single frame. - The default value is the set {"single_rgb_array", "single_depth_array", "single_state_pixels"}. + The default value is the set {"rgb_array", "depth_array", "state_pixels", "ansi"}. """ if no_returns_render is None: no_returns_render = NO_RETURNS_RENDER diff --git a/gym/utils/save_video.py b/gym/utils/save_video.py index 8caa2fe4b50..f134e6e178a 100644 --- a/gym/utils/save_video.py +++ b/gym/utils/save_video.py @@ -61,7 +61,7 @@ def save_video( Example: >>> import gym >>> from gym.utils.save_video import save_video - >>> env = gym.make("FrozenLake-v1", render_mode="rgb_array") + >>> env = gym.make("FrozenLake-v1", render_mode="rgb_array_list") >>> env.reset() >>> step_starting_index = 0 >>> episode_index = 0 @@ -82,9 +82,7 @@ def save_video( >>> env.close() """ if not isinstance(frames, list): - logger.error( - f"Expected a list of frames, got a {frames.__class__.__name__} instead." - ) + logger.error(f"Expected a list of frames, got a {type(frames)} instead.") if episode_trigger is None and step_trigger is None: episode_trigger = capped_cubic_video_schedule diff --git a/gym/wrappers/human_rendering.py b/gym/wrappers/human_rendering.py index 50bc12751d9..f488e9d1b80 100644 --- a/gym/wrappers/human_rendering.py +++ b/gym/wrappers/human_rendering.py @@ -6,17 +6,17 @@ class HumanRendering(gym.Wrapper): - """Performs human rendering for an environment that only supports rgb_array rendering. + """Performs human rendering for an environment that only supports "rgb_array"rendering. This wrapper is particularly useful when you have implemented an environment that can produce RGB images but haven't implemented any code to render the images to the screen. If you want to use this wrapper with your environments, remember to specify ``"render_fps"`` in the metadata of your environment. - The ``render_mode`` of the wrapped environment must be either ``'rgb_array'`` or ``'single_rgb_array'``. + The ``render_mode`` of the wrapped environment must be either ``'rgb_array'`` or ``'rgb_array_list'``. Example: - >>> env = gym.make("LunarLander-v2", render_mode="single_rgb_array") + >>> env = gym.make("LunarLander-v2", render_mode="rgb_array") >>> wrapped = HumanRendering(env) >>> wrapped.reset() # This will start rendering to the screen @@ -28,10 +28,10 @@ class HumanRendering(gym.Wrapper): >>> env = gym.make("NoNativeRendering-v2", render_mode="human") # NoNativeRendering-v0 doesn't implement human-rendering natively >>> env.reset() # This will start rendering to the screen - Warning: If the base environment uses ``render_mode="rgb_array"``, its (i.e. the *base environment's*) render method + Warning: If the base environment uses ``render_mode="rgb_array_list"``, its (i.e. the *base environment's*) render method will always return an empty list: - >>> env = gym.make("LunarLander-v2", render_mode="rgb_array") + >>> env = gym.make("LunarLander-v2", render_mode="rgb_array_list") >>> wrapped = HumanRendering(env) >>> wrapped.reset() >>> env.render() @@ -47,9 +47,9 @@ def __init__(self, env): """ super().__init__(env) assert env.render_mode in [ - "single_rgb_array", "rgb_array", - ], f"Expected env.render_mode to be one of 'rgb_array' or 'single_rgb_array' but got '{env.render_mode}'" + "rgb_array_list", + ], f"Expected env.render_mode to be one of 'rgb_array' or 'rgb_array_list' but got '{env.render_mode}'" assert ( "render_fps" in env.metadata ), "The base environment must specify 'render_fps' to be used with the HumanRendering wrapper" @@ -79,7 +79,7 @@ def render(self): """This method doesn't do much, actual rendering is performed in :meth:`step` and :meth:`reset`.""" return None - def _render_frame(self, mode="human", **kwargs): + def _render_frame(self): """Fetch the last frame from the base environment and render it to the screen.""" try: import pygame @@ -87,43 +87,40 @@ def _render_frame(self, mode="human", **kwargs): raise DependencyNotInstalled( "pygame is not installed, run `pip install gym[box2d]`" ) - if self.env.render_mode == "rgb_array": - last_rgb_array = self.env.render(**kwargs) + if self.env.render_mode == "rgb_array_list": + last_rgb_array = self.env.render() assert isinstance(last_rgb_array, list) last_rgb_array = last_rgb_array[-1] - elif self.env.render_mode == "single_rgb_array": - last_rgb_array = self.env.render(**kwargs) + elif self.env.render_mode == "rgb_array": + last_rgb_array = self.env.render() else: raise Exception( - f"Wrapped environment must have mode 'rgb_array' or 'single_rgb_array', actual render mode: {self.env.render_mode}" + f"Wrapped environment must have mode 'rgb_array' or 'rgb_array_list', actual render mode: {self.env.render_mode}" ) assert isinstance(last_rgb_array, np.ndarray) - if mode == "human": - rgb_array = np.transpose(last_rgb_array, axes=(1, 0, 2)) + rgb_array = np.transpose(last_rgb_array, axes=(1, 0, 2)) - if self.screen_size is None: - self.screen_size = rgb_array.shape[:2] + if self.screen_size is None: + self.screen_size = rgb_array.shape[:2] - assert ( - self.screen_size == rgb_array.shape[:2] - ), f"The shape of the rgb array has changed from {self.screen_size} to {rgb_array.shape[:2]}" - - if self.window is None: - pygame.init() - pygame.display.init() - self.window = pygame.display.set_mode(self.screen_size) - - if self.clock is None: - self.clock = pygame.time.Clock() - - surf = pygame.surfarray.make_surface(rgb_array) - self.window.blit(surf, (0, 0)) - pygame.event.pump() - self.clock.tick(self.metadata["render_fps"]) - pygame.display.flip() - else: - raise Exception("Can only use 'human' rendering in HumanRendering wrapper") + assert ( + self.screen_size == rgb_array.shape[:2] + ), f"The shape of the rgb array has changed from {self.screen_size} to {rgb_array.shape[:2]}" + + if self.window is None: + pygame.init() + pygame.display.init() + self.window = pygame.display.set_mode(self.screen_size) + + if self.clock is None: + self.clock = pygame.time.Clock() + + surf = pygame.surfarray.make_surface(rgb_array) + self.window.blit(surf, (0, 0)) + pygame.event.pump() + self.clock.tick(self.metadata["render_fps"]) + pygame.display.flip() def close(self): """Close the rendering window.""" diff --git a/gym/wrappers/monitoring/video_recorder.py b/gym/wrappers/monitoring/video_recorder.py index 876bfaf696f..9e34102de77 100644 --- a/gym/wrappers/monitoring/video_recorder.py +++ b/gym/wrappers/monitoring/video_recorder.py @@ -55,10 +55,10 @@ def __init__( self.render_mode = env.render_mode - if "rgb_array" != self.render_mode and "single_rgb_array" != self.render_mode: + if "rgb_array_list" != self.render_mode and "rgb_array" != self.render_mode: logger.warn( f"Disabling video recorder because environment {env} was not initialized with any compatible video " - "mode between `single_rgb_array` and `rgb_array`" + "mode between `rgb_array` and `rgb_array_list`" ) # Disable since the environment has not been initialized with a compatible `render_mode` self.enabled = False diff --git a/gym/wrappers/pixel_observation.py b/gym/wrappers/pixel_observation.py index 628e53e2428..6d70d4657e1 100644 --- a/gym/wrappers/pixel_observation.py +++ b/gym/wrappers/pixel_observation.py @@ -24,13 +24,13 @@ class PixelObservationWrapper(gym.ObservationWrapper): Example: >>> import gym - >>> env = PixelObservationWrapper(gym.make('CarRacing-v1', render_mode="single_rgb_array")) + >>> env = PixelObservationWrapper(gym.make('CarRacing-v1', render_mode="rgb_array")) >>> obs = env.reset() >>> obs.keys() odict_keys(['pixels']) >>> obs['pixels'].shape (400, 600, 3) - >>> env = PixelObservationWrapper(gym.make('CarRacing-v1', render_mode="single_rgb_array"), pixels_only=False) + >>> env = PixelObservationWrapper(gym.make('CarRacing-v1', render_mode="rgb_array"), pixels_only=False) >>> obs = env.reset() >>> obs.keys() odict_keys(['state', 'pixels']) @@ -38,7 +38,7 @@ class PixelObservationWrapper(gym.ObservationWrapper): (96, 96, 3) >>> obs['pixels'].shape (400, 600, 3) - >>> env = PixelObservationWrapper(gym.make('CarRacing-v1', render_mode="single_rgb_array"), pixel_keys=('obs',)) + >>> env = PixelObservationWrapper(gym.make('CarRacing-v1', render_mode="rgb_array"), pixel_keys=('obs',)) >>> obs = env.reset() >>> obs.keys() odict_keys(['obs']) @@ -95,10 +95,10 @@ def __init__( default_render_kwargs = {} if not env.render_mode: - default_render_kwargs = {"mode": "rgb_array"} + default_render_kwargs = {"mode": "rgb_array_list"} logger.warn( "env.render_mode must be specified to use PixelObservationWrapper:" - "`gym.make(env_name, render_mode='single_rgb_array')`." + "`gym.make(env_name, render_mode='rgb_array')`." ) for key in pixel_keys: diff --git a/tests/envs/test_envs.py b/tests/envs/test_envs.py index a3ffd6c4330..c53bd9e46ce 100644 --- a/tests/envs/test_envs.py +++ b/tests/envs/test_envs.py @@ -1,16 +1,17 @@ import pickle import warnings +import numpy as np import pytest import gym from gym.envs.registration import EnvSpec +from gym.logger import warn from gym.utils.env_checker import check_env, data_equivalence from tests.envs.utils import ( all_testing_env_specs, all_testing_initialised_envs, assert_equals, - gym_testing_env_specs, ) # This runs a smoketest on each official registered env. We may want @@ -30,7 +31,6 @@ "A Box observation space minimum value is -infinity. This is probably too low.", "A Box observation space maximum value is -infinity. This is probably too high.", "For Box action spaces, we recommend using a symmetric and normalized space (range=[-1, 1] or [0, 1]). See https://stable-baselines3.readthedocs.io/en/master/guide/rl_tips.html for more information.", - "Initializing environment in done (old) step API which returns one bool instead of two.", ] ] @@ -48,9 +48,6 @@ def test_envs_pass_env_checker(spec): for warning in caught_warnings: if warning.message.args[0] not in CHECK_ENV_IGNORE_WARNINGS: - print() - print(warning.message.args[0]) - print(CHECK_ENV_IGNORE_WARNINGS[-1]) raise gym.error.Error(f"Unexpected warning: {warning.message}") @@ -118,19 +115,69 @@ def test_env_determinism_rollout(env_spec: EnvSpec): env_2.close() +def check_rendered(rendered_frame, mode: str): + """Check that the rendered frame is as expected.""" + if mode == "rgb_array_list": + assert isinstance(rendered_frame, list) + for frame in rendered_frame: + check_rendered(frame, "rgb_array") + elif mode == "rgb_array": + assert isinstance(rendered_frame, np.ndarray) + assert len(rendered_frame.shape) == 3 + assert rendered_frame.shape[2] == 3 + assert np.all(rendered_frame >= 0) and np.all(rendered_frame <= 255) + elif mode == "ansi": + assert isinstance(rendered_frame, str) + assert len(rendered_frame) > 0 + elif mode == "state_pixels_list": + assert isinstance(rendered_frame, list) + for frame in rendered_frame: + check_rendered(frame, "rgb_array") + elif mode == "state_pixels": + check_rendered(rendered_frame, "rgb_array") + elif mode == "depth_array_list": + assert isinstance(rendered_frame, list) + for frame in rendered_frame: + check_rendered(frame, "depth_array") + elif mode == "depth_array": + assert isinstance(rendered_frame, np.ndarray) + assert len(rendered_frame.shape) == 2 + else: + warn( + f"Unknown render mode: {mode}, cannot check that the rendered data is correct. Add case to `check_rendered`" + ) + + +non_mujoco_py_env_specs = [ + spec + for spec in all_testing_env_specs + if "mujoco" not in spec.entry_point or "v4" in spec.id +] + + @pytest.mark.parametrize( - "spec", gym_testing_env_specs, ids=[spec.id for spec in gym_testing_env_specs] + "spec", non_mujoco_py_env_specs, ids=[spec.id for spec in non_mujoco_py_env_specs] ) def test_render_modes(spec): + """There is a known issue where rendering a mujoco environment then mujoco-py will cause an error on non-mac based systems. + + Therefore, we are only testing with mujoco environments. + """ env = spec.make() - for mode in env.metadata.get("render_modes", []): + assert len(env.metadata["render_modes"]) > 0 + for mode in env.metadata["render_modes"]: if mode != "human": new_env = spec.make(render_mode=mode) new_env.reset() + rendered = new_env.render() + check_rendered(rendered, mode) + new_env.step(new_env.action_space.sample()) - new_env.render() + rendered = new_env.render() + check_rendered(rendered, mode) + new_env.close() env.close() diff --git a/tests/envs/test_make.py b/tests/envs/test_make.py index cf80995758c..cdba7b2772b 100644 --- a/tests/envs/test_make.py +++ b/tests/envs/test_make.py @@ -175,8 +175,10 @@ def test_make_render_mode(): env.close() # Make sure that render_mode is applied correctly - env = gym.make("CartPole-v1", render_mode="rgb_array", disable_env_checker=True) - assert env.render_mode == "rgb_array" + env = gym.make( + "CartPole-v1", render_mode="rgb_array_list", disable_env_checker=True + ) + assert env.render_mode == "rgb_array_list" env.reset() renders = env.render() assert isinstance( @@ -226,7 +228,9 @@ def test_make_render_mode(): TypeError, match=re.escape("got an unexpected keyword argument 'render_mode'") ): gym.make( - "test/NoHumanOldAPI-v0", render_mode="rgb_array", disable_env_checker=True + "test/NoHumanOldAPI-v0", + render_mode="rgb_array_list", + disable_env_checker=True, ) # Make sure that an additional error is thrown a user tries to use the wrapper on an environment with old API diff --git a/tests/envs/utils_envs.py b/tests/envs/utils_envs.py index a7e01831b26..8282f921681 100644 --- a/tests/envs/utils_envs.py +++ b/tests/envs/utils_envs.py @@ -23,7 +23,7 @@ def __init__(self, arg1, arg2, arg3): class NoHuman(gym.Env): """Environment that does not have human-rendering.""" - metadata = {"render_modes": ["rgb_array"], "render_fps": 4} + metadata = {"render_modes": ["rgb_array_list"], "render_fps": 4} def __init__(self, render_mode=None): assert render_mode in self.metadata["render_modes"] @@ -33,7 +33,7 @@ def __init__(self, render_mode=None): class NoHumanOldAPI(gym.Env): """Environment that does not have human-rendering.""" - metadata = {"render_modes": ["rgb_array"], "render_fps": 4} + metadata = {"render_modes": ["rgb_array_list"], "render_fps": 4} def __init__(self): pass diff --git a/tests/test_core.py b/tests/test_core.py index 6b28cd8e7e2..439f4e151cb 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -101,7 +101,7 @@ def test_env_instantiation(): }, {"action_space": spaces.Discrete(2)}, {"reward_range": (-1.0, 1.0)}, - {"metadata": {"render_modes": ["human", "rgb_array"]}}, + {"metadata": {"render_modes": ["human", "rgb_array_list"]}}, { "observation_space": spaces.Box( low=0.0, high=1.0, shape=(64, 64, 3), dtype=np.float32 diff --git a/tests/testing_env.py b/tests/testing_env.py index e72d399d8a7..957f25fc806 100644 --- a/tests/testing_env.py +++ b/tests/testing_env.py @@ -1,6 +1,6 @@ """Provides a generic testing environment for use in tests with custom reset, step and render functions.""" import types -from typing import List, Optional, Tuple, Union +from typing import Any, Dict, Optional, Tuple, Union import gym from gym import spaces @@ -46,14 +46,11 @@ def __init__( reset_fn: callable = basic_reset_fn, step_fn: callable = new_step_fn, render_fn: callable = basic_render_fn, - render_modes: Optional[List[str]] = None, - render_fps: Optional[int] = None, + metadata: Optional[Dict[str, Any]] = None, render_mode: Optional[str] = None, spec: EnvSpec = EnvSpec("TestingEnv-v0"), ): - self.metadata = {"render_modes": render_modes} - if render_fps: - self.metadata["render_fps"] = render_fps + self.metadata = {} if metadata is None else metadata self.render_mode = render_mode self.spec = spec @@ -76,7 +73,10 @@ def reset( options: Optional[dict] = None, ) -> Union[ObsType, Tuple[ObsType, dict]]: # If you need a default working reset function, use `basic_reset_fn` above - raise NotImplementedError("TestingEnv reset_fn is not set") + raise NotImplementedError("TestingEnv reset_fn is not set.") def step(self, action: ActType) -> Tuple[ObsType, float, bool, dict]: - raise NotImplementedError("TestingEnv step_fn is not set") + raise NotImplementedError("TestingEnv step_fn is not set.") + + def render(self): + raise NotImplementedError("testingEnv render_fn is not set.") diff --git a/tests/utils/test_passive_env_checker.py b/tests/utils/test_passive_env_checker.py index 5b41ed08956..c6cdc7d8964 100644 --- a/tests/utils/test_passive_env_checker.py +++ b/tests/utils/test_passive_env_checker.py @@ -396,24 +396,25 @@ def test_passive_env_step_checker( [ [ UserWarning, - GenericTestEnv(render_modes=None), + GenericTestEnv(metadata={"render_modes": None}), "No render modes was declared in the environment (env.metadata['render_modes'] is None or not defined), you may have trouble when calling `.render()`.", ], [ UserWarning, - GenericTestEnv(render_modes="Testing mode"), + GenericTestEnv(metadata={"render_modes": "Testing mode"}), "Expects the render_modes to be a sequence (i.e. list, tuple), actual type: ", ], [ UserWarning, - GenericTestEnv(render_modes=["Testing mode", 1], render_fps=1), + GenericTestEnv( + metadata={"render_modes": ["Testing mode", 1], "render_fps": 1}, + ), "Expects all render modes to be strings, actual types: [, ]", ], [ UserWarning, GenericTestEnv( - render_modes=["Testing mode"], - render_fps=None, + metadata={"render_modes": ["Testing mode"], "render_fps": None}, render_mode="Testing mode", render_fn=lambda self: 0, ), @@ -421,18 +422,23 @@ def test_passive_env_step_checker( ], [ UserWarning, - GenericTestEnv(render_modes=["Testing mode"], render_fps="fps"), + GenericTestEnv( + metadata={"render_modes": ["Testing mode"], "render_fps": "fps"} + ), "Expects the `env.metadata['render_fps']` to be an integer or a float, actual type: ", ], [ AssertionError, - GenericTestEnv(render_modes=[], render_fps=30, render_mode="Test"), + GenericTestEnv( + metadata={"render_modes": [], "render_fps": 30}, render_mode="Test" + ), "With no render_modes, expects the Env.render_mode to be None, actual value: Test", ], [ AssertionError, GenericTestEnv( - render_modes=["Testing mode"], render_fps=30, render_mode="Non mode" + metadata={"render_modes": ["Testing mode"], "render_fps": 30}, + render_mode="Non mode", ), "The environment was initialized successfully however with an unsupported render mode. Render mode: Non mode, modes: ['Testing mode']", ], diff --git a/tests/utils/test_play.py b/tests/utils/test_play.py index af819913d25..67162509094 100644 --- a/tests/utils/test_play.py +++ b/tests/utils/test_play.py @@ -1,6 +1,6 @@ -from dataclasses import dataclass +from functools import partial from itertools import product -from typing import Callable, Optional +from typing import Callable import numpy as np import pygame @@ -10,31 +10,18 @@ import gym from gym.utils.play import MissingKeysToAction, PlayableGame, play +from tests.testing_env import GenericTestEnv RELEVANT_KEY_1 = ord("a") # 97 RELEVANT_KEY_2 = ord("d") # 100 IRRELEVANT_KEY = 1 -@dataclass -class DummyEnvSpec: - id: str - - -class DummyPlayEnv(gym.Env): - def __init__(self, render_mode: Optional[str] = None): - self.render_mode = render_mode - - def step(self, action): - obs = np.zeros((1, 1)) - rew, terminated, truncated, info = 1, False, False, {} - return obs, rew, terminated, truncated, info - - def reset(self, seed=None): - ... - - def render(self): - return np.zeros((1, 1)) +PlayableEnv = partial( + GenericTestEnv, + metadata={"render_modes": ["rgb_array"]}, + render_fn=lambda self: np.ones((10, 10, 3)), +) class KeysToActionWrapper(gym.Wrapper): @@ -76,14 +63,13 @@ def close_pygame(): def test_play_relevant_keys(): - env = DummyPlayEnv(render_mode="single_rgb_array") + env = PlayableEnv(render_mode="rgb_array") game = PlayableGame(env, dummy_keys_to_action()) assert game.relevant_keys == {RELEVANT_KEY_1, RELEVANT_KEY_2} def test_play_relevant_keys_no_mapping(): - env = DummyPlayEnv(render_mode="single_rgb_array") - env.spec = DummyEnvSpec("DummyPlayEnv") + env = PlayableEnv(render_mode="rgb_array") with pytest.raises(MissingKeysToAction): PlayableGame(env) @@ -91,27 +77,27 @@ def test_play_relevant_keys_no_mapping(): def test_play_relevant_keys_with_env_attribute(): """Env has a keys_to_action attribute""" - env = DummyPlayEnv(render_mode="single_rgb_array") + env = PlayableEnv(render_mode="rgb_array") env.get_keys_to_action = dummy_keys_to_action game = PlayableGame(env) assert game.relevant_keys == {RELEVANT_KEY_1, RELEVANT_KEY_2} def test_video_size_no_zoom(): - env = DummyPlayEnv(render_mode="single_rgb_array") + env = PlayableEnv(render_mode="rgb_array") game = PlayableGame(env, dummy_keys_to_action()) - assert game.video_size == list(env.render().shape) + assert game.video_size == env.render().shape[:2] def test_video_size_zoom(): - env = DummyPlayEnv(render_mode="single_rgb_array") + env = PlayableEnv(render_mode="rgb_array") zoom = 2.2 game = PlayableGame(env, dummy_keys_to_action(), zoom) - assert game.video_size == tuple(int(shape * zoom) for shape in env.render().shape) + assert game.video_size == tuple(int(dim * zoom) for dim in env.render().shape[:2]) def test_keyboard_quit_event(): - env = DummyPlayEnv(render_mode="single_rgb_array") + env = PlayableEnv(render_mode="rgb_array") game = PlayableGame(env, dummy_keys_to_action()) event = Event(pygame.KEYDOWN, {"key": pygame.K_ESCAPE}) assert game.running is True @@ -120,7 +106,7 @@ def test_keyboard_quit_event(): def test_pygame_quit_event(): - env = DummyPlayEnv(render_mode="single_rgb_array") + env = PlayableEnv(render_mode="rgb_array") game = PlayableGame(env, dummy_keys_to_action()) event = Event(pygame.QUIT) assert game.running is True @@ -129,7 +115,7 @@ def test_pygame_quit_event(): def test_keyboard_relevant_keydown_event(): - env = DummyPlayEnv(render_mode="single_rgb_array") + env = PlayableEnv(render_mode="rgb_array") game = PlayableGame(env, dummy_keys_to_action()) event = Event(pygame.KEYDOWN, {"key": RELEVANT_KEY_1}) game.process_event(event) @@ -137,7 +123,7 @@ def test_keyboard_relevant_keydown_event(): def test_keyboard_irrelevant_keydown_event(): - env = DummyPlayEnv(render_mode="single_rgb_array") + env = PlayableEnv(render_mode="rgb_array") game = PlayableGame(env, dummy_keys_to_action()) event = Event(pygame.KEYDOWN, {"key": IRRELEVANT_KEY}) game.process_event(event) @@ -145,7 +131,7 @@ def test_keyboard_irrelevant_keydown_event(): def test_keyboard_keyup_event(): - env = DummyPlayEnv(render_mode="single_rgb_array") + env = PlayableEnv(render_mode="rgb_array") game = PlayableGame(env, dummy_keys_to_action()) event = Event(pygame.KEYDOWN, {"key": RELEVANT_KEY_1}) game.process_event(event) @@ -189,7 +175,7 @@ def callback(obs_t, obs_tp1, action, rew, terminated, truncated, info): return obs_t, obs_tp1, action, rew, terminated, truncated, info - env = gym.make(ENV, render_mode="single_rgb_array", disable_env_checker=True) + env = gym.make(ENV, render_mode="rgb_array", disable_env_checker=True) env.reset(seed=SEED) keys_to_action = ( dummy_keys_to_action_str() if str_keys else dummy_keys_to_action() @@ -202,9 +188,7 @@ def callback(obs_t, obs_tp1, action, rew, terminated, truncated, info): action = keys_to_action[chr(e.key) if str_keys else (e.key,)] obs, _, _, _, _ = env.step(action) - env_play = gym.make( - ENV, render_mode="single_rgb_array", disable_env_checker=True - ) + env_play = gym.make(ENV, render_mode="rgb_array", disable_env_checker=True) if apply_wrapper: env_play = KeysToActionWrapper(env, keys_to_action=keys_to_action) assert hasattr(env_play, "get_keys_to_action") diff --git a/tests/utils/test_save_video.py b/tests/utils/test_save_video.py index c7c52746786..1a1d9dd73ab 100644 --- a/tests/utils/test_save_video.py +++ b/tests/utils/test_save_video.py @@ -8,7 +8,9 @@ def test_record_video_using_default_trigger(): - env = gym.make("CartPole-v1", render_mode="rgb_array", disable_env_checker=True) + env = gym.make( + "CartPole-v1", render_mode="rgb_array_list", disable_env_checker=True + ) env.reset() step_starting_index = 0 @@ -45,7 +47,7 @@ def step_trigger(step_index): def test_record_video_step_trigger(): - env = gym.make("CartPole-v1", render_mode="rgb_array") + env = gym.make("CartPole-v1", render_mode="rgb_array_list") env._max_episode_steps = 20 env.reset() @@ -76,7 +78,7 @@ def test_record_video_step_trigger(): def test_record_video_within_vector(): envs = gym.vector.make( - "CartPole-v1", num_envs=2, asynchronous=True, render_mode="rgb_array" + "CartPole-v1", num_envs=2, asynchronous=True, render_mode="rgb_array_list" ) envs.reset() episode_frames = [] diff --git a/tests/vector/test_async_vector_env.py b/tests/vector/test_async_vector_env.py index 63e8da1253b..efe9be3f01a 100644 --- a/tests/vector/test_async_vector_env.py +++ b/tests/vector/test_async_vector_env.py @@ -97,7 +97,9 @@ def test_step_async_vector_env(shared_memory, use_single_action_space): @pytest.mark.parametrize("shared_memory", [True, False]) def test_call_async_vector_env(shared_memory): - env_fns = [make_env("CartPole-v1", i, render_mode="rgb_array") for i in range(4)] + env_fns = [ + make_env("CartPole-v1", i, render_mode="rgb_array_list") for i in range(4) + ] env = AsyncVectorEnv(env_fns, shared_memory=shared_memory) _ = env.reset() diff --git a/tests/vector/test_sync_vector_env.py b/tests/vector/test_sync_vector_env.py index 961473b5059..d18faf97b27 100644 --- a/tests/vector/test_sync_vector_env.py +++ b/tests/vector/test_sync_vector_env.py @@ -77,7 +77,9 @@ def test_step_sync_vector_env(use_single_action_space): def test_call_sync_vector_env(): - env_fns = [make_env("CartPole-v1", i, render_mode="rgb_array") for i in range(4)] + env_fns = [ + make_env("CartPole-v1", i, render_mode="rgb_array_list") for i in range(4) + ] env = SyncVectorEnv(env_fns) _ = env.reset() diff --git a/tests/wrappers/test_human_rendering.py b/tests/wrappers/test_human_rendering.py index 1e43beda707..4af806158d1 100644 --- a/tests/wrappers/test_human_rendering.py +++ b/tests/wrappers/test_human_rendering.py @@ -7,7 +7,7 @@ def test_human_rendering(): - for mode in ["rgb_array", "single_rgb_array"]: + for mode in ["rgb_array", "rgb_array_list"]: env = HumanRendering( gym.make("CartPole-v1", render_mode=mode, disable_env_checker=True) ) @@ -25,7 +25,7 @@ def test_human_rendering(): with pytest.raises( AssertionError, match=re.escape( - "Expected env.render_mode to be one of 'rgb_array' or 'single_rgb_array' but got 'human'" + "Expected env.render_mode to be one of 'rgb_array' or 'rgb_array_list' but got 'human'" ), ): HumanRendering(env) diff --git a/tests/wrappers/test_order_enforcing.py b/tests/wrappers/test_order_enforcing.py index 76c0d58a65e..d1aee9ea954 100644 --- a/tests/wrappers/test_order_enforcing.py +++ b/tests/wrappers/test_order_enforcing.py @@ -21,7 +21,7 @@ def test_gym_make_order_enforcing(spec): def test_order_enforcing(): """Checks that the order enforcing works as expected, raising an error before reset is called and not after.""" # The reason for not using gym.make is that all environments are by default wrapped in the order enforcing wrapper - env = CartPoleEnv(render_mode="rgb_array") + env = CartPoleEnv(render_mode="rgb_array_list") assert not has_wrapper(env, OrderEnforcing) # Assert that the order enforcing works for step and render before reset @@ -40,6 +40,6 @@ def test_order_enforcing(): order_enforced_env.render() # Assert that with disable_render_order_enforcing works, the environment has already been reset - env = CartPoleEnv(render_mode="rgb_array") + env = CartPoleEnv(render_mode="rgb_array_list") env = OrderEnforcing(env, disable_render_order_enforcing=True) env.render() # no assertion error diff --git a/tests/wrappers/test_passive_env_checker.py b/tests/wrappers/test_passive_env_checker.py index 41f0d087464..8348b5d4793 100644 --- a/tests/wrappers/test_passive_env_checker.py +++ b/tests/wrappers/test_passive_env_checker.py @@ -68,7 +68,9 @@ def _step_failure(self, action): def test_api_failures(): env = GenericTestEnv( - reset_fn=_reset_failure, step_fn=_step_failure, render_modes="error" + reset_fn=_reset_failure, + step_fn=_step_failure, + metadata={"render_modes": "error"}, ) env = PassiveEnvChecker(env) assert env.checked_reset is False diff --git a/tests/wrappers/test_record_video.py b/tests/wrappers/test_record_video.py index 1d7d2c9d6a2..c865c199b50 100644 --- a/tests/wrappers/test_record_video.py +++ b/tests/wrappers/test_record_video.py @@ -6,7 +6,9 @@ def test_record_video_using_default_trigger(): - env = gym.make("CartPole-v1", render_mode="rgb_array", disable_env_checker=True) + env = gym.make( + "CartPole-v1", render_mode="rgb_array_list", disable_env_checker=True + ) env = gym.wrappers.RecordVideo(env, "videos") env.reset() for _ in range(199): diff --git a/tests/wrappers/test_step_compatibility.py b/tests/wrappers/test_step_compatibility.py index 5e0bd39f1ea..bb22e139d82 100644 --- a/tests/wrappers/test_step_compatibility.py +++ b/tests/wrappers/test_step_compatibility.py @@ -78,3 +78,5 @@ def test_step_compatibility_in_make(apply_step_compatibility): assert len(step_returns) == 4 _, _, done, _ = step_returns assert isinstance(done, bool) + + gym.envs.registry.pop("OldStepEnv-v0") diff --git a/tests/wrappers/test_video_recorder.py b/tests/wrappers/test_video_recorder.py index 5ea3cf66c98..db36f61cf6c 100644 --- a/tests/wrappers/test_video_recorder.py +++ b/tests/wrappers/test_video_recorder.py @@ -10,9 +10,9 @@ class BrokenRecordableEnv(gym.Env): - metadata = {"render_modes": ["rgb_array"]} + metadata = {"render_modes": ["rgb_array_list"]} - def __init__(self, render_mode="rgb_array"): + def __init__(self, render_mode="rgb_array_list"): self.render_mode = render_mode def render(self): @@ -30,7 +30,9 @@ def render(self): def test_record_simple(): - env = gym.make("CartPole-v1", render_mode="rgb_array", disable_env_checker=True) + env = gym.make( + "CartPole-v1", render_mode="rgb_array_list", disable_env_checker=True + ) rec = VideoRecorder(env) env.reset() rec.capture_frame() @@ -45,7 +47,9 @@ def test_record_simple(): def test_autoclose(): def record(): - env = gym.make("CartPole-v1", render_mode="rgb_array", disable_env_checker=True) + env = gym.make( + "CartPole-v1", render_mode="rgb_array_list", disable_env_checker=True + ) rec = VideoRecorder(env) env.reset() rec.capture_frame() @@ -77,7 +81,9 @@ def test_no_frames(): def test_record_unrecordable_method(): with pytest.warns( UserWarning, - match="Disabling video recorder because environment was not initialized with any compatible video mode between `single_rgb_array` and `rgb_array`", + match=re.escape( + "\x1b[33mWARN: Disabling video recorder because environment was not initialized with any compatible video mode between `rgb_array` and `rgb_array_list`\x1b[0m" + ), ): env = UnrecordableEnv() rec = VideoRecorder(env) @@ -101,7 +107,9 @@ def test_record_breaking_render_method(): def test_text_envs(): - env = gym.make("FrozenLake-v1", render_mode="rgb_array", disable_env_checker=True) + env = gym.make( + "FrozenLake-v1", render_mode="rgb_array_list", disable_env_checker=True + ) video = VideoRecorder(env) try: env.reset()