pythonblackjack

Check if a certain character of a string is a number


I'm trying to create blackjack using python. I have code that creates a deck of cards:

import random

values = ['2','3','4','5','6','7','8','9','10','Jack','Queen','King','Ace']
suites = ['Hearts', 'Clubs', 'Diamonds', 'Spades']
deck = []
outOfDeck = []
for s in suites:
    for v in values:
        deck.append(v + ' of ' + s)
random.shuffle(deck)

and I'm trying to make code that calculates the total value of your cards. Right now, I have made:

def calcTotal():
    total1 = 0
    total2 = 0
    for card in playerCards:
        if card[1] == '2' or card[1] == '3' or card[1] == '4' or card[1] == '5' or card[1] == '6' or card[1] == '7' or card[1] == '8' or card[1] == '9':
                total1 += int(card[:2])
                total2 += int(card[:2])
        else:
            if card[1] != 'a':
                total1 += 10
                total2 += 10
            else:
                total1 += 1
                total2 += 11
    if total1 == total2:
        return total1
    else:
        return str(total1, total2)

The second total is only used if the player gets an ace, in which two totals are possible since it could equal 1 or 11.

However, this only always returns 20. I know the if statement is sloppy so I wouldn't be surprised if that was the problem. I can tell that since the return is always 20, the code thinks I always have two face cards. Is there a better way to see if the cards are numbers?

Keep in mind I don't want to check if the string is a number, because there are other things in the string. Only the first one or two digits.


Solution

  • Compiling all of my comment suggestions as an answer:

    The current problem you're seeing is caused by you looking at card[1], which is the second character of the string. Remember that in python, indices start at 0 so you need to look at card[0].

    Also remember that string comparison is case-sensitive, so 'a' is not equal to 'A'. You need to check if the first character is 'A' in your check for aces.

    You could simplify your condition to read:

    if card[0] == 'A': 
        # do ace logic
    if card[0] in ('K', 'Q', 'J', '1'):
        total += 10
    else:
        total += int(card[0])
    

    Another problem you'll see soon -- str(total1, total2) will throw an error when you get to it. The str constructor expects the second argument (if any) to be an encoding. If you're trying to return a tuple containing both total1 and total2 as strings, you need to return str(total1), str(total2). I don't think this is what you want though, since a blackjack hand has only one value. If you want to return the lesser of the two, you should return str(min(total1, total2))

    Also also, your calcTotal still won't handle a case with e.g. three aces. The value of a hand with A♣, A♦, A♥ is 13, not 3 or 33. The logic should be: "can I add 11 without going bust? Yes - add 11; No - add 1". That way you only need one total variable. One caveat for this approach is that you need to save the aces to process after you've totalled all other cards.

    Yet another suggestion I have is not to return a string -- return the integer instead, so you can do subsequent calculations on it if you need to. Convert it to a string only when you need to, which is presumably when you're printing it or appending it to another string.

    Finally, remember that global variables make your code harder to read and debug. It's easy enough to pass the variables your function needs as arguments to your function.

    Incorporating all these suggestions, this is how I'd do it:

    def calc_total(player_cards):
        total = 0
        num_aces = 0
        for card in player_cards:
            if card[0] == 'A':
                num_aces += 1 # Keep track of number of aces to process later
            elif card[0] in ('K', 'Q', 'J', '1'):
                total += 10
            else:
                total += int(card[0])
    
        # Now process the aces
        
        # Try to use a decreasing number of aces as 11 
        # If you bust, try with one fewer 11-ace
        # If you don't, that's the score!
        eleven_point_aces = num_aces
        while eleven_point_aces >= 0:
            new_score = total + 11 * eleven_point_aces + (num_aces - eleven_point_aces)
            if new_score < 22:
                return new_score
            else:
                eleven_point_aces -= 1
                
        # Will bust even with all aces given one point, so just go ahead and do that
        return total + num_aces
    

    (Note that I changed the function and variable names to conform to PEP8, the Python style guide)