Skip to main content

A custom RL environment with C++ backend and Gymnasium wrapper.

Project description

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:

  1. Jam: Discrete(num_enemies) - Selects which enemy to jam.
  2. 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)

  1. jammer: Discrete(num_enemies) - Selects which enemy to jam. (jammed enemy does not cause negative reward this turn)
  2. 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 (None otherwise).
  • 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 is 0.
  • truncations[i] == True → the step limit was reached with enemies alive; still bootstrap from infos["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.

Project details


Download files

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

Source Distribution

hybrid_shoot-0.3.0.tar.gz (25.1 kB view details)

Uploaded Source

Built Distribution

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

hybrid_shoot-0.3.0-cp312-cp312-win_amd64.whl (113.0 kB view details)

Uploaded CPython 3.12Windows x86-64

File details

Details for the file hybrid_shoot-0.3.0.tar.gz.

File metadata

  • Download URL: hybrid_shoot-0.3.0.tar.gz
  • Upload date:
  • Size: 25.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.6

File hashes

Hashes for hybrid_shoot-0.3.0.tar.gz
Algorithm Hash digest
SHA256 b2e59e324c4f1150c0581e3ce754ef7ed5f9cc83c15a7ea52f335b84a8da3e3e
MD5 aa81d895d9cdbff5edc46740d21ff015
BLAKE2b-256 7d63fff0c4d594bd21a2bb8ea86bcc2f7cdd3a29a76ec253afb689285902dfa6

See more details on using hashes here.

File details

Details for the file hybrid_shoot-0.3.0-cp312-cp312-win_amd64.whl.

File metadata

File hashes

Hashes for hybrid_shoot-0.3.0-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 7cee74449e95c5cc32de1278a88860a0e107d460368efb41772343ca7d32dfcd
MD5 9486dfdbd68eca96a2e204005769b6b6
BLAKE2b-256 58c5b740f2831a6ee4362729b3f606a5065c4bc8654d757053ceab7f9b5a3358

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page