A full house is the poker pattern that looks almost too easy to recognize: three cards of one rank and two of another. Yet “find a three-of-a-kind and a pair” can misclassify a hand, because two cards inside the triple also look like a pair. The reliable question is whether the five ranks reduce to exactly two adjacent runs of lengths three and two. section 4.19 section 4.20
Normalize before matching. Sort the hand in descending rank order, discard suits, then inspect only the rank string. A valid result has either a high pair followed by a low triple, or a high triple followed by a low pair. This direct pattern avoids accidentally reusing cards across separate predicates. section 4.20 section 4.10
A practical full-house predicate has three jobs:
- Convert typed suit letters to the preferred card representation when needed, then sort the cards. section 4.20
- Strip everything except ranks, leaving a five-character sequence such as KK666 or AAA88. section 4.20
- Match two rank groups whose lengths are two and three, and return false for every other arrangement. section 4.20
The tempting shortcut
Consider K♠ K♥ 4♠ 4♥ 4♣. It is a full house: two Ks and three 4s. A check that tests whether either of the first two extracted ranks occurs three times fails, since both initial ranks are K. The condition has the right arithmetic but samples the wrong positions. Checking the last rank instead repairs that particular sorted-style example; matching the complete two-run pattern is clearer about the actual requirement. section 4.21
Classification order matters when predicates overlap. A full house is also, in a loose containment sense, a three-of-a-kind and a pair; four-of-a-kind similarly contains weaker-looking patterns. Run hand tests from strongest to weakest and accept the first successful classification, rather than treating every true helper predicate as the hand’s final identity. section 4.16 section 4.10
>>> def is_full_house(hand):
... try:
... hand = prettify(hand)
... except:
... pass # Already pretty
... hand = cardsort(hand)
... hand = re.sub(r'[^AKQJT98765432]', '', hand)
... # Either three of suit then two of other, or
... # Two of suit then three of other
... pat = r"^((.)\2{1,2})((.)\4{1,2})$"
... match = re.search(pat, hand)
... if not match:
... return False
... elif len(match.group(3)) > len(match.group(1)):
... return hand[4] + hand [0]
... else:
... return hand[0] + hand[4]
>>> is_full_house(prettify('AS AC 8H 8D 8C'))
'8A'
>>> is_full_house(prettify('AS AH AC 8D 8C'))
'A8'
>>> is_full_house(prettify('AS AH JD 8D 8C'))
False
Inspect a complete predicate that sorts, removes suits, matches the two possible run orders, and returns the triple rank first for later hand comparison. section 4.20
