pub trait AecEnvironment {
type AgentId: Eq + Hash + Clone + Send + Sync + 'static;
type Observation: Clone + Send + Sync + 'static;
type Action: Clone + Send + Sync + 'static;
type Info: Default + Clone + Send + Sync + 'static;
// Required methods
fn possible_agents(&self) -> &[Self::AgentId];
fn agents(&self) -> &[Self::AgentId];
fn agent_selection(&self) -> &Self::AgentId;
fn step(&mut self, action: Option<Self::Action>);
fn reset(&mut self, seed: Option<u64>);
fn observe(&self, agent: &Self::AgentId) -> Option<Self::Observation>;
fn agent_state(
&self,
agent: &Self::AgentId,
) -> (f64, EpisodeStatus, Self::Info);
fn sample_action(
&self,
agent: &Self::AgentId,
rng: &mut impl Rng,
) -> Self::Action;
// Provided methods
fn last(
&self,
) -> (Option<Self::Observation>, f64, EpisodeStatus, Self::Info) { ... }
fn is_done(&self) -> bool { ... }
fn num_agents(&self) -> usize { ... }
fn max_num_agents(&self) -> usize { ... }
}Expand description
A multi-agent environment with Agent Environment Cycle (turn-based) semantics.
Mirrors the semantics of PettingZoo’s AEC API, adapted for Rust’s type system. Use this when agents act one at a time — board games, card games, or any domain where simultaneous action is not meaningful.
§Design principles
-
Turn-based execution: one agent acts per
step()call.agent_selection()identifies whose turn it is. After each call, the selection advances to the next active agent. -
Persistent state: the environment tracks each agent’s most recent reward, status, and info as mutable state. Read it via
agent_state()before deciding on an action.last()is a convenience that combinesobserve()andagent_state()for the current agent. -
Cycling out terminated agents: when
agent_state()reports a done status foragent_selection(), passNonetostep()to advance the turn without applying an action. The type signature makes this contract explicit — passingSome(action)for a done agent is undefined behaviour. -
Bevy-compatible by design: same
Send + Sync + 'staticbounds asParallelEnvironment. The turn-based nature is inherently sequential, so ECS parallelisation applies less directly than withParallelEnvironment. -
No
render(): visualisation is bevy-gym’s concern. -
No
close(): implementDropif your environment holds resources.
§Example
Typical AEC loop:
env.reset(None);
while !env.is_done() {
let (obs, _reward, status, _info) = env.last();
let action = if status.is_done() {
None // cycle the terminated agent out
} else {
Some(policy.act(env.agent_selection(), &obs.unwrap()))
};
env.step(action);
}Required Associated Types§
Sourcetype AgentId: Eq + Hash + Clone + Send + Sync + 'static
type AgentId: Eq + Hash + Clone + Send + Sync + 'static
Identifier for each agent. Same semantics as ParallelEnvironment::AgentId.
Sourcetype Observation: Clone + Send + Sync + 'static
type Observation: Clone + Send + Sync + 'static
The observation type produced by observe().
Send + Sync + 'static are required for Bevy ECS compatibility.
Required Methods§
Sourcefn possible_agents(&self) -> &[Self::AgentId]
fn possible_agents(&self) -> &[Self::AgentId]
The complete, fixed set of agent IDs for this environment.
Does not change between episodes or as agents terminate mid-episode.
Sourcefn agents(&self) -> &[Self::AgentId]
fn agents(&self) -> &[Self::AgentId]
The agents currently active in this episode.
Starts equal to possible_agents() after reset(). Shrinks as agents
terminate or are truncated; never grows. Empty when the episode is over.
Sourcefn agent_selection(&self) -> &Self::AgentId
fn agent_selection(&self) -> &Self::AgentId
The agent whose turn it currently is to act.
Sourcefn step(&mut self, action: Option<Self::Action>)
fn step(&mut self, action: Option<Self::Action>)
Execute the current agent’s action and advance to the next agent.
Pass None when agent_state(agent_selection()) reports a done status,
to cycle the agent out without applying an action. Pass Some(action)
otherwise.
Sourcefn reset(&mut self, seed: Option<u64>)
fn reset(&mut self, seed: Option<u64>)
Reset the environment to an initial state, starting a new episode.
Unlike ParallelEnvironment::reset, this returns nothing. Retrieve
initial observations via observe() after calling reset().
If seed is Some(u64), it is used to seed the internal RNG.
Sourcefn observe(&self, agent: &Self::AgentId) -> Option<Self::Observation>
fn observe(&self, agent: &Self::AgentId) -> Option<Self::Observation>
Retrieve the current observation for the given agent.
Returns None if the agent has terminated or been truncated — their
last observation is no longer valid.
Sourcefn agent_state(&self, agent: &Self::AgentId) -> (f64, EpisodeStatus, Self::Info)
fn agent_state(&self, agent: &Self::AgentId) -> (f64, EpisodeStatus, Self::Info)
Retrieve the persistent (reward, status, info) for the given agent.
This state is updated each time the agent acts and persists until its next turn. It reflects what the agent received as a result of its last action.
Sourcefn sample_action(
&self,
agent: &Self::AgentId,
rng: &mut impl Rng,
) -> Self::Action
fn sample_action( &self, agent: &Self::AgentId, rng: &mut impl Rng, ) -> Self::Action
Sample a random action for the given agent.
The rng is caller-supplied so exploration randomness can be seeded
and tracked independently from environment randomness.
Provided Methods§
Sourcefn last(&self) -> (Option<Self::Observation>, f64, EpisodeStatus, Self::Info)
fn last(&self) -> (Option<Self::Observation>, f64, EpisodeStatus, Self::Info)
Returns the full state for the currently selected agent.
Convenience wrapper around observe(agent_selection()) and
agent_state(agent_selection()). This is the idiomatic way to read the
current agent’s situation at the top of the AEC loop.
Sourcefn num_agents(&self) -> usize
fn num_agents(&self) -> usize
Number of currently active agents.
Sourcefn max_num_agents(&self) -> usize
fn max_num_agents(&self) -> usize
Maximum number of agents that could ever be active simultaneously.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.