111 lines
3.3 KiB
Python
111 lines
3.3 KiB
Python
"""
|
|
The transition module allows agents to reflect on the choices they
|
|
(and others) have made during recent games.
|
|
|
|
Agents are generally intended to be stateless, so this allows them to
|
|
later understand how well certain moves turned out.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from .games import FinishState, Game
|
|
from dataclasses import dataclass
|
|
from typing import Any, Generator
|
|
|
|
@dataclass(frozen=True)
|
|
class Transition:
|
|
"""
|
|
A transition is a simplified view on a user's single move.
|
|
|
|
The transition contains the game state, the move they made,
|
|
and what the game ended up looking like the next time they were
|
|
prompted for a move. If the game ends, the `state_after` field contains
|
|
the final state.
|
|
"""
|
|
|
|
full_state_after : Game
|
|
full_state_before : Game
|
|
move : dict[str, Any]
|
|
player : int
|
|
|
|
@property
|
|
def end_result(self) -> FinishState | None:
|
|
"""
|
|
Return the result of this move if it caused the game to end,
|
|
or None if the game still continues after this.
|
|
|
|
:return: The player's result in the game with this move.
|
|
:rtype: FinishState | None
|
|
"""
|
|
w = self.full_state_after.winner()
|
|
|
|
if w is None:
|
|
return None
|
|
|
|
return w[self.player]
|
|
|
|
@property
|
|
def game_has_ended(self) -> bool:
|
|
"""
|
|
Shorthand property for whether this was the player's last move.
|
|
|
|
Tip: Use the methods `move_caused_draw`, `move_caused_loss` and
|
|
`move_caused_win` to determine the outcome.
|
|
"""
|
|
return self.full_state_after.winner() is not None
|
|
|
|
@property
|
|
def move_caused_draw(self) -> bool:
|
|
"""
|
|
Shorthand property for whether this move caused the player to draw.
|
|
|
|
:rtype: bool
|
|
"""
|
|
return self.end_result == FinishState.draw
|
|
|
|
@property
|
|
def move_caused_loss(self) -> bool:
|
|
"""
|
|
Shorthand property for whether this move caused the player to lose.
|
|
|
|
:rtype: bool
|
|
"""
|
|
return self.end_result == FinishState.loss
|
|
|
|
@property
|
|
def move_caused_win(self) -> bool:
|
|
"""
|
|
Shorthand property for whether this move caused the player to win.
|
|
|
|
:rtype: bool
|
|
"""
|
|
return self.end_result == FinishState.win
|
|
|
|
@property
|
|
def state_after(self) -> dict[str, Any]:
|
|
"""
|
|
The initial state that the agent was shown after they were asked for
|
|
a move.
|
|
|
|
Keep in mind that this state does not necessarily contain
|
|
ALL information, as players are often partially kept in the dark
|
|
about a game's state.
|
|
|
|
:rtype: dict[str, Any]
|
|
"""
|
|
return self.full_state_after.as_seen_by(self.player)
|
|
|
|
@property
|
|
def state_before(self) -> dict[str, Any]:
|
|
"""
|
|
The initial state that the agent was shown when they were asked for
|
|
a move.
|
|
|
|
Keep in mind that this state does not necessarily contain
|
|
ALL information, as players are often partially kept in the dark
|
|
about a game's state.
|
|
|
|
:rtype: dict[str, Any]
|
|
"""
|
|
return self.full_state_before.as_seen_by(self.player)
|