4
\$\begingroup\$

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 < 17:
 chand.append(deck[position])
 del deck[position]
 cscore = sum(chand)
 if 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 < 17:
 chand.append(deck[position])
 del deck[position]
 cscore = sum(chand)
 else:
 if 11 in chand:
 chand.remove(11)
 chand.append(1)
 cscore = sum(chand)
 if 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" + str(chand))
 print("Your hand was" + str(hand))
 if cbust == "y":
 '''determines if computer/player won, adds/subtracts tokens, add/subtract point to score'''
 if bust == "y":
 print(c.yellow + "It's a tie!" + c.base01)
 print("P",wins,": C",cwins)
 else:
 print(c.green + "You won!" + c.b01)
 wins += 1
 print("P",wins,": C",cwins)
 tokens += bet.cBet
 print("You now have",tokens," tokens")
 elif bust == "y":
 if cbust == "y":
 print(c.yellow + "It's a tie!" + c.b01)
 print("P",wins,": C",cwins)
 else:
 print(c.red + "You lost."+ c.b01)
 cwins += 1
 print("P",wins,": C",cwins)
 tokens -= bet.cBet
 print("You now have",tokens," tokens")
 else:
 if cscore > score:
 print(c.red + "You lost."+ c.b01)
 cwins += 1
 print("P",wins,": C",cwins)
 tokens -= bet.cBet
 print("You now have",tokens," tokens")
 elif cscore < score:
 print(c.green + "You won!" + c.b01)
 wins += 1
 print("P",wins,": C",cwins)
 tokens += bet.cBet
 print("You now have",tokens," tokens")
 else:
 print(c.yellow + "It's a tie!" + c.b01)
 print("P",wins,": C",cwins)
def playAgain():
 again = input("Would you like to play again?(y/n) > ")
 if again == "y":
 print(c.cl)
 else:
 exit()
def newcard():
 score = sum(hand)
 while score <= 21:
 userCard = input(c.b01 + "Would you like a new card?(y/n) > ").lower()
 if userCard == "y":
 hand.append(deck[position])
 score = sum(hand)
 del deck[position]
 print(c.cl + "Your hand contains " + str(hand) + " for a total of",score,"points.")
 else:
 break
 else:
 if 11 in hand:
 '''changes 11 in hand to 1'''
 hand.remove(11)
 print(c.cl + "Your ace valued 11 was changed into a 1")
 hand.append(1)
 score = sum(hand)
 print("You now have"+str(hand)+"and",score,"Points")
 if score < 21:
 '''go to beginning or has computer play depending'''
 newcard()
 else:
 pass
def shuffleAndStart():
 print(c.cl)
 random.shuffle(deck)
 hand.append(deck[position]) 
 del deck[position]
 hand.append(deck[position]) 
 del deck[position]
 score = sum(hand)
 '''shuffles the deck, deals the player 1 card''' 
 print("Your hand contains: "+ str(hand) + " for a total of", score,"points.")
if __name__ == '__main__':
 try:
 print(c.cl + c.b01 + "Welcome to Blackjack V.Alpha.1.2! Created by Peter")
 print("You have 500 tokens.")
 tokens = 500
 hardDifficulty = input("Would you like an" + c.red + " intelligent computer"+c.b01+ "(y/n) > ").lower()
 if hardDifficulty == "y":
 mode = True
 else:
 mode = False
 deck = [2,3,4,5,6,7,8,9,10,10,10,10,11] * 4
 hand = []
 chand = []
 cwins = 0
 wins = 0
 position = 0
 #Which card to pull out of deck
 while True:
 bet()
 shuffleAndStart()
 newcard()
 computerPlay()
 winOrLose()
 playAgain()
 del hand[:]
 del chand[:]
 deck = [2,3,4,5,6,7,8,9,10,10,10,10,11] * 4
 except KeyboardInterrupt:
 print(c.cl + "Thanks for playing!\n" + c.x)
 exit()
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Jan 17, 2016 at 16:54
\$\endgroup\$
3
  • \$\begingroup\$ Welcome to Code Review! If you're at all unclear how global works I actually just wrote a post about it here. I'll try get you a review today, but the short answer is that making your code more object oriented will cut down on globals. \$\endgroup\$ Commented Jan 18, 2016 at 10:47
  • \$\begingroup\$ Thanks! I haven't even heard of classes until now. \$\endgroup\$ Commented Jan 18, 2016 at 14:49
  • \$\begingroup\$ Actually after reading your code more closely, I think classes aren't strictly necessary. I instead tried to explain more about how functions' scope works. Looking up more on classes would be a good idea though, as they're very helpful for managing scope and organising code. \$\endgroup\$ Commented Jan 18, 2016 at 16:01

1 Answer 1

3
\$\begingroup\$

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.

answered Jan 18, 2016 at 15:54
\$\endgroup\$
1
  • \$\begingroup\$ Great, thanks! I'm still a little confused about return and the stuff in () of the function. Please look at the top of my code. \$\endgroup\$ Commented Jan 18, 2016 at 22:26

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.