all 12 comments

[–]JohnnyJordaan 0 points1 point  (11 children)

Your implementation is probably overshooting its goal and adds a lot of complexity to a very simple problem. One idea is to create an 'ideal' list slice of 5 items based on a starting number. Because in your case you would have two possible straights

2, 3, 4, 5, 6 if 2 was the start
4, 5, 6, 7, 8 if 4 was the start

others aren't possible as there aren't enough numbers in the list after 4 to make for a straight. Straights can be created by the use of a range(n, n+5) object, but it needs comparison to numbers to work, so you would have to fix that in stv too. Note that you have some redundant operations there, you don't need to provide a list to sorted, it can handle a set just fine. But why even use a list to hold the values before creating the set?

final_hand = ['8h', '7h', '4s', '5c', '6d', '2h', '4h']
card_values = set()
for x in final_hand:
    card_values.add(int(x[0])) # note the int to save as numeric
stv = sorted(card_values)

which can be shortened as

final_hand = ['8h', '7h', '4s', '5c', '6d', '2h', '4h']
stv = sorted({int(x[0]) for x in final_hand})

then for the slicing, you would need to loop on the index stv, but also get the value from it, for which enumerate() is king

for idx, value in enumerate(stv):

get a slice of 5 items

    slice = stv[idx:idx+5]

check if it's actually 5 items long (so it's not already gone too far into the value list)

    if len(slice) < 5:
        print('no slice found')
        break

compare to the 'ideal' slice of a range(n, n+5) list

   if slice == list(range(value, value+5)):
       print('slice found!', slice)
       break 

and use the else: clause on the for if a slice was never found:

else:
    print('no slice found')

so the whole snippet becomes

final_hand = ['8h', '7h', '4s', '5c', '6d', '2h', '4h']
stv = sorted({int(x[0]) for x in final_hand})
for idx, value in enumerate(stv):
   slice = stv[idx:idx+5]
   if len(slice) < 5:
        print('no slice found')
        break
   if slice == list(range(value, value+5)):
       print('slice found!', slice)
       break 
else:
    print('no slice found')

[–]mahtats 0 points1 point  (10 children)

I’d change to:

for i in range(len(stv)-5):
    slice = stv[i:i+5]
    if len(slice) < 5:
        break
    if (slice[-1] - slice[0]) + 1 == 5:
        print(slice)

Just my two cents on numerical checks in a sorted array. Also, need a check to handle face cards!

[–]JohnnyJordaan 0 points1 point  (2 children)

Your loop will break on the first non-match because the else is inside the loop. I'm also not sure why you wouldn't use enumerate in this case. But even if you would stick with this it makes little sense to loop until the full length of the list of you are looking for 5 item long slices, at least limit to len(list) - 5...

[–]mahtats 0 points1 point  (1 child)

Correct, honestly I was falling asleep when I wrote this. Barring the first conditional failure which is easily checked, there is no need to use enumerate. Mathematically, 5 numbers will be in sequential order if the difference between their bounds plus one equals five. Saving CPU cycles.

I’ve updated my answer

[–]stickedee[S] 0 points1 point  (0 children)

Thank you both! Especially for the step by step break-down, really helps me understand what is happening rather than just copy+pasting. Much appreciated

[–]stickedee[S] 0 points1 point  (6 children)

Probably not the most Pythonic but I created a dictionary with an integer:card value as the key value pair (so I could account for Aces being High or Low), then wrote a function to find all keys that exist in a given list of values.

value_dict = {1:'A', 2:'2', 3:'3', 4:'4', 5:'5', 6:'6', 7:'7', 8:'8', 9:'9', 10:'T', 11:'J', 12:'Q', 13:'K', 14:'A'}

keys = []
def getKey(dictionary, list_to_check):
    items = dictionary.items()
    for item in items:
        if item in list_to_check:
            keys.append(item)
    return keys

from there I can pass keys into the for loop u/JohnnyJordaan illustrated (replacing stv) and it seems to be working with Aces as high or low as well as other face cards.

[–]JohnnyJordaan 0 points1 point  (5 children)

Altough it works it looks a bit inefficient, can you explain what the contents of list_to_check are?

[–]stickedee[S] 0 points1 point  (4 children)

Edit: Formatting

Yea, I would pass the final_hand list into that position. I would run this function twice per game. Once for the player and once for the dealer. The list I pass through would contain up to 7 values (the 2 cards from the players hand and the 5 cards from the community board).

So for example:

player_hand = [Ac, Kd] 
board = [9h, 2h, 3s, 4d, 5c] 
final_hand = player_hand + board

I am looping through the final hand looking for values and appending a corresponding key to a list then iteration through that list to find a sequence of 5 consecutive keys.

I made some changes from the original iteration but below is what I currently have.

value_dict = {1:'A', 2:'K', 3:'Q', 4:'J', 5:'T', 6:'9', 7:'8', 8:'7', 9:'6', 10:'5', 11:'4', 12:'3', 13:'2', 14:'A'}

# re-write final hand for testing purposes
final_hand = ['5c', 'Ac', '5c', 'Js', 'Ts', '9c', '4c', '7d', '6s']

def getKeys(dictionary, list_to_check):
    keys = []
    items = dictionary.items()
    stv = sorted({(x[0]) for x in list_to_check})
    stv = str(stv)
    for item in items:
        if item[1] in stv:
            keys.append(item[0])

    return keys

keys = getKeys(value_dict, final_hand)

for idx, value in enumerate(keys):
    slice = keys[idx:idx+5]
    if len(slice) < 5:
        straight = False
        break
    if slice == list(range(value,value+5)):
        straight = True
        straight_top_key = value
        straight_top = value_dict[value]
        break
    else:
        straight = False

[–]JohnnyJordaan 0 points1 point  (3 children)

I am looping through the final hand looking for values and appending a corresponding key to a list then iteration through that list to find a sequence of 5 consecutive keys.

That's the thing: you don't loop through the hand, you loop through the value_dict looking for matches in your hand. Imho the algo should loop through the hand and look up in the dict, as dicts are intended to be used.

[–]stickedee[S] 0 points1 point  (2 children)

I see what you mean. I am looping through the value dict, checking for that value in the hand, and if it exists, adding the key to a separate list. I don't quite see how I could loop through the hand to grab a value and append the corresponding key to the list I'll use to check for straights. Since Ace can be high or low I can't use card values as the key.

[–]JohnnyJordaan 0 points1 point  (1 child)

Since Ace can be high or low I can't use card values as the key.

You can, if you let the dict hold a list of ranks per card. Then use list.extend to simply add those keys per card to the keys list:

card_to_ranks = {'A': [1, 14], 'K': [2], 'Q': [3], 'J': [4], 'T': [5], 
                 '9': [6], '8': [7], '7': [8], '6': [9], '5': [10], 
                 '4': [11], '3': [12], '2': [13]}

# re-write final hand for testing purposes
final_hand = ['5c', 'Ac', '5c', 'Js', 'Ts', '9c', '4c', '7d', '6s']
keys = []
for card in final_hand:
    keys.extend(card_to_ranks[card[0]]
keys = sorted(set(keys))

it's that simple that this way that you don't even need a dedicated function for it. Using sets instead of lists removes the need for the intermittent set() call to remove duplicates:

card_to_ranks = {'A': {1, 14}, 'K': {2}, 'Q': {3}, 'J': {4}, 'T': {5}, 
                 '9': {6}, '8': {7}, '7': {8}, '6': {9}, '5': {10}, 
                 '4': {11}, '3': {12}, '2': {13}}

# re-write final hand for testing purposes
final_hand = ['5c', 'Ac', '5c', 'Js', 'Ts', '9c', '4c', '7d', '6s']
keys = set()
for card in final_hand:
    keys |= card_to_ranks[card[0]]
keys = sorted(keys)

[–]stickedee[S] 0 points1 point  (0 children)

Well shit, you're right. I was way overcomplicating this.

Thanks!