from __future__ import annotations from dataclasses import dataclass SUITS: dict[str, tuple[str, str]] = { "H": ("hearts", "red"), "D": ("diamonds", "red"), "C": ("clubs", "black"), "S": ("spades", "black"), } RANK_LABELS = ("A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K") RANK_VALUES = { label: index + 1 for index, label in enumerate(RANK_LABELS) } @dataclass(frozen=True) class Card: symbol: str suit: str suit_symbol: str color: str rank: int rank_label: str @property def is_ace(self) -> bool: return self.rank == 1 @property def is_face(self) -> bool: return self.rank in {11, 12, 13} def __str__(self) -> str: return self.symbol def __contains__(self, value: object) -> bool: return str(value) in self.symbol def __getitem__(self, index: int | slice) -> str: return self.symbol[index] def __iter__(self): return iter(self.symbol) def __len__(self) -> int: return len(self.symbol) def count(self, value: str) -> int: return self.symbol.count(value) def index(self, value: str) -> int: return self.symbol.index(value) def startswith(self, prefix: str) -> bool: return self.symbol.startswith(prefix) def endswith(self, suffix: str) -> bool: return self.symbol.endswith(suffix) def deck() -> list[str]: return [f"{rank}{suit}" for suit in SUITS for rank in RANK_LABELS] def parse_card(symbol: str) -> Card: normalized = str(symbol or "").strip().upper() if len(normalized) < 2: raise ValueError(f"Unknown card: {symbol}") suit_symbol = normalized[-1] rank_label = normalized[:-1] if suit_symbol not in SUITS: raise ValueError(f"Unknown card suit: {symbol}") if rank_label not in RANK_VALUES: raise ValueError(f"Unknown card rank: {symbol}") suit, color = SUITS[suit_symbol] return Card( symbol=f"{rank_label}{suit_symbol}", suit=suit, suit_symbol=suit_symbol, color=color, rank=RANK_VALUES[rank_label], rank_label=rank_label, )