121 lines
4.8 KiB
Python
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"]),
|
|
)
|