first prototype

This commit is contained in:
2026-09-27 09:52:00 +02:00
parent 4635199a41
commit 6d011d474f
109 changed files with 6354 additions and 507 deletions
@@ -0,0 +1,73 @@
"""Actions a player can take and their (de)serialization.
Actions are serialized as small JSON objects which are shared by the web
frontend, the game server and the AI player:
- ``{"type": "bid", "value": 2}``
- ``{"type": "play", "card": 17}``
- ``{"type": "trump", "suit": "red"}``
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
from .cards import Card, Suit, card_by_id
@dataclass(frozen=True, slots=True)
class Bid:
value: int
def to_dict(self) -> dict:
return {"type": "bid", "value": self.value}
@dataclass(frozen=True, slots=True)
class PlayCard:
card: Card
def to_dict(self) -> dict:
return {"type": "play", "card": self.card.id}
@dataclass(frozen=True, slots=True)
class ChooseTrump:
suit: Suit
def to_dict(self) -> dict:
return {"type": "trump", "suit": self.suit.value}
Action = Bid | PlayCard | ChooseTrump
def action_to_dict(action: Action) -> dict:
return action.to_dict()
def action_from_dict(data: dict[str, Any]) -> Action:
"""Parse an action from its JSON representation.
Raises ``ValueError`` if the data is malformed.
"""
if not isinstance(data, dict):
raise ValueError("Action must be an object")
kind = data.get("type")
try:
if kind == "bid":
value = data["value"]
if not isinstance(value, int) or isinstance(value, bool):
raise ValueError("Bid value must be an integer")
return Bid(value)
if kind == "play":
card_id = data["card"]
if not isinstance(card_id, int) or isinstance(card_id, bool):
raise ValueError("Card must be an integer id")
return PlayCard(card_by_id(card_id))
if kind == "trump":
return ChooseTrump(Suit(data["suit"]))
except KeyError as exc:
raise ValueError(f"Missing field {exc} in action") from exc
raise ValueError(f"Unknown action type: {kind!r}")