HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Python Blackjack program

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
programpythonblackjack

Problem

I just finished making a Blackjack program, and I hate my global variables in the program. Is there any way to change the code to not use globals and still work?

```
#!/usr/bin/env python3
import skilstak.colors as c
import random
import time
def bet():
global tokens
bet.cBet = input("How many tokens would you like to bet?"+c.green+" All "+c.b01+ "to go all in > ").lower()
if bet.cBet == "all":
bet.cBet = int(tokens)
if tokens == 0:
print("You lost. You're all out of tokens!")
exit()
elif bet.cBet == str:
exit()
else:
bet.cBet = int(bet.cBet)
if bet.cBet > tokens:
if tokens == 0:
print("You lost. You're all out of tokens!")
exit()
else:
print("You can't bet what you don't have...")
print("Your bet was changed to all your credits, or all in.")
bet.cBet = tokens
time.sleep(2)
return bet.cBet, tokens
def computerPlay():
global mode
cscore = sum(chand)
score = sum(hand)
if mode == True:
while cscore score:
break
else:
if 11 in chand:
chand.remove(11)
chand.append(1)
cscore = sum(chand)
if cscore > 21:
pass
else:
computerPlay()
else:
while cscore 21:
pass
else:
computerPlay()
def winOrLose():
global cwins
global wins
global tokens
cscore = sum(chand)
score = sum(hand)

if cscore > 21:
'''determines if computer orplayer is busted'''
cbust = "y"
if score > 21:
bust = "y"
else:
bust = "n"
else:
cbust = "n"
if score > 21:
bust = "y"
else:
bust = "n"
print(c.cl + "The computer's hand was" +

Solution

The main thing you need to do is refactor and adjust your scoping. You have some quite large functions and reducing them down will make your code less unwieldy. Let's take bet for example. In here, you have:

  • Requesting user input for how much to bet



  • Testing if the user is out of tokens



  • Validating that the user has enough tokens to match their bet



  • Validating that the user entered an integer



This should be rearranged into three functions, where each does one job (though you could simultaneously validate the bet as an int and having enough tokens for it). Here's how I'd rework them:

def get_bet(tokens)
    while True:
        user_bet = input("How many tokens would you like to bet?" + c.green
                         + " All " + c.b01 + "to go all in > ").lower()

        if user_bet == "all":
            return tokens  # betting all tokens
        try:
            bet = int(user_bet)
        except ValueError:
            print("Please enter either 'all' or an integer.")
            continue

        if bet <= tokens:
            return bet
        else:
            print("You don't have that much, you only have: " + str(tokens))


First, using while True means that the user will keep being asked for values until the give an integer that's equal to or below the amount they have. The loop will continue forever until it hits the return bet line, when it exits with the value of their bet.

The try except ValueError lines are to catch if the user enters something like "Banana", which int can't parse and would throw an error. The except part means you prevent the error, instead printing a helpful warning about the user's mistake and then you continue on to the next iteration of the loop, asking for their bet again.

You might have been trying to test this with elif bet.cBet == str, but that doesn't work. If you wanted to test for the bet being a string type, you'd need to use isinstance(bet.cBet, str). If you were testing for an empty string, you can do that with just if bet.cBet:. Empty strings evaluate as False in Python, while strings with characters will be True. Either way, your test wouldn't catch errors from people using letters instead of number characters. In Python, the best way to test is to just try do what you need and catch if an error comes from it.

While we're on this, you used bet.cBet here, but it's totally unnecessary! You're assigning an attribute to a function. This is something you can do in Python but is rarely used like this. You can just use any name, like cBet and it will be scoped locally to the function. That means it will be created in bet, and then destroyed at the end. It does exactly what you need. I know you later used bet.cBet to refer back to this, but this is actually the opposite of what you really need. Instead, you want to use return.

return is what will take a value from the function's scope and return it to where it was called from. You use it, but you haven't quite gotten the point. Instead of using it and then attaching the value to the bet function, you can return it and store it in a name in the global space. Here's how I'd call my function from above:

bet = get_bet(tokens)


tokens is of course the full amount of tokens the user has. It's going the opposite to how return works, it sends the value of tokens into the function so that the bet amount can be validated. Then at the end, the bet amount is returned and now stored in my bet value. No need to worry about connecting values to their functions.

If you try to organise all your code this way, you'll get around the problem of scope and you can stop using global, instead passing and returning values between functions.

Code Snippets

def get_bet(tokens)
    while True:
        user_bet = input("How many tokens would you like to bet?" + c.green
                         + " All " + c.b01 + "to go all in > ").lower()

        if user_bet == "all":
            return tokens  # betting all tokens
        try:
            bet = int(user_bet)
        except ValueError:
            print("Please enter either 'all' or an integer.")
            continue

        if bet <= tokens:
            return bet
        else:
            print("You don't have that much, you only have: " + str(tokens))
bet = get_bet(tokens)

Context

StackExchange Code Review Q#117070, answer score: 3

Revisions (0)

No revisions yet.