From daa293d11b89920593199963706fe6ee7bc17e26 Mon Sep 17 00:00:00 2001 From: Marek Wydmuch Date: Sat, 7 Sep 2024 13:03:42 +0200 Subject: [PATCH 1/3] Remove Gym wrappers --- .github/workflows/build-docs-dev.yml | 2 +- .../workflows/build-docs-manual-version.yml | 2 +- .github/workflows/build-docs-version.yml | 2 +- gym_wrapper/__init__.py | 62 --- gym_wrapper/base_gym_env.py | 410 ------------------ gym_wrapper/gym_env_defns.py | 23 - pyproject.toml | 4 +- setup.py | 4 +- tests/manual_test_gym_wrapper.py | 356 --------------- 9 files changed, 6 insertions(+), 859 deletions(-) delete mode 100644 gym_wrapper/__init__.py delete mode 100644 gym_wrapper/base_gym_env.py delete mode 100644 gym_wrapper/gym_env_defns.py delete mode 100755 tests/manual_test_gym_wrapper.py diff --git a/.github/workflows/build-docs-dev.yml b/.github/workflows/build-docs-dev.yml index 741e0149f..ed64771f5 100644 --- a/.github/workflows/build-docs-dev.yml +++ b/.github/workflows/build-docs-dev.yml @@ -25,7 +25,7 @@ jobs: run: sudo apt install cmake git libboost-all-dev libsdl2-dev libopenal-dev - name: Install ViZDoom - run: pip install .[gym] + run: pip install . - name: Install docs dependencies run: pip install -r docs/requirements.txt diff --git a/.github/workflows/build-docs-manual-version.yml b/.github/workflows/build-docs-manual-version.yml index 75de00124..6283f5093 100644 --- a/.github/workflows/build-docs-manual-version.yml +++ b/.github/workflows/build-docs-manual-version.yml @@ -37,7 +37,7 @@ jobs: run: sudo apt install cmake git libboost-all-dev libsdl2-dev libopenal-dev - name: Install ViZDoom - run: pip install .[gym] + run: pip install . - name: Install docs dependencies run: pip install -r docs/requirements.txt diff --git a/.github/workflows/build-docs-version.yml b/.github/workflows/build-docs-version.yml index 187aec14c..08540ba0d 100644 --- a/.github/workflows/build-docs-version.yml +++ b/.github/workflows/build-docs-version.yml @@ -22,7 +22,7 @@ jobs: run: sudo apt install cmake git libboost-all-dev libsdl2-dev libopenal-dev - name: Install ViZDoom - run: pip install .[gym] + run: pip install . - name: Install docs dependencies run: pip install -r docs/requirements.txt diff --git a/gym_wrapper/__init__.py b/gym_wrapper/__init__.py deleted file mode 100644 index 98571a8f0..000000000 --- a/gym_wrapper/__init__.py +++ /dev/null @@ -1,62 +0,0 @@ -from gym.envs.registration import register - - -register( - id="VizdoomBasic-v0", - entry_point="vizdoom.gym_wrapper.gym_env_defns:VizdoomScenarioEnv", - kwargs={"scenario_file": "basic.cfg"}, -) - -register( - id="VizdoomCorridor-v0", - entry_point="vizdoom.gym_wrapper.gym_env_defns:VizdoomScenarioEnv", - kwargs={"scenario_file": "deadly_corridor.cfg"}, -) - -register( - id="VizdoomDefendCenter-v0", - entry_point="vizdoom.gym_wrapper.gym_env_defns:VizdoomScenarioEnv", - kwargs={"scenario_file": "defend_the_center.cfg"}, -) - -register( - id="VizdoomDefendLine-v0", - entry_point="vizdoom.gym_wrapper.gym_env_defns:VizdoomScenarioEnv", - kwargs={"scenario_file": "defend_the_line.cfg"}, -) - -register( - id="VizdoomHealthGathering-v0", - entry_point="vizdoom.gym_wrapper.gym_env_defns:VizdoomScenarioEnv", - kwargs={"scenario_file": "health_gathering.cfg"}, -) - -register( - id="VizdoomMyWayHome-v0", - entry_point="vizdoom.gym_wrapper.gym_env_defns:VizdoomScenarioEnv", - kwargs={"scenario_file": "my_way_home.cfg"}, -) - -register( - id="VizdoomPredictPosition-v0", - entry_point="vizdoom.gym_wrapper.gym_env_defns:VizdoomScenarioEnv", - kwargs={"scenario_file": "predict_position.cfg"}, -) - -register( - id="VizdoomTakeCover-v0", - entry_point="vizdoom.gym_wrapper.gym_env_defns:VizdoomScenarioEnv", - kwargs={"scenario_file": "take_cover.cfg"}, -) - -register( - id="VizdoomDeathmatch-v0", - entry_point="vizdoom.gym_wrapper.gym_env_defns:VizdoomScenarioEnv", - kwargs={"scenario_file": "deathmatch.cfg"}, -) - -register( - id="VizdoomHealthGatheringSupreme-v0", - entry_point="vizdoom.gym_wrapper.gym_env_defns:VizdoomScenarioEnv", - kwargs={"scenario_file": "health_gathering_supreme.cfg"}, -) diff --git a/gym_wrapper/base_gym_env.py b/gym_wrapper/base_gym_env.py deleted file mode 100644 index 763d4af10..000000000 --- a/gym_wrapper/base_gym_env.py +++ /dev/null @@ -1,410 +0,0 @@ -import itertools -import warnings -from typing import Optional - -import gym -import numpy as np -import pygame -from gym.utils import EzPickle - -import vizdoom.vizdoom as vzd - - -# A fixed set of colors for each potential label -# for rendering an image. -# 256 is not nearly enough for all IDs, but we limit -# ourselves here to avoid hogging too much memory. -LABEL_COLORS = ( - np.random.default_rng(1993).uniform(25, 256, size=(256, 3)).astype(np.uint8) -) - - -class VizdoomEnv(gym.Env, EzPickle): - metadata = {"render_modes": ["human", "rgb_array"]} - - def __init__( - self, - level, - frame_skip=1, - max_buttons_pressed=1, - render_mode: Optional[str] = None, - ): - """ - Base class for Gym interface for ViZDoom. Thanks to https://github.com/shakenes/vizdoomgym - Child classes are defined in gym_env_defns.py, - - Arguments: - level (str): path to the config file to load. Most settings should be set by this config file. - frame_skip (int): how many frames should be advanced per action. 1 = take action on every frame. Default: 1. - max_buttons_pressed (int): defines the number of binary buttons that can be selected at once. Default: 1. - Should be >= 0. If < 0 a RuntimeError is raised. - If == 0, the binary action space becomes MultiDiscrete([2] * num_binary_buttons) - and [0, num_binary_buttons] number of binary buttons can be selected. - If > 0, the binary action space becomes Discrete(n) - and [0, max_buttons_pressed] number of binary buttons can be selected. - render_mode(Optional[str]): the render mode to use could be either 'human' or 'rgb_array' - - This environment forces window to be hidden. Use `render()` function to see the game. - - Observations are dictionaries with different amount of entries, depending on if depth/label buffers were - enabled in the config file (CHANNELS == 1 if GRAY8, else 3): - - - "screen" = the screen image buffer (always available) in shape (HEIGHT, WIDTH, CHANNELS) - - "depth" = the depth image in shape (HEIGHT, WIDTH, 1), if enabled by the config file, - - "labels" = the label image buffer in shape (HEIGHT, WIDTH, 1), if enabled by the config file. - For info on labels, access `env.state.labels` variable. - - "automap" = the automap image buffer in shape (HEIGHT, WIDTH, CHANNELS), if enabled by the config file - - "gamevariables" = all game variables, in the order specified by the config file - - Action space can be a single one of binary/continuous action space, or a Dict containing both. - - - "binary": - = MultiDiscrete([2] * num_binary_buttons): if max_buttons_pressed == 0 - = Discrete(n): if max_buttons_pressed > 1 - - "continuous": - = Box(float32.min, float32.max, (num_delta_buttons,), float32). - """ - EzPickle.__init__(self, level, frame_skip, max_buttons_pressed, render_mode) - self.frame_skip = frame_skip - self.render_mode = render_mode - - # init game - self.game = vzd.DoomGame() - self.game.load_config(level) - self.game.set_window_visible(False) - - screen_format = self.game.get_screen_format() - if ( - screen_format != vzd.ScreenFormat.RGB24 - and screen_format != vzd.ScreenFormat.GRAY8 - ): - warnings.warn( - f"Detected screen format {screen_format.name}. Only RGB24 and GRAY8 are supported in the Gym" - f" wrapper. Forcing RGB24." - ) - self.game.set_screen_format(vzd.ScreenFormat.RGB24) - - self.state = None - self.window_surface = None - self.isopen = True - self.channels = 3 - if screen_format == vzd.ScreenFormat.GRAY8: - self.channels = 1 - - self.depth = self.game.is_depth_buffer_enabled() - self.labels = self.game.is_labels_buffer_enabled() - self.automap = self.game.is_automap_buffer_enabled() - - # parse buttons defined by config file - self.__parse_available_buttons() - - # check for valid max_buttons_pressed - if max_buttons_pressed > self.num_binary_buttons > 0: - warnings.warn( - f"max_buttons_pressed={max_buttons_pressed} " - f"> number of binary buttons defined={self.num_binary_buttons}. " - f"Clipping max_buttons_pressed to {self.num_binary_buttons}." - ) - max_buttons_pressed = self.num_binary_buttons - elif max_buttons_pressed < 0: - raise RuntimeError( - f"max_buttons_pressed={max_buttons_pressed} < 0. Should be >= 0. " - ) - - # specify action space(s) - self.max_buttons_pressed = max_buttons_pressed - self.action_space = self.__get_action_space() - - # specify observation space(s) - self.observation_space = self.__get_observation_space() - - self.game.init() - - def step(self, action): - assert self.action_space.contains( - action - ), f"{action!r} ({type(action)}) invalid" - assert self.state is not None, "Call `reset` before using `step` method." - - env_action = self.__build_env_action(action) - reward = self.game.make_action(env_action, self.frame_skip) - self.state = self.game.get_state() - terminated = self.game.is_episode_finished() - truncated = False # Truncation to be handled by the TimeLimit wrapper - if self.render_mode == "human": - self.render() - return self.__collect_observations(), reward, terminated, truncated, {} - - def __parse_binary_buttons(self, env_action, agent_action): - if self.num_binary_buttons != 0: - if self.num_delta_buttons != 0: - agent_action = agent_action["binary"] - - if np.issubdtype(type(agent_action), np.integer): - agent_action = self.button_map[agent_action] - - # binary actions offset by number of delta buttons - env_action[self.num_delta_buttons :] = agent_action - - def __parse_delta_buttons(self, env_action, agent_action): - if self.num_delta_buttons != 0: - if self.num_binary_buttons != 0: - agent_action = agent_action["continuous"] - - # delta buttons have a direct mapping since they're reorganized to be prior to any binary buttons - env_action[0 : self.num_delta_buttons] = agent_action - - def __build_env_action(self, agent_action): - # encode users action as environment action - env_action = np.array( - [0 for _ in range(self.num_delta_buttons + self.num_binary_buttons)], - dtype=np.float32, - ) - self.__parse_delta_buttons(env_action, agent_action) - self.__parse_binary_buttons(env_action, agent_action) - return env_action - - def reset( - self, - *, - seed: Optional[int] = None, - options: Optional[dict] = None, - ): - super().reset(seed=seed) - if seed is not None: - self.game.set_seed(seed) - self.game.new_episode() - self.state = self.game.get_state() - - return self.__collect_observations(), {} - - def __collect_observations(self): - observation = {} - if self.state is not None: - observation["screen"] = self.state.screen_buffer - if self.channels == 1: - observation["screen"] = self.state.screen_buffer[..., None] - if self.depth: - observation["depth"] = self.state.depth_buffer[..., None] - if self.labels: - observation["labels"] = self.state.labels_buffer[..., None] - if self.automap: - observation["automap"] = self.state.automap_buffer - if self.channels == 1: - observation["automap"] = self.state.automap_buffer[..., None] - if self.num_game_variables > 0: - observation["gamevariables"] = self.state.game_variables.astype( - np.float32 - ) - else: - # there is no state in the terminal step, so a zero observation is returned instead - for space_key, space_item in self.observation_space.spaces.items(): - observation[space_key] = np.zeros( - space_item.shape, dtype=space_item.dtype - ) - - return observation - - def __build_human_render_image(self): - """Stack all available buffers into one for human consumption""" - game_state = self.game.get_state() - valid_buffers = game_state is not None - - if not valid_buffers: - # Return a blank image - num_enabled_buffers = 1 + self.depth + self.labels + self.automap - img = np.zeros( - ( - self.game.get_screen_height(), - self.game.get_screen_width() * num_enabled_buffers, - 3, - ), - dtype=np.uint8, - ) - return img - - image_list = [game_state.screen_buffer] - if self.channels == 1: - image_list = [ - np.repeat(game_state.screen_buffer[..., None], repeats=3, axis=2) - ] - - if self.depth: - image_list.append( - np.repeat(game_state.depth_buffer[..., None], repeats=3, axis=2) - ) - - if self.labels: - # Give each label a fixed color. - # We need to connect each pixel in labels_buffer to the corresponding - # id via `value`` - labels_rgb = np.zeros_like(image_list[0]) - labels_buffer = game_state.labels_buffer - for label in game_state.labels: - color = LABEL_COLORS[label.object_id % 256] - labels_rgb[labels_buffer == label.value] = color - image_list.append(labels_rgb) - - if self.automap: - automap_buffer = game_state.automap_buffer - if self.channels == 1: - automap_buffer = np.repeat(automap_buffer[..., None], repeats=3, axis=2) - image_list.append(automap_buffer) - - return np.concatenate(image_list, axis=1) - - def render(self): - render_image = self.__build_human_render_image() - if self.render_mode == "rgb_array": - return render_image - elif self.render_mode == "human": - # Transpose image (pygame wants (width, height, channels), we have (height, width, channels)) - render_image = render_image.transpose(1, 0, 2) - if self.window_surface is None: - pygame.init() - pygame.display.set_caption("ViZDoom") - self.window_surface = pygame.display.set_mode(render_image.shape[:2]) - - surf = pygame.surfarray.make_surface(render_image) - self.window_surface.blit(surf, (0, 0)) - pygame.display.update() - else: - return self.isopen - - def close(self): - if self.window_surface: - pygame.quit() - self.isopen = False - - def __parse_available_buttons(self): - """ - Parses the currently available game buttons, - reorganizes all delta buttons to be prior to any binary buttons - sets self.num_delta_buttons, self.num_binary_buttons - """ - delta_buttons = [] - binary_buttons = [] - for button in self.game.get_available_buttons(): - if vzd.is_delta_button(button) and button not in delta_buttons: - delta_buttons.append(button) - else: - binary_buttons.append(button) - # force all delta buttons to be first before any binary buttons - self.game.set_available_buttons(delta_buttons + binary_buttons) - self.num_delta_buttons = len(delta_buttons) - self.num_binary_buttons = len(binary_buttons) - if delta_buttons == binary_buttons == 0: - raise RuntimeError( - "No game buttons defined. Must specify game buttons using `available_buttons` in the " - "config file." - ) - - def __get_binary_action_space(self): - """ - return binary action space: either Discrete(n)/MultiDiscrete([2,]*num_binary_buttons) - """ - if self.max_buttons_pressed == 0: - button_space = gym.spaces.MultiDiscrete( - [ - 2, - ] - * self.num_binary_buttons - ) - else: - self.button_map = [ - np.array(list(action)) - for action in itertools.product((0, 1), repeat=self.num_binary_buttons) - if (self.max_buttons_pressed >= sum(action) >= 0) - ] - button_space = gym.spaces.Discrete(len(self.button_map)) - return button_space - - def __get_continuous_action_space(self): - """ - return continuous action space: Box(float32.min, float32.max, (num_delta_buttons,), float32) - """ - return gym.spaces.Box( - np.finfo(np.float32).min, - np.finfo(np.float32).max, - (self.num_delta_buttons,), - dtype=np.float32, - ) - - def __get_action_space(self): - """ - return action space: - if both binary and delta buttons defined in the config file, action space will be: - Dict("binary": MultiDiscrete|Discrete, "continuous", Box) - else: - action space will be only one of the following MultiDiscrete|Discrete|Box - """ - if self.num_delta_buttons == 0: - return self.__get_binary_action_space() - elif self.num_binary_buttons == 0: - return self.__get_continuous_action_space() - else: - return gym.spaces.Dict( - { - "binary": self.__get_binary_action_space(), - "continuous": self.__get_continuous_action_space(), - } - ) - - def __get_observation_space(self): - """ - return observation space: Dict with Box entry for each activated buffer: - "screen", "depth", "labels", "automap", "gamevariables" - """ - spaces = { - "screen": gym.spaces.Box( - 0, - 255, - ( - self.game.get_screen_height(), - self.game.get_screen_width(), - self.channels, - ), - dtype=np.uint8, - ) - } - - if self.depth: - spaces["depth"] = gym.spaces.Box( - 0, - 255, - (self.game.get_screen_height(), self.game.get_screen_width(), 1), - dtype=np.uint8, - ) - - if self.labels: - spaces["labels"] = gym.spaces.Box( - 0, - 255, - (self.game.get_screen_height(), self.game.get_screen_width(), 1), - dtype=np.uint8, - ) - - if self.automap: - spaces["automap"] = gym.spaces.Box( - 0, - 255, - ( - self.game.get_screen_height(), - self.game.get_screen_width(), - # "automap" buffer uses same number of channels - # as the main screen buffer, - self.channels, - ), - dtype=np.uint8, - ) - - self.num_game_variables = self.game.get_available_game_variables_size() - if self.num_game_variables > 0: - spaces["gamevariables"] = gym.spaces.Box( - np.finfo(np.float32).min, - np.finfo(np.float32).max, - (self.num_game_variables,), - dtype=np.float32, - ) - - return gym.spaces.Dict(spaces) diff --git a/gym_wrapper/gym_env_defns.py b/gym_wrapper/gym_env_defns.py deleted file mode 100644 index 7b12b35db..000000000 --- a/gym_wrapper/gym_env_defns.py +++ /dev/null @@ -1,23 +0,0 @@ -import os - -from gym.utils import EzPickle - -from vizdoom import scenarios_path -from vizdoom.gym_wrapper.base_gym_env import VizdoomEnv - - -class VizdoomScenarioEnv(VizdoomEnv, EzPickle): - """Basic ViZDoom environments which reside in the `scenarios` directory""" - - def __init__( - self, scenario_file, frame_skip=1, max_buttons_pressed=1, render_mode=None - ): - EzPickle.__init__( - self, scenario_file, frame_skip, max_buttons_pressed, render_mode - ) - super().__init__( - os.path.join(scenarios_path, scenario_file), - frame_skip, - max_buttons_pressed, - render_mode, - ) diff --git a/pyproject.toml b/pyproject.toml index afd3457b7..92b4ce220 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,14 +7,14 @@ safe = true [tool.isort] atomic = true profile = "black" -src_paths = ["examples/python", "gym_wrapper", "gymnasium_wrapper", "tests"] +src_paths = ["examples/python", "gymnasium_wrapper", "tests"] indent = 4 lines_after_imports = 2 multi_line_output = 3 known_first_party = ['vizdoom'] [tool.pyright] -include = ["examples/python/**", "gym_wrapper", "gymnasium_wrapper", "tests/**"] +include = ["examples/python/**", "gymnasium_wrapper", "tests/**"] exclude = ["**/__pycache__"] strict = [] diff --git a/setup.py b/setup.py index e4117aa15..5254670b4 100644 --- a/setup.py +++ b/setup.py @@ -40,7 +40,6 @@ def add_subpackage(dir_path): add_subpackage("scenarios") -add_subpackage("gym_wrapper") add_subpackage("gymnasium_wrapper") # Platform specific package data @@ -204,7 +203,7 @@ def run(self): except subprocess.CalledProcessError: sys.stderr.write( "\033[1m\nInstallation from source failed, you may be missing some dependencies. " - "\nPlease check https://vizdoom.farama.org/introduction/pythonQuickstart for details.\n\n\033[0m" + "\nPlease check https://vizdoom.farama.org/introduction/python_quickstart for details.\n\n\033[0m" ) raise @@ -221,7 +220,6 @@ def run(self): author="Marek Wydmuch, Michał Kempka, Wojciech Jaśkowski, Farama Foundation, and the respective contributors", author_email="mwydmuch@cs.put.poznan.pl", extras_require={ - "gym": ["gym>=0.26.0", "pygame>=2.1.3"], "test": ["pytest", "psutil"], }, install_requires=["numpy", "gymnasium>=0.28.0", "pygame>=2.1.3"], diff --git a/tests/manual_test_gym_wrapper.py b/tests/manual_test_gym_wrapper.py deleted file mode 100755 index 6f1581d5e..000000000 --- a/tests/manual_test_gym_wrapper.py +++ /dev/null @@ -1,356 +0,0 @@ -#!/usr/bin/env python3 - -# Tests for Gym wrapper. -# This test can be run as Python script or via PyTest - -import os - -import gym -import numpy as np -from gym.spaces import Box, Dict, Discrete, MultiDiscrete -from gym.utils.env_checker import check_env - -from vizdoom import gym_wrapper # noqa -from vizdoom.gym_wrapper.base_gym_env import VizdoomEnv - - -vizdoom_envs = [ - env - for env in [env_spec.id for env_spec in gym.envs.registry.values()] # type: ignore - if "Vizdoom" in env -] -test_env_configs = f"{os.path.dirname(os.path.abspath(__file__))}/env_configs" - - -# Testing with different non-default kwargs (since each has a different obs space) -# should give warning forcing RGB24 screen type -def test_gym_wrapper(): - print("Testing Gym wrapper compatibility with gym API") - for env_name in vizdoom_envs: - for frame_skip in [1, 4]: - env = gym.make(env_name, frame_skip=frame_skip, max_buttons_pressed=0) - - # Test if env adheres to Gym API - check_env(env.unwrapped, skip_render_check=True) - - ob_space = env.observation_space - act_space = env.action_space - ob, _ = env.reset() - assert ob_space.contains(ob), f"Reset observation: {ob!r} not in space" - - a = act_space.sample() - observation, reward, terminated, truncated, _info = env.step(a) - assert ob_space.contains( - observation - ), f"Step observation: {observation!r} not in space" - assert np.isscalar(reward), f"{reward} is not a scalar for {env}" - assert isinstance( - terminated, bool - ), f"Expected {terminated} to be a boolean" - assert isinstance(truncated, bool), f"Expected {truncated} to be a boolean" - - env.close() - - -# Testing obs on terminal state (terminal state is handled differently) -# should give warning forcing RGB24 screen type -def test_gym_wrapper_terminal_state(): - print("Testing Gym rollout (checking terminal state)") - for env_name in vizdoom_envs: - for frame_skip in [1, 4]: - env = gym.make(env_name, frame_skip=frame_skip, max_buttons_pressed=0) - - def agent(ob): - return env.action_space.sample() - - ob = env.reset() - terminated = False - truncated = False - done = terminated or truncated - while not done: - a = agent(ob) - (ob, _reward, terminated, truncated, _info) = env.step(a) - done = terminated or truncated - if done: - break - env.close() - assert env.observation_space.contains(ob) - - -# Testing various observation spaces -# Using both screen types `(GRAY8, RGB24)` for various combinations of buffers `(screen|depth|labels|automap)` -def test_gym_wrapper_obs_space(): - print("Testing Gym wrapper observation spaces") - env_configs = [ - "basic_rgb_i_1_3", - "basic_g8_i_1_0", - "basic_g8_idla_4_2", - "basic_g8_idl_3_1", - "basic_rgb_id_2_0", - "basic_rgb_idla_0_1", - ] - tri_channel_screen_obs_space = Box(0, 255, (240, 320, 3), dtype=np.uint8) - single_channel_screen_obs_space = Box(0, 255, (240, 320, 1), dtype=np.uint8) - observation_spaces = [ - Dict({"screen": tri_channel_screen_obs_space}), - Dict({"screen": single_channel_screen_obs_space}), - Dict( - { - "screen": single_channel_screen_obs_space, - "depth": single_channel_screen_obs_space, - "labels": single_channel_screen_obs_space, - "automap": single_channel_screen_obs_space, - } - ), - Dict( - { - "screen": single_channel_screen_obs_space, - "depth": single_channel_screen_obs_space, - "labels": single_channel_screen_obs_space, - } - ), - Dict( - { - "screen": tri_channel_screen_obs_space, - "depth": single_channel_screen_obs_space, - } - ), - Dict( - { - "screen": tri_channel_screen_obs_space, - "depth": single_channel_screen_obs_space, - "labels": single_channel_screen_obs_space, - "automap": tri_channel_screen_obs_space, - } - ), - ] - - for i in range(len(env_configs)): - env = VizdoomEnv( - level=os.path.join(test_env_configs, env_configs[i] + ".cfg"), - frame_skip=1, - max_buttons_pressed=0, - ) - assert env.observation_space == observation_spaces[i], ( - f"Incorrect observation space: {env.observation_space!r}, " - f"should be: {observation_spaces[i]!r}" - ) - obs, _ = env.reset() - assert env.observation_space.contains( - obs - ), f"Step observation: {obs!r} not in space" - - -# Testing all possible action space combinations -def test_gym_wrapper_action_space(): - print("Testing Gym wrapper action spaces") - env_configs = [ - "basic_rgb_i_1_3", - "basic_g8_i_1_0", - "basic_g8_idla_4_2", - "basic_g8_idl_3_1", - "basic_rgb_id_2_0", - "basic_rgb_idla_0_1", - ] - action_spaces = [ - # max_button_pressed = 0, binary action space is MultiDiscrete - [ - Dict( - { - "binary": MultiDiscrete([2]), - "continuous": Box( - np.finfo(np.float32).min, - np.finfo(np.float32).max, - (3,), - dtype=np.float32, - ), - } - ), - MultiDiscrete([2]), - Dict( - { - "binary": MultiDiscrete([2, 2, 2, 2]), - "continuous": Box( - np.finfo(np.float32).min, - np.finfo(np.float32).max, - (2,), - dtype=np.float32, - ), - } - ), - Dict( - { - "binary": MultiDiscrete([2, 2, 2]), - "continuous": Box( - np.finfo(np.float32).min, - np.finfo(np.float32).max, - (1,), - dtype=np.float32, - ), - } - ), - MultiDiscrete([2, 2]), - Box( - np.finfo(np.float32).min, - np.finfo(np.float32).max, - (1,), - dtype=np.float32, - ), - ], - # max_button_pressed = 1, binary action space is Discrete(num_binary_buttons + 1) - [ - Dict( - { - "binary": Discrete(2), - "continuous": Box( - np.finfo(np.float32).min, - np.finfo(np.float32).max, - (3,), - dtype=np.float32, - ), - } - ), - Discrete(2), - Dict( - { - "binary": Discrete(5), - "continuous": Box( - np.finfo(np.float32).min, - np.finfo(np.float32).max, - (2,), - dtype=np.float32, - ), - } - ), - Dict( - { - "binary": Discrete(4), - "continuous": Box( - np.finfo(np.float32).min, - np.finfo(np.float32).max, - (1,), - dtype=np.float32, - ), - } - ), - Discrete(3), - Box( - np.finfo(np.float32).min, - np.finfo(np.float32).max, - (1,), - dtype=np.float32, - ), - ], - # max_button_pressed = 2, binary action space is Discrete(m) m=all combinations - # indices=[0,1] should give warning clipping max_buttons_pressed to 1 - [ - Dict( - { - "binary": Discrete(2), - "continuous": Box( - np.finfo(np.float32).min, - np.finfo(np.float32).max, - (3,), - dtype=np.float32, - ), - } - ), - Discrete(2), - Dict( - { - "binary": Discrete(11), - "continuous": Box( - np.finfo(np.float32).min, - np.finfo(np.float32).max, - (2,), - dtype=np.float32, - ), - } - ), - Dict( - { - "binary": Discrete(7), - "continuous": Box( - np.finfo(np.float32).min, - np.finfo(np.float32).max, - (1,), - dtype=np.float32, - ), - } - ), - Discrete(4), - Box( - np.finfo(np.float32).min, - np.finfo(np.float32).max, - (1,), - dtype=np.float32, - ), - ], - # max_button_pressed = 3, binary action space is Discrete(m) m=all combinations - # indices=[0,1, 4] should give warning clipping max_buttons_pressed to 1 or 2 - [ - Dict( - { - "binary": Discrete(2), - "continuous": Box( - np.finfo(np.float32).min, - np.finfo(np.float32).max, - (3,), - dtype=np.float32, - ), - } - ), - Discrete(2), - Dict( - { - "binary": Discrete(15), - "continuous": Box( - np.finfo(np.float32).min, - np.finfo(np.float32).max, - (2,), - dtype=np.float32, - ), - } - ), - Dict( - { - "binary": Discrete(8), - "continuous": Box( - np.finfo(np.float32).min, - np.finfo(np.float32).max, - (1,), - dtype=np.float32, - ), - } - ), - Discrete(4), - Box( - np.finfo(np.float32).min, - np.finfo(np.float32).max, - (1,), - dtype=np.float32, - ), - ], - ] - for max_button_pressed in range(0, 4): - for i in range(len(env_configs)): - env = VizdoomEnv( - level=os.path.join(test_env_configs, env_configs[i] + ".cfg"), - frame_skip=1, - max_buttons_pressed=max_button_pressed, - ) - assert env.action_space == action_spaces[max_button_pressed][i], ( - f"Incorrect action space: {env.action_space!r}, " - f"should be: {action_spaces[max_button_pressed][i]!r}" - ) - env.reset() - # check successful call to step using action_space.sample() - sample_action = env.action_space.sample() - env.step(sample_action) - - -if __name__ == "__main__": - test_gym_wrapper() - test_gym_wrapper_terminal_state() - test_gym_wrapper_action_space() - test_gym_wrapper_obs_space() From f57c3b400b5cf3cad3b9cb151949839da0a88280 Mon Sep 17 00:00:00 2001 From: Marek Wydmuch Date: Sat, 7 Sep 2024 13:11:33 +0200 Subject: [PATCH 2/3] Update mentions of Gym in the docs --- README.md | 17 ++++++----------- docs/environments/default.md | 2 +- docs/introduction/apis_and_wrappers.md | 18 +++++------------- docs/introduction/building.md | 6 +----- docs/introduction/python_quickstart.md | 24 +++++++++++++----------- examples/python/audio_buffer.py | 4 ++-- examples/python/gym_wrapper.py | 21 --------------------- examples/python/gymnasium_wrapper.py | 2 +- 8 files changed, 29 insertions(+), 65 deletions(-) delete mode 100755 examples/python/gym_wrapper.py diff --git a/README.md b/README.md index 4606351f0..a227c6766 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ ViZDoom is based on [ZDoom](https://zdoom.org) engine to provide the game mechan ## Features - Multi-platform (Linux, macOS, Windows), - API for Python and C++, -- [Gymnasium](https://gymnasium.farama.org/)/[OpenAI Gym](https://www.gymlibrary.dev/) environment wrappers (thanks to [Arjun KG](https://github.com/arjun-kg) [Benjamin Noah Beal](https://github.com/bebeal), [Lawrence Francis](https://github.com/ldfrancis), and [Mark Towers](https://github.com/pseudo-rnd-thoughts)), +- [Gymnasium](https://gymnasium.farama.org/)/Gym environment wrappers (thanks to [Arjun KG](https://github.com/arjun-kg) [Benjamin Noah Beal](https://github.com/bebeal), [Lawrence Francis](https://github.com/ldfrancis), and [Mark Towers](https://github.com/pseudo-rnd-thoughts)), - Easy-to-create custom scenarios (visual editors, scripting language, and examples available), - Async and sync single-player and multiplayer modes, - Fast (up to 7000 fps in sync mode, single-threaded), @@ -81,7 +81,7 @@ Both x86-64 and AArch64 (ARM64) architectures are supported. Wheels are available for Python 3.8+ on Linux. If Python wheel is not available for your platform (Python version <3.8, distros below manylinux_2_28 standard), pip will try to install (build) ViZDoom from the source. -ViZDoom requires a C++11 compiler, CMake 3.12+, Boost 1.54+ SDL2, OpenAL (optional), and Python 3.8+ to install from source. See [documentation](https://vizdoom.farama.org/introduction/pythonQuickstart/) for more details. +ViZDoom requires a C++11 compiler, CMake 3.12+, Boost 1.54+ SDL2, OpenAL (optional), and Python 3.8+ to install from source. See [documentation](https://vizdoom.farama.org/introduction/python_quickstart/) for more details. ### macOS @@ -111,20 +111,13 @@ Wheels are available for Python 3.9+ on Windows. Please note that the Windows version is not as well-tested as Linux and macOS versions. It can be used for development and testing but if you want to conduct serious (time and resource-extensive) experiments on Windows, -please consider using [Docker](https://docs.docker.com/docker-for-windows/install/) or [WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10) with Linux version. +please consider using [Docker](https://docs.docker.com/docker-for-windows/install/) or [WSL](https://docs.microsoft.com/en-us/windows/wsl) with Linux version. ### Gymnasium/Gym wrappers -Gymnasium environments are installed along with ViZDoom. +Gymnasium environments are installed along with ViZDoom and are available on all platforms. See [documentation](https://github.com/Farama-Foundation/ViZDoom/blob/master/doc/Gymnasium.md) and [examples](https://github.com/Farama-Foundation/ViZDoom/blob/master/examples/python/gymnasium_wrapper.py) on the use of Gymnasium API. -OpenAI-Gym wrappers are also available, to install them run: -``` -pip install vizdoom[gym] -``` -See [documentation](https://github.com/Farama-Foundation/ViZDoom/blob/master/doc/Gym.md) and [examples](https://github.com/Farama-Foundation/ViZDoom/blob/master/examples/python/gym_wrapper.py) on the use of Gym API. -**OpenAI-Gym wrappers are deprecated and will be removed in future versions in favor of Gymnasium.** - ## Examples - [Python](https://github.com/Farama-Foundation/ViZDoom/blob/master/examples/python) (contain learning examples implemented in PyTorch, TensorFlow, and Theano) @@ -166,6 +159,8 @@ Useful articles (for advanced users who want to create custom environments/scena - [LevDoom](https://github.com/TTomilin/LevDoom) - Generalization benchmark in ViZDoom featuring difficulty levels in visual complexity. - [COOM](https://github.com/hyintell/COOM) - Continual learning benchmark in ViZDoom offering task sequences with diverse objectives. +If you have a cool project that uses ViZDoom or could be interesting to ViZDoom community, feel free to open PR to add it to this list! + ## Contributions This project is maintained and developed in our free time. All bug fixes, new examples, scenarios, and other contributions are welcome! We are also open to feature ideas and design suggestions. diff --git a/docs/environments/default.md b/docs/environments/default.md index c38f9d1d4..323560423 100644 --- a/docs/environments/default.md +++ b/docs/environments/default.md @@ -15,7 +15,7 @@ game = vzd.DoomGame() game.load_config(os.path.join(vzd.scenarios_path, "basic.cfg")) # or any other scenario file ``` -When using Gymnasium (or Gym) API the scenario can be loaded by passing the scenario id to `make` method like-this: +When using Gymnasium API the scenario can be loaded by passing the scenario id to `make` method like-this: ```{code-block} python import gymnasium diff --git a/docs/introduction/apis_and_wrappers.md b/docs/introduction/apis_and_wrappers.md index 164d4ee09..42391d646 100644 --- a/docs/introduction/apis_and_wrappers.md +++ b/docs/introduction/apis_and_wrappers.md @@ -35,27 +35,19 @@ and implements the necessary API to function as a Gymnasium API. See the following examples for use: - [examples/python/gymnasium_wrapper.py](https://github.com/Farama-Foundation/ViZDoom/tree/master/examples/python/gymnasium_wrapper.py) for basic usage -- [examples/python/learning_stable_baselines.py](https://github.com/Farama-Foundation/ViZDoom/tree/master/examples/python/learning_stable_baselines.py) for example training with [stable-baselines3](https://github.com/DLR-RM/stable-baselines3/) (Update - Currently facing issues, to be fixed) +- [examples/python/learning_stable_baselines.py](https://github.com/Farama-Foundation/ViZDoom/tree/master/examples/python/learning_stable_baselines.py) for example training with [stable-baselines3](https://github.com/DLR-RM/stable-baselines3/). ## OpenAI Gym wrappers -> Gym is deprecated in favor of Gymnasium and these wrappers will be removed in the future. +> Original OpenAI Gym wrappers are no longer maintained and were removed from master branch. We recommend using Gymnasium wrappers instead. -Installing ViZDoom with `pip install vizdoom[gym]` will include -Gym wrappers to interact with ViZDoom over Gym API. - -These wrappers are under `gym_wrappers`, containing the basic environment and -a few example environments based on the built-in scenarios. This environment -simply initializes ViZDoom with the settings from the scenario config files -and implements the necessary API to function as a Gym API. - -See the following examples for use: -- [examples/python/gym_wrapper.py](https://github.com/Farama-Foundation/ViZDoom/tree/master/examples/python/gym_wrapper.py) for basic usage +Installing ViZDoom version < 1.3.0 with `pip install vizdoom[gym]` will include +Gym 0.26 wrappers to interact with ViZDoom over Gym API. ## Julia, Lua, and Java APIs -> Julia, Lua, and Java bindings are no longer maintained. +> Julia, Lua, and Java bindings are no longer maintained and were removed from master branch Julia, Lua, and Java can be found in [julia](https://github.com/Farama-Foundation/ViZDoom/tree/julia) and [java&lua](https://github.com/Farama-Foundation/ViZDoom/tree/java%26lua) branches for manual building. diff --git a/docs/introduction/building.md b/docs/introduction/building.md index c238f0c91..31f5e636a 100644 --- a/docs/introduction/building.md +++ b/docs/introduction/building.md @@ -1,7 +1,7 @@ # Building from source Here we describe how to build ViZDoom from source. -If you want to install pre-build ViZDoom wheels for Python, see [Python quick start](./pythonQuickstart.md). +If you want to install pre-build ViZDoom wheels for Python, see [Python quick start](./python_quickstart.md). ## Dependencies @@ -67,7 +67,6 @@ python setup.py build && python setup.py install ### MacOS - To build ViZDoom on MacOS, the following dependencies are required: * CMake 3.12+ * Clang 5.0+ @@ -135,7 +134,6 @@ Instructions below can be used to build ViZDoom manually. We recommend doing it only if you want to use C++ API, work on the ViZDoom, or if you have problems with pip installation. ### Linux / MacOS - In ViZDoom's root directory: ```bash mkdir build @@ -148,7 +146,6 @@ where `-DBUILD_ENGINE=ON` and `-DBUILD_PYTHON=ON` CMake options are optional (de ### Windows - 1. Run CMake GUI or cmake command in cmd/powershell in ViZDoom root directory with the following paths provided: * BOOST_ROOT * BOOST_INCLUDEDIR @@ -176,5 +173,4 @@ Compilation output will be placed in `build/bin` and it should contain the follo ### Manual installation - To manually install Python package copy `vizdoom_root_dir/build/bin/pythonX.X/vizdoom` contents to `python_root_dir/lib/pythonX.X/site-packages/site-packages/vizdoom`. diff --git a/docs/introduction/python_quickstart.md b/docs/introduction/python_quickstart.md index 3ecc56d67..a27f90058 100644 --- a/docs/introduction/python_quickstart.md +++ b/docs/introduction/python_quickstart.md @@ -1,5 +1,6 @@ # Python quick start + ## Linux To install the latest release of ViZDoom, just run: ```sh @@ -9,7 +10,6 @@ Both x86-64 and AArch64 (ARM64) architectures are supported. Wheels are available for Python 3.8+ on Linux. ### Audio buffer requirement - If you want to use audio buffer, you need to have OpenAL library installed. It is installed by default in many desktop distros. Otherwise it can be installed from the package manager. On apt-based distros (Ubuntu, Debian, Linux Mint, etc.), you can install it by running: @@ -23,13 +23,11 @@ dnf install openal-soft On RHEL/CentOS/Alma/Rocky Linux 9, you may need first enable crb repository by running `dnf --enablerepo=crb install`. ### Installing from source distribution on Linux - If Python wheel is not available for your platform (distros incompatible with manylinux_2_28 standard), pip will try to install (build) ViZDoom from the source. ViZDoom requires a C++11 compiler, CMake 3.12+, Boost 1.54+ SDL2, OpenAL (optional), and Python 3.8+ to install from source. Below, you will find instructions on how to install these dependencies. #### apt-based distros (Ubuntu, Debian, Linux Mint, etc.) - To build ViZDoom run (it may take a few minutes): ```sh apt install cmake git libboost-all-dev libsdl2-dev libopenal-dev @@ -38,7 +36,6 @@ pip install vizdoom We recommend using at least Ubuntu 18.04+ or Debian 10+ with Python 3.7+. #### dnf/yum-based distros (Fedora, RHEL, CentOS, Alma/Rocky Linux, etc.) - To install ViZDoom, run (it may take a few minutes): ```sh dnf install cmake git boost-devel SDL2-devel openal-soft-devel @@ -48,7 +45,6 @@ We recommend using at least Fedora 35+ or RHEL/CentOS/Alma/Rocky Linux 9+ with P To install openal-soft-devel on RHEL/CentOS/Alma/Rocky Linux 9, one needs to enable crb repository first by running `dnf --enablerepo=crb install`. ### Installing master branch version - To install the master branch version of ViZDoom run: ```sh pip install git+https://github.com/Farama-Foundation/ViZDoom @@ -59,14 +55,20 @@ It requires the to have the above dependencies installed. ## macOS To install the latest release of ViZDoom, just run (it may take a few minutes as it will build ViZDoom from source on M1/M2 chips): ```sh -brew install cmake boost sdl2 openal-soft pip install vizdoom ``` -Both Intel and Apple Silicon (M1/2/3) CPUs are supported. +Both Intel and Apple Silicon CPUs are supported. +Pre-build wheels are available for Intel macOS 12.0+ and Apple Silicon macOS 14.0+. + +If Python wheel is not available for your platform (Python version <3.8, older macOS version), pip will try to install (build) ViZDoom from the source. +In this case, install the required dependencies using Homebrew: +```sh +brew install cmake boost sdl2 openal-soft +``` We recommend using at least macOS High Sierra 10.13+ with Python 3.8+. -On Apple Silicon, make sure you are using Python/Pip for Apple Silicon. +On Apple Silicon (M1, M2, and M3), make sure you are using Python/Pip for Apple Silicon. -To install the master branch version of ViZDoom, run: +To install the master branch version of ViZDoom, run, in this case you also need to have the above dependencies installed: ```sh pip install git+https://github.com/Farama-Foundation/ViZDoom ``` @@ -78,9 +80,9 @@ To install the latest release of ViZDoom, just run: pip install vizdoom ``` At the moment, only x86-64 architecture is supported on Windows. -Wheels are available for Python 3.8+ x86-64 on Windows. +Wheels are available for Python 3.9+ x86-64 on Windows. Please note that the Windows version is not as well-tested as Linux and macOS versions. It can be used for development and testing but if you want to conduct serious (time and resource-extensive) experiments on Windows, -please consider using [Docker](https://docs.docker.com/docker-for-windows/install/) or [WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10) with Linux version. +please consider using [Docker](https://docs.docker.com/docker-for-windows/install/) or [WSL](https://docs.microsoft.com/en-us/windows/wsl) with Linux version. Windows version is bundled with OpenAL library, so you don't need to install it separately. diff --git a/examples/python/audio_buffer.py b/examples/python/audio_buffer.py index 73ef9cbe5..750c59bcf 100755 --- a/examples/python/audio_buffer.py +++ b/examples/python/audio_buffer.py @@ -27,7 +27,7 @@ # Setting game.set_sound_enabled(True) is not required for audio buffer to work. # Note: This requires OpenAL library to be installed on your system. # It is installed by default on many Linux desktop distros. - # And it can be installed from package manager, see: https://vizdoom.farama.org/introduction/pythonQuickstart/#audio-buffer-requirements + # And it can be installed from package manager, see: https://vizdoom.farama.org/introduction/python_quickstart/#audio-buffer-requirements AUDIO_BUFFER_ENABLED = True game.set_audio_buffer_enabled(AUDIO_BUFFER_ENABLED) @@ -95,7 +95,7 @@ "[WARNING] Audio buffers were full of silence. This is a common bug on e.g. Ubuntu 20.04\n" " See https://github.com/Farama-Foundation/ViZDoom/pull/486\n" " There are some possible fixes:\n" - " 1) Check that you have OpenAL installed, if not install, see: https://vizdoom.farama.org/introduction/pythonQuickstart/#audio-buffer-requirements\n" + " 1) Check that you have OpenAL installed, if not install, see: https://vizdoom.farama.org/introduction/python_quickstart/#audio-buffer-requirements\n" " 2) Try setting game.add_game_args('+snd_efx 0'). This my disable some audio effects\n" " 3) Try installing a newer version of OpenAL Soft library, see https://github.com/Farama-Foundation/ViZDoom/pull/486#issuecomment-889389185" ) diff --git a/examples/python/gym_wrapper.py b/examples/python/gym_wrapper.py deleted file mode 100755 index 6b2114151..000000000 --- a/examples/python/gym_wrapper.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python3 - -##################################################################### -# Example for running a vizdoom scenario as a gym env -##################################################################### - -import gym - -from vizdoom import gym_wrapper # noqa - - -if __name__ == "__main__": - env = gym.make("VizdoomHealthGatheringSupreme-v0", render_mode="human") - - # Rendering random rollouts for ten episodes - for _ in range(10): - done = False - obs = env.reset() - while not done: - obs, rew, terminated, truncated, info = env.step(env.action_space.sample()) - done = terminated or truncated diff --git a/examples/python/gymnasium_wrapper.py b/examples/python/gymnasium_wrapper.py index be8c0ec26..65d7e5f9e 100755 --- a/examples/python/gymnasium_wrapper.py +++ b/examples/python/gymnasium_wrapper.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ##################################################################### -# Example for running a vizdoom scenario as a gym env +# Example for running a vizdoom scenario as a Gymnasium env ##################################################################### import gymnasium From 53cfa8a0fb723c0fbe42dcd86ba8da58df9a7a30 Mon Sep 17 00:00:00 2001 From: Marek Wydmuch Date: Sat, 7 Sep 2024 13:21:11 +0200 Subject: [PATCH 3/3] Remove gym_wrappers from CMakeLists.txt --- src/lib_python/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib_python/CMakeLists.txt b/src/lib_python/CMakeLists.txt index b712439c9..cc5704b27 100644 --- a/src/lib_python/CMakeLists.txt +++ b/src/lib_python/CMakeLists.txt @@ -78,7 +78,6 @@ add_custom_target(assemble_package ALL COMMAND ${CMAKE_COMMAND} -E copy ${VIZDOOM_SRC_DIR}/bots.cfg ${VIZDOOM_PYTHON_PACKAGE_DIR} COMMAND ${CMAKE_COMMAND} -E copy ${VIZDOOM_SRC_DIR}/freedoom2.wad ${VIZDOOM_PYTHON_PACKAGE_DIR} COMMAND ${CMAKE_COMMAND} -E copy_directory ${VIZDOOM_ROOT_DIR}/scenarios ${VIZDOOM_PYTHON_PACKAGE_DIR}/scenarios - COMMAND ${CMAKE_COMMAND} -E copy_directory ${VIZDOOM_ROOT_DIR}/gym_wrapper ${VIZDOOM_PYTHON_PACKAGE_DIR}/gym_wrapper COMMAND ${CMAKE_COMMAND} -E copy_directory ${VIZDOOM_ROOT_DIR}/gymnasium_wrapper ${VIZDOOM_PYTHON_PACKAGE_DIR}/gymnasium_wrapper COMMENT "Assembling Python package in ${VIZDOOM_PYTHON_OUTPUT_DIR}/vizdoom" )