PettingZoo Wrappers

PettingZoo includes the following types of wrappers:

  • Conversion Wrappers: wrappers for converting environments between the AEC and Parallel APIs

  • Utility Wrappers: a set of wrappers which provide convenient reusable logic, such as enforcing turn order or clipping out-of-bounds actions.

Conversion wrappers

AEC to Parallel

pettingzoo.utils.conversions.aec_to_parallel(aec_env: AECEnv[AgentID, ObsType, ActionType]) → ParallelEnv[AgentID, ObsType, ActionType][source]

Converts an AEC environment to a Parallel environment.

In the case of an existing Parallel environment wrapped using a parallel_to_aec_wrapper, this function will return the original Parallel environment. Otherwise, it will apply the aec_to_parallel_wrapper to convert the environment.

An environment can be converted from an AEC environment to a parallel environment with the aec_to_parallel wrapper shown below. Note that this wrapper makes the following assumptions about the underlying environment:

  1. The environment steps in a cycle, i.e. it steps through every live agent in order.

  2. The environment does not update the observations of the agents except at the end of a cycle.

Most parallel environments in PettingZoo only allocate rewards at the end of a cycle. In these environments, the reward scheme of the AEC API an the parallel API is equivalent. If an AEC environment does allocate rewards within a cycle, then the rewards will be allocated at different timesteps in the AEC environment an the Parallel environment. In particular, the AEC environment will allocate all rewards from one time the agent steps to the next time, while the Parallel environment will allocate all rewards from when the first agent stepped to the last agent stepped.

To convert an AEC environment into a parallel environment:

from pettingzoo import make
from pettingzoo.utils.conversions import aec_to_parallel

env = make("aec", "butterfly/pistonball-v6")
env = aec_to_parallel(env)

Parallel to AEC

pettingzoo.utils.conversions.parallel_to_aec(par_env: ParallelEnv[AgentID, ObsType, ActionType | None]) → AECEnv[AgentID, ObsType, ActionType | None][source]

Converts a Parallel environment to an AEC environment.

In the case of an existing AEC environment wrapped using a aec_to_parallel_wrapper, this function will return the original AEC environment. Otherwise, it will apply the parallel_to_aec_wrapper to convert the environment.

Any parallel environment can be efficiently converted to an AEC environment with the parallel_to_aec wrapper.

To convert a parallel environment into an AEC environment:

from pettingzoo import make
from pettingzoo.utils import parallel_to_aec

env = make("parallel", "butterfly/pistonball-v6")
env = parallel_to_aec(env)

Utility Wrappers

We wanted our pettingzoo environments to be both easy to use and easy to implement. To combine these, we have a set of simple wrappers which provide input validation and other convenient reusable logic.

You can apply these wrappers to your environment in a similar manner to the below examples:

To wrap an AEC environment:

from pettingzoo import make
from pettingzoo.utils import TerminateIllegalWrapper

env = make("aec", "classic/tictactoe-v3")
env = TerminateIllegalWrapper(env, illegal_reward=-1)

env.reset()
for agent in env.agent_iter():
    observation, reward, termination, truncation, info = env.last()
    if termination or truncation:
        action = None
    else:
        action = env.action_space(agent).sample()  # this is where you would insert your policy
    env.step(action)
env.close()

Note: Most AEC environments include TerminateIllegalWrapper in their initialization, so this code does not change the environment’s behavior.

To wrap a Parallel environment.

from pettingzoo import make
from pettingzoo.utils import BaseParallelWrapper

parallel_env = make("parallel", "butterfly/pistonball-v6", render_mode="human")
parallel_env = BaseParallelWrapper(parallel_env)

observations, infos = parallel_env.reset()

while parallel_env.agents:
    actions = {agent: parallel_env.action_space(agent).sample() for agent in parallel_env.agents}  # this is where you would insert your policy
    observations, rewards, terminations, truncations, infos = parallel_env.step(actions)

Note

Wrappers are specific to either the AEC or Parallel API unless documented otherwise. Parallel variants include Parallel in their name, such as AgentIndicatorParallelV1. To apply an AEC-only wrapper to a Parallel environment, convert it to AEC, apply the wrapper, and convert it back.

ClipOutOfBoundsWrapper supports both APIs through the same constructor. It clips each Parallel agent’s action to that agent’s Box bounds and preserves the environment’s reset and step return values.

from pettingzoo import make
from pettingzoo.utils import ClipOutOfBoundsWrapper

parallel_env = make("parallel", "sisl/multiwalker-v9", render_mode="human")
parallel_env = ClipOutOfBoundsWrapper(parallel_env)

observations, infos = parallel_env.reset()

while parallel_env.agents:
    actions = {agent: parallel_env.action_space(agent).sample() for agent in parallel_env.agents}  # this is where you would insert your policy
    observations, rewards, terminations, truncations, infos = parallel_env.step(actions)

BlackDeathParallelV4 keeps the agents present at reset visible until the underlying episode finishes. After an agent leaves, later steps use a zero observation, zero reward, and empty info for that agent, and actions for it are ignored. Early termination/truncation flags are held back while the wrapped agent set remains active; on the final step, each agent’s original termination versus truncation cause is reported. Environments that add new agents after reset are not supported.

Replacing NaN actions with a no-op

NanNoopV1 (AEC) and NanNoopParallelV1 (Parallel) replace numeric actions containing a NaN with a caller-supplied no-op and emit a warning. They use the same no-op value for every agent, so choose a value that belongs to each affected agent’s action space. The wrapper checks known agents when constructed and checks the affected agent again before replacing an action. Incompatible no-ops raise ValueError; replacements are copied so mutable arrays are not shared between agents or steps.

For example, action 1 means stay still in discrete Pistonball:

import numpy as np
from pettingzoo import make
from pettingzoo.utils.wrappers import NanNoopParallelV1

env = NanNoopParallelV1(
    make("parallel", "butterfly/pistonball-v6", continuous=False),
    no_op_action=1,
)
observations, infos = env.reset(seed=42)
actions = dict.fromkeys(env.agents, 1)
actions[env.agents[0]] = np.nan
observations, rewards, terminations, truncations, infos = env.step(actions)
env.close()

For an AEC environment, use NanNoopV1(env, no_op_action=...). Its step(None) for a dead agent is passed through unchanged. Both wrappers preserve actions without NaNs, even if they are otherwise invalid: they do not clip actions or choose a legal action from an action mask. Action and observation spaces are unchanged.

These classes replace SuperSuit’s nan_noop_v0 for the respective PettingZoo APIs. Supply the no-op when constructing the wrapper, then call step normally.

Replacing NaN actions with a random action

NanRandomV1 (AEC) and NanRandomParallelV1 (Parallel) replace numeric actions containing a NaN with a random action from the acting agent’s own action space and emit a warning. If the agent has an action_mask, in its dictionary observation or otherwise in its info, the replacement is drawn only from the actions the mask allows. Masks are supported for Discrete action spaces; a mask with the wrong shape, values other than 0 and 1, or no allowed action raises ValueError. Replacements come from the wrapper’s own RNG, which reset(seed=...) reseeds, so seeded runs are reproducible.

For example, every NaN below becomes a random legal Connect Four move:

import numpy as np
from pettingzoo import make
from pettingzoo.utils.wrappers import NanRandomV1

env = NanRandomV1(make("aec", "classic/connect_four-v3"))
env.reset(seed=42)
for agent in env.agent_iter():
    observation, reward, termination, truncation, info = env.last()
    env.step(None if termination or truncation else np.nan)
env.close()

Actions without NaNs pass through unchanged, even if the mask forbids them, and step(None) for a dead AEC agent is passed through. Action and observation spaces are unchanged. These classes replace SuperSuit’s nan_random_v0, which looked for the mask under the key "action mask" and so ignored PettingZoo’s action_mask.

Repeating actions for several steps

FrameSkipV1 (AEC) and FrameSkipParallelV1 (Parallel) use each action for num_frames steps of the wrapped environment. Rewards from those steps are added up. The observation, termination, truncation and info are the latest ones from the wrapped environment, and stepping stops early when the episode ends.

from pettingzoo import make
from pettingzoo.utils.wrappers import FrameSkipParallelV1

env = FrameSkipParallelV1(
    make("parallel", "butterfly/pistonball-v6", continuous=False), num_frames=4
)
observations, infos = env.reset(seed=42)
while env.agents:
    actions = {agent: env.action_space(agent).sample() for agent in env.agents}
    observations, rewards, terminations, truncations, infos = env.step(actions)
env.close()

FrameSkipParallelV1 also accepts a range num_frames=(low, high). The number of steps is then drawn on every call from the np_random generator of env.unwrapped, which the environment seeds on reset(seed=...). Agents added partway through a call use default_action until the next call, and a ValueError is raised if none was given.

FrameSkipV1 takes a fixed num_frames only. An agent’s action is replayed on its next num_frames - 1 turns, and the caller is asked for an action whenever the wrapped environment selects an agent with nothing left to replay, so the turn order is unchanged. last() reports the rewards the agent collected since it last acted. Agents that finish during replaying still get their step(None) from the caller.

These classes replace SuperSuit’s frame_skip_v0 for the respective PettingZoo APIs.

Delaying observations

DelayObservationV1 (AEC) and DelayObservationParallelV1 (Parallel) delay each agent’s observations by a non-negative integer number of observation updates. Each agent has an independent history, which is cleared on reset. Repeated AEC observe() calls do not advance that history. Initial observations use a valid zero-like value from the agent’s space; dictionary observations containing observation and action_mask use an all-ones initial action mask.

These classes replace SuperSuit’s delay_observations_v0. Import them from pettingzoo.utils.wrappers and wrap the corresponding environment with delay=.... Rewards, termination flags, infos, and observation spaces are unchanged. An action mask is delayed with its observation, so a historical mask may differ from the environment’s current legal actions.

Flattening observations

FlattenObservationV1 (AEC) and FlattenObservationParallelV1 (Parallel) flatten each agent’s observation using Gymnasium’s flatten function and advertise the corresponding flatten_space. Box observations become one-dimensional arrays; Discrete observations become one-hot arrays, and Dict and Tuple components are combined in their space order. Each agent keeps its own observation space.

These classes replace SuperSuit’s flatten_v0. Import them from pettingzoo.utils.wrappers and wrap the environment for the corresponding API.

class pettingzoo.utils.wrappers.BaseWrapper(env: AECEnv[AgentID, ObsType, ActionType])[source]

Creates a wrapper around env parameter.

All AECEnv wrappers should inherit from this base class

class pettingzoo.utils.wrappers.BlackDeathParallelV4(env: ParallelEnv[AgentID, Any, ActionType])[source]

Keep the reset-time agent set visible until the whole episode ends.

When an agent leaves the wrapped environment early, subsequent steps expose a zero-valued observation with the same shape and dtype as that agent’s Box observation space, zero reward, and an empty info dictionary. Actions for departed agents are ignored and only actions for currently active wrapped agents are forwarded.

To keep the public agent set stable, an early departure is reported as terminated=False and truncated=False until the underlying episode ends. The wrapper remembers the original cause of each departure and reports those per-agent termination and truncation flags on the final step instead of collapsing both causes into one done flag.

Agents that appear after reset are not supported because they would change the fixed agent set exposed by this wrapper.

Parameters:

env – The Parallel environment to wrap.

class pettingzoo.utils.wrappers.TerminateIllegalWrapper(env: AECEnv[AgentID, ObsType, ActionType], illegal_reward: float)[source]

This wrapper terminates the game with the current player losing in case of illegal values.

Parameters:

illegal_reward – number that is the value of the player making an illegal move.

class pettingzoo.utils.wrappers.CaptureStdoutWrapper(env: AECEnv[AgentID, ObsType, ActionType])[source]

Takes an environment which prints to terminal, and gives it an ansi render mode where it captures the terminal output and returns it as a string instead.

class pettingzoo.utils.wrappers.AssertOutOfBoundsWrapper(env: AECEnv[AgentID, ObsType, ActionType])[source]

Asserts if the action given to step is outside of the action space.

class pettingzoo.utils.wrappers.ClipOutOfBoundsWrapper(env: AECEnv[Any, Any, Any] | None = None)[source]
class pettingzoo.utils.wrappers.ClipOutOfBoundsWrapper(env: ParallelEnv[AgentID, ObsType, Any])

Clips the input action to fit in the continuous action space (emitting a warning if it does so).

Supports both AEC and Parallel environments with Box action spaces. For AEC environments, step takes a single action (or None for a dead agent). For Parallel environments, step takes an action dictionary and clips each agent’s action to that agent’s bounds. In-range actions pass through unchanged; NaNs and malformed shapes are rejected. The action spaces are unchanged.

class pettingzoo.utils.wrappers.ClipRewardV1(env: AECEnv[AgentID, ObsType, ActionType], min_reward: float = -1, max_reward: float = 1)[source]

Clips each agent’s per-step reward into [min_reward, max_reward].

After every reset and step the inner rewards dict is clipped and the running total is rebuilt from that, so last() reports the sum of clipped steps. That is SuperSuit’s clip_reward_v0.

Parameters:
  • env – The AEC environment to wrap.

  • min_reward – Lower bound, inclusive. Default -1.

  • max_reward – Upper bound, inclusive. Default 1.

class pettingzoo.utils.wrappers.ClipRewardParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType], min_reward: float = -1, max_reward: float = 1)[source]

Clips each agent’s per-step reward into [min_reward, max_reward].

SuperSuit’s clip_reward_v0 has no Parallel implementation of its own. This one clips the reward dict that step() returns.

Parameters:
  • env – The parallel environment to wrap.

  • min_reward – Lower bound, inclusive. Default -1.

  • max_reward – Upper bound, inclusive. Default 1.

class pettingzoo.utils.wrappers.OrderEnforcingWrapper(env: AECEnv[AgentID, ObsType, ActionType])[source]

Checks if function calls or attribute access are in a disallowed order.

The following are raised: * AttributeError if any of the following are accessed before reset():

rewards, terminations, truncations, infos, agent_selection, num_agents, agents.

  • An error if any of the following are called before reset: render(), step(), observe(), state(), agent_iter()

  • A warning if step() is called when there are no agents remaining.

class pettingzoo.utils.wrappers.NanNoopV1(env: AECEnv[AgentID, ObsType, ActionType], no_op_action: ActionType)[source]

Replaces a numeric action containing a NaN with a supplied no-op action.

A warning is emitted for each replacement. Numeric scalars and arrays are supported. Actions without NaNs, and None actions for dead agents, pass through unchanged. The advertised action spaces are not modified.

The same caller-supplied no-op value is used for every agent. It must belong to each agent’s action space, including its shape and dtype, or a ValueError is raised. Known agents are checked at construction and the affected agent is checked on each replacement. Each replacement receives a copy of the value.

Parameters:
  • env – The AEC environment to wrap.

  • no_op_action – The no-op action, valid for all affected agents.

class pettingzoo.utils.wrappers.NanNoopParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType], no_op_action: ActionType)[source]

Replaces each numeric action containing a NaN with a supplied no-op action.

Each supplied action is checked independently and a warning is emitted for each replacement. Numeric scalars and arrays are supported. Actions without NaNs pass through unchanged, and the input dictionary is not modified.

As in NanNoopV1, the same caller-supplied no-op value must be valid in every affected agent’s action space. Known agents are checked at construction and the affected agent is checked on each replacement. Each replacement receives a copy. The advertised action spaces are not modified.

Parameters:
  • env – The parallel environment to wrap.

  • no_op_action – The no-op action, valid for all affected agents.

class pettingzoo.utils.wrappers.NanRandomV1(env: AECEnv[AgentID, ObsType, ActionType])[source]

Replaces a numeric action containing a NaN with a random action.

The replacement is sampled from the acting agent’s action space, and a warning is emitted for each one. If the agent has an action_mask, in its dictionary observation or else in its info, the replacement is drawn uniformly from the actions the mask allows. Masks are supported for Discrete action spaces; a mask on any other space, or one with the wrong shape, values other than 0 and 1, or no allowed action, raises a ValueError.

Numeric scalars and arrays are supported. Actions without NaNs, and None actions for dead agents, pass through unchanged. The advertised action spaces are not modified.

The random actions come from the wrapper’s own RNG, which reset() reseeds from the seed it is given, so a seeded reset gives reproducible replacements. As with StickyActionV1, reset(seed=None) starts a fresh unseeded stream.

Parameters:

env – The AEC environment to wrap.

class pettingzoo.utils.wrappers.NanRandomParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType])[source]

Replaces each numeric action containing a NaN with a random action.

Each supplied action is checked independently. A replacement is sampled from that agent’s action space, restricted to its action_mask when the agent’s latest dictionary observation or else its latest info has one, and a warning is emitted for each replacement. Masks follow the same rules as in NanRandomV1.

Numeric scalars and arrays are supported. Actions without NaNs pass through unchanged, and the input dictionary is not modified. The advertised action spaces are not modified. Seeding works as in NanRandomV1.

Parameters:

env – The parallel environment to wrap.

class pettingzoo.utils.wrappers.NanZerosV1(env: AECEnv[AgentID, ObsType, ActionType])[source]

Replaces an action containing a NaN with the all-zeros action.

The replacement has the shape and dtype of the agent’s action space, and a warning is emitted whenever it happens. Actions without a NaN, and the None action of a dead agent, are passed through unchanged. The advertised action spaces are not modified.

Only floating-point Box action spaces whose bounds contain zero are supported; any other action space raises an error rather than silently receiving an out-of-space replacement.

Parameters:

env – The AEC environment to wrap.

class pettingzoo.utils.wrappers.NanZerosParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType])[source]

Replaces an action containing a NaN with the all-zeros action.

Each agent’s action is checked separately, so only the agents that sent a NaN receive the replacement. The replacement has the shape and dtype of the agent’s action space, and a warning is emitted whenever it happens. The advertised action spaces are not modified.

Only floating-point Box action spaces whose bounds contain zero are supported; any other action space raises an error rather than silently receiving an out-of-space replacement.

Parameters:

env – The parallel environment to wrap.

class pettingzoo.utils.wrappers.AgentIndicatorV1(env: AECEnv[AgentID, ObsType, ActionType], type_only: bool = False)[source]

Adds an agent indicator to each observation.

With type_only=True, agents named <type>_<n> share an indicator for their type.

Parameters:
  • env – The AEC environment to wrap.

  • type_only – Whether to indicate agent types instead of individual agents.

class pettingzoo.utils.wrappers.AgentIndicatorParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType], type_only: bool = False)[source]

Adds an agent indicator to each observation.

With type_only=True, agents named <type>_<n> share an indicator for their type.

Parameters:
  • env – The parallel environment to wrap.

  • type_only – Whether to indicate agent types instead of individual agents.

class pettingzoo.utils.wrappers.ColorReductionObservationV1(env: AECEnv[AgentID, ObsType, ActionType], mode: str = 'full')[source]

Reduces an image observation to a single channel.

"full" converts to grayscale with the luminance weights [0.299, 0.587, 0.114] and returns uint8 whatever the input dtype was. "R", "G" and "B" take the named channel and keep the input dtype. Either way the trailing channel axis is dropped, so an (H, W, 3) observation becomes (H, W).

Parameters:
  • env – The AEC environment to wrap.

  • mode – One of “full”, “R”, “G”, “B”.

class pettingzoo.utils.wrappers.ColorReductionObservationParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType], mode: str = 'full')[source]

Reduces an image observation to a single channel.

"full" converts to grayscale with the luminance weights [0.299, 0.587, 0.114] and returns uint8 whatever the input dtype was. "R", "G" and "B" take the named channel and keep the input dtype. Either way the trailing channel axis is dropped, so an (H, W, 3) observation becomes (H, W).

Parameters:
  • env – The parallel environment to wrap.

  • mode – One of “full”, “R”, “G”, “B”.

class pettingzoo.utils.wrappers.DelayObservationV1(env: AECEnv[AgentID, ObsType, ActionType], delay: int)[source]

Add a fixed, per-agent delay to observations from an AEC environment.

Each agent has an independent observation history. Before an agent has accumulated delay previous observations, the wrapper returns a valid zero-like observation from that agent’s observation space. For observation dictionaries containing "observation" and "action_mask", the initial action mask contains ones so that it does not mark every action as illegal.

Observation histories advance only when the environment progresses to an agent. Calling observe() repeatedly does not advance the delay. Histories are cleared whenever the environment is reset.

Parameters:
  • env – The AEC environment to wrap.

  • delay – The number of that agent’s observation updates to delay. Must be a non-negative integer.

class pettingzoo.utils.wrappers.DelayObservationParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType], delay: int)[source]

Add a fixed, per-agent delay to observations from a Parallel environment.

Each agent has an independent observation history. Before an agent has accumulated delay previous observations, the wrapper returns a valid zero-like observation from that agent’s observation space. For observation dictionaries containing "observation" and "action_mask", the initial action mask contains ones so that it does not mark every action as illegal. Histories are cleared whenever the environment is reset.

Parameters:
  • env – The Parallel environment to wrap.

  • delay – The number of that agent’s observation updates to delay. Must be a non-negative integer.

class pettingzoo.utils.wrappers.DtypeObservationV1(env: AECEnv[AgentID, ObsType, ActionType], dtype: Any)[source]

Recasts each agent’s observation to dtype.

The observation space becomes a Box with the same shape and the new dtype, with the old bounds put through the same cast. Every space in possible_agents is built and checked when the wrapper is created.

Bounds that do not fit the new dtype wrap around, and casting an infinite bound to an integer dtype is undefined in numpy, so an unbounded Box cast to an integer dtype collapses to a single point that will not contain the observations. Both match SuperSuit’s dtype_v0.

Parameters:
  • env – The AEC environment to wrap.

  • dtype – Anything numpy.dtype accepts, e.g. np.float32.

class pettingzoo.utils.wrappers.DtypeObservationParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType], dtype: Any)[source]

Recasts each agent’s observation to dtype.

The observation space becomes a Box with the same shape and the new dtype, with the old bounds put through the same cast. Every space in possible_agents is built and checked when the wrapper is created.

Bounds that do not fit the new dtype wrap around, and casting an infinite bound to an integer dtype is undefined in numpy, so an unbounded Box cast to an integer dtype collapses to a single point that will not contain the observations. Both match SuperSuit’s dtype_v0.

Parameters:
  • env – The parallel environment to wrap.

  • dtype – Anything numpy.dtype accepts, e.g. np.float32.

class pettingzoo.utils.wrappers.FlattenObservationV1(env: AECEnv[AgentID, ObsType, ActionType])[source]

Flattens each agent’s observation into a 1D array.

Parameters:

env – The AEC environment to wrap.

class pettingzoo.utils.wrappers.FlattenObservationParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType])[source]

Flattens each agent’s observation into a 1D array.

Parameters:

env – The parallel environment to wrap.

class pettingzoo.utils.wrappers.FrameSkipV1(env: AECEnv[AgentID, ObsType, ActionType], num_frames: int)[source]

Repeats each agent’s action for num_frames of that agent’s turns.

When an agent acts, its action is replayed on the agent’s next num_frames - 1 turns of the wrapped environment without asking the caller. Control returns to the caller as soon as the wrapped environment selects an agent that has no action left to replay, so the turn order the caller sees is the wrapped environment’s own order. Rewards from every replayed turn are added up, and last() reports them as the agent’s cumulative reward since it last acted.

Agents that terminate or truncate while actions are being replayed are handed back to the caller for their usual step(None), and replaying continues after it. Their final observation is the wrapped environment’s. Agents added during replaying appear in agents and get their first turn from the caller.

num_frames=1 leaves the environment unchanged. Unlike FrameSkipParallelV1, only a fixed count is supported.

Parameters:
  • env – The AEC environment to wrap.

  • num_frames – Number of turns each supplied action is used for, at least 1.

class pettingzoo.utils.wrappers.FrameSkipParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType], num_frames: int | tuple[int, int], default_action: ActionType | None = None)[source]

Repeats each step’s actions for several steps of the wrapped environment.

Each call to step() steps the wrapped environment num_frames times with the same actions, or fewer if every agent is done earlier. Rewards are added up per agent. The observation, termination, truncation and info of each agent are the last ones the wrapped environment returned for it, so an agent that finishes partway through still reports its final transition.

num_frames can also be a tuple (low, high). The number of steps is then drawn uniformly from low to high (inclusive) on every call, using the np_random generator of env.unwrapped, which the environment seeds on reset(seed=...).

An agent added by the wrapped environment partway through has no action from the caller yet, so it uses default_action until the next call to step(). A ValueError is raised if that happens without a default_action. Agents that are added and removed again within one call are not reported.

Parameters:
  • env – The parallel environment to wrap.

  • num_frames – Number of steps per call, at least 1, or a (low, high) range with 1 <= low <= high.

  • default_action – Action used for agents added partway through a call.

class pettingzoo.utils.wrappers.FrameStackV3(env: AECEnv[Any, Any, Any] | ParallelEnv[Any, Any, Any] | None = None, stack_size: int = 4, stack_dim: int = -1, initial: str = 'first_obs')[source]

Stacks each agent’s last stack_size observations into one observation.

Works on both AEC and parallel environments: FrameStackV3(env) returns an AEC wrapper for an AECEnv and a parallel wrapper for a ParallelEnv, and either one is an instance of FrameStackV3.

Box observations with 1, 2 or 3 dimensions are stacked as in SuperSuit: 1D frames are concatenated, 2D frames get a new last (stack_dim=-1) or first (stack_dim=0) axis, and 3D frames are concatenated along the last or first (channel) axis. A Discrete(n) observation becomes one Discrete(n**stack_size) observation with the newest frame as the least significant base-n digit (each digit is the observation minus the space’s start). The observation space is updated to match and keeps the dtype.

initial says what fills the history before an agent has stack_size frames: "first_obs" repeats the agent’s first observation (SuperSuit’s frame_stack_v2) and "zeros" fills it with zeros (SuperSuit’s frame_stack_v1). In "zeros" mode the Box bounds are widened to include 0. The version suffix continues SuperSuit’s numbering.

Histories are per agent and cleared on reset. When a history advances:

  • AEC: at the start of each of the agent’s turns, after reset or step, so observe never changes it. Reading an observation any number of times, or reading another agent’s, returns the same stack until the next turn. An agent that has not had a turn yet sees the stack its current observation would start. An agent’s history is dropped when it leaves agents.

  • Parallel: each time reset or step returns an observation for the agent. An agent’s history is dropped once a step returns no observation for it.

Either way, an agent that comes back starts a fresh history.

Parameters:
  • env – The AEC or parallel environment to wrap.

  • stack_size – How many observations to stack. Must be a positive int.

  • stack_dim – The axis to stack along, -1 (last) or 0 (first).

  • initial – "first_obs" or "zeros", what fills the history at first.

class pettingzoo.utils.wrappers.MaxObservationV1(env: AECEnv[AgentID, ObsType, ActionType], memory: int)[source]

Replaces each agent’s observation with the elementwise max over its last memory observations.

This removes the flicker in environments that draw sprites on alternate frames. Histories are per agent and cleared on reset, and the observation space is unchanged. Only array observation spaces work: Box, MultiBinary, MultiDiscrete. Ported from SuperSuit’s max_observation_v0; the version suffix continues that numbering.

Parameters:
  • env – The AEC environment to wrap.

  • memory – How many observations to take the max over. Must be a positive int.

class pettingzoo.utils.wrappers.MaxObservationParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType], memory: int)[source]

Replaces each agent’s observation with the elementwise max over its last memory observations.

This removes the flicker in environments that draw sprites on alternate frames. Histories are per agent and cleared on reset, and the observation space is unchanged. Only array observation spaces work: Box, MultiBinary, MultiDiscrete. Ported from SuperSuit’s max_observation_v0; the version suffix continues that numbering.

Parameters:
  • env – The parallel environment to wrap.

  • memory – How many observations to take the max over. Must be a positive int.

class pettingzoo.utils.wrappers.PadActionSpaceV1(env: AECEnv[AgentID, ObsType, ActionType])[source]

Pads each agent’s action space up to one shared action space.

The shared space covers the action spaces of possible_agents, built the same way as PadObservationsV1 builds its observation space: for Box spaces the largest size along each axis, for Discrete spaces the range covering all of them. Every agent then reports that same space.

Before an action reaches the wrapped environment it is mapped back into the acting agent’s own space: a Box action is sliced to the agent’s original shape, and a Discrete action outside the agent’s own range becomes its first action (0 for a space starting at zero). None actions of dead agents pass through.

Box spaces have to agree on dtype and number of dimensions. Anything other than Box and Discrete, or a mix of both, is rejected.

Ported from SuperSuit’s pad_action_space_v0; the version suffix continues that numbering.

Parameters:

env – The AEC environment to wrap.

class pettingzoo.utils.wrappers.PadActionSpaceParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType])[source]

Pads each agent’s action space up to one shared action space.

The shared space covers the action spaces of possible_agents, built the same way as PadObservationsParallelV1 builds its observation space: for Box spaces the largest size along each axis, for Discrete spaces the range covering all of them. Every agent then reports that same space.

Before the actions reach the wrapped environment, each one is mapped back into its agent’s own space: a Box action is sliced to the agent’s original shape, and a Discrete action outside the agent’s own range becomes its first action (0 for a space starting at zero).

Box spaces have to agree on dtype and number of dimensions. Anything other than Box and Discrete, or a mix of both, is rejected.

Ported from SuperSuit’s pad_action_space_v0; the version suffix continues that numbering.

Parameters:

env – The parallel environment to wrap.

class pettingzoo.utils.wrappers.PadObservationsV1(env: AECEnv[AgentID, ObsType, ActionType])[source]

Pads each agent’s observation up to one shared observation space.

The shared space covers the observation spaces of possible_agents. For Box spaces it takes the largest size along each axis, and each observation is padded with zeros at the end of every axis, so the real values stay at the front. For Discrete spaces it takes the range covering all of them and observations pass through unchanged. Every agent then reports that same space.

Box spaces have to agree on dtype and number of dimensions. Anything other than Box and Discrete is rejected.

An agent whose observation is already the full shape gets the array back without a copy, so writing into it writes into the environment.

Ported from SuperSuit’s pad_observations_v0; the version suffix continues that numbering.

Parameters:

env – The AEC environment to wrap.

class pettingzoo.utils.wrappers.PadObservationsParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType])[source]

Pads each agent’s observation up to one shared observation space.

The shared space covers the observation spaces of possible_agents. For Box spaces it takes the largest size along each axis, and each observation is padded with zeros at the end of every axis, so the real values stay at the front. For Discrete spaces it takes the range covering all of them and observations pass through unchanged. Every agent then reports that same space.

Box spaces have to agree on dtype and number of dimensions. Anything other than Box and Discrete is rejected.

An agent whose observation is already the full shape gets the array back without a copy, so writing into it writes into the environment.

Ported from SuperSuit’s pad_observations_v0; the version suffix continues that numbering.

Parameters:

env – The parallel environment to wrap.

class pettingzoo.utils.wrappers.RescaleObservationV1(env: AECEnv[AgentID, ObsType, ActionType], min_obs: float = 0.0, max_obs: float = 1.0)[source]

Linearly rescales each agent’s Box observation into a fixed range.

Every element is mapped from its own [low, high] in the wrapped space onto [min_obs, max_obs], and the result is clipped so it always sits inside the advertised space. The wrapped space has to be a float Box whose elements all have finite low and high with high > low, since anything else gives nothing to scale from. Every space in possible_agents is checked when the wrapper is built. Finite float32 and float64 bounds are supported even when their difference overflows the observation dtype.

Ported from SuperSuit’s normalize_obs_v0; the version suffix continues that numbering.

Parameters:
  • env – The AEC environment to wrap.

  • min_obs – Lower bound of the rescaled observations.

  • max_obs – Upper bound of the rescaled observations.

class pettingzoo.utils.wrappers.RescaleObservationParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType], min_obs: float = 0.0, max_obs: float = 1.0)[source]

Linearly rescales each agent’s Box observation into a fixed range.

Every element is mapped from its own [low, high] in the wrapped space onto [min_obs, max_obs], and the result is clipped so it always sits inside the advertised space. The wrapped space has to be a float Box whose elements all have finite low and high with high > low, since anything else gives nothing to scale from. Every space in possible_agents is checked when the wrapper is built. Finite float32 and float64 bounds are supported even when their difference overflows the observation dtype.

Ported from SuperSuit’s normalize_obs_v0; the version suffix continues that numbering.

Parameters:
  • env – The parallel environment to wrap.

  • min_obs – Lower bound of the rescaled observations.

  • max_obs – Upper bound of the rescaled observations.

class pettingzoo.utils.wrappers.ReshapeObservationV1(env: AECEnv[AgentID, ObsType, ActionType], shape: tuple[int, ...])[source]

Reshapes each agent’s observation to the given shape.

The observation space is reshaped the same way, so per-element bounds are kept. Only works on Box observation spaces.

Parameters:
  • env – The AEC environment to wrap.

  • shape – The new observation shape. Must have as many elements as the old one.

class pettingzoo.utils.wrappers.ReshapeObservationParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType], shape: tuple[int, ...])[source]

Reshapes each agent’s observation to the given shape.

The observation space is reshaped the same way, so per-element bounds are kept. Only works on Box observation spaces.

Parameters:
  • env – The parallel environment to wrap.

  • shape – The new observation shape. Must have as many elements as the old one.

class pettingzoo.utils.wrappers.ScaleActionV1(env: AECEnv[AgentID, ObsType, ActionType], scale: float)[source]

Scales the bounds of each agent’s Box action space by a constant factor.

The action space this wrapper advertises has low and high multiplied by scale. Actions are divided by scale before they reach the wrapped environment, so an action drawn from the advertised space is a valid action for the wrapped environment. Actions on a bound of the advertised space are clipped to the wrapped bound, since the round trip through scale is not exact in floating point. An action outside the advertised space is passed through as is.

Scaling an integer Box narrows what can be reached: at scale 0.5, a Box(0, 10, dtype=int64) advertises Box(0, 5) and only even actions are reachable. A bound large enough that bound * scale overflows the dtype becomes infinite, and actions there stay infinite.

Parameters:
  • env – The AEC environment to wrap.

  • scale – Non-zero factor applied to the action space bounds. A negative scale flips the bounds, so they are swapped to keep the Box valid.

class pettingzoo.utils.wrappers.ScaleActionParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType], scale: float)[source]

Scales the bounds of each agent’s Box action space by a constant factor.

The action space this wrapper advertises has low and high multiplied by scale. Actions are divided by scale before they reach the wrapped environment, so an action drawn from the advertised space is a valid action for the wrapped environment. Actions on a bound of the advertised space are clipped to the wrapped bound, since the round trip through scale is not exact in floating point. An action outside the advertised space is passed through as is.

Scaling an integer Box narrows what can be reached: at scale 0.5, a Box(0, 10, dtype=int64) advertises Box(0, 5) and only even actions are reachable. A bound large enough that bound * scale overflows the dtype becomes infinite, and actions there stay infinite.

Parameters:
  • env – The parallel environment to wrap.

  • scale – Non-zero factor applied to the action space bounds. A negative scale flips the bounds, so they are swapped to keep the Box valid.

class pettingzoo.utils.wrappers.StickyActionV1(env: AECEnv[AgentID, ObsType, ActionType], repeat_action_probability: float)[source]

Repeats an agent’s previous action with probability repeat_action_probability.

Each agent keeps its own previous action, and an agent has nothing to repeat until it has acted once since the last reset(), so the first action after a reset is always passed through. Only this wrapper’s own reset clears the memory: an environment that auto-resets underneath it, such as MultiEpisodeParallelEnv on the inside, can still have the first action of a later episode repeated. The remembered action is a deep copy, so reusing an action buffer between steps does not defeat the wrapper.

Seeding follows the usual PettingZoo pattern: reset() reseeds from the seed it is given, which means reset(seed=None) starts a fresh unseeded stream. Pass a seed on every reset if you want a whole multi-episode run to be reproducible.

Parameters:
  • env – The AEC environment to wrap.

  • repeat_action_probability – Probability of using the previous action, in the interval [0, 1).

class pettingzoo.utils.wrappers.StickyActionParallelV1(env: ParallelEnv[AgentID, ObsType, ActionType], repeat_action_probability: float)[source]

Repeats an agent’s previous action with probability repeat_action_probability.

Each agent keeps its own previous action, and an agent has nothing to repeat until it has acted once since the last reset(), so the first action after a reset is always passed through. Only this wrapper’s own reset clears the memory: an environment that auto-resets underneath it, such as MultiEpisodeParallelEnv on the inside, can still have the first action of a later episode repeated. The remembered action is a deep copy, so reusing an action buffer between steps does not defeat the wrapper.

Seeding follows the usual PettingZoo pattern: reset() reseeds from the seed it is given, which means reset(seed=None) starts a fresh unseeded stream. Pass a seed on every reset if you want a whole multi-episode run to be reproducible.

Parameters:
  • env – The parallel environment to wrap.

  • repeat_action_probability – Probability of using the previous action, in the interval [0, 1).