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))