Search code examples
pythonsequenceplaying-cards

How to determine a straight (sequence) in a poker hand in Python


In python I have a score tracker of a poker hand.

Possible card ranks:

A23456789TJQK

Straights of 3 or more cards score 1 per card. Example straight of 4 cards gives you 4 points.

So I have a score:

score = 0

A sorted list of strings (so I don't have to worry about cards at the end of the list having a straight with the beginning) representing the card ranks of my particular hand:

L4
['6', '8', '8', '9', 'T']
# Note: 8, 9, T is a straight. So I want to add 3 to score

But how can I determine if I have a straight?

What I've tried so far:

I tried converting the strings into integers, but then what do I do with the T, J, K, Q, and A? They aren't numbers.

Using strings I get an error:

for i in range(len(L4) - 1):
   if(L4[i] is L4[i + 1] + 1):
      sC = sC + 1
if(sC >= 3):
   score += sC


L4
['6', '8', '8', '9', 'T']
Traceback (most recent call last):
  File "myFile.py", line 66, in <module>
    if(L4[i] is L4[i + 1] + 1):
TypeError: must be str, not int

Should I convert them to ints or should I try another way? Thanks for help in advance.


Solution

  • You can find all substrings of the current hand, and filter the results to find those that are sorted with an increment of 1:

    def stringify_result(f):
      def wrapper(_d):
        cards = {10:'T', 11:'J', 12:'Q', 13:'K'}
        return list(map(lambda x:cards.get(x, str(x)), f(_d)))
      return wrapper
    
    @stringify_result
    def has_straight(d):
      cards = {'J': 11, 'K': 13, 'T': 10, 'Q': 12}
      subs = list(filter(None, [d[b:i] for i in range(len(d)+1) for b in range(len(d)+1)]))
      possibilities = list(filter(lambda x:all(x[i+1] - x[i] == 1 for i in range(len(x)-1)), [[int(cards.get(b, b)) for b in i] for i in subs]))
      return [] if not possibilities else max(possibilities, key=len)
    
    straight = has_straight(['6', '8', '8', '9', 'T'])
    score = len(straight)
    

    Output:

    ['8', '9', 'T']
    3
    

    Edit: to account for multiple runs, you can use itertools.groupby:

    import itertools
    def has_straight(d):
       cards = {'J': 11, 'K': 13, 'T': 10, 'Q': 12}
       mutated_cards = list(map(lambda x:int(cards.get(x, x)), d))
       _grouped = [list(b) for _, b in itertools.groupby(mutated_cards)]
       subs = list(filter(None, [_grouped[b:i] for i in range(len(_grouped)+1) for b in range(len(_grouped)+1)]))
       final_filtered = list(filter(lambda x:all(x[i+1][0] - x[i][0] == 1 for i in range(len(x)-1)), subs))
       new_subs = [] if not final_filtered else max(final_filtered, key=lambda x:sum(len(i) for i in x))
       return sum(len(i) for i in new_subs if len(i) > 1), list(map(lambda x:x[0], new_subs))
    
    print(has_straight(['6', '8', '8', '9', 'T']))
    print(has_straight(['4', '4', '5', '5', '6']))
    

    Output:

    (2, [8, 9, 10])
    (4, [4, 5, 6])