Source code for pettingzoo.utils.wrappers.terminate_illegal
from __future__ import annotations
from typing import Any
from typing_extensions import override
from pettingzoo.utils.env import ActionType, AECEnv, AgentID, ObsType
from pettingzoo.utils.env_logger import EnvLogger
from pettingzoo.utils.wrappers.base import BaseWrapper
[docs]
class TerminateIllegalWrapper(BaseWrapper[AgentID, ObsType, ActionType]):
"""This wrapper terminates the game with the current player losing in case of illegal values.
Args:
illegal_reward: number that is the value of the player making an illegal move.
"""
def __init__(
self, env: AECEnv[AgentID, ObsType, ActionType], illegal_reward: float
):
super().__init__(env)
self._illegal_value = illegal_reward
self._prev_obs: Any = None
self._prev_info: Any = None
self._terminated = False # terminated by an illegal move
@override
def reset(
self, seed: int | None = None, options: dict[str, Any] | None = None
) -> None:
self._terminated = False
self._prev_obs = None
self._prev_info = None
super().reset(seed=seed, options=options)
@override
def observe(self, agent: AgentID) -> ObsType | None:
obs = super().observe(agent)
if agent == self.agent_selection:
self._prev_obs = obs
if self.agent_selection in self.infos:
self._prev_info = self.infos[self.agent_selection]
else:
self._prev_info = {}
return obs
@override
def step(self, action: ActionType) -> None:
current_agent = self.agent_selection
if self._prev_obs is None:
self.observe(self.agent_selection)
if isinstance(self._prev_obs, dict):
assert "action_mask" in self._prev_obs, (
f"`action_mask` not found in dictionary observation: {self._prev_obs}. Action mask must either be in `observation['action_mask']` or `info['action_mask']` to use TerminateIllegalWrapper."
)
_prev_action_mask = self._prev_obs["action_mask"]
else:
assert self._prev_info is not None
assert "action_mask" in self._prev_info, (
f"`action_mask` not found in info for non-dictionary observation: {self._prev_info}. Action mask must either be in observation['action_mask'] or info['action_mask'] to use TerminateIllegalWrapper."
)
_prev_action_mask = self._prev_info["action_mask"]
self._prev_obs = None
self._prev_info = None
if self._terminated and (
self.terminations[self.agent_selection]
or self.truncations[self.agent_selection]
):
self.env.unwrapped._was_dead_step(action)
elif (
not self.terminations[self.agent_selection]
and not self.truncations[self.agent_selection]
and not _prev_action_mask[action]
):
EnvLogger.warn_on_illegal_move()
self.env.unwrapped._cumulative_rewards[self.agent_selection] = 0
self.env.unwrapped.terminations = dict.fromkeys(self.agents, True)
self.env.unwrapped.truncations = dict.fromkeys(self.agents, True)
self.env.unwrapped.rewards = dict.fromkeys(self.truncations, 0)
self.env.unwrapped.rewards[current_agent] = float(self._illegal_value)
self.env.unwrapped._accumulate_rewards()
self.env.unwrapped._deads_step_first()
self._terminated = True
else:
super().step(action)
@override
def __str__(self) -> str:
return str(self.env)