Coverage for src / beaverbunch / core / deck.py: 100.0%
26 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-05 20:45 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-05 20:45 +0000
1from __future__ import annotations
3import random
4from dataclasses import dataclass, field
6from beaverbunch.core.card import Card, Suit
9@dataclass
10class Deck:
11 """Represents a deck of playing cards, which can be shuffled and drawn from.
13 Attributes:
14 cards: A list of Card objects currently in the deck. The top of the deck is the end of the list (cards[-1]).
15 """
16 cards: list[Card] = field(default_factory=list)
18 @classmethod
19 def create_new(cls, include_jokers: bool = True) -> Deck:
20 """Creates a standard 52-card deck."""
21 cards = [
22 Card(suit=suit, value=value)
23 for suit in Suit
24 for value in range(1, 14)
25 ]
26 if include_jokers:
27 cards += [Card(suit=None, value=0), Card(suit=None, value=0)]
28 return cls(cards=cards)
30 @classmethod
31 def create_custom(cls, cards: list[Card]) -> "Deck":
32 """Creates a deck from a custom list of cards."""
33 return cls(cards=cards)
35 def shuffle(self) -> Deck:
36 """Shuffles the deck in place and returns self for chaining."""
37 random.shuffle(self.cards)
38 return self
40 def draw(self, n: int = 1) -> Card | list[Card]:
41 """Draws a card from the top of the deck. Raises an error if the deck is empty."""
42 if not self.cards:
43 raise IndexError("Cannot draw from an empty deck")
44 drawn_cards = [self.cards.pop() for _ in range(n)]
45 return drawn_cards if n > 1 else drawn_cards[0]
47 def __len__(self) -> int:
48 """Returns the number of cards left in the deck."""
49 return len(self.cards)