Hybrid Shoot Environment
This environment is designed as a sanity check for reinforcement learning with hybrid action spaces (discrete + continuous). It supports both Gymnasium (single agent with hybrid actions) and PettingZoo (multi-agent decomposition).
There are num_enemies enemies in a 2D space. The goal is to Jam and Shoot them.
Installation
pip install .
Gymnasium Usage
The Gymnasium environment presents a single agent with a Tuple action space.
from hybrid_shoot import HybridShootEnv
env = HybridShootEnv()
obs, info = env.reset()
# Action: (Jam_Target_Index, [Shoot_X, Shoot_Y])
action = (0, [0.5, 0.5])
obs, reward, terminated, truncated, info = env.step(action)
Action Space (Gymnasium)
A spaces.Tuple containing:
- Jam:
Discrete(num_enemies)- Selects which enemy to jam. - Shoot:
Box(low=0, high=map_size, shape=(2,))-[x, y]coordinates to shoot at.
PettingZoo Usage
The PettingZoo environment decomposes the task into two cooperating agents.
from hybrid_shoot import HybridShootPettingZooEnv
env = HybridShootPettingZooEnv()
observations, infos = env.reset()
Agents & Action Spaces (PettingZoo)
jammer:Discrete(num_enemies)- Selects which enemy to jam. (jammed enemy does not cause negative reward this turn)shooter:Box(low=0, high=map_size, shape=(2,))- Selects the[x, y]coordinates to shoot.
Vectorized Usage (Parallel)
For high-throughput training, HybridShootVecEnv runs num_envs independent
copies of the environment and steps them in parallel inside C++ using OpenMP.
All batching stays in C++ — there are no Python subprocesses, pipes, or pickling
(unlike Gymnasium's AsyncVectorEnv), and observations are returned as batched
NumPy arrays. The API mirrors gymnasium.vector.VectorEnv / envpool.
import numpy as np
from hybrid_shoot import HybridShootVecEnv
num_envs = 1024
env = HybridShootVecEnv(num_envs=num_envs)
obs, infos = env.reset() # obs: (num_envs, obs_dim)
# Actions are a (discrete, continuous) tuple, batched over envs:
# discrete: (num_envs,) int - jam target per env
# continuous: (num_envs, cont_dim) float - shoot [x, y] per env
discrete = np.zeros(num_envs, dtype=np.int32)
continuous = np.random.rand(num_envs, 2)
obs, rewards, terminations, truncations, infos = env.step((discrete, continuous))
num_threads controls the OpenMP thread count (0 = use all available cores).
Auto-reset and bootstrapping
Each env auto-resets the moment it ends (envpool / SB3 style). On the step where
an env finishes, the returned obs for that env is already the first
observation of the next episode, while the genuine final observation (the s'
needed for value bootstrapping) is preserved in infos:
infos["final_observation"]: object array; for every env that just ended it holds that env's true post-step observation (Noneotherwise).infos["_final_observation"]: boolean mask of which entries are populated.
terminations and truncations are reported separately:
terminations[i] == True→ a true terminal state (all enemies cleared); bootstrap target is0.truncations[i] == True→ the step limit was reached with enemies alive; still bootstrap frominfos["final_observation"][i].
This matches gymnasium.vector.SyncVectorEnv, so RL libraries (Stable-Baselines3,
CleanRL, etc.) can consume it directly.
Hilbert joint action (optional)
Passing joint_xy_action=True (available on every env class) replaces the 2D
[x, y] shoot action with a single scalar in [0, 1], mapped onto the map
via a Hilbert curve of resolution xy_hilbert_width (default 16). This is handy
for algorithms that prefer a single continuous output; the mapping covers the full
[0, map_size]² map.
Game Mechanics
Jamming: Stops the targeted enemy from dealing damage this turn.
Shooting: Fires at location (x, y).
- Standard Mode (
independent_mode=False): An enemy must be jammed to be vulnerable to being shot. Shooting an unjammed enemy does nothing. - Independent Mode (
independent_mode=True): Jamming prevents damage, and Shooting kills enemies regardless of whether they are jammed.
Metadata
Release files for hybrid-shoot 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| hybrid_shoot-0.3.0.tar.gz | 25.1 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| hybrid_shoot-0.3.0-cp312-cp312-win_amd64.whl | CPython 3.12 | CPython 3.12 | Windows x86-64 | Details |
Total release size: 138.1 kB
Release files / hybrid_shoot-0.3.0.tar.gz
| Download URL | hybrid_shoot-0.3.0.tar.gz |
|---|---|
| Size | 25.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b2e59e324c4f1150c0581e3ce754ef7ed5f9cc83c15a7ea52f335b84a8da3e3e
|
|
BLAKE2b-256 checksum How to use checksums |
7d63fff0c4d594bd21a2bb8ea86bcc2f7cdd3a29a76ec253afb689285902dfa6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.6
|
Release files / hybrid_shoot-0.3.0-cp312-cp312-win_amd64.whl
| Download URL | hybrid_shoot-0.3.0-cp312-cp312-win_amd64.whl |
|---|---|
| Size | 113.0 kB |
| Tags | CPython 3.12 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
7cee74449e95c5cc32de1278a88860a0e107d460368efb41772343ca7d32dfcd
|
|
BLAKE2b-256 checksum How to use checksums |
58c5b740f2831a6ee4362729b3f606a5065c4bc8654d757053ceab7f9b5a3358
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.6
|
Security for hybrid-shoot 0.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.