Skip to main content
⚡ High-throughput ViZDoom rollouts, native-vector by design. ⚡

env-ViZDoom-turbo

env-ViZDoom-turbo is a Python library for reinforcement-learning researchers who need fast, parallel ViZDoom environments. It provides a Gymnasium vector environment that can be used directly or selected as an isolated environment provider in GradLab.

Each vector lane owns an independent DoomGame. Lanes advance concurrently through ViZDoom's native API, while a bounded Rust worker pool applies max-pooling, crop, resize, grayscale conversion, frame-stack rotation, and final CHW/HWC layout in one GIL-free native call. Resize geometry and area-sampling tables are compiled once per environment instead of rebuilt per step.

Install

Install the published package from PyPI:

uv add env-vizdoom-turbo

To work from source:

git clone git@github.com:tsilva/env-ViZDoom-turbo.git
cd env-ViZDoom-turbo/turbo
uv sync --all-extras

Run Python and project commands through uv run.

Use

import gymnasium as gym
import numpy as np

env = gym.make_vec(
    "vizdoom_turbo:Vizdoom-Turbo-v0",
    game="VizdoomBasic-v1",
    num_envs=16,
    num_threads=8,
    obs_resize=(84, 84),
    obs_grayscale=True,
    obs_layout="chw",
    frame_skip=4,
    frame_stack=4,
    use_restricted_actions="minimal",
)

try:
    observations, infos = env.reset(seed=7)
    actions = np.zeros(env.num_envs, dtype=np.int64)
    observations, rewards, terminated, truncated, infos = env.step(actions)

    done = terminated | truncated
    if np.any(done):
        observations, infos = env.reset(
            options={
                "reset_mask": done,
                "state_indices": np.zeros(env.num_envs, dtype=np.int32),
            }
        )
finally:
    env.close()

The module-qualified ID imports the package and registers the factory. This ID is vector-only and requires an explicit game, which can be a canonical registered Vizdoom... Gymnasium ID or a ViZDoom .cfg path. VizdoomTurboVecEnv remains available for direct use, and the existing scenario-specific vector IDs remain registered for compatibility.

Crop or mask observations

The crop API matches the other turbo environments. obs_crop always contains raw-screen edge widths in (top, bottom, left, right) order. With obs_crop_mode="remove", those edges are removed before resize. With obs_crop_mode="mask", the raw geometry is preserved and those edges are replaced with obs_crop_fill before resize.

For example, the classic 320×240 Doom HUD occupies the bottom 32 pixels. This keeps the HUD enabled in ViZDoom while masking it out of policy observations:

env = VizdoomTurboVecEnv(
    "VizdoomBasic-v1",
    vizdoom_config={"render_hud": True},
    obs_crop=(0, 32, 0, 0),
    obs_crop_mode="mask",
    obs_crop_fill=0,
)

The 320×240-to-84×84 grayscale area-resize profile applies crop removal and masking directly in the indexed native pipeline, without an intermediate RGB frame conversion.

Frame-aligned info histories

Pass info_frame_stack_keys to request policy-transition histories whose depth always matches the resolved frame_stack:

env = VizdoomTurboVecEnv(
    "VizdoomBasic-v1",
    frame_skip=4,
    frame_stack=4,
    game_variables=["HEALTH", "ARMOR", "AMMO2", "SELECTED_WEAPON"],
    info_filter={
        "mode": "all",
        "keys": ["health", "armor", "ammo2", "selected_weapon"],
    },
    info_frame_stack_keys=["health", "armor", "ammo2", "selected_weapon"],
)

For every selected key such as health, the existing current value and mask remain infos["health"] and infos["_health"]. The opt-in history adds infos["health_frame_stack"] with shape (num_envs, frame_stack) and infos["_health_frame_stack"] with shape (num_envs,); a non-scalar signal's trailing dimensions follow the history axis unchanged. Histories are ordered oldest-to-newest. An ordinary reset repeats the reset value, a masked reset changes only selected lanes, and every vector-environment step() shifts and appends exactly once regardless of frame_skip. Terminal histories are returned before reset, and live snapshots preserve continuation histories exactly. These are policy-transition histories, not raw ViZDoom-tic histories.

Selected keys must be present in an info_filter with mode="all", available on reset and every step, and unique. Unknown, unavailable, colliding, or filtered-out selections fail during construction rather than falling back to current-only values.

Augmented environments

Augmented variants use the <base>-Plus-v<version> naming convention. VizdoomBasic-Plus-v1 preserves the canonical Basic scenario while sampling one target appearance and one coordinated wall/floor/ceiling texture set per lane on every reset:

env = VizdoomTurboVecEnv(
    "VizdoomBasic-Plus-v1",
    num_envs=16,
    enemy_variants={
        "target": [
            "original",
            "basalt-furnace-sentinel-v1",
            "verdigris-ram-hound-v1",
        ],
    },
    surface_variants={
        "texture_set": [
            "original",
            "polar-bunker-v1",
            "solar-shrine-v1",
            "verdant-ruin-v1",
        ],
    },
)
observations, infos = env.reset(seed=7)
print(infos["target_variant_index"])
print(infos["texture_set_variant_index"])

Transition infos stay numeric and portable. Resolve those indices through the immutable enemy_variants and surface_variants catalogs, or use active_enemy_variant_ids() and active_surface_variant_ids() outside the transition path when human-readable IDs are needed.

Each non-original texture-set choice changes all three room surfaces together, so a lane cannot mix materials from different themes. Omitting either variant mapping samples uniformly from every catalog default.

VizdoomDefendLine-Plus-v1 preserves the canonical Defend the Line mechanics while independently selecting one configured appearance for each enemy and surface role in every vector lane on every reset:

env = VizdoomTurboVecEnv(
    "VizdoomDefendLine-Plus-v1",
    num_envs=16,
    enemy_variants={
        "shooter": [
            "original",
            "basalt-furnace-sentinel-v1",
        ],
        "fighter": [
            "original",
            "verdigris-ram-hound-v1",
        ],
    },
    surface_variants={
        "wall": [
            "original",
            "basalt-blocks-v1",
            "steel-panels-v1",
        ],
        "floor": [
            "original",
            "dark-stone-v1",
        ],
        "ceiling": [
            "original",
            "industrial-grid-v1",
        ],
    },
)
observations, infos = env.reset(seed=7)
print(infos["shooter_variant_index"])
print(infos["fighter_variant_index"])
print(infos["wall_variant_index"])
print(infos["floor_variant_index"])
print(infos["ceiling_variant_index"])

Selection is uniform within each configured role, reproducible under reset(seed=...), and driven by separate role RNG streams so it does not consume gameplay, no-op, sticky-action, or another role's randomness. Omit enemy_variants and surface_variants to use every role's catalog defaults. An enemy_variants sequence remains a shorthand for configuring the shooter role only. Masked resets resample selected lanes only. enemy_variant_roles and surface_variant_roles, their read-only two-dimensional active-index arrays, and the role-keyed active_enemy_variant_ids() and active_surface_variant_ids() mappings expose the current choices.

The surface catalog also exposes three image-generated, coordinated sets through the immutable surface_variant_themes mapping: polar-bunker-v1, solar-shrine-v1, and verdant-ruin-v1. Each maps to matching wall, floor, and ceiling ids generated as individual material sources with shared theme references. Role selection remains independent, so configure each role with the corresponding singleton id when a reset must use one intact visual theme.

In a GradLab environment config, declare the same list under env_config.env_args.enemy_variants or env_config.env_args.surface_variants.

Reusable source frames, Doom patch lumps, manifests, proofs, and provenance live under vizdoom_turbo/assets/enemy_variants/. Seamless 64×64 PLAYPAL surface tiles, tiled proofs, prompts, manifests, and provenance live under vizdoom_turbo/assets/surface_variants/. The packaged Plus WADs are built from the editable scenario sources with:

uv run python scripts/build_basic_plus.py \
  --acc /absolute/path/to/acc \
  --acc-include /absolute/path/to/acc-source

uv run python scripts/build_defend_line_plus.py \
  --acc /absolute/path/to/acc \
  --acc-include /absolute/path/to/acc-source

New generated surface sources can be normalized into a compatible tile with scripts/process_surface_variant.py; its manifest gate verifies opacity, palette membership, exact dimensions, and measured wrap seams. The default pipeline center-crops one material source and downsamples it directly to 64×64, repairing only wrap axes that exceed the seam threshold. Legacy generated grids remain supported through --grid-row and --grid-column; source and processed comparison grids live with the editable Defend the Line Plus scenario sources.

Turbo Vector API v2

VizdoomTurboVecEnv implements the strict Turbo Vector API v2:

  • metadata["turbo_api_version"] is 2, metadata["transition_transport"] is "numpy", and metadata["render_modes"] advertises rgb_array.
  • Immutable capabilities and signal_schema declarations describe supported features and the dtype, shape, and reset/step availability of every signal. capabilities["supports_info_frame_stack"] advertises opt-in aligned info histories, and each generated history has shape (frame_stack, *original_shape) in signal_schema.
  • buttons, action_mode, action_preset, action_table, action_meanings, and action_table_hash expose the resolved action semantics without provider-specific probing.
  • state_catalog is an immutable ordered tuple. Callers select reset states with an int32 state_indices array and inspect the read-only active indices with active_state_indices(); state sampling and lane routing remain caller-owned.
  • observation_ownership and observation_buffer_depth declare the exact lifetime of returned observations. Rendering is opt-in: with render_mode="rgb_array", render_lane(index) renders one lane, get_images() renders all lanes, and render() renders lane zero. With the default render_mode=None, the first two methods return None and get_images() returns one None entry per lane.

Use with rlab

Install this distribution in the rlab runtime, then select its provider:

environment:
  env_provider: env-vizdoom-turbo
  env_config:
    game: VizdoomBasic-v1
    state: default
    n_envs: 16
    env_args:
      num_threads: 8
      use_restricted_actions: minimal
      obs_grayscale: true
      obs_layout: chw
      frame_stack: 4
    preprocessing:
      frame_skip: 4
      max_pool_frames: true
      observation_size: 84
      obs_resize_algorithm: area
    task:
      id: identity
      action: {set: native}
      signals: {}
      events: {}
      termination: {}
      reward: {reward_mode: native}

Commands

uv sync --all-extras                                      # install project and dev dependencies
uv run pytest -q                                          # run Python and live-environment tests
uv run ruff check .                                       # lint Python
cargo fmt --check                                         # check Rust formatting
cargo clippy --all-targets --all-features -- -D warnings  # lint Rust
uv build --wheel                                          # build the distributable wheel
VIZDOOM_TURBO_PREBUILT_CORE=/path/to/vizdoom uv build --wheel
                                                             # package a validated optimized core

Install TurboBench 1.0.0:

uv tool install \
  --exclude-newer-package turbobench-cli=2026-08-12T00:00:00Z \
  turbobench-cli==1.0.0

Use its immutable vizdoom/basic-v1 profile for correctness-gated throughput comparisons. The repository-local benchmarks/compare_contract.py remains available for focused deterministic trace checks.

Notes

  • Python 3.14 is supported. Release wheels target macOS ARM64 and Linux x86-64. Source builds require Rust 1.85 or newer.
  • ViZDoom 1.3.0 supplies built-in scenarios and Freedoom assets. Commercial Doom IWADs are not included; pass one with rom_path when required.
  • Autoreset is disabled. Terminal lanes retain their final observation and must be selected explicitly with a masked reset.
  • Preprocessing supports crop removal or masking, max-pooling, nearest/bilinear/area resize, grayscale or RGB, frame skip, frame stacking, and CHW or HWC layouts.
  • The native vector path supports image observations, rgb_array rendering, and one player. Recording is not supported.

Architecture

env-ViZDoom-turbo architecture

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

env_vizdoom_turbo-1.3.0.post27-cp314-cp314-manylinux_2_28_x86_64.whl (39.9 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.28+ x86-64

env_vizdoom_turbo-1.3.0.post27-cp314-cp314-macosx_15_0_arm64.whl (42.9 MB view details)

Uploaded CPython 3.14macOS 15.0+ ARM64

File details

Details for the file env_vizdoom_turbo-1.3.0.post27-cp314-cp314-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for env_vizdoom_turbo-1.3.0.post27-cp314-cp314-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 7d895d44cac2c58dd10e192ebdc84e1cd6bdad62abe6ab6fba7b54562e2c649d
MD5 a72989e9c54506cde2bdd9f04b5de49a
BLAKE2b-256 e5205cc05f31e704fd4c19630c53d4b2493dc694df0d4d26337fa53029f307dc

See more details on using hashes here.

Provenance

The following attestation bundles were made for env_vizdoom_turbo-1.3.0.post27-cp314-cp314-manylinux_2_28_x86_64.whl:

Publisher: release.yml on tsilva/env-ViZDoom-turbo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file env_vizdoom_turbo-1.3.0.post27-cp314-cp314-macosx_15_0_arm64.whl.

File metadata

File hashes

Hashes for env_vizdoom_turbo-1.3.0.post27-cp314-cp314-macosx_15_0_arm64.whl
Algorithm Hash digest
SHA256 24e497212f7dd21e59fb1051333eb3c99fbfb999e79274e27341f50d74f56178
MD5 60ab0e43a50ae9a4fc0edd29be579aa9
BLAKE2b-256 f5c9834fd9d4b07d26ab3aeb72bc54c8df77f405d0c9a75b15c61419650d88bb

See more details on using hashes here.

Provenance

The following attestation bundles were made for env_vizdoom_turbo-1.3.0.post27-cp314-cp314-macosx_15_0_arm64.whl:

Publisher: release.yml on tsilva/env-ViZDoom-turbo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

1.3.0.post30

2 files

1.3.0.post29

2 files

This release

1.3.0.post27 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page