Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

To try out the concepts and tools I've been learning, I decided to write a Blackjack game. I wrote it entirely with functions, so it hasn't helped me at all with the concept I really lack understanding in: classes. Could someone point me in the right direction as to how I could rewrite this program utilizing classes?

import random
from deckofcards import values
from deckofcards import deck
from deckofcards import rules

def draw_two():
    """Creates a list with two random values drawn from the "deck". This serves
    as an inital hand."""
    card1 = random.choice(deck)
    card2 = random.choice(deck)
    hand = []
    hand.append(card1)
    hand.append(card2)
    return hand

def draw_one(hand):
    """Randomly adds one value to the given list. Equivilent to "Hit Me\""""
    card = random.choice(deck)
    hand.append(card)
    return hand

def counth(hand):
    """Evaluates the "score" of a given hand. """
    count = 0
    for i in hand:
        if i in values:
            count += values[i]
        else:
            pass
    for x in hand:
        if x == 'A':
        ## makes exception for aces
            if count + 11 > 21:
                count += 1
            elif hand.count('A') == 1:
                count += 11
            else:
                count += 1
        else:
            pass
    return count

def dealer():
    """Simulates a random dealer hand.
    The dealer starts with two cards --dh(0) and dh(1)--,
    then hits if his score is below a mandatory score X.
    If there is an ace among his first two cards, he must hit.
    Returns the hand. (which is a list)"""

    dh = draw_two()
    count = counth(dh)

    # forces hit if ace in first two cards
    if 'A' in dh:
        dh = draw_one(dh)
        count = counth(dh)

    ## defines maximum hit score X
    while count < 16:
        draw_one(dh)
        count = counth(dh)
    return dh

def player(dh):
    ph = draw_two()
    count = counth(ph)
    print "Your cards: %s" % " ".join(ph)
    print "Your score: %d" % count
    print "The dealer shows: %s" % dh[0]
    print "hit or stay?"
    a = True
    while a:
        choice = raw_input("> ")
        if choice == "hit":
            ph = draw_one(ph)
            count = counth(ph)
            print "Your cards: %s" % " ".join(ph)
            print "Your score: %d" % count
            print "The dealer shows: %s" % dh[0]
            if count >= 21:
                a = False
            else:
                print "hit or stay?"
        elif choice == "stay":
            a = False
        else:
            print "You must select hit or stay."
    return ph



def game():
    dh = dealer()
    ph = player(dh)
    dc = counth(dh)
    pc = counth(ph)
    if dc > 21 and pc <= 21:
        print "----------------------"
        print " The dealer's hand: %s" % " ".join(dh)
        print "The dealer's score: %d" % dc
        print "        Your score: %d" %pc
        print " The dealer went bust!"
        print "*******You win!*******"
    elif pc > 21:
        print "----------------------"
        print "You went bust!"
        print "**You lose!!**"
    elif dc >= pc:
        print "----------------------"
        print " The dealer's hand: %s" % " ".join(dh)
        print "The dealer's score: %d" % dc
        print "        Your score: %d" %pc
        print "******You  lose!******"
    else:
        print "----------------------"
        print " The dealer's hand: %s" % " ".join(dh)
        print "The dealer's score: %d" % dc
        print "        Your score: %d" %pc
        print "  You beat the dealer!"

def start():
    a = True
    print "Welcome to Blackjack."
    while a == True:
        print "\n1.Rules\n2.New Game\n3.Exit"
        choice = raw_input("[1 2 or 3] > ")
        if choice == "1":
            print rules
        elif choice == "2":
            game()
        elif choice == "3":
            print "Bye!"
            a = False
        else:
            print "Thats not an option!"

start()

My "file" program:

deck = ['A','2','3','4','5','6','7','8','9','T','J','Q','K']
values = {'2': 2,
          '3': 3,
          '4': 4,
          '5': 5,
          '6': 6,
          '7': 7,
          '8': 8,
          '9': 9,
          'T': 10,
          'J': 10,
          'Q': 10,
          'K': 10
         }

rules = """---------
    Black jack is a card game played between the player and the dealer.

    At the start, both you and the dealer are dealt two cards. The dealer shows
you his first.

    Your cards, or hand, have a score. You get your score by
adding up the values of each card in your hand. Every number card's value is
its number, and any face card is worth 10. Ace is worth 1 or 11, whichever is
most beneficial for the player. The winner of the game is whoever's score is
closest to 21. Ties go to the dealer.

    With your two cards, you have the option to hit or stay. Hit means draw one
more card. Stay means you're happy with your score.
---------"""
share|improve this question
    
Your second question is basically "what's the point of OOP?", which is a long way off-topic here. Maybe look at Programmers. –  jonrsharpe Jul 23 at 21:59
    
Sorry, I'll remove it from the question. –  User1996 Jul 23 at 22:26

2 Answers 2

up vote 5 down vote accepted

In this case, adding classes is really easy.

  1. Create a Card class, which has a value (1, 2 ... 10) and a name (2, ... K, A)
  2. Create a Deck class, which is a list/tuple or other collection of Card with a shuffle function and a draw_card function.
  3. Create a Hand class, which is also similar to the Deck class
  4. Create a Player class, which basically has a Hand class object associated with it.
  5. Create a Blackjack class which has the main game logic.

By separating the logic out, it makes the code more maintainable, swap-able (you can change your implementation of Card without affecting anything else), and more understandable. You can also implement additional functionality (like adding names to players and etc.) without changing too much in the way of code.

Also, if you ever wanted to have multiple concurrent blackjack games, you can instantiate multiple Blackjack objects, whereas your current code would require significant changes.

share|improve this answer
    
I provided an example class structure for a Blackjack game on an earlier question - see here. –  jonrsharpe Jul 23 at 22:12

In terms of reviewing your current implementation:

PEP-8 compliance

Generally pretty good. A few more descriptive names would be helpful: counth could be calculate_score, dh could be dealer_hand, etc. Also, deckofcards.py should be deck_of_cards.py.

It's good that you've avoided from deckofcards import *, but you could simplify the current version to:

from deckofcards import deck, rules, values

deckofcards

Your values dictionary seems odd. Note that most of the mappings are just {c: int(c)}. One option would be to hold a mix of integers and strings in deck, (deck = ['A', 2, 3, ...]) have only the string keys (face cards) in values and use values.get(card, card) to get the score.

Alternatively, to keep the current strings:

score = values[card] if card in values else int(card)

This would all mean changing your test in counth from if i in values to if i != 'A', but that arguably makes things clearer anyway!

main

(Or whatever it's called)

Having separate draw_one and draw_two methods is an obvious piece of duplication. Just have e.g. draw(hand, deck, n), which reduces the duplication and improves your code by making all necessary objects explicit parameters. Note also that random.choice could give the same card multiple times - someone could end up with seven aces!

You have some duplication in game, too - think carefully about what needs to happen and in what order, you could improve the scoping and simplify this function.

In counth, be aware that you can leave out else: pass; it doesn't really do anything but take up two lines! You should make values an explicit parameter here.

I would move the input from the player function to a separate get_input function. This could be slightly simplified too, see e.g. this SO community wiki. You could do the same in game, too.

share|improve this answer
1  
7 Aces is a good day at the table if you've got the money to keep splitting them. –  RubberDuck Jul 24 at 2:38

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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