from PokerHand import PokerHand
class MyPokerHand(PokerHand):
"""Custom PokerHand subclass for computing hand probabilities."""
def rank_hist(self):
"""Compute a histogram of the ranks (analog to "suit_hist")."""
self.ranks = {}
for card in self.cards:
self.ranks[card.rank] = self.ranks.get(card.rank, 0) + 1
# The "has_*" methods return whether a hand has a
# particular feature, regardless of the presence
# of a better feature. For example, both
# "has_three_of_a_kind" and "has_full_house" will
# return True for a hand with a full house.
#
# Note that "has_*" methods assume that the suit
# and rank histograms have already been computed.
def has_pair(self):
"""Return whether this hand contains a pair."""
for count in self.ranks.values():
if count >= 2:
return True
return False
def has_two_pair(self):
"""Return whether this hand contains two pairs."""
pair_count = 0
for count in self.ranks.values():
if count >= 2:
pair_count += 1
# With hands of more than five cards, there may be
# more than two pairs, so check >= instead of ==.
return pair_count >= 2
def has_three_of_a_kind(self):
"""Return whether this hand contains three of a kind."""
for count in self.ranks.values():
if count >= 3:
return True
return False
def has_straight(self):
"""Return whether this hand contains 5-card straight,
including A-K-Q-J-10."""
# First check for ace-high straight
found = True
if 1 not in self.ranks:
# No ace, can't have ace-high straight
found = False
for rank in range(10, 14):
# Check for 10-J-Q-K
if rank not in self.ranks:
found = False
if found:
return True
# Check for king-high straight and down
for maxRank in range(13, 4, -1):
found = True
for decrement in range(5):
rank = maxRank - decrement
if rank not in self.ranks:
found = False
break
if found:
return True
return False
def has_flush(self):
"""Return whether this hand contains 5-card flush."""
for count in self.suits.values():
if count >= 5:
return True
return False
def has_full_house(self):
"""Return whether this hand contains full house."""
three_count = 0 # number of ranks whose count >= 3
two_count = 0 # number of ranks whose count == 2
for count in self.ranks.values():
if count >= 3:
three_count += 1
elif count >= 2:
two_count += 1
return (three_count >= 2) or (three_count == 1 and two_count > 0)
def has_four_of_a_kind(self):
"""Return whether this hand contains four of a kind."""
for count in self.ranks.values():
if count >= 4:
return True
return False
def has_straight_flush(self):
"""Return whether this hand contains straigh flush."""
# Divide up the hand by suits. If one of the suited
# hands has a straight, it must be a straight flush.
# Note how we reuse existing code rather than duplicating.
suited_hands = [ MyPokerHand(), MyPokerHand(), MyPokerHand(),
MyPokerHand() ]
for card in self.cards:
suited_hands[card.suit].add_card(card)
for hand in suited_hands:
hand.rank_hist()
if hand.has_straight():
return True
return False
def classify(self):
"""Classify a hand by the best feature it contains."""
# Compute histograms used by "has_*" methods
self.rank_hist()
self.suit_hist()
# Check for hands in order from best to worst.
if self.has_straight_flush():
self.label = "straight flush"
elif self.has_four_of_a_kind():
self.label = "four of a kind"
elif self.has_full_house():
self.label = "full house"
elif self.has_flush():
self.label = "flush"
elif self.has_straight():
self.label = "straight"
elif self.has_three_of_a_kind():
self.label = "three of a kind"
elif self.has_two_pair():
self.label = "two pair"
elif self.has_pair():
self.label = "pair"
else:
self.label = "high card"
def collect_stats(hand_size=5, sample_size=1000):
"""Collect feature occurrence histogram and return
as a dictionary whose keys are feature labels and
whose values are counts.."""
# "sample_size" needs to be about 10000 for the probabilities
# to approach those reported in the Wikipedia page.
from Card import Deck
stats = {}
for i in range(sample_size):
deck = Deck()
deck.shuffle()
hand = MyPokerHand()
deck.move_cards(hand, hand_size)
hand.classify()
stats[hand.label] = stats.get(hand.label, 0) + 1
return stats
def print_stats(stats):
"""Print statistics from a collection run."""
labels = [
"straight flush",
"four of a kind",
"full house",
"flush",
"straight",
"three of a kind",
"two pair",
"pair",
"high card",
]
width = max([ len(label) for label in labels ])
format = "%%%ds: %%8.4f" % width
total = float(sum(stats.values()))
for label in labels:
count = stats.get(label, 0)
pct = count / total * 100.0
print(format % (label, pct))
if __name__ == "__main__":
print_stats(collect_stats(sample_size=10000))