Sep-03-2021, 08:38 AM
(This post was last modified: Sep-03-2021, 09:15 AM by deanhystad.)
My ace is high. Is easy to make if low. Tougher to make it both.
But not a lot tougher.
But not a lot tougher.
import random import collections card_suits = ['Clubs', 'Diamonds', 'Hearts', 'Spades'] card_ranks = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'] straights = [set(card_ranks[i:i+5]) for i in range(len(card_ranks)-4)] + [{'A', '2', '3', '4', '5'}] class Grouper(dict): '''Similar to a Counter, but instead of increasing a counter I append to a value list''' def __init__(self, items=[]): super().__init__() for item in items: self[item[0]] = item[1] def __setitem__(self, key, value): group = self.get(key, []) group.append(value) super().__setitem__(key, group) def most_common(self): '''Return items sorted in decreasing order by count''' items = list(super().items()) items.sort(key=lambda item: len(item[1]), reverse=True) return items class Card(): '''A playing card. Has a suite and a rank''' def __init__(self, rank, suit): self.suit = suit self.rank = rank self.value = card_ranks.index(rank) + 2 self.name = f'{rank} {suit}' self.abbr = f'{rank}{suit[0]}' def __eq__(self, other): '''Return True if self has same value as other''' return self.value == other.value def __lt__(self, other): '''Return True if self has lower value than other. Used for sorting''' return self.value < other.value def __hash__(self): '''Use value as the hash value. Allows using sets to remove duplicate ranks. Not sure if this is a good idea as it means Cards cannot be used as keys in dictionaries. ''' return self.value def __repr__(self): '''Return nice string representation for card''' return self.name class Hand(list): '''A list of cards that knows a little bit about poker''' def __init__(self, cards=None): self.cards = [] if cards is None else cards.copy() def deal(self, deck, count): '''Deal count cards from deck into hand''' for _ in range(count): self.cards.append(deck.pop()) return self def __getitem__(self, index): '''Return card[index]''' return self.cards[index] def append(self, card): '''Append card to self.cards''' self.cards.append(card) def __len__(self): '''Return number of cards in hand''' return len(self.cards) def __add__(self, other): '''Make a new hand containing all cards from self and other''' return Hand(self.cards + other.cards) def __repr__(self): '''Make a pretty string rep of a hand''' return f"{{{', '.join([card.abbr for card in self.cards])}}}" def group_by_rank(self): """Return list of cards grouped by rank. Groups are sorted by count in decreasing order""" return Grouper([(card.rank, card) for card in self.cards]).most_common() def group_by_suit(self): """Return list of cards grouped by suit. Groups are sorted by count in decreasing order""" return Grouper([(card.suit, card) for card in self.cards]).most_common() def full_house(self): '''Return full-house if hand contains 3 of one rank and 2 of another''' ranks = self.group_by_rank() if len(ranks[0][1]) >= 3 and len(ranks[1][1]) >= 2: return ranks[0][1] + ranks[1][1] return None def two_pair(self): '''Return pairs if there are two pairs of matching ranks''' ranks = self.group_by_rank() if len(ranks[0][1]) >= 2 and len(ranks[1][1]) >= 2: return ranks[0][1] + ranks[1][1] return None def royal_flush(self): "Return cards if 10 through Ace if all in same suit" flush = self.straight_flush() if flush and flush[-1].rank == 'A': return flush return None def straight_flush(self): '''Return matching cards if straight is also a flush''' flush = self.flush() if flush: return Hand(flush).straight() return None def flush(self): '''Return matching cards if 5 or more cards have same suit''' suits = self.group_by_suit() if len(suits[0][1]) >= 5: return suits[0][1] return False def straight(self): '''Return matching cards if hand is superset of a straight set''' cards = set(self.cards) ranks = set([card.rank for card in cards]) for straight in straights: if straight & ranks == straight: return [card for card in cards if card.rank in straight] return None results = {} def print_result(key, hand): print(key, hand) results[key] = results.get(key, 0) + 1 for _ in range(1000): deck = [Card(r, s) for s in card_suits for r in card_ranks] random.shuffle(deck) table = Hand().deal(deck, 5) hand = Hand().deal(deck, 2) both = table + hand of_a_kind = both.group_by_rank() if cards := both.royal_flush(): print_result('Royal Flush', cards) elif cards := both.straight_flush(): print_result('Straight Flush', cards) elif len(of_a_kind[0][1]) > 3: print_result('Four of a kind', of_a_kind[0][1]) elif cards := both.full_house(): print_result('Full House', cards) elif cards := both.flush(): print_result('Flush', cards) elif cards := both.straight(): print_result('Straight', cards) elif len(of_a_kind[0][1]) > 2: print_result('Three of a kind', of_a_kind[0][1]) print(results)