6
\$\begingroup\$

I am trying to create a simple Blackjack game. I am using GIST because the code sample takes forever to use.

Current code:

from random import randint
def card_deck():
    #sets the card types and values
    card_value = ['Ace','2','3','4','5','6','7','8','9','10','J','Q','K']
    card_type = ['Hearts','Spades','Clubs','Diamonds']
    deck = []
    #This iterates all 52 cards into a deck
    for i in card_type:
        for j in card_value:
            deck.append(j + ' of ' + i)
    return deck

def card_value(card):
    #only reading first slice to determine value of the card
    if card[:1] in ('J','Q','K','1'):
        return int(10)
    elif card[:1] in ('2','3','4','5','6','7','8','9'):
        #card[:1] example '2' out of the full '2 of Hearts' string
        return int(card[:1])
    elif card[:1] == 'A':
        print ("\n"+ str(card))
        num = input("Do you want this to be 1 or 11?\n>")
        while num !='1' or num !='11':
            if num == '1':
                return int(1)
            elif num == '11':
                return int(11)
            else:
                num = input("Do you want this to be 1 or 11?\n>")


def new_card(deck):
    return deck[randint(0,len(deck)-1)]

def remove_card(deck,card):
    return deck.remove(card)


play_again = ''
while play_again != 'EXIT':
    #deck creation, card creation, card removal from deck, values and totals
    new_deck = card_deck()
    card1 = new_card(new_deck)
    remove_card(new_deck,card1)
    card2 = new_card(new_deck)
    remove_card(new_deck,card2)
    print ("\n\n\n\n" + card1 + " and " + card2) #doing this statement first allows for selection between 1 and 11
    valu1 = card_value(card1)
    valu2 = card_value(card2)
    total = valu1 + valu2
    print("with a total of " + str(total) )

    #dealer's hand
    dealer_card1 = new_card(new_deck)
    remove_card(new_deck,dealer_card1)
    dealer_card2 = new_card(new_deck)
    remove_card(new_deck,dealer_card2)
    dealer_value1 = card_value(dealer_card1)
    dealer_value2 = card_value(dealer_card1)
    dealer_total = dealer_value1 + dealer_value2
    print ('\nThe Dealer smiles as he looks at you and\n deals one card up and one card face down')
    print ("First a " + dealer_card1 + " and face down card.")

    if total == 21:
        print("Blackjack!")
    else:
        while total < 21: #not win or loss yet
            answer = input("Would you like to hit or stand?\n> ")
            if answer.lower() == 'hit':
                #more card creation, removal, and value added to total
                more_card = new_card(new_deck)
                remove_card(new_deck,more_card)
                more_value = card_value(more_card)
                total += int(more_value)
                print (more_card + " for a new total of " + str(total))
                if total > 21: #lose condition
                    print("You LOSE!")
                    play_again = input("Would you like to continue? EXIT to leave\n")
                elif total == 21: #winning condition
                    print("You WIN BIG WIN WOO WOO")
                    play_again = input("Would you like to continue? EXIT to leave\n")
                else:
                    continue
            elif answer.lower() == 'stand':
                print("The dealer nods and reveals his other card to be ")
                print("a " + dealer_card2 + " for a total of " + str(dealer_total))
                if dealer_total < 17:
                    print("The Dealer hits again.")
                    dealer_more = new_card(new_deck)
                    more_dealer_value = card_value(dealer_more)
                    print("The card is a " + str(dealer_more))
                    dealer_total += int(more_dealer_value)
                    if dealer_total > 21 and total <=21:
                        print("Dealer Bust! You win!")
                    elif dealer_total < 21 and dealer_total > total:
                        print("Dealer has " + str(dealer_total) + " You lose!")
                    else:
                        continue
                elif dealer_total == total:
                    print("Equal Deals, no winner")
                elif dealer_total < total:
                    print("You win!")
                else:
                    print("You Lose!")
                play_again = input("\nWould you like to continue? EXIT to leave\n")
                break
print("Thank you for Playing")

New (is my Blackjack program code so far using classes):

from random import randint

class Card:
    def __init__(self):
        pass

    def card_value(self):
        pass

    card_face = ['Ace','2','3','4','5','6','7','8','9','10','J','Q','K']
    card_suit = ['Hearts','Spades','Clubs','Diamonds']



class Deck(Card):
    new_deck = []
    length = len(new_deck) #testing purposes

    for i in Card.card_suit:
        for j in Card.card_face:
            new_deck.append(j + ' of ' + i)

    def new_card(self):
        #instead of return, use yield?
        return (self.new_deck[randint(0,len(self.new_deck)-1)])
    def remove_card(self,card):
        self.new_deck.remove(card)



deck1 = Deck()
card1 = deck1.new_card()
deck1.remove_card(card1)
card2 = deck1.new_card()
deck1.remove_card(card2)
print(str(card1) + " and " + str(card2))
print(len(deck1.new_deck))

I am looking for beginner advice on how or where to start with my process on fleshing out my functions/code into classes. My original code does work for what it does, but I feel that it has too much manual code and I would like some tips/advice on where to go from here.

\$\endgroup\$
  • \$\begingroup\$ okay I fixed it \$\endgroup\$ – Taku_ Dec 14 '16 at 15:51
  • \$\begingroup\$ do you want us to review your code(which is not finished yet) or just help you build skeleton(help to find right approach) of your game? \$\endgroup\$ – Alex Dec 14 '16 at 16:08
  • \$\begingroup\$ help to find the right approach would be great or if you notice anything that I definitely should not be doing, that would be good to point out as well :) \$\endgroup\$ – Taku_ Dec 14 '16 at 16:18
  • 1
    \$\begingroup\$ I think you really need to read the documentation on how classes in python work. For example, you would rarely ever have free standing code as seen in the Deck class, you would either put it in the __init__ function or under a different function \$\endgroup\$ – Navidad20 Dec 14 '16 at 18:26
  • \$\begingroup\$ So does this mean you couldn't use classes for this at all? \$\endgroup\$ – Taku_ Dec 14 '16 at 18:42
5
\$\begingroup\$

Let's look at your Card class.

class Card:
    def __init__(self):
        pass

    def card_value(self):
        pass

    card_face = ['Ace','2','3','4','5','6','7','8','9','10','J','Q','K']
    card_suit = ['Hearts','Spades','Clubs','Diamonds']

There are multiple design mistakes.

1. Card should not evaluate it's value, since a value of card defined by the game. Basically, if you would like to reuse this class for say Poker you would have to create a child class for the Card just to evaluate its value for a different game, which is wrong.

2. In your case, Card class knows about suits and ranks, which is also in my opinion wrong solution, since you might use any other deck except for French.

So as a result of first 2 problems, it turns out that your Card is just a container(structure) without any logics inside, one of the best things to use for that in python in namedtuple

from collections import namedtuple
Card = namedtuple('Card', ('rank', 'suit'))

Now let's talk about Deck

class Deck(Card):
    new_deck = []
    length = len(new_deck) #testing purposes

    for i in Card.card_suit:
        for j in Card.card_face:
            new_deck.append(j + ' of ' + i)

    def new_card(self):
        #instead of return, use yield?
        return (self.new_deck[randint(0,len(self.new_deck)-1)])
    def remove_card(self,card):
        self.new_deck.remove(card)

1. Deck is definitely not a Card child, Deck is sort of collection of cards.

2. Your new_card and remove_card methods should be combined into single one called draw_card.

3. Since now we know that Deck is not a Card then the most right place to keep track of ranks and suits is Deck. Here is example:

class Deck:
    card_ranks = []
    card_suits = []

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

    def refresh_deck(self):
        self.cards = list(map(Card, product(self.card_ranks, self.card_suits)))

    def shuffle(self):
        shuffle(self.cards)

    def draw_card(self):
        return self.cards.pop()

Game class

Now things like play_game, card_value(hand_value?) and other things/rules related to specific game should be defined by Game class

class Game:
    def __init__(self, deck):
        raise NotImplementedError

    def card_value(self, card):
        raise NotImplementedError

    def hand_value(self, hand):
        raise NotImplementedError

    def play(self):
        raise NotImplementedError

So in the end what we have as a game skeleton is this:

from collections import namedtuple
from itertools import product
from random import shuffle
from typing import List

Card = namedtuple('Card', ('rank', 'suit'))


class Deck:
    card_ranks = []
    card_suits = []

    def __init__(self) -> None:
        self.cards = []
        self.refresh_deck()

    def refresh_deck(self) -> int:
        self.cards = map(Card, product(self.card_ranks, self.card_suits))

    def shuffle(self) -> None:
        shuffle(self.cards)

    def draw_card(self) -> Card:
        return self.cards.pop()


class FrenchDeck(Deck):
    card_ranks = ['Ace', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
    card_suits = ['Hearts', 'Spades', 'Clubs', 'Diamonds']


class Game:
    def __init__(self, deck: Deck) -> None:
        raise NotImplementedError

    def card_value(self, card: Card) -> int:
        raise NotImplementedError

    def hand_value(self, hand: List[Card]) -> int:
        raise NotImplementedError

    def play(self) -> None:
        raise NotImplementedError
\$\endgroup\$
  • \$\begingroup\$ Sorry I havent had a chance to respond to this, the internet connection from my home was down~ This will give me some wonderful tips and a skeleton to look at and work off of, thank you so much! \$\endgroup\$ – Taku_ Dec 17 '16 at 12:27
3
\$\begingroup\$

When you "stand" it tells you the dealers other card, then he hits/stands, then you get to hit or stand, that's not blackjack, you need to take out the other hit/stand after the dealer reveals his/her hand.

\$\endgroup\$

protected by Jamal Jan 9 at 2:06

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

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