2
\$\begingroup\$

Blackjack card game with hit and stand.

Wasn't allowed to use functions outside of classes in code.

Had some difficulty with __str__ dunder method on Dealer and Player classes (got weird solutions for the time-being)

Would appreciate any feedback on how to improve readability and make code more efficient.

Code

#Blackjack


#imports
import random
import time
from IPython.display import clear_output


#dictionaries
ranks = ['Ace', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten', 'Jack', 'Queen', 'King']
suits = ['Diamonds', 'Spades', 'Clubs', 'Hearts']
values = {'Ace':1, 'Two':2, 'Three':3, 'Four':4, 'Five':5, 'Six':6, 'Seven':7, 'Eight':8, 'Nine':9, 'Ten':10, 'Jack':10, 'Queen':10, 'King':10}


#card class
class Card():
    def __init__(self, rank, suit):
        self.rank = rank
        self.suit = suit
        self.value = values[rank]
        
    def __str__(self):
        return f'{self.rank} of {self.suit}'
    

#deck class
class Deck():
    def __init__(self):
        self.deck_cards = []
        
        for rank in ranks:
            for suit in suits:
                self.deck_cards.append(Card(rank,suit))
                
    def shuffle(self):
        random.shuffle(self.deck_cards)
        
    def dealCards(self):
        return self.deck_cards.pop(0)
    
    def __str__(self):
        for item in self.deck_cards:
            print(item)
        return 'Joker'

    
#hand class
class Player():
    def __init__(self):
        self.hand = []
        
    def addCard(self, card):
        self.hand.append(card)
    
    def handSum(self):
        card_sum = 0
        for card in self.hand:
            card_sum += card.value
        return card_sum
    
    def __str__(self):
        print('------------\nPlayer Cards\n------------')
        for item in self.hand:
            print(item)
        print(f"{player_name}'s card sum: {player1.handSum()}")
        return('')
    

#chip class
class Chips():

    def __init__(self, player_name):
            self.player_name = player_name
            self.chip_count = 3
            
    def betChip(self,num):
        self.chip_count -= num
        return num
    
    def addChip(self,num):
        self.chip_count += num
        
    def subtractChip(self,num):
        self.chip_count -= num
    
    def __str__(self):
        if self.chip_count == 1:
            return f'{self.player_name} has {self.chip_count} chip!'
        else:
            return f'{self.player_name} has {self.chip_count} chips!'
        

#dealer class
class Dealer(Player):
    
    def __str__(self):
        print('------------\nDealer Cards\n------------')
        for item in self.hand:
            if item == self.hand[-1]:
                pass
            else:
                print(item)
        print('**********')
        return str('')
        

#variable initialization
player_name = input('What is your name?: ')

running = False
game_on = True
gameDeck = Deck()
gameDeck.shuffle()
player1 = Player()
player1_chips = Chips(player_name)
dealer = Dealer()

#game loop
while game_on:
    
    if player1_chips.chip_count == 0:
        time.sleep(3)
        clear_output()
        print('You ran out of chips\nThanks for playing')
        break
    else:
        pass
    
    gameDeck = Deck()
    gameDeck.shuffle()
    player1 = Player()
    dealer = Dealer()
    
    ready_game = input('Ready for a round of BlackJack?(Y/N) ')
    if ready_game == 'Y' or ready_game == 'y':
        clear_output()
        running = True
    elif ready_game == 'N' or ready_game == 'n':
        clear_output()
        print('Thanks for playing!')
        break
    else:
        clear_output()
        print("Please input 'Y' or 'N'")
        continue
        
    while running:

        #placing bets
        bet = input(f'You have {player1_chips.chip_count} chips. How much would you like to bet? ')
        clear_output()
        if bet.isdigit() == True:
            bet = int(bet)
            if bet > player1_chips.chip_count:
                print('Your bet is above your total chip count!')
                time.sleep(1)
                clear_output()
                continue
            else:
                print(f'You have bet {bet} chips')
                time.sleep(1)
                clear_output()
        else:
            print('Your bet is not a number!')
            time.sleep(1)
            clear_output()
            continue


        #card initialization
        print(f'Distributing card to {player_name}...')
        player1.addCard(gameDeck.dealCards())
        time.sleep(.85)
        clear_output()
        print('Distributing card to Dealer...')
        dealer.addCard(gameDeck.dealCards())
        time.sleep(.85)
        clear_output()
        print(f'Distributing card to {player_name}...')
        player1.addCard(gameDeck.dealCards())
        time.sleep(.85)
        clear_output()
        print('Distributing card to Dealer...')
        dealer.addCard(gameDeck.dealCards())
        time.sleep(.85)
        clear_output()
        print('Cards distributed')
        clear_output()
        print(player1)
        print(dealer)

        #check for 21:
        if player1.handSum() == 21:
            print(f'You win and get {bet * 1.5} chips')
            player1_chips.addChip(bet * 1.5)
            time.sleep(2)
            clear_output()
            running = False
        else:
            pass
        
        temp_var = True
        
        #gameplay
        while temp_var:
            choice = input('Hit(H) or Stand(S)? ')
            if choice == 'H' or choice == 'h':
                clear_output()
                print(f'Distributing card to {player_name}...')
                player1.addCard(gameDeck.dealCards())
                time.sleep(1)
                temp_var = False
            elif choice == 'S' or choice == 's':
                temp_var = False
                clear_output()
            else:
                clear_output()
                print("Please input 'H' or 'S'")
                time.sleep(.5)
                continue
                
        #printing updated hands
        print('Dealer will reveal their cards')
        time.sleep(1.3)
        clear_output()
        print(player1)
        print('')
        
        
        print('------------\nDealer Cards\n------------')
        [print(item) for item in dealer.hand[0::]]
        print(f"Dealer's card sum: {dealer.handSum()}")
        print(' \n \n ')
        time.sleep(.9)
        
        #no more user input/game continues
        if player1.handSum() == 21:
            if dealer.handSum() == 21:
                print('You have tied with the dealer')
                time.sleep(1)
                print('You won no chips. Your total remains the same')
                break
            else:
                print('You win!')
                time.sleep(.9)
                player1_chips.addChip(bet)
                print(f'You have won {bet} chips. Bringing your total up to {player1_chips.chip_count} chips')
                break
        elif player1.handSum() > 21:
            print('You have BUSTED\nYou Lose!')
            time.sleep(1)
            player1_chips.subtractChip(bet)
            print(f'You lost {bet} chips. Bringing your total down to {player1_chips.chip_count} chips')
            break
        elif dealer.handSum() <= 16:
            dealer.addCard(gameDeck.dealCards())
            print('Dealer is adding a card to their hand as they have a 16 or less....')
            time.sleep(3)
            clear_output()
            if dealer.handSum() > 21:
                
                #printing hands
                ##################################################
                print(player1)
        
                print('------------\nDealer Cards\n------------')
                [print(item) for item in dealer.hand[0::]]
                print(f"Dealer's card sum: {dealer.handSum()}")
                time.sleep(1)
                ##################################################
                
                print('You win! Dealer Busts!')
                time.sleep(.9)
                player1_chips.addChip(bet)
                print(f'You have won {bet} chips. Bringing your total up to {player1_chips.chip_count} chips')
                break
            elif dealer.handSum() > player1.handSum():
                
                #printing hands
                ##################################################
                print(player1)
        
                print('------------\nDealer Cards\n------------')
                [print(item) for item in dealer.hand[0::]]
                print(f"Dealer's card sum: {dealer.handSum()}")
                time.sleep(1)
                ##################################################
                
                print('You Lose! Dealer got a higher sum of cards!')
                time.sleep(.9)
                player1_chips.subtractChip(bet)
                print(f'You lost {bet} chips. Bringing your total down to {player1_chips.chip_count} chips')
                break
        else:
            if dealer.handSum() > player1.handSum():
                print('You Lose! Dealer got a higher sum of cards!')
                time.sleep(1)
                player1_chips.subtractChip(bet)
                print(f'You lost {bet} chips. Bringing your total down to {player1_chips.chip_count} chips')
                break
            else:
                print('You win! Dealer got a lesser sum of cards!')
                time.sleep(1)
                player1_chips.addChip(bet)
                print(f'You have won {bet} chips. Bringing your total up to {player1_chips.chip_count} chips')
                break
\$\endgroup\$
2
  • \$\begingroup\$ Is this homework? (in other words, is it your prof that restricted your library use?) \$\endgroup\$
    – Reinderien
    Oct 10, 2022 at 23:06
  • 1
    \$\begingroup\$ No not homework. Im doing this for my self using udemy. The objective was understanding OOP better, as to why I was restricted in what I was allowed to use. \$\endgroup\$
    – Beginner
    Oct 11, 2022 at 4:41

1 Answer 1

4
\$\begingroup\$

Don't print in __str__

Your Card class does things correctly:

>>> card = Card("Ace", "Spades")
>>> s = str(card)
>>> print(s)
Ace of Spades

Notice that str(card) doesn't print anything. It just silently returns the string for the card.

Where this is useful is the print() function. It automatically turns everything passed to it into a string. This means you don't have to str(card) yourself; it is automatic.

>>> print(card)
Ace of Spaces

In contrast, stringifying a dealer produces strange results. First of all, it has a side effect of printing. Second, it returns an empty string!

>>> dealer = Dealer()
>>> s = str(dealer)
------------
Dealer Cards
------------
**********

>>> print(repr(s))
''

This is not the correct behaviour. s = str(something) should never cause any side effects, including printing. It must simply return a string.

Not referencing self

>>> player_name = "Anna"
>>> player1 = Player()
>>> player1.addCard(Card("Ace", "Spades"))
>>> player_name = "Bob"
>>> player2 = Player()
>>> player2.addCard(Card("Two", "Clubs"))
>>> player_name = "Chris"
>>> print(player1, player2)

What do you think the output is, and why?

After you've tried it out, can you explain why both players appear to have the same name, and the same score despite having a different card?

The section title gives it away, I suppose. You're using player1.handSum(), not self.handSum(). You should also be storing the player's name inside the Player object, and using self.player_name.

Hidden Dealer's Card

        for item in self.hand:
            if item == self.hand[-1]:
                pass
            else:
                print(item)

Here, you loop through all the cards in the dealer's hand, compare them with the last card, and if different, they get printed out.

This is too much code, and may cause a subtle bug down the road.

First, the subtle bug: Imagine upgrading the program to use a shoe with 5 decks. Since the Seven of Diamonds will exist 5 times, the dealer's hand could be two Seven of Diamonds. The loop runs with the first card, and compares it with the last card. Are they the same card? Will it print the first card? Maybe not, depending on exactly how you've implemented things.

We can avoid the problem all together, by looping over all but the last card, eliminating the need to compare the current card with the last one:

        for item in self.hand[:-1]:
            print(item)

The magic here is self.hand[:-1], which is a sliced copy of the self.hand, starting at the beginning and stopping just before the end. Since the sliced copy doesn't include last item, we don't have to test if we've reached it and handle it separately anymore.

PEP 8

The Style Guide for Python Code has many recommendations that all Python programmers should follow. It includes things like naming conventions. addCard is a member function and should be written in snake_case, instead of as a bumpyWord.


There is much more to review feedback to give here, but I'm out of time for now.

\$\endgroup\$
1
  • \$\begingroup\$ Appreciate the feedback, will take your advice into account moving forward \$\endgroup\$
    – Beginner
    Oct 11, 2022 at 19:48

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.