6
\$\begingroup\$

I made a simple Blackjack game in Python 3.4.3. I'm a beginner, so it would be awesome if you could rip up my code and tell me everything I did wrong. I know there's no insurance for when the dealer has an ace (yet), and there's some other blackjack rules that I'm going to add in the future, but I wanted to get a base code out there.

import random as r
import itertools as i


suit = 'scdh'
rank = '23456789TJQKA'
deck = tuple(''.join(card) for card in i.product(rank, suit))
val = ()
for _ in range(9):
    val = val + (_+2, _+2, _+2, _+2)
    if _ == 8:
        for __ in range(3):
            val = val + (10, 10, 10, 10)
val = val + (1, 1, 1, 1)
deckval = dict(zip(deck, val))


def deal():
    global hand, dealer_hand, player_hand, counter
    hand = r.sample(deck, 52)
    counter = 0
    dealer_hand = list(hand[counter:counter + 1])
    counter += 2
    player_hand = list(hand[counter:counter + 2])
    counter += 2


def sum_player_hand():
    global hand, player_hand, counter, player_sum, opt_player_sum
    player_sum = 0
    opt_player_sum = 0
    for a in range(len(player_hand)):
        if int(deckval[player_hand[a]]) == 1 and opt_player_sum + int(deckval[player_hand[a]]) <= 21:
            opt_player_sum = player_sum + int(deckval[player_hand[a]]) + 10
            player_sum += int(deckval[player_hand[a]])
        elif opt_player_sum > 21:
            player_sum += int(deckval[player_hand[a]])
            opt_player_sum = player_sum
        else:
            player_sum += int(deckval[player_hand[a]])
            opt_player_sum += int(deckval[player_hand[a]])


def dealer_init():
    global hand, dealer_hand, counter, dealer_sum, opt_dealer_sum
    dealer_sum = 0
    opt_dealer_sum = 0
    if int(deckval[dealer_hand[0]]) == 1:
        dealer_sum += int(deckval[dealer_hand[0]])
        opt_dealer_sum += dealer_sum + 10
    else:
        dealer_sum = int(deckval[dealer_hand[0]])
        opt_dealer_sum = int(deckval[dealer_hand[0]])
    dealer_logic()


def dealer_logic():
    global hand, dealer_hand, counter, dealer_sum, opt_dealer_sum
    if dealer_sum >= 17 or opt_dealer_sum >= 17:
        pass
    else:
        while opt_dealer_sum <= 16:
            dealer_sum = 0
            opt_dealer_sum = 0
            dealer_hand = dealer_hand + list(hand[counter:counter + 1])
            counter += 1
            for _ in range(len(dealer_hand)):
                if int(deckval[dealer_hand[_]]) == 1 and (opt_dealer_sum + int(deckval[dealer_hand[_]])) <= 21:
                    opt_dealer_sum += int(deckval[dealer_hand[_]])
                    dealer_sum += int(deckval[dealer_hand[_]])
                else:
                    dealer_sum += int(deckval[dealer_hand[_]])
                    opt_dealer_sum += int(deckval[dealer_hand[_]])


def main():
    global hand, dealer_hand, player_hand, counter, player_sum,     dealer_sum, opt_player_sum, opt_dealer_sum
    sum_player_hand()
    print('\nDealer has:', dealer_hand[0:2], '--')
    if player_sum <= 21:
        if opt_player_sum == player_sum or opt_player_sum > 21:
            print('Your hand is:', player_hand, '\n', 'Your sum is:', player_sum)
        else:
            print('Your hand is:', player_hand, '\n', 'Your sum is:', player_sum, 'or', opt_player_sum)
        choice = input('Hit or stay? ').lower()
        if choice == 'hit':
            player_hand = player_hand + list(hand[counter:counter + 1])
            counter += 1
            main()
        elif choice == 'stay':
            print('')
            if opt_player_sum <= 21:
                print('Final Hand: ', player_hand, 'Final Sum:', opt_player_sum)
                dealer_init()
                if opt_dealer_sum <= 21:
                    print('Dealer has:', dealer_hand, 'Sum:', opt_dealer_sum)
                    if 21 >= opt_dealer_sum > opt_player_sum:
                        print('DEALER WINS')
                    else:
                        print('YOU WIN')
                    run()
                else:
                    print('Dealer has:', dealer_hand, 'Sum:', dealer_sum)
                    if 21 >= dealer_sum > opt_player_sum:
                        print('DEALER WINS')
                    else:
                        print('YOU WIN')
                    run()
            else:
                print('Final Hand: ', player_hand, '\n', 'Final Sum:', player_sum)
                dealer_init()
                if opt_dealer_sum <= 21:
                        print('Dealer has:', dealer_hand, 'Sum:', opt_dealer_sum)
                    if 21 >= opt_dealer_sum > player_sum:
                        print('DEALER WINS')
                    else:
                        print('YOU WIN')
                    run()
                else:
                    print('Dealer has:', dealer_hand, 'Sum:', dealer_sum)
                    if 21 >= dealer_sum > player_sum:
                        print('DEALER WINS')
                    else:
                        print('YOU WIN')
                    run()
            else:
                print('')
                print('***Please enter hit or stay***')
                main()
        else:
            print('BUST\nYOUR HAND WAS:', player_hand, '\nYOUR SUM WAS:', player_sum, '\n')
        dealer_init()
        if opt_dealer_sum < 21:
            print('Dealer has:', dealer_hand, 'Sum:', opt_dealer_sum)
            if dealer_sum > 21:
                print('DEALER BUSTS')
            run()
        else:
            print('Dealer has:', dealer_hand, 'Sum:', dealer_sum)
            if dealer_sum > 21:
                print('DEALER BUSTS')
            run()


def run():
    play = input('********************\nWould you like to play again?').lower()
    if play == 'yes':
        deal()
        main()
    elif play == 'no':
        pass
    else:
        print('Please enter yes or no')
        run()


deal()
main()
run()
\$\endgroup\$
1

3 Answers 3

5
\$\begingroup\$

I'd like to add few things to previous answer:

  • Constants should be in upper case, even if they constructed dynamically, e.g. SUIT, RANK, DECK.
  • Group you code by responsibility, let Card class cover SUIT, RANK and VALUE constants to handle every static information that belongs to cards. The same relate to deck and hand.
  • global do not use it. It makes you program hard for reading and understanding. There are a lot of other techniques that are lot cleaner. Also you are not just using global variables you modify them, it is really hard to understand full logic. At least, every function should receive all needed data via arguments and return modified tuple of values, but do not modify globals.
  • main function. Well, it is pretty dirty. It should chain other functions with minimal possible logic, no direct input or output. I'm talking not about your main function, but about what everybody think when somebody says: "main function". Main is entry point, this is where your entire module start and finish working.

UPD: I'd start with this draft

import itertools


class Card(object):

    SUIT = 'scdh'
    RANK = '23456789TJQKA'

    def __init__(self, suit, rank):
        self.suit = suit
        self.rank = rank

    def get_value(self):
        return 2 + self.RANK.find(self.rank)

    def __repr__(self):
        return '{}{}'.format(self.suit, self.rank)


DECK = [
    Card(suit, rank) 
    for suit, rank in itertools.product(Card.SUIT, Card.RANK)
]


class Hand(object):

    def __init__(self):
        self.cards = []

    def add_card(card):
        self.cards.append(card)

    def get_value():
        return sum([card.get_value() for card in self.cards])


class Match(object):

    def prompt_continue(self):
        """Asks user for starting new round"""
        pass

    def run(self):
        """Single round logic"""
        self.init_deck()
        self.init_player_hand()
        self.init_dealer_hand()
        while not self.all_ready():
            self.player_take_card()
            self.dealer_take_card()
        self.print_results()

    def run_loop(self):
        while self.prompt_continue():
            self.run()


if __name__ == '__main__':
    match = Match()
    match.run_loop()
\$\endgroup\$
5
\$\begingroup\$

There are just a few nitpicky things that I want to cover.

  • Don't use _ in a for loop unless you don't use the value. The variable _ is slightly unclear anyways.
  • There is no need to make module names one letter, like itertools as i, or random as r. It makes the code much harder to read.
  • Add some docstrings to your functions to describe what they do. While well written code should be fairly easy to read, you should still have comments that describe what the function does, and how it works.
  • Finally, at the end of the file where you run deal(), main(), and run(), should be underneath an if __name__ == "__main__":.

That's about all I can come up with. If there's anything else that you want me to cover, just mention it in the comments, and I'll see what I can do. Hope this helps!

\$\endgroup\$
2
\$\begingroup\$
  1. Initially you set val to the empty tuple (parentheses), then you build it up a piece at a time. That doesn't make sense. Tuples are immutable; you use them when you know exactly how many elements there are and what they mean. Since you're building up val a piece at a time, it should be a list (square brackets). Please see https://stackoverflow.com/questions/1708510/python-list-vs-tuple-when-to-use-each/1708538#1708538
  2. It doesn't make sense to have a special case for the first or last iteration of a loop. Instead, make the loop one iteration smaller, and put the special case(s) outside the loop:

    for rank in range(9):
       val.extend( [rank+2] * 4)
    for _ in range(3):
       val.extend( [10] * 4 )
    

though there are more pythonic ways of doing this.

Try to avoid global variables.

\$\endgroup\$

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.