The Puzzling Quirks of Regular Expressions

  1. Acknowledgments
  2. Rights of (Wo)Man
  3. Credits
  4. Preface
  5. Quantifiers and Special Sub-Patterns
    1. Wildcard Scope
    2. Words and Sequences
    3. Endpoint Classes
    4. A Configuration Format
    5. The Human Genome
  6. Pitfalls and Sand in the Gears
    1. Catastrophic Backtracking
    2. Playing Dominoes
    3. Advanced Dominoes
    4. Sensor Art
  7. Creating Functions using Regexen
    1. Reimplementing str.count()
    2. Reimplementing str.count() (stricter)
    3. Finding a Name for a Function
    4. Playing Poker (Part 1)
    5. Playing Poker (Part 2)
    6. Playing Poker (Part 3)
    7. Playing Poker (Part 4)
    8. Playing Poker (Part 5)
  8. Easy, Difficult, and Impossible Tasks
    1. Identifying Equal Counts
    2. Matching Before Duplicate Words
    3. Testing an IPv4 Address
    4. Matching a Numeric Sequence
    5. Matching the Fibonacci Sequence
    6. Matching the Prime Numbers
    7. Matching Relative Prime Numbers

Support the author!
Lulu Editions
Paypal Donation
Other Publications

Playing Poker (Part 4)

Keeping in mind that we need only minimally identify each type of hand within the corresponding function, not rule out other higher ranked hands, we can take several different approaches to poker regexen.

Recall our possible hands:

Four-of-a-kind we did in the last puzzle, so now we want to deal with a full house. Write a function, using regular expressions as much as possible, to identify a hand that contains a full house.

Before you turn the page…

You might risk identifying the “dead man’s hand.”

One approach you might take for this puzzle is to identify both is_three_of_kind() and is_pair() in the same hand. Every full house will match those functions. However, in many of the obvious implementations of those support functions, the two initial cards that make up a triple would trigger is_pair() even if the last two cards are unmatched. There are ways to make that work, but let’s instead do it directly.

For this solution we use regular expressions to strip the suits, and also to match the actual pattern. We can utilize the cardsort() function, from Part 1 of the poker puzzles, to guarantee the hand is sorted; we also make sure it is the “pretty” version rather than the ASCII version since sorting assumes that.

The pattern itself is either two of the high number followed by three of the low number, or three of the high number followed by two of the low number. For later use, we can be extra nice by returning the 3-card number first as the “truthy” value in a match. In most poker rules, the 3-card match takes precedence when the same hands are evaluated for the win.

>>> 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 =, hand)
...     if not match:
...         return False
...     elif len( > len(
...         return hand[4] + hand [0]
...     else:
...         return hand[0] + hand[4]
>>> is_full_house(prettify('AS AC 8H 8D 8C'))
>>> is_full_house(prettify('AS AH AC 8D 8C'))
>>> is_full_house(prettify('AS AH JD 8D 8C'))

Obviously, this solution involves a moderate amount of non-regex Python. But the heart of it is the same reduction to number-only we saw with is_four_of_kind() followed by the pattern described. The just-Python part is really only to provide the friendly truthy values, not in asking the predicate itself.