# Identify valid 8 card poker straight

Write an improved code to identify a valid straight of eight cards, e.g. 4H,5S,AC,7C,8H,AH,0S,JC. Note that the card combination may include Wilds (as we see in our example, with the Ace of Clubs standing in for a Six and the Ace of Hearts standing in for a Nine), but must include at least two "natural" cards (i.e. non-Wild cards). Note also that the sequence of the cards is significant for this group type, and that 4H,5S,AC,8C,7H,AH,AS,AC, e.g., is not a valid straight of eight, as it is not in sequence. Aces are the only wild cards and must represent another card. Duplicate cards are allowed.

Card inputs are in a list of 2 value strings with the value:

• 2 to 9 - for 2 to 9
• 0 - for 10
• J - for Jack
• Q - for Queen
• K - for King
• A - for Ace

The suits are shown with values S(spade), C(club), H(heart), D(diamond)

1. Example input to be checked: 4H,5S,AC,7C,8H,AH,0S,JC
Output: True
2. Example input to be checked: AH,5S,AC,7C,AH,AH,AS,AC
Output: True
3. Example input to be checked: 4H,5S,AC,8C,7H,AH,AS,AC
Output: False

Here is my code:

group = input().split(",")
if len(group) == 8:
#check for ace
ace = [0 for card in group if card == 'A']
run_vals = {"0": 10, "J": 11, "Q": 12, "K":13}
if len(ace) < 6:
miss_count = 0
for index in range(len(group)-1):
if group[index]  == "A":
miss_count += 1
if group[index+1]  == "A" and index == len(group)-2:
miss_count += 1
continue
else:
continue

if group[index+1]  == "A" and index == len(group)-2:
miss_count += 1
continue
if group[index+1]  == "A" and index <len(group)-2:
continue

if group[index+1] in run_vals:
num1 = run_vals[group[index+1]]
else:
num1 = int(group[index+1])

if group[index] in run_vals:
num2 = run_vals[group[index]]
else:
num2 = int(group[index])

if (num1 - num2) != 1:
miss_count += 1

if miss_count == len(ace):
print(True)
else:
print(False)

elif len(ace) == 6:
ace_in_between = 0
range_limits = [card for card in group if card != "A"]
for index in range(len(group)):
if index > group.index(range_limits) and index< group.index(range_limits):
ace_in_between += 1
range_limits = [run_vals[val] if val in run_vals else val for val in range_limits]
if ace_in_between + 1 == (int(range_limits) - int(range_limits) ):
print(True)
else:
print(False)
else:
print(False)
else:
print(False)

• Can an ace represent a “low ace”? How about a “high ace”? As in, is “ace, 2, 3 … 8” a valid straight? How about “7, 8, … king, ace”? May 22 at 15:40
• ace only represents other cards, not an ace itself. The highest possible straight card is the King of any suit and the lowest possible straight card is the 2 of any suit. May 22 at 15:55
– Mast
May 25 at 7:55

Good news! There's lots of room for improvement in your code! (How's that for a positive spin?)

# Counting Aces

    ace = [0 for card in group if card == 'A']


This code creates a list (ace), which only contains 0 elements equal in number to the number of aces in group. The contents of the list is never used, only the length. Finally, the length of the list is queried twice.

That is a very heavy implementation for a simple count of the number of aces:

    aces = sum(card == 'A' for card in group)


Or slightly less obliquely:

    aces = sum(1 for card in group if card == 'A')


# Decoding Cards

    run_vals = {"0": 10, "J": 11, "Q": 12, "K":13}
...
if group[index+1] in run_vals:
num1 = run_vals[group[index+1]]
else:
num1 = int(group[index+1])

if group[index] in run_vals:
num2 = run_vals[group[index]]
else:
num2 = int(group[index])
...
range_limits = [run_vals[val] if val in run_vals else val for val in range_limits]
if ace_in_between + 1 == (int(range_limits) - int(range_limits) ):


I'm seeing a lot of in run_vals tests and int(...) casts. Imagine how much cleaner your code could look if you simply filled in all of the run_vals?

    run_vals = {"A": 1,
"2": 2, "3": 3, "4": 4, "5": 5,
"6": 6, "7": 7, "8": 8, "9": 9,
"0": 10, "J": 11, "Q": 12, "K":13}


I've added "A" mapping to 1 just for completeness. It would allow you to test all(card in run_vals for card in group) to check that all the cards have a valid rank, such that nobody sneaks in a "1 of Spades" for example.

For that matter, you could check for valid suits too, such as all(card in {'C', 'D', 'H', 'S'} for card in group), to avoid someone sneaking in the "7 of Pentacles".

# A Straight

The problem would be dead simple if there was no wild cards. You could write:

    rank = [run_vals[card] for card in group]
start = rank
for idx in range(len(rank)):
if rank[idx] != start + idx:
return False
return True


Supporting wild cards would be easy too, if we knew the first card was not an ace. We could just add an exception for the aces:

    for idx in range(len(rank)):
if rank[idx] != start + idx and rank[idx] != 1:
return False


# Starting Value

What happens if the ace is the first card? Well, the second card (assume it was not an ace) would dictate the start was one rank lower. Or if that was an ace, then the third card would, and so on:

    for idx in range(len(rank)):
if rank[idx] == 1:
start = rank[idx] - idx
break
else:
return False     # Only aces exist!


# Functions!

Your code is one complex script without any functions. If you haven't learned how to write them yet, learn. If you have learned, use them!

# Reworked Code

The following code is a reimplementation of some of the above ideas. However, the loops like for idx in len(range(list)) have been replaced with more Pythonic for idx, value in enumerate(list) loops. Loops which return a False if one iteration produces a False result have been replaced with an all(...) statement with a generator expression. It also demonstrates some type-hints, """docstrings""" (including the use of (doctest) to help write code which is more understandable to others, and contain built-in unit tests.

Another improvement is the problem statement states there must be at least 2 "natural" cards. Instead of testing for the number of aces is less than or equal to 6 -- a number which appears nowhere in the problem statement -- it directly tests against the given value 2.

RANK = dict(zip("A234567890JQK", range(1, 14)))
WILD = RANK['A']

def straight_of_eight(card_string: str) -> bool:
"""
Determine if a string of cards is an ascending straight,
with at least 2 natural cards.

>>> straight_of_eight("4H,5S,AC,7C,8H,AH,0S,JC")
True

>>> straight_of_eight("4H,5S,AC,8H,7C,AH,0S,JC")
False
"""

ranks = [RANK[card] for card in card_string.split(',')]

if len(ranks) != 8:
return False   # Were not given exactly 8 cards

if sum(rank != WILD for rank in ranks) < 2:
return False   # Not enough natural cards

# Determine starting rank for the straight
start = next(rank - idx for idx, rank in enumerate(ranks) if rank != WILD)

return 2 <= start <= 6 and all(rank == idx or rank == WILD
for idx, rank in enumerate(ranks, start))

if __name__ == '__main__':
import doctest
doctest.testmod()

group = input()
print(straight_of_eight(group))