Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c3bd0e0
Added vizdoom python interface
Trenza1ore Jul 24, 2025
a3bbf9e
Added to CMakeLists
Trenza1ore Jul 24, 2025
ad4ee72
Update vizdoom.pyi to fix a few oversights
Trenza1ore Jul 24, 2025
78df416
Update vizdoom.pyi (mostly formatting)
Trenza1ore Jul 24, 2025
540cbf3
Update vizdoom.pyi to add comment
Trenza1ore Jul 24, 2025
e67b908
Create py.typed
Trenza1ore Jul 24, 2025
47c4bb8
Update CMakeLists.txt
Trenza1ore Jul 24, 2025
e4ea837
Update setup.py
Trenza1ore Jul 24, 2025
2a5b8a2
Fixed vizdoom.pyi issues
Trenza1ore Jul 24, 2025
c544e5d
Updated setup.py for pre-commit formatting
Trenza1ore Jul 24, 2025
af97222
Updated outdated examples & tests (gymnasium still has a few type che…
Trenza1ore Jul 24, 2025
bf258f8
Forgot to update learning_pytorch.py
Trenza1ore Jul 24, 2025
fd2ffe8
Formatted vizdoom.pyi better, make sure all files (except gymnasium_w…
Trenza1ore Jul 25, 2025
7099437
Made sure stub is as close to mypy requirements as possible
Trenza1ore Jul 26, 2025
6bd9610
Format fix for gymnasium_vect_bench.py
Trenza1ore Jul 26, 2025
936fc46
Finally resolved all stubtest errors except those pybind11 metaclass …
Trenza1ore Jul 26, 2025
322f412
Additional format fix for pre-commit check
Trenza1ore Jul 26, 2025
0331263
Added a enum_typing example to demonstrate the expected behavior of V…
Trenza1ore Jul 26, 2025
429ea1b
Integrated type stub generation into build, updated docs
Trenza1ore Jul 27, 2025
cee1ca6
Updated comments
Trenza1ore Jul 27, 2025
070c7ba
Updated docs
Trenza1ore Jul 27, 2025
001a163
Make CREATE_PYTHON_STUBS an option
Trenza1ore Jul 27, 2025
6a422a5
Updated docs with building instructions for new DCREATE_PYTHON_STUBS …
Trenza1ore Jul 27, 2025
ea88f27
Make generate_vizdoom_stubs.py work outside of build process
Trenza1ore Jul 27, 2025
cf40878
Made the requested changes
Trenza1ore Jul 27, 2025
58b838c
Added \r\n (Windows line end to make the Github diff go away)
Trenza1ore Jul 27, 2025
3a15bd6
Also write py.typed when patching
Trenza1ore Jul 27, 2025
019c881
Removed the UTF-8 emojis, should have known they won't work
Trenza1ore Jul 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ set(ViZDoom_VERSION_ID ${ViZDoom_VERSION_MAJOR}${ViZDoom_VERSION_MINOR}${ViZDoom

option(BUILD_PYTHON "Build ViZDoom Python (3) binding/module" ON)
option(BUILD_ENGINE "Build ViZDoom Engine (required to build Python package)" ON)
option(CREATE_PYTHON_STUBS "Build ViZDoom Python (3) .pyi type stubs" OFF)


# CMake options
Expand Down
31 changes: 27 additions & 4 deletions docs/introduction/building.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ To get all the dependencies install [homebrew](https://brew.sh/) first, than exe
brew install cmake boost sdl2 openal-soft
```

⚠️ On Apple Silicon (M1, M2, M3 and M4), make sure you are using homebrew for `arm64`.

Here is a helpful command to set up the environment variables for homebrew on Apple Silicon if both `arm64` and `x86` versions were installed:
```sh
eval "$(/opt/homebrew/bin/brew shellenv)"
```

### Windows
* CMake 3.12+
Expand All @@ -91,6 +97,23 @@ Additionally, [ZDoom dependencies](http://zdoom.org/wiki/Compile_ZDoom_on_Window
Most of them (except Boost) are gathered in this repository: [ViZDoomWinDepBin](https://github.com/mwydmuch/ViZDoomWinDepBin).
You can download Boost from [here](https://www.boost.org/users/download).

## Building Python Type Stubs (`vizdoom.pyi`)

To enable Python typing support, the creation of a Python Interface file `vizdoom.pyi` is now part of the build process.

To ensure `vizdoom.pyi` is properly created, the following dependencies are required:
* pybind11-stubgen
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If pybind11-stubgen is needed as a part of the building process, then it should be added as build deps to pyproject.toml

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added it to pyproject.toml

* black (optional)
* isort (optional)

They can all be installed via pip:
```sh
pip install pybind11-stubgen black isort
```
To test the generated stub manually:
```
stubtest vizdoom --allowlist stubtest_allowlists.txt
```

## Building via pip (recommended for Python users)

Expand Down Expand Up @@ -138,11 +161,11 @@ In ViZDoom's root directory:
```bash
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_ENGINE=ON -DBUILD_PYTHON=ON
cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_ENGINE=ON -DBUILD_PYTHON=ON -DCREATE_PYTHON_STUBS=OFF
make
```

where `-DBUILD_ENGINE=ON` and `-DBUILD_PYTHON=ON` CMake options are optional (default ON).
where `-DBUILD_ENGINE=ON` and `-DBUILD_PYTHON=ON` CMake options are optional (default ON). Setting `-DCREATE_PYTHON_STUBS=OFF` will skip the creation of `vizdoom.pyi` (which is OFF by default).


### Windows
Expand All @@ -154,9 +177,9 @@ where `-DBUILD_ENGINE=ON` and `-DBUILD_PYTHON=ON` CMake options are optional (de
* PYTHON_LIBRARY (optional, for Python/Anaconda bindings)
* ZDoom dependencies paths

2. In configuration select `DBUILD_ENGINE` and `DBUILD_PYTHON` (optional, default ON).
1. In configuration select `DBUILD_ENGINE`, `DBUILD_PYTHON` (optional, default ON) and `DCREATE_PYTHON_STUBS` (optional, default OFF).

3. Use generated Visual Studio solution to build all parts of ViZDoom environment.
2. Use generated Visual Studio solution to build all parts of ViZDoom environment.

The process of building ViZDoom this way on Windows is demonstarted in [scripts/windows_build_cmake.bat](https://github.com/Farama-Foundation/ViZDoom/tree/master/scripts/windows_build_cmake.bat) script.

Expand Down
1 change: 1 addition & 0 deletions examples/python/audio_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@

# Gets the state
state = game.get_state()
assert state is not None

audio_buffer = state.audio_buffer
audio_slices.append(audio_buffer)
Expand Down
2 changes: 2 additions & 0 deletions examples/python/automap_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
while not game.is_episode_finished():
# Gets the state
state = game.get_state()
assert state is not None and state.game_variables is not None

# Shows automap buffer
map = state.automap_buffer
Expand All @@ -88,6 +89,7 @@
game.make_action(choice(actions))

print(f"State #{state.number}")

print(
"Player position X:",
state.game_variables[0],
Expand Down
1 change: 1 addition & 0 deletions examples/python/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@

# Gets the state
state = game.get_state()
assert state is not None

# Which consists of:
n = state.number
Expand Down
1 change: 1 addition & 0 deletions examples/python/buffers.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
while not game.is_episode_finished():
# Gets the state and possibly do something with it
state = game.get_state()
assert state is not None

# Display all the buffers here!

Expand Down
2 changes: 2 additions & 0 deletions examples/python/cig_multiplayer_bots.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@

# Get the state.
state = game.get_state()
assert state is not None

# Analyze the state.

Expand All @@ -92,6 +93,7 @@

print("Results:")
server_state = game.get_server_state()
assert server_state is not None
for i in range(len(server_state.players_in_game)):
if server_state.players_in_game[i]:
print(f"{server_state.players_names[i]}: {server_state.players_frags[i]}")
Expand Down
1 change: 1 addition & 0 deletions examples/python/cig_singleplayer.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
print(f"Map {i}")
while not game.is_episode_finished():
state = game.get_state()
assert state is not None

game.advance_action()
last_action = game.get_last_action()
Expand Down
1 change: 1 addition & 0 deletions examples/python/delta_buttons.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
while not game.is_episode_finished():

state = game.get_state()
assert state is not None
reward = game.make_action(action)

time = game.get_episode_time()
Expand Down
2 changes: 1 addition & 1 deletion examples/python/gymnasium_vect_bench.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,5 @@
# no need for env.reset() here since the default is AutoReset(https://farama.org/Vector-Autoreset-Mode)
# if terminated or truncated:
# observation, info = env.reset()
print(f"{args.n_envs} {n_steps *args.n_envs /round(time.time() - start,1)}")
print(f"{args.n_envs} {n_steps * args.n_envs / round(time.time() - start, 1)}")
envs.close()
2 changes: 2 additions & 0 deletions examples/python/labels_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ def color_labels(labels):

# Get the state
state = game.get_state()
assert state is not None and state.game_variables is not None

# Get labels buffer, that is always in 8-bit grey channel format.
# Show only visible game objects (enemies, pickups, exploding barrels etc.), each with a unique label.
Expand Down Expand Up @@ -149,6 +150,7 @@ def color_labels(labels):
game.make_action(choice(actions))

print(f"State #{state.number}")

print(
"Player position: x:",
state.game_variables[0],
Expand Down
4 changes: 3 additions & 1 deletion examples/python/learning_pytorch.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,9 @@ def train(self):
for _ in range(episodes_to_watch):
game.new_episode()
while not game.is_episode_finished():
state = preprocess(game.get_state().screen_buffer)
game_state = game.get_state()
assert game_state is not None
state = preprocess(game_state.screen_buffer)
best_action_index = agent.get_action(state)

# Instead of make_action(a, frame_repeat) in order to make the animation smooth
Expand Down
2 changes: 1 addition & 1 deletion examples/python/learning_stable_baselines3.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@


DEFAULT_ENV = "VizdoomBasic-v0"
AVAILABLE_ENVS = [env for env in gymnasium.envs.registry.keys() if "Vizdoom" in env]
AVAILABLE_ENVS = [env for env in gymnasium.envs.registry.keys() if "Vizdoom" in env] # type: ignore
# Height and width of the resized image
IMAGE_SHAPE = (60, 80)

Expand Down
4 changes: 3 additions & 1 deletion examples/python/learning_tensorflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,9 @@ def call(self, x):
for _ in range(episodes_to_watch):
game.new_episode()
while not game.is_episode_finished():
state = preprocess(game.get_state().screen_buffer)
game_state = game.get_state()
assert game_state is not None
state = preprocess(game_state.screen_buffer)
best_action_index = agent.choose_action(state)

# Instead of make_action(a, frame_repeat) in order to make the animation smooth
Expand Down
2 changes: 2 additions & 0 deletions examples/python/multiple_instances_advance.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ def player_host(p):
episode_start_time = time()

state = game.get_state()
assert state is not None
print("Player0:", state.number, action_count, game.get_episode_time())

player_action(game, player_sleep_time, actions, player_skip)
Expand Down Expand Up @@ -136,6 +137,7 @@ def player_join(p):

while not game.is_episode_finished():
state = game.get_state()
assert state is not None
print(
f"Player{p}:",
state.number,
Expand Down
1 change: 1 addition & 0 deletions examples/python/objects_and_sectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@

# Gets the state
state = game.get_state()
assert state is not None and state.game_variables is not None
game.make_action(choice(actions))

print(f"State #{state.number}")
Expand Down
1 change: 1 addition & 0 deletions examples/python/pyoblige.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
time = 0
while not game.is_episode_finished():
state = game.get_state()
assert state is not None and state.game_variables is not None
time = game.get_episode_time()

game.advance_action()
Expand Down
2 changes: 2 additions & 0 deletions examples/python/record_episodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@

while not game.is_episode_finished():
s = game.get_state()
assert s is not None and s.game_variables is not None

a = choice(actions)
r = game.make_action(choice(actions))
Expand Down Expand Up @@ -81,6 +82,7 @@
while not game.is_episode_finished():
# Get a state
s = game.get_state()
assert s is not None and s.game_variables is not None

# Use advance_action instead of make_action to proceed
game.advance_action()
Expand Down
2 changes: 2 additions & 0 deletions examples/python/save_load_game.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@

while not game.is_episode_finished():
state = game.get_state()
assert state is not None and state.game_variables is not None
reward = game.make_action(choice(actions))

if state.number == save_after_steps:
Expand All @@ -68,6 +69,7 @@
game.load("save.png")
# A new state is available after loading.
state = game.get_state()
assert state is not None and state.game_variables is not None

# There can be small difference in some of the game variables
print("\nGame loaded!")
Expand Down
1 change: 1 addition & 0 deletions examples/python/scenarios.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@

# Gets the state and possibly to something with it
state = game.get_state()
assert state is not None

# Makes a random action and save the reward.
reward = game.make_action(choice(actions))
Expand Down
1 change: 1 addition & 0 deletions examples/python/seed.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
while not game.is_episode_finished():
# Gets the state and possibly to something with it
state = game.get_state()
assert state is not None
screen_buf = state.screen_buffer
vars = state.game_variables

Expand Down
1 change: 1 addition & 0 deletions examples/python/shaping.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@

# Gets the state and possibly to something with it
state = game.get_state()
assert state is not None and state.game_variables is not None

# Makes a random action and save the reward.
reward = game.make_action(choice(actions))
Expand Down
1 change: 1 addition & 0 deletions examples/python/spectator.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
game.new_episode()
while not game.is_episode_finished():
state = game.get_state()
assert state is not None

game.advance_action()
last_action = game.get_last_action()
Expand Down
4 changes: 3 additions & 1 deletion examples/python/test_pytorch.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,9 @@ def initialize_vizdoom(config_file_path):
for _ in range(episodes_to_watch):
game.new_episode()
while not game.is_episode_finished():
state = preprocess(game.get_state().screen_buffer)
game_state = game.get_state()
assert game_state is not None
state = preprocess(game_state.screen_buffer)
state = state.reshape([1, 1, resolution[0], resolution[1]])
best_action_index = get_best_action(state)

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[build-system]
requires = ["cmake>=3.12.0", "setuptools>=65", "wheel"]
requires = ["cmake>=3.12.0", "setuptools>=65", "wheel", "pybind11-stubgen"]

[tool.black]
safe = true
Expand Down
2 changes: 2 additions & 0 deletions scripts/add_typing_to_vizdoom.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
python src/lib_python/generate_vizdoom_stubs.py -p
10 changes: 9 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,15 @@
packages = ["vizdoom"]
shutil.rmtree(package_path, ignore_errors=True)
os.makedirs(package_path, exist_ok=True)
package_data = ["__init__.py", "bots.cfg", "freedoom2.wad", "vizdoom.pk3"]
package_data = [
"__init__.py",
"generate_vizdoom_stubs.py",
"vizdoom.pyi",
"py.typed",
"bots.cfg",
"freedoom2.wad",
"vizdoom.pk3",
]


# Add subpackages
Expand Down
14 changes: 14 additions & 0 deletions src/lib_python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,17 @@ set_target_properties(assemble_package

# vizdoom, vizdoom.pk3 are needed to assemble the package
add_dependencies(assemble_package libvizdoom_python vizdoom pk3)

# Generate type stubs using pybind11-stubgen and copy vizdoom.pyi & py.typed to the package
if (CREATE_PYTHON_STUBS)
add_custom_command(
TARGET assemble_package
COMMAND ${Python_EXECUTABLE} ${VIZDOOM_PYTHON_SRC_DIR}/generate_vizdoom_stubs.py --output ${VIZDOOM_PYTHON_SRC_DIR}/vizdoom.pyi --verbose --module ${VIZDOOM_PYTHON_OUTPUT_DIR}/vizdoom
)
endif()

add_custom_command(
TARGET assemble_package
COMMAND ${CMAKE_COMMAND} -E copy ${VIZDOOM_PYTHON_SRC_DIR}/vizdoom.pyi ${VIZDOOM_PYTHON_PACKAGE_DIR}
COMMAND ${CMAKE_COMMAND} -E copy ${VIZDOOM_PYTHON_SRC_DIR}/py.typed ${VIZDOOM_PYTHON_PACKAGE_DIR}
)
2 changes: 1 addition & 1 deletion src/lib_python/__init__.py.in
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ configs = [cfg for cfg in sorted(_os.listdir(scenarios_path)) if cfg.endswith(".

# Farama notifications
try:
from farama_notifications import notifications
from farama_notifications import notifications # type: ignore

if "vizdoom" in notifications and __version__ in notifications["vizdoom"]:
print(notifications["vizdoom"][__version__], file=_sys.stderr)
Expand Down
Loading
Loading