Back to AI Coding

Triplet Draw

One way to clear each phase. Compare the approach with your own, then try the patterns yourself.

src/card.py
"""Phase 1: Card equality must include suit, not just rank."""

from enum import Enum


class Suit(Enum):
    SPADES = "S"
    HEARTS = "H"
    DIAMONDS = "D"
    CLUBS = "C"


class Card:
    """A playing card. Rank is 1-13 where 1=Ace, 11=Jack, 12=Queen, 13=King."""

    def __init__(self, rank: int, suit: Suit):
        if rank < 1 or rank > 13:
            raise ValueError(f"rank must be 1..13, got {rank}")
        self._rank = rank
        self._suit = suit

    @property
    def rank(self) -> int:
        return self._rank

    @property
    def suit(self) -> Suit:
        return self._suit

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Card):
            return False
        # Bug fix: a card is identified by both rank AND suit.
        return self._rank == other._rank and self._suit == other._suit

    def __hash__(self) -> int:
        return hash((self._rank, self._suit))

    def __repr__(self) -> str:
        names = {1: "A", 11: "J", 12: "Q", 13: "K"}
        label = names.get(self._rank, str(self._rank))
        return f"{label}{self._suit.value}"
src/hand.py
"""Phase 1: Hand.card_value scoring rules."""

from typing import List
from card import Card


class Hand:
    """A collection of cards with rules for how each card scores."""

    def __init__(self, ace_high: bool = False):
        self._cards: List[Card] = []
        self._ace_high = ace_high

    def add(self, card: Card) -> None:
        self._cards.append(card)

    def size(self) -> int:
        return len(self._cards)

    def get(self, index: int) -> Card:
        return self._cards[index]

    def all_cards(self) -> List[Card]:
        return list(self._cards)

    def card_value(self, index: int) -> int:
        """
        Scoring value for the card at index. Rules:
          - Ace (rank 1): 11 if ace_high else 1
          - Face cards (Jack=11, Queen=12, King=13): all score as 10
          - All others (2-10): score as their rank
        """
        card = self._cards[index]
        rank = card.rank
        if rank == 1:
            # Bug fix: ace_high must actually score the Ace as 11.
            return 11 if self._ace_high else 1
        if rank >= 11:
            # Bug fix: face cards (J/Q/K) all score as 10, not their rank.
            return 10
        return rank

Discuss