Files
texas_hold_x/texas_holdem/cards.py
T
2026-05-11 15:46:30 +08:00

67 lines
1.7 KiB
Python

from __future__ import annotations
from dataclasses import dataclass
from random import Random
SUITS = ("c", "d", "h", "s")
RANK_LABELS = {
2: "2",
3: "3",
4: "4",
5: "5",
6: "6",
7: "7",
8: "8",
9: "9",
10: "T",
11: "J",
12: "Q",
13: "K",
14: "A",
}
LABEL_RANKS = {label: rank for rank, label in RANK_LABELS.items()}
@dataclass(frozen=True, slots=True)
class Card:
rank: int
suit: str
def __post_init__(self) -> None:
if self.rank not in RANK_LABELS:
raise ValueError(f"invalid rank: {self.rank}")
if self.suit not in SUITS:
raise ValueError(f"invalid suit: {self.suit}")
def __str__(self) -> str:
return f"{RANK_LABELS[self.rank]}{self.suit}"
@classmethod
def parse(cls, value: str) -> "Card":
if len(value) != 2:
raise ValueError(f"card must have two characters: {value!r}")
rank_label = value[0].upper()
suit = value[1].lower()
if rank_label not in LABEL_RANKS:
raise ValueError(f"invalid rank label: {rank_label}")
return cls(LABEL_RANKS[rank_label], suit)
class Deck:
def __init__(self, rng: Random | None = None) -> None:
self._rng = rng or Random()
self._cards = [Card(rank, suit) for suit in SUITS for rank in range(2, 15)]
self._rng.shuffle(self._cards)
def draw(self, count: int = 1) -> list[Card]:
if count < 1:
raise ValueError("count must be positive")
if len(self._cards) < count:
raise ValueError("deck does not have enough cards")
drawn = self._cards[-count:]
del self._cards[-count:]
return drawn
def burn(self) -> None:
self.draw(1)