93 lines
3.2 KiB
Python
93 lines
3.2 KiB
Python
"""
|
|
This module helps describe & evaluate previously played games.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from .games import Game
|
|
from .transition import Transition
|
|
from dataclasses import dataclass
|
|
from typing import Any, Generator
|
|
|
|
@dataclass(frozen=True)
|
|
class GameReplay:
|
|
"""
|
|
Game replay detailing the events of what happened in a previously
|
|
played game.
|
|
"""
|
|
|
|
start : Game
|
|
turns : list[Turn]
|
|
|
|
def gen_transitions(self) -> Generator[Transition, None, None]:
|
|
"""
|
|
Generate transitions from the game's summary.
|
|
|
|
:return: Transitions that describe consequences throughout the game.
|
|
:rtype: Generator[Transition, None, None]
|
|
"""
|
|
prev_state : Game = self.start
|
|
seen_participants : dict[int, tuple[Game, dict[str, Any]]] = {}
|
|
|
|
for turn in self.turns:
|
|
# If a player has made a move before, show the current state as
|
|
# the result of their previous turn's action.
|
|
state, action = seen_participants.get(turn.player, ( None, {} ))
|
|
if state is not None:
|
|
yield Transition(
|
|
full_state_after=prev_state,
|
|
full_state_before=state,
|
|
move=action,
|
|
player=turn.player,
|
|
)
|
|
|
|
# Save the player's current action for the future.
|
|
# We'll see how this action worked out!
|
|
seen_participants[turn.player] = ( prev_state, turn.action )
|
|
prev_state = turn.state
|
|
|
|
else:
|
|
# Record to all players that the game has finished.
|
|
for player, ( state, action ) in seen_participants.items():
|
|
yield Transition(
|
|
full_state_after=prev_state,
|
|
full_state_before=state,
|
|
move=action,
|
|
player=player,
|
|
)
|
|
|
|
def to_transitions(self, player : int | None = None) -> list[Transition]:
|
|
"""
|
|
Convert the GameSummary into a list of transitions that can be
|
|
fed to a system that can reflect on past moves.
|
|
|
|
:param player: Filter to only show transitions of a given player.
|
|
:type player: int | None
|
|
:return: A list of transitions extracted from a game.
|
|
:rtype: list[Transition]
|
|
"""
|
|
if player is None:
|
|
return list(self.gen_transitions())
|
|
else:
|
|
return [ t for t in self.gen_transitions() if t.player == player ]
|
|
|
|
@dataclass(frozen=True)
|
|
class Turn:
|
|
"""
|
|
A turn is a snapshot of a moment where a user was prompted to take an
|
|
action. A turn consists of three values:
|
|
|
|
1. The player (number) that was prompted for an action
|
|
2. The action that that player has decided to take
|
|
3. The game's state after the action was prompted and before the next
|
|
prompted move.
|
|
|
|
Note that turns might not correlate with in-game turns. A user might
|
|
take multiple actions in a Risk game (recruit, attack, move) and each
|
|
action is registered as an independent move.
|
|
"""
|
|
|
|
player : int
|
|
action : dict[str, Any]
|
|
state : Game
|