aboutsummaryrefslogtreecommitdiff
path: root/poker/rank
diff options
context:
space:
mode:
authorPaul Harrison <paul@harrison.sh>2022-11-18 21:00:37 +0000
committerPaul Harrison <paul@harrison.sh>2022-12-15 16:02:14 +0000
commitd924e39608861362e08c5e3706ff46a7a1af919b (patch)
tree93a3db318a4f46fed2a545afea8474f7193d7308 /poker/rank
parent5393e7b79939f7da36d55570af3374cef2db2d51 (diff)
feat: Get hand rank
Method to get rank of a given hand.
Diffstat (limited to 'poker/rank')
-rw-r--r--poker/rank/__init__.py0
-rw-r--r--poker/rank/descriptions.py0
-rw-r--r--poker/rank/hands.py110
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