Source code for blackjack21.players

__all__ = (
    "BetAmount",
    "GameResult",
    "GameState",
    "Hand",
    "Player",
)

from collections.abc import Iterator
from enum import Enum
from typing import NewType

from .deck import Card
from .utils import calculate_hand

PlayerName = NewType("PlayerName", str)
BetAmount = NewType("BetAmount", int)


[docs] class GameResult(str, Enum): """Represents the outcome of a player's hand.""" BLACKJACK = "BLACKJACK" PLAYER_WIN = "PLAYER_WIN" DEALER_BUST = "DEALER_BUST" PUSH = "PUSH" PLAYER_BUST = "PLAYER_BUST" DEALER_WIN = "DEALER_WIN" SURRENDER = "SURRENDER"
[docs] class GameState(str, Enum): """Represents the current phase of the game.""" INIT = "INIT" PLAYERS_TURN = "PLAYERS_TURN" DEALER_TURN = "DEALER_TURN" ROUND_OVER = "ROUND_OVER"
[docs] class Hand: """A single hand of cards. Does not know its owner. :param bet: int """ __slots__ = ("_bet", "_cards", "_stood_manually", "_surrendered", "result") def __init__(self, bet: BetAmount) -> None: self._cards: list[Card] = [] self._bet = bet self._stood_manually = False self._surrendered = False self.result: GameResult | None = None def __iter__(self) -> Iterator[Card]: """Iterate through the cards in hand.""" yield from self._cards def __getitem__(self, index: int) -> Card: """Get card at index.""" return self._cards[index] def __len__(self) -> int: """Number of cards in the hand.""" return len(self._cards) @property def bet(self) -> BetAmount: """Player's bet amount.""" return self._bet @property def bust(self) -> bool: """Player's bust status.""" return self.total > 21 @property def is_complete(self) -> bool: """Hand cannot take more actions.""" return self._stood_manually or self._surrendered or self.total >= 21 @property def surrendered(self) -> bool: """Player's surrender status.""" return self._surrendered @property def total(self) -> int: """Player's hand total.""" return calculate_hand(self._cards).value
[docs] def add_card(self, card: Card) -> None: """Adds a card to the hand.""" self._cards.append(card)
[docs] def mark_stood(self) -> None: """Record that the player explicitly chose to stand.""" self._stood_manually = True
[docs] def surrender(self) -> None: """Mark this hand as surrendered.""" self._surrendered = True
[docs] def double_bet(self) -> None: """Double the bet (for double-down).""" self._bet = BetAmount(self._bet * 2)
[docs] def pop_card(self) -> Card: """Remove and return the last card (for splitting).""" return self._cards.pop()
[docs] class Player: """Player class, containing one or more hands. :param name: str :param bet: int """ __slots__ = ( "_hands", "_initial_bet", "_name", ) def __init__(self, name: PlayerName, bet: BetAmount) -> None: self._name = name self._initial_bet = bet self._hands = [Hand(bet)] @property def hands(self) -> list[Hand]: """List of Hand class objects for the player.""" return self._hands @property def name(self) -> PlayerName: """Player name.""" return self._name
[docs] def insert_hand_after(self, existing: Hand, new_hand: Hand) -> None: """Insert a new hand immediately after an existing one (for splitting).""" index = self._hands.index(existing) self._hands.insert(index + 1, new_hand)