diff options
Diffstat (limited to 'poker/rank')
-rw-r--r-- | poker/rank/__init__.py | 0 | ||||
-rw-r--r-- | poker/rank/descriptions.py | 0 | ||||
-rw-r--r-- | poker/rank/hands.py | 110 |
3 files changed, 110 insertions, 0 deletions
diff --git a/poker/rank/__init__.py b/poker/rank/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/poker/rank/__init__.py diff --git a/poker/rank/descriptions.py b/poker/rank/descriptions.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/poker/rank/descriptions.py diff --git a/poker/rank/hands.py b/poker/rank/hands.py new file mode 100644 index 0000000..8764ce9 --- /dev/null +++ b/poker/rank/hands.py @@ -0,0 +1,110 @@ +from poker.constants import Rank, Value +from poker.models import Card, Hand + + +def _is_flush(cards: list[Card]) -> bool: + """Calculate whether hand is a flush.""" + return len(set(card.suit for card in cards)) == 1 + + +def _is_straight(cards: list[Card]) -> bool: + """Calculate whether hand is a straight.""" + card_values = sorted([card.value for card in cards]) + + # If card values are not unique it is not a straight + if len(set(card_values)) != len(card_values): + return False + + # Check for a ace low straight + if card_values == [Value.TWO, Value.THREE, Value.FOUR, Value.FIVE, Value.ACE]: + return True + + # If all card differences are 1 it is a straight + diffs = [second - first for first, second in zip(card_values[:-1], card_values[1:])] + if set(diffs) == {1}: + return True + + return False + + +def is_royal_flush(hand: Hand) -> bool: + """Calculate whether hand a royal flush.""" + if not _is_flush(hand.cards): + return False + + values = [card.value for card in hand.cards] + return all( + value in values + for value in [Value.ACE, Value.KING, Value.QUEEN, Value.JACK, Value.TEN] + ) + + +def is_straight_flush(hand: Hand) -> bool: + """Calculate whether hand a straight flush.""" + return _is_straight(hand.cards) and _is_flush(hand.cards) + + +def is_four_of_a_kind(hand: Hand) -> bool: + """Calculate whether hand has four of a kind.""" + return hand.value_counts[0][1] == 4 + + +def is_full_house(hand: Hand) -> bool: + """Calculate whether hand is a full house.""" + return hand.value_counts[0][1] == 3 and hand.value_counts[1][1] == 2 + + +def is_flush(hand: Hand) -> bool: + """Calculate whether hand is a flush.""" + return _is_flush(hand.cards) and not _is_straight(hand.cards) + + +def is_straight(hand: Hand) -> bool: + """Calculate whether hand is a flush.""" + return _is_straight(hand.cards) and not _is_flush(hand.cards) + + +def is_three_of_a_kind(hand: Hand) -> bool: + """Calculate whether hand is a full house.""" + return ( + hand.value_counts[0][1] == 3 + and hand.value_counts[1][1] == 1 + and hand.value_counts[2][1] == 1 + ) + + +def is_two_pair(hand: Hand) -> bool: + """Calculate whether hand is a two pair.""" + return hand.value_counts[0][1] == 2 and hand.value_counts[1][1] == 2 + + +def is_pair(hand: Hand) -> bool: + """Calculate whether hand is a pair.""" + return hand.value_counts[0][1] == 2 and hand.value_counts[1][1] == 1 + + +def get_rank(hand: Hand) -> Rank: + """Get hand rank. + + TODO: Use a factory pattern to avoid this huge flow control. + """ + if is_royal_flush(hand): + return Rank.ROYAL_FLUSH + elif is_straight_flush(hand): + return Rank.STRAIGHT_FLUSH + elif is_four_of_a_kind(hand): + return Rank.FOUR_OF_A_KIND + elif is_full_house(hand): + return Rank.FULL_HOUSE + elif is_flush(hand): + return Rank.FLUSH + elif is_straight(hand): + return Rank.STRAIGHT + elif is_three_of_a_kind(hand): + return Rank.THREE_OF_A_KIND + elif is_two_pair(hand): + return Rank.TWO_PAIR + elif is_pair(hand): + return Rank.PAIR + else: + return Rank.HIGH_CARD |