Implementation of the deck collection type.
Project description
Deck
Deck is an implementation of the deck
collection type, commonly confused with collections.deque
.
>>> from deck import Deck
>>> d = Deck()
>>> d.shuffle()
>>> d.deal()
Card(<Suit.Diamonds: '♦'>, <Value.Two: 2>)
>>> d.deal()
Card(<Suit.Diamonds: '♦'>, <Value.Three: 3>)
>>> d.deal()
Card(<Suit.Hearts: '♥'>, <Value.Ten: 10>)
>>> d.deal()
Card(<Suit.Diamonds: '♦'>, <Value.Nine: 9>)
Deck supports cheating, if that's how you want to play.
>>> d.deal_from_bottom()
Card(<Suit.Spades: '♠'>, <Value.Five: 5>)
Importing the deck
module also globally corrects other typographical errors that may occur in your code.
>>> import deck
>>> from collections import deck
>>> deck
<class 'deck.Deck'>
Taking this module too seriously would be a mistake.
API
Despite its origins as a bit of a joke, this module does provide some useful building blocks for card games.
Deck
The Deck
class is, in fact, a subclass of deque
, and has default contents of 54 Card
instances representing a standard deck of cards, including jokers. To omit jokers, pass include_jokers=False
when initialising.
>>> from deck import Deck
>>> deck = Deck(include_jokers=False)
>>> if not any(card.joker for card in deck):
... print("No jokers here!")
...
No jokers here!
Decks may be shuffled using the shuffle
method, which optionally takes a random
parameter to override the default random
module.
>>> from random import Random
>>> r = Random(100)
>>> deck.shuffle(r)
>>> deck.deal()
Card(<Suit.Hearts: '♥'>, <Value.Ten: 10>)
The standard pop
and popleft
methods are aliased as deal
and deal_from_bottom
. All other deque
methods are available, however no validation is performed to stop you adding whatever you like into the deck.
>>> deck.appendleft("not a card")
>>> deck.deal_from_bottom()
'not a card'
The Hand
object is a convenient way to handle collections of cards, and the Deck.deal_hands
method is the easiest way to create them.
>>> deck.deal_hands(hands=2, cards=2)
[
<Hand([
Card(<Suit.Clubs: '♣'>, <Value.Four: 4>),
Card(<Suit.Hearts: '♥'>, <Value.Queen: 12>)
])>,
<Hand([
Card(<Suit.Diamonds: '♦'>, <Value.Queen: 12>),
Card(<Suit.Diamonds: '♦'>, <Value.Seven: 7>)
])>
]
Hand
A Hand
object is actually just a list
containing cards, and all traditional list
methods are available. Some additional methods are provided for ease of use as a hand of cards, and some are overridden for similar reasons.
Hands can be directly instantiated just like a list. No type checking is performed at this stage, however, you can call the check_contents
method explicitly to verify that all elements are Card objects.
>>> card1 = deck.deal()
>>> cards = [deck.deal(), deck.deal(), deck.deal()]
>>> hand = Hand([card1, *cards])
>>> hand.check_contents()
>>> hand.append('not a card')
>>> hand.check_contents()
ValueError: invalid objects in hand: 'not a card'
The index
and count
methods are overridden to allow finding cards with either a card object, a value, a suit, or a string or int that represents one of these. Similarly, the in
operator will also accept these values.
If a card instance is provided, the default comparison method (see below) is used to search. By default, this is an exact match, but can be changed to only match suit or values.
>>> deck = Deck()
>>> deck.shuffle(random=Random(103))
>>> hand = deck.deal_hands(hands=1, cards=10)[0]
>>> 9 in hand
True
>>> "diamonds" in hand
True
>>> hand.index(Card(Suit.Diamonds, Value.Nine))
4
>>> hand.index(Suit.Diamonds)
0
>>> hand.index("diamonds")
0
>>> hand.index(Value.Nine)
4
>>> hand.count("♦")
4
>>> hand.count(nine_diamonds)
1
>>> nine_diamonds = Card(Suit.Diamonds, Value.Nine)
>>> hand.append(nine_diamonds)
>>> hand.count(nine_diamonds)
2
intersect
and union
methods are added to allow combining two hands (more precisely, a Hand with an iterable of Cards). They return lazy iterables that will yield the new set of cards, potentially reordered from the original hand.
Intersection returns all cards from the original hand (self
) that match cards in the comparator (other
).
Union returns all cards from the original hand (self
), and also those from the other that do not match cards in the original.
Whether cards are considered to match depends on the cmp
argument (a value from the HandComparison
enum) or the current default comparison. Note that these operations are sensitive to the order of arguments, and should be considered
The bitwise operators |
, |=
and &
, &=
are likewise enhanced for these operations, and result in a new Hand
instance or an in-place update. The default comparison method is always used.
>>> hand1 = Hand([Card(Suit.Diamonds, Value.Nine), Card(Suit.Spades, Value.Queen)])
>>> hand2 = Hand([Card(Suit.Diamonds, Value.Nine), Card(Suit.Hearts, Value.Queen)])
>>> hand1.intersect(hand2)
<iterator object at 0x...>
>>> str(Hand(hand1.intersect(hand2)))
'9♦ '
>>> str(Hand(hand1.union(hand2)))
'Q♠ Q♥ 9♦ '
>>> str(hand1 | hand2)
'Q♠ Q♥ 9♦ '
>>> with Hand.default_comparison(HandComparison.Values):
... str(hand1 | hand2)
...
'Q♠ 9♦ '
>>> with Hand.default_comparison(HandComparison.Suits):
... str(hand1 & hand2)
...
'9♦ '
Hands may be sorted, either in place or by returning a new iterable. Note that the built-in sorted
function may not behave appropriately, and the sorted
method should be preferred.
Ordering may be selected between HandSort.Default
(cards in numeric, then suit, order), HandSort.AcesHigh
(same but aces are highest, not lowest), or HandSort.Poker
(quads/triples/pairs sort highest, then by numeric and suit order). Suits sort according to bridge rankings.
The default ordering may be overridden for the active context by entering the Hand.default_sort
context manager. This is the only way to affect the sort order used by the 'asc' or 'desc' format specifier.
>>> deck = Deck()
>>> deck.shuffle(random=Random(101))
>>> hand1, hand2 = deck.deal_hands(hands=2, cards=5)
>>> str(hand1)
'Q♣ 9♣ 4♣ 4♦ A♠ '
>>> hand1.sort()
>>> str(hand1)
'A♠ 4♣ 4♦ 9♣ Q♣ '
>>> str(Hand(hand1.sorted(HandSort.Poker, reverse=True)))
'4♦ 4♣ A♠ Q♣ 9♣ '
>>> with Hand.default_sort(HandSort.AcesHigh):
... print(f'{hand1:5.3desc}')
...
A♠ Q♣ 9♣ 4♦ 4♣
get_poker_hand
The get_poker_hand
function takes an iterable of cards and calculates the best available poker hand. If more than five cards are provided, all combinations of five are checked.
The result of the function is a tuple containing first a PokerHand
value, followed by the relevant values of cards in the hand for determining a winner. The number of elements varies, but tuples can always be compared to determine the stronger hand. Stronger hands compare greater than others.
Suits are not taken into account for breaking ties.
>>> from deck import Deck, get_poker_hand
>>> deck = Deck(include_jokers=False)
>>> deck.shuffle()
>>> p1, p2 = deck.deal_hands(hands=2, cards=5)
>>> with Hand.default_sort(HandSort.Poker):
... print(f'Player 1: {p1:>6.5desc}')
... print(f'Player 2: {p2:>6.5desc}')
...
Player 1: 6♦ 6♣ Ace♦ 9♣ 8♦
Player 2: 4♠ 4♦ Que♠ 7♠ 5♠
>>> if get_poker_hand(p1) > get_poker_hand(p2):
... print("Player 1 wins!")
...
Player 1 wins!
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.