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

1from __future__ import annotations 

2 

3import random 

4from dataclasses import dataclass, field 

5 

6from beaverbunch.core.card import Card, Suit 

7 

8 

9@dataclass 

10class Deck: 

11 """Represents a deck of playing cards, which can be shuffled and drawn from. 

12 

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) 

17 

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) 

29 

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) 

34 

35 def shuffle(self) -> Deck: 

36 """Shuffles the deck in place and returns self for chaining.""" 

37 random.shuffle(self.cards) 

38 return self 

39 

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] 

46 

47 def __len__(self) -> int: 

48 """Returns the number of cards left in the deck.""" 

49 return len(self.cards)