Files
Wizard/wizard_server/game_logic/wizard_game/observation.py
T
2026-09-27 09:52:00 +02:00

121 lines
4.8 KiB
Python

"""The view of the game a single player is allowed to see.
An ``Observation`` contains everything a player knows at a point in time and
nothing more (no opponent hands, hidden bids stay hidden). It is used
- by the server to send the game state to a web client, and
- as the input of AI players, both in training and in production.
``to_dict`` / ``from_dict`` provide a lossless JSON representation.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
from .actions import Action, action_from_dict
from .cards import Card, Suit, card_by_id
from .state import GameOptions, Phase, PlayedCard, RoundResult, TrickResult
@dataclass(frozen=True, slots=True)
class Observation:
player: int
num_players: int
options: GameOptions
phase: Phase
round_number: int
total_rounds: int
dealer: int
current_player: int | None
hand: tuple[Card, ...]
hand_sizes: tuple[int, ...]
trump_card: Card | None
trump_suit: Suit | None
# ``None`` for players who have not bid yet, or whose bid is still hidden.
bids: tuple[int | None, ...]
has_bid: tuple[bool, ...]
tricks_won: tuple[int, ...]
scores: tuple[int, ...]
trick_number: int
trick_leader: int
current_trick: tuple[PlayedCard, ...]
lead_suit: Suit | None
# All cards played in the current round, including the current trick.
played_cards: tuple[Card, ...]
last_trick: TrickResult | None
round_history: tuple[RoundResult, ...]
# Empty unless it is this player's turn.
legal_actions: tuple[Action, ...]
winners: tuple[int, ...]
@property
def is_my_turn(self) -> bool:
return self.current_player == self.player
def to_dict(self) -> dict:
return {
"player": self.player,
"num_players": self.num_players,
"options": self.options.to_dict(),
"phase": self.phase.value,
"round_number": self.round_number,
"total_rounds": self.total_rounds,
"dealer": self.dealer,
"current_player": self.current_player,
"hand": [card.to_dict() for card in self.hand],
"hand_sizes": list(self.hand_sizes),
"trump_card": self.trump_card.to_dict() if self.trump_card else None,
"trump_suit": self.trump_suit.value if self.trump_suit else None,
"bids": list(self.bids),
"has_bid": list(self.has_bid),
"tricks_won": list(self.tricks_won),
"scores": list(self.scores),
"trick_number": self.trick_number,
"trick_leader": self.trick_leader,
"current_trick": [played.to_dict() for played in self.current_trick],
"lead_suit": self.lead_suit.value if self.lead_suit else None,
"played_cards": [card.to_dict() for card in self.played_cards],
"last_trick": self.last_trick.to_dict() if self.last_trick else None,
"round_history": [result.to_dict() for result in self.round_history],
"legal_actions": [action.to_dict() for action in self.legal_actions],
"winners": list(self.winners),
}
@classmethod
def from_dict(cls, data: dict[str, Any]) -> Observation:
def card(value: dict | None) -> Card | None:
return card_by_id(value["id"]) if value else None
def suit(value: str | None) -> Suit | None:
return Suit(value) if value else None
return cls(
player=data["player"],
num_players=data["num_players"],
options=GameOptions.from_dict(data["options"]),
phase=Phase(data["phase"]),
round_number=data["round_number"],
total_rounds=data["total_rounds"],
dealer=data["dealer"],
current_player=data["current_player"],
hand=tuple(card_by_id(c["id"]) for c in data["hand"]),
hand_sizes=tuple(data["hand_sizes"]),
trump_card=card(data["trump_card"]),
trump_suit=suit(data["trump_suit"]),
bids=tuple(data["bids"]),
has_bid=tuple(data["has_bid"]),
tricks_won=tuple(data["tricks_won"]),
scores=tuple(data["scores"]),
trick_number=data["trick_number"],
trick_leader=data["trick_leader"],
current_trick=tuple(PlayedCard.from_dict(p) for p in data["current_trick"]),
lead_suit=suit(data["lead_suit"]),
played_cards=tuple(card_by_id(c["id"]) for c in data["played_cards"]),
last_trick=TrickResult.from_dict(data["last_trick"]) if data["last_trick"] else None,
round_history=tuple(RoundResult.from_dict(r) for r in data["round_history"]),
legal_actions=tuple(action_from_dict(a) for a in data["legal_actions"]),
winners=tuple(data["winners"]),
)