8
\$\begingroup\$

I've been screwing around and adding in new features for a few days(aces high/low, sayings for the dealer, etc.). However I'm fairly inexperienced and I'm wondering if any of my code can be replaced with preexisting functions or the like.

import random
from random import shuffle

count = 0

def deck(money):
    print('You sit at the dealer\'s table. You have $' + str(money) + '.')
    card_values = ['Ace High', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'Jack', 'Queen', 'King']
    suits = ['Spades', 'Clubs' ,'Hearts', 'Diamond']
    deck = []
    for i in range(0, 4):
        for n in range(0, len(card_values)):
            card = [card_values[n],suits[i]]
            deck.append(card)
    shuffle(deck)
    start_deal(deck,[],0,[],0,money)


def print_game_status(deck, player_hand, player_count, dealer_hand, dealer_count, bet, money):

    print("------------------------------------")
    print("Your hand: {0}".format(player_hand))
    print("Your count: {0}".format(player_count))
    print("Your bet: ${0}".format(bet))
    print("Your money: ${0}".format(money))
    print("Dealer's hand: {0}".format(dealer_hand))
    print("Dealer's count: {0}".format(dealer_count))
    print("Number of cards in deck: {0}".format(len(deck)))
    print("------------------------------------")



def start_deal(deck, player_hand, player_count, dealer_hand, dealer_count, money):
    global count
    count += 1
    if count >= 500:
        print('Your kids miss you')

    bet = int(input('Dealer : So what\'re ya bringing to the table?\n'))
    while bet > money:
        bet = int(input('Dealer : You can\'t bet what you don\'t have kid.\n'))
    money -= bet
    for i in range(0,2):
        results = dealing(deck, player_hand, player_count)
        deck = results[0]
        player_hand = results[1]
        player_count = results [2]

        results = dealing(deck, dealer_hand, dealer_count)
        deck = results[0]
        dealer_hand = results[1]
        dealer_count = results [2]

    if dealer_count == 21:
        print_game_status(deck, player_hand, player_count, dealer_hand, dealer_count, bet, money)
        if player_count == 21:
            end_game(player_count, dealer_count, 2, money, bet)
        else:
            end_game(player_count, dealer_count, 1, money, bet)
    if  player_count == 21:
        print_game_status(deck, player_hand, player_count, dealer_hand, dealer_count, bet, money)
        end_game(player_count, dealer_count, 3, money, bet)
    else:
        player_turn(deck, player_hand, player_count, dealer_hand, dealer_count, True, money, bet)

def dealing(deck, hand, count):
    if deck[0][0] in [2, 3, 4, 5, 6, 7, 8, 9, 10]:
            count += deck[0][0]
    elif deck[0][0] == 'Ace High':
        if count > 10:
            count += 1
            deck[0][0] = 'Ace Low'
        else:
            count += 11
    else:
        count += 10
    hand.append(deck[0])
    del deck[0]
    return(deck, hand, count)


def player_turn(deck, player_hand, player_count, dealer_hand, dealer_count, intro, money, bet):
    print_game_status(deck, player_hand, player_count, dealer_hand, dealer_count, bet, money)
    if intro == True:
        sayings = ['So, hit or stand?', 'Whatcha feelin? hit or stand?','Ouch, if I were you, I\'d stand.',
                   'If I had those cards I\'d hit.','Feeling lucky enough to hit? Or you gonna stand.', 'Get better mate, take a hit.']    
        choice = input('Dealer : ' + sayings[random.randint(0,len(sayings) - 1)] + '\nYour choice: ')
    else:
        choice = input('Your choice: ')

    if choice == 'hit':
        results = dealing(deck, player_hand, player_count)
        deck = results[0]
        player_hand = results[1]
        player_count = results [2]


        if player_count <= 21:
            player_turn(deck, player_hand, player_count, dealer_hand, dealer_count, True, money, bet)
        else:
            for i in range(0,len(player_hand)):
                if player_hand[i][0] == 'Ace High':
                    player_hand[i][0] = 'Ace Low'
                    player_count = player_count - 10
                    player_turn(deck, player_hand, player_count, dealer_hand, dealer_count, True, money, bet)
            print_game_status(deck, player_hand, player_count, dealer_hand, dealer_count, bet, money)
            end_game(player_count, dealer_count, 0, money, bet)

    elif choice == 'stand':
        dealer_turn(deck, player_hand, player_count, dealer_hand, dealer_count, money, bet)

    else:
        retorts = ['You\'re slurring your words a lot there, too much to drink?', 'Didn\'t catch that',
                   'What?', 'Did you just sneeze?', 'You studying pig latin, or did I miss hear you?']
        print('Dealer : ' + retorts[random.randint(0,len(retorts) - 1)] + '\n')
        player_turn(deck, player_hand, player_count, dealer_hand, dealer_count, False, money, bet)


def dealer_turn(deck, player_hand, player_count, dealer_hand, dealer_count, money, bet):

    magic_num = random.randint(10,17)

    if dealer_count >= magic_num:
        print_game_status(deck, player_hand, player_count, dealer_hand, dealer_count, bet, money)

    while dealer_count < magic_num or player_count > dealer_count:
        results = dealing(deck, dealer_hand, dealer_count)
        deck = results[0]
        dealer_hand = results[1]
        dealer_count = results [2]
        print_game_status(deck, player_hand, player_count, dealer_hand, dealer_count, bet, money)

    end_game(player_count, dealer_count, 0, money, bet)


def end_game(player_count, dealer_count, db, money, bet):
    if db == 1:
        print('Dealer blackjack, table busts.')
        bet = 0
    elif db == 2:
        print('Dealer and Player blackjack. Push.')
    elif db == 3:
        print('Natural blackjack, you win!')
        bet = bet*2
    elif player_count > 21:
        print('You lost!')
        bet = 0
    elif dealer_count > 21:
        print('Dealer busts, you won!')
        bet = bet*2
    elif player_count == dealer_count:
        print('It\'s a tie!')
    elif dealer_count < player_count:
        print('You won!')
        bet = bet*2
    else:
        print('You lost!')
        bet = 0
    money += bet
    print('You now have $' + str(money) + '.')
    another = input('Dealer : Another round?\n')
    if another == 'yes':
        if money > 0:
            deck(money)
        else:
            print('Dealer: Look\'s like you\'re a bit low on cash there son.\nGame Over')
    else:
        print('You left the table with $' + str(money) + '.')



# Had fun.
\$\endgroup\$
2
  • \$\begingroup\$ Welcome to codereview! I hope you get some good answers \$\endgroup\$ Feb 22, 2018 at 9:24
  • 3
    \$\begingroup\$ I like the quirky messages that the dealer says. ;P \$\endgroup\$ Feb 22, 2018 at 12:50

1 Answer 1

4
\$\begingroup\$

Diclaimer: the comments in this answer will be in a fairly random order. Also, because of the random AND interactive aspects of your code, my comments are not very well tested.

Iterable unpacking

The dealing functions return multiple elements. You always store these in a variable, then access each member individually using the [index] syntax. A more concise way to do so is to use iterable unpacking and write something like:

    deck, player_hand, player_count = dealing(deck, player_hand, player_count)
    deck, dealer_hand, dealer_count = dealing(deck, dealer_hand, dealer_count)

This also apply inside the dealing function that could be written:

def dealing(deck, hand, count):
    card_value, card_suit = deck[0]
    if card_value in [2, 3, 4, 5, 6, 7, 8, 9, 10]:
            count += card_value
    elif card_value == 'Ace High':
        if count > 10:
            count += 1
            card_value = 'Ace Low'
        else:
            count += 11
    else:
        count += 10
    hand.append([card_value, card_suit])
    del deck[0]
    return(deck, hand, count)

Using the right function/method

In dealing, you process the first card of the deck then remove it from the deck. You could do this in a single step using my_list.pop(0).

You'd write something like:

def dealing(deck, hand, count):
    card_value, card_suit = deck.pop(0)

In player_turn (and other places), you use a combination of [indexing], randint and len to get a random element from a list. This is a problem common enough to have a clean solution: random.choice(my_list).

Help your user

Whenever you ask your user some input, it may help to give him/her the list of options and when possible, the default option.

For instance:

another = input('Dealer : Another round? (\'yes\' to continue, anything else to stop)\n')

or

if intro == True:
    sayings = ['So, hit or stand?', 'Whatcha feelin? hit or stand?','Ouch, if I were you, I\'d stand.',
               'If I had those cards I\'d hit.','Feeling lucky enough to hit? Or you gonna stand.', 'Get better mate, take a hit.']
    prompt = 'Dealer : ' + random.choice(sayings) + '\n'
else:
    prompt = ''

choice = input(prompt + 'Your choice: (\'hit\' or \'stand\') ')

(I took this chance to factorize out the common part)

Comparison to literal boolean

When you know that a value is a boolean, if value == True is equivalent to the shorter and more elegant if value. In your case, this if intro == True becomes if intro.

Recursion and user input

In player_turn, an invalid user input is handled by calling the same function again and again. This has different kinds of drawbacks. From a readibility point of view, you could have more straight-forward solution. From a technical point of view, every invalid input leads to another level of function calls. It isn't such a big deal until: you reach the maximum depth (then your program will crash), you want to debug your program by reading the stacktrace (then it is unusable because of the amount of irrelevant and similar looking lines).

A much clearer solution is to use a while loop to keep asking the user until he/she provides a valid answer.

   choices = ['hit', 'stand']
    while True:
        choice = input(prompt + 'Your choice: (\'hit\' or \'stand\') ')
        if choice not in choices:
            retorts = ['You\'re slurring your words a lot there, too much to drink?', 'Didn\'t catch that',
                       'What?', 'Did you just sneeze?', 'You studying pig latin, or did I miss hear you?']
            print('Dealer : ' + random.choice(retorts) + '\n')
        else:
            break

    if choice == 'hit':
        etc
    else:   # 'stand'
        etc

This also removes the need for an intro argument to know whether this is the required input is asked for the first time or not.

def player_turn(deck, player_hand, player_count, dealer_hand, dealer_count, money, bet):
    print_game_status(deck, player_hand, player_count, dealer_hand, dealer_count, bet, money)
    sayings = ['So, hit or stand?', 'Whatcha feelin? hit or stand?','Ouch, if I were you, I\'d stand.',
               'If I had those cards I\'d hit.','Feeling lucky enough to hit? Or you gonna stand.', 'Get better mate, take a hit.']
    prompt = 'Dealer : ' + random.choice(sayings) + '\n'

    choices = ['hit', 'stand']
    while True:
        choice = input(prompt + 'Your choice: (\'hit\' or \'stand\') ')
        if choice not in choices:
            retorts = ['You\'re slurring your words a lot there, too much to drink?', 'Didn\'t catch that',
                       'What?', 'Did you just sneeze?', 'You studying pig latin, or did I miss hear you?']
            print('Dealer : ' + random.choice(retorts) + '\n')
            prompt = ""
        else:
            break

The same kind of issue appears with start_deal calling end_game which itself calls start_deal. Also, because of all these recursive calls (direct or indirect), you have hidden problems in your current control flow. For instance start_deal may end up calling directly end_game more than once. This leads to a situation where the user wants to stop playing, yet another game is launched.

Getting rid of the recursive with more simple solution like loops is an interesting exercice (actually, more than an exercise, this is definitly the best way your code could be improved).

Return or update - you have to choose (and document it)

At the moment, the dealing takes (deck, hand, count) as input, updates them (or not) and return them. It makes the function weird to use: as a user, am I suppose to rely on the fact that data have been updated properly or should I rely on the data returned. The good thing is that it seems like you've chosen to rely on the data and be consistent on this.

You code could be made more concise but maybe not clearer by stating clearly (in a function docstring for instance) that the input is updated and that the return value is only the new count (because integers are not mutable).

Another way of doing things could be to say that the dealing function does not need a deck as a parameter, only a card and a head. Then, you'd write something like:

player_hand, player_count = dealing(deck.pop(0), player_hand, player_count)

It looks a bit like half a solution but it is not so bad...

Another more interesting way to handle this could be to take a more object-oriented approach and define classes (or a single class) to represent the multiple entities you work with and method to use them. For instance, a straightforward solution could be to use the values you keep forwarding from one function to another (deck, player_hand, player_count, dealer_hand, dealer_count, bet, money) as members of your class.

Data type

At the moment, cards are represented with a list containing a value and a suit. Even though this is technically correct, I'd rather use a tuple for this. I highly suggest reading Ned Batchelder's article about lists and tuples.

Loop like a native

In Python, you usually don't need to get elements from an object by index. What you want is usually to iterate over the different elements. I highly recommand reading/watching Ned Batchelder talk called "Loop Like A Native". (At this stage, I can confess that from my point of view, any article from Ned Batchelder, about Python or not, is worth reading, more than once).

In your case, the deck functions becomes:

VALUES = ['Ace High', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'Jack', 'Queen', 'King']
SUITS = ['Spades', 'Clubs' ,'Hearts', 'Diamond']

def deck(money):
    print('You sit at the dealer\'s table. You have $' + str(money) + '.')
    deck = []
    for s in SUITS:
        for v in VALUES:
            deck.append((v, s))

Also, this can be rewritten in a simple way using list comprehension:

def deck(money):
    print('You sit at the dealer\'s table. You have $' + str(money) + '.')
    deck = [(val, suit) for suit in SUITS for val in VALUES]
    shuffle(deck)
    start_deal(deck,[],0,[],0,money)

This could also apply in player_turn.

Leaving it here

I've given many comment. Many more comments could be given but before going too far, I reckon it's best you think about the few comments I've left for you to take into account, regarding how to reorganise the logic, with or without an OOP approach. I guess trying to explain the rules of the game to yourself could help you untangle the pretty complicated nested functions calls.

At this stage, the code looks like:

import random
from random import shuffle

count = 0

VALUES = ['Ace High', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'Jack', 'Queen', 'King']
SUITS = ['Spades', 'Clubs' ,'Hearts', 'Diamond']

def deck(money):
    print('You sit at the dealer\'s table. You have $' + str(money) + '.')
    deck = [(val, suit) for suit in SUITS for val in VALUES]
    shuffle(deck)
    start_deal(deck,[],0,[],0,money)


def print_game_status(deck, player_hand, player_count, dealer_hand, dealer_count, bet, money):

    print("------------------------------------")
    print("Your hand: {0}".format(player_hand))
    print("Your count: {0}".format(player_count))
    print("Your bet: ${0}".format(bet))
    print("Your money: ${0}".format(money))
    print("Dealer's hand: {0}".format(dealer_hand))
    print("Dealer's count: {0}".format(dealer_count))
    print("Number of cards in deck: {0}".format(len(deck)))
    print("------------------------------------")



def start_deal(deck, player_hand, player_count, dealer_hand, dealer_count, money):
    global count
    count += 1
    if count >= 500:
        print('Your kids miss you')

    bet = int(input('Dealer : So what\'re ya bringing to the table?\n'))
    while bet > money:
        bet = int(input('Dealer : You can\'t bet what you don\'t have kid.\n'))
    money -= bet
    for i in range(0,2):
        deck, player_hand, player_count = dealing(deck, player_hand, player_count)
        deck, dealer_hand, dealer_count = dealing(deck, dealer_hand, dealer_count)

    if dealer_count == 21:
        print_game_status(deck, player_hand, player_count, dealer_hand, dealer_count, bet, money)
        if player_count == 21:
            end_game(player_count, dealer_count, 2, money, bet)
        else:
            end_game(player_count, dealer_count, 1, money, bet)
    if  player_count == 21:
        print_game_status(deck, player_hand, player_count, dealer_hand, dealer_count, bet, money)
        end_game(player_count, dealer_count, 3, money, bet)
    else:
        player_turn(deck, player_hand, player_count, dealer_hand, dealer_count, money, bet)

def dealing(deck, hand, count):
    card_value, card_suit = deck.pop(0)
    if card_value in [2, 3, 4, 5, 6, 7, 8, 9, 10]:
            count += card_value
    elif card_value == 'Ace High':
        if count > 10:
            count += 1
            card_value = 'Ace Low'
        else:
            count += 11
    else:
        count += 10
    hand.append([card_value, card_suit])
    return(deck, hand, count)


def player_turn(deck, player_hand, player_count, dealer_hand, dealer_count, money, bet):
    print_game_status(deck, player_hand, player_count, dealer_hand, dealer_count, bet, money)
    sayings = ['So, hit or stand?', 'Whatcha feelin? hit or stand?','Ouch, if I were you, I\'d stand.',
               'If I had those cards I\'d hit.','Feeling lucky enough to hit? Or you gonna stand.', 'Get better mate, take a hit.']
    prompt = 'Dealer : ' + random.choice(sayings) + '\n'

    choices = ['hit', 'stand']
    while True: 
        choice = input(prompt + 'Your choice: (\'hit\' or \'stand\') ')
        if choice not in choices:
            retorts = ['You\'re slurring your words a lot there, too much to drink?', 'Didn\'t catch that',
                       'What?', 'Did you just sneeze?', 'You studying pig latin, or did I miss hear you?']
            print('Dealer : ' + random.choice(retorts) + '\n')
            prompt = ""
        else:
            break

    if choice == 'hit':
        deck, player_hand, player_count = dealing(deck, player_hand, player_count)

        if player_count <= 21:
            player_turn(deck, player_hand, player_count, dealer_hand, dealer_count, money, bet)
        else:
            for i in range(0,len(player_hand)):
                if player_hand[i][0] == 'Ace High':
                    player_hand[i][0] = 'Ace Low'
                    player_count = player_count - 10
                    player_turn(deck, player_hand, player_count, dealer_hand, dealer_count, money, bet)
            print_game_status(deck, player_hand, player_count, dealer_hand, dealer_count, bet, money)
            end_game(player_count, dealer_count, 0, money, bet)
    else:
        dealer_turn(deck, player_hand, player_count, dealer_hand, dealer_count, money, bet)


def dealer_turn(deck, player_hand, player_count, dealer_hand, dealer_count, money, bet):

    magic_num = random.randint(10,17)

    if dealer_count >= magic_num:
        print_game_status(deck, player_hand, player_count, dealer_hand, dealer_count, bet, money)

    while dealer_count < magic_num or player_count > dealer_count:
        deck, dealer_hand, dealer_count = dealing(deck, dealer_hand, dealer_count)
        print_game_status(deck, player_hand, player_count, dealer_hand, dealer_count, bet, money)

    end_game(player_count, dealer_count, 0, money, bet)


def end_game(player_count, dealer_count, db, money, bet):
    if db == 1:
        print('Dealer blackjack, table busts.')
        bet = 0
    elif db == 2:
        print('Dealer and Player blackjack. Push.')
    elif db == 3:
        print('Natural blackjack, you win!')
        bet = bet*2
    elif player_count > 21:
        print('You lost!')
        bet = 0
    elif dealer_count > 21:
        print('Dealer busts, you won!')
        bet = bet*2
    elif player_count == dealer_count:
        print('It\'s a tie!')
    elif dealer_count < player_count:
        print('You won!')
        bet = bet*2
    else:
        print('You lost!')
        bet = 0
    money += bet
    print('You now have $' + str(money) + '.')
    another = input('Dealer : Another round? (\'yes\' to continue, anything else to stop)\n')
    if another == 'yes':
        if money > 0:
            deck(money)
        else:
            print('Dealer: Look\'s like you\'re a bit low on cash there son.\nGame Over')
    else:
        print('You left the table with $' + str(money) + '.')


deck(400)

# Had fun.
\$\endgroup\$
1
  • \$\begingroup\$ deck = [(val, suit) for suit in SUITS for val in VALUES] could even be changed to list(itertools.product(SUITS, VALUES)) \$\endgroup\$ Feb 23, 2018 at 1:15

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.