Logic, Data Structures
In this challenge, you’ll need to write code to evaluate the strength of a poker hand.
Poker is played with a standard 52-card deck, made up of cards of thirteen “ranks” (2, 3, 4, 5, 6, 7, 8, 9, 10, Jack, Queen, King, Ace) and four suits (Clubs, Diamonds, Hearts, Spades; these are written here as “c”, “d”, “h”, “s”). There are no wild cards or jokers in the version of Poker we’ll use here.
The card ranks tell the strength of a card: a Jack is stronger than a 10. Aces are typically the strongest rank, but we’ll see there’s one special case where an Ace can be the weakest card. The suits have nothing to do with the strength of a card: the Ks is the exact same strength as the Kh.
A poker hand is made up of exactly five cards. The order of the cards in the hand does not matter (players can rearrange the cards in their hands).
The strength of a hard is found is described here (strongest combinations to weakest):
Name |
Description |
Examples |
---|---|---|
Straight Flush |
Cards are in order (straight) and are all same suit (flush). When comparing two straight flushes, card rank break ties. See note below on straights. |
|
Four of a Kind |
Four cards of the same rank. When comparing two four-of-kinds, break ties using rank of the four-of-kind then using other cards. |
|
Full House |
Three cards of one rank and two cards of a second rank (both three-of-kind and one pair). When comparing two full house hands, compare first using the three-of-kind rank, breaking tie with pair rank. |
|
Flush |
All cards have the same suit. When comparing two flush hands, use rank. |
|
Straight |
Card ranks in order. When comparing two straights, use ranks. Aces can be high (as normal, coming after king) or low (coming before 2). It cannot “wrap around” (Q-K-A-2-3 isn’t a straight). An Ace played low is rank as 1 for breaking ties using card ranks. |
|
Three of a Kind |
Three cards of same rank. When comparing two three-of-kind hands, use rank of three-of-kind, then ranks of other cards to break ties. |
|
Two Pair |
Two pairs of cards of same rank. When comparing two two-pair hands, compare higher-ranking pairs first, then lower-ranking pairs, then remaining card. |
|
Pair |
One pair of cards of same rank. When comparing two one-pair hands, compare pair ranks, then remaining cards. |
|
High Card |
No combinations listed above. When comparing two hands like this, use ranks of cards. |
|
(The examples for each combination in order of strongest-to-weakest).
We’ve given you a Card class to represent a playing card:
class Card(object):
"""Playing card."""
def __init__(self, name):
"""Create a card.
rank: 2-10 or 11=J, 12=Q, 13=K, 14=A
suit: h, d, c, s
We create cards by name::
>>> ks = Card("Ks")
>>> ks.rank
13
>>> ks.suit
's'
>>> ks.name
'Ks'
Other examples::
>>> ac = Card("Ac")
>>> ac.rank
14
>>> td = Card("10d")
>>> td.rank
10
"""
rank = name[0:-1] # "10" is 2 chars
suit = name[-1]
assert rank in RANK_NAME_TO_RANK, "Bad rank: %s" % name
assert suit in "hdcs", "Bad suit: %s" % name
self.name = name
self.rank = RANK_NAME_TO_RANK.get(rank)
self.suit = suit
def __str__(self):
"""Public print representation of a card."""
return self.name
def __repr__(self):
"""Debugging representation of a card."""
return "<Card %s>" % self.name
We’ve also given you a Hand class with a lot of existing code:
class Hand(object):
"""Hand of poker cards."""
def __init__(self, cards):
"""Add cards to hand.
>>> h1 = Hand([Card("As"),
... Card("Ks"),
... Card("Qs"),
... Card("Js"),
... Card("10s")])
>>> h1.cards
[<Card As>, <Card Ks>, <Card Qs>, <Card Js>, <Card 10s>]
As a convenience, you can list the card names instead and
this will turn them into Card objects before adding them::
>>> h2 = Hand("As Ks Qs Js 10s")
>>> h2.cards
[<Card As>, <Card Ks>, <Card Qs>, <Card Js>, <Card 10s>]
"""
if type(cards) is str:
cards = cards.split()
self.cards = [c if type(c) is Card else Card(c) for c in cards]
assert len(self.cards) == 5, "Hands must have 5 cards."
def __repr__(self):
"""Display hand.
To make testing easier, we'll sort hand before displaying it.
There's no formal order for suits, so we'll just use
alphabetical order for display purposes:
>>> h2 = Hand("8d 7s 7h 7c 7d")
>>> h2
<Hand 8d 7c 7d 7h 7s>
"""
hand = sorted(self.cards,
key=lambda c: (14 - c.rank, c.suit))
return "<Hand %s>" % (" ".join(str(c) for c in hand))
def eval(self):
"""Evaluate the value of a hand."""
def __eq__(self, other):
"""Are these two hands equal?
>>> h1 = Hand("2d 3d 4d 5d 6d")
>>> h2 = Hand("6d 5d 4d 3d 2d")
>>> h1 == h2
True
"""
return self.eval() == other.eval()
def __ne__(self, other):
"""Are these two hands not equal?
>>> h1 = Hand("2d 3d 4d 5d 6d")
>>> h2 = Hand("6d 5d 4d 3d 2d")
>>> h1 != h2
False
"""
return self.eval() != other.eval()
def __lt__(self, other):
"""Is this hand lower-ranked than the other hand?
>>> h1 = Hand("2d 3d 4d 5d 6d")
>>> h2 = Hand("3d 4d 5d 6d 7d")
>>> h1 < h2
True
"""
return self.eval() < other.eval()
def __le__(self, other):
"""Is this hand lower-ranked than the other hand?
>>> h1 = Hand("2d 3d 4d 5d 6d")
>>> h2 = Hand("3d 4d 5d 6d 7d")
>>> h1 <= h2
True
"""
return self.eval() <= other.eval()
A hand contains 5 cards, and this has an __init__ method to take those cards at the time of instantiation (note that, to be helpful, you can either supply the cards as a list of Card objects or as simple string of card names, like “Qs Kd 9c 8c 7c”).
It contains an eval method, which is unimplemented. This method should output the “strength” of a hand. (How this is done is up to you: it could be a number, a string, a tuple, etc).
It then contains several special methods (__eq__, __ne__, __lt__,
__le__) which handle comparisons of hands. These methods are used
when you use the ==
, !=
, <
, and <=
operators to compare
hands). Having these defines allows us to do things like:
>>> king_high_straight == another_king_high_straight
True
>>> king_high_straight != another_king_high_straight
False
>>> queen_high_straight < king_high_straight
True
Of course, since these special methods rely on the eval method returning something useful, they don’t yet really work.
Implement the eval method. This is a tricky challenge.
This method could return a number of the strength of a hand (so, an ace-high straight flush might return 1,000,000, whereas a king-high straight flush might return 999,999). You’d have to figure out how to map the combinations to numbers.
It might be easier to return a tuple, though: imagine if you we only wanted to compare flushes and straights. A flush is higher than a straight, so any flush beats a straight. We could return a tuple like:
(is_a_flush, rank_of_straight)
And these hands would become:
Js 9s 6s 4s 2s → (1, 0) # is flush Ac Kd Qh Js 10d → (0, 14) # ace-high straight Kd Qh Js 10d 9d → (0, 13) # king-high straight
When Python compares or sorts tuples (or lists), it first compares the first item and, if they’re equal, compares the second item, and so on. You could use this feature to make a data structure with enough information about the combinations that comparing this structure could compare the strengths of the hands.
Don’t forget about the tie-breaking rules (listed in the table above). So, for two hands that both contain the same two pair, make sure your eval method emits something higher for the first of these:
>>> h1 = Hand("6d 6s 2d 2s Ac")
>>> h2 = Hand("6h 6c 2h 2c Kc")
That way, we can compare them correctly:
>>> h1 == h2
False
>>> h1 > h2
True
Take your time and think carefully. Good luck!