Katkat

Compare Complete and Partial Poker Hands

RipplingSoftware Engineer · Technical Screen

Last updated

Implement a poker hand evaluator that determines whether an incomplete poker hand can be completed from the remaining deck to beat an opponent's complete five-card hand.

Problem Statement

You are given two poker hands drawn from a standard 52-card deck without replacement:

  • complete_hand: A list of exactly 5 cards representing your opponent's revealed hand.
  • partial_hand: A list of 3 or 4 cards representing your incomplete hand.

A card is represented as a two-character string: the first character is the rank ('2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A') and the second character is the suit ('C', 'D', 'H', 'S').

Rank order from lowest to highest: 2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 < T < J < Q < K < A.

Hand categories from lowest to highest:

  1. High Card: Highest rank card.
  2. One Pair: Two cards of the same rank.
  3. Two Pair: Two different pairs.
  4. Three of a Kind: Three cards of the same rank.
  5. Straight: Five cards of sequential rank (Ace can be high T-J-Q-K-A or low A-2-3-4-5).
  6. Flush: Five cards of the same suit.
  7. Full House: Three of a kind and a pair.
  8. Four of a Kind: Four cards of the same rank.
  9. Straight Flush: Five cards of sequential rank and the same suit.

When comparing two 5-card hands, compare their category first. If categories match, break ties by comparing key card ranks (e.g., higher pair rank first), followed by kickers in descending order. In this problem, suit does not break ties (equal hands result in a tie).

Return True if there exists at least one combination of cards from the remaining unseen cards in the deck that can complete partial_hand to 5 cards such that it strictly beats complete_hand. Return False otherwise.

Function Signature

def can_beat(complete_hand: list[str], partial_hand: list[str]) -> bool:
    pass

Examples

Example 1

complete_hand = ["9C", "9D", "4S", "7H", "2D"]  # One Pair of 9s
partial_hand = ["AS", "KS", "QS", "JS"]        # 4 cards to a Royal Flush

can_beat(complete_hand, partial_hand) # Returns: True

Explanation: Adding "TS" completes a Straight Flush (Royal Flush), which beats One Pair.

Example 2

complete_hand = ["AS", "AC", "AD", "AH", "KS"]  # Four of a Kind (Aces)
partial_hand = ["2C", "3D", "4H", "5S"]        # Open-ended straight draw

can_beat(complete_hand, partial_hand) # Returns: False

Explanation: Even if completed into a straight (with a 6 or Ace), the best category achievable cannot beat Four of a Kind.

Constraints

  • complete_hand contains exactly 5 unique valid cards.
  • partial_hand contains 3 or 4 unique valid cards.
  • There are no duplicate cards across both hands.
  • Cards are drawn from a standard 52-card deck.

Hints

Hint 1

Modularize your code by writing a helper function that maps any 5-card hand into a comparable tuple: `(category_score, tie_breaker_ranks...)`.

Hint 2

Generate the pool of available cards by subtracting cards in both `complete_hand` and `partial_hand` from the full 52-card deck.

Hint 3

Since `partial_hand` has 3 or 4 cards, there are at most 44 choose 2 (946) combinations to check, making a direct search feasible.

Follow-up questions

  • How would you optimize the search if the partial hand only contained 1 or 2 cards (up to 160,000+ combinations)?
  • How would you modify the function to return the exact probability (equity) of winning rather than just a boolean?
  • How would your evaluation logic change if the game allowed community cards shared between players (e.g., Texas Hold'em)?