2
\$\begingroup\$

This program is an implementation of the Blackjack which is similar to what is played in the casinos.

Here were the requirements:

  • I needed to create a simple text-based BlackJack game
  • The game needs to have one player versus an automated dealer
  • The player can stand or hit
  • The player must be able to pick their betting amount
  • Keep track of the player's total money
  • Alert the player of wins, losses, or busts, etc...

I need advice on the following topics:

  • My method and approach
  • Efficiency of my code and scope for cutting it short
  • Implementation of OOP
  • Suggestions for best practices
  • Styling approach
  • Readability
  • Comments and variable naming practices (Use of docstring)

Since I am new to CS and to Python and this is my first ever OOP program, please feel free to give me suggestions that could help me to better my programs in future.

import random # Random library is imported to enable us choose a random card
class Deck():
 """This is an abstract class. Player and Dealer
 are meant to inherit its pick function for picking 
 a random card from the deck """
 deck_cards = ['A', 'K', 'Q', 'J', '10', '9', '8', '7', '6', '5', '4', '3', '2'] * 4
 #represents all the cards in the deck
 def pick(self, num):
 '''
 Picks the specified number of cards from the existing
 desk and returns it in a form of a list.
 '''
 picked = []
 for _ in range(num):
 choosen = random.randint(0, len(self.deck_cards) - 1)
 picked.append(self.deck_cards[choosen])
 #Removes the card that is picked up
 self.deck_cards.pop(choosen)
 return picked
class Player(Deck):
 """An object of this class keeps the track of players
 progress in the game including its cards, money, burst 
 situation. It also initiates the player with 500 units"""
 cards = []
 burst = False
 def __init__(self):
 self.money = 500
class Dealer(Deck):
 """An object of this class keeps the track of dealers
 progress in the game including its cards, and burst
 situation."""
 cards = []
 burst = False
#FUNCTIONS
def print_cards(cards):
 """prints the card value inside a box """
 for i in range(len(cards)):
 print("{0:^4}-----".format(''))
 print("{0:^4}|{1:^3}|".format('', cards[i]))
 print("{0:^4}-----".format(''))
def print_board(cards1, cards2):
 """Prints the complete board situation including both
 the players as well as the dealers cards"""
 print("\n"*50)
 print("*************")
 print("Player cards:")
 print("*************")
 print_cards(cards1)
 print("*************")
 print("Dealer cards:")
 print("*************")
 print_cards(cards2)
 print("*************")
def find_value(cards):
 """It finds the total value of the cards"""
 card_value = []
 total = 0
 ace = False
 value_dict = {'A': 11, 'K': 10, 'Q': 10, 'J': 10, '10': 10,
 '9': 9, '8': 8, '7': 7, '6': 6, '5': 5, '4': 4, '3': 3, '2': 2, }
 #makes a corrosponding car_value list
 for i in range(len(cards)):
 card_value.append(value_dict[cards[i]])
 if cards[i] == 'A':
 ace = True
 for i in range(len(card_value)):
 total += card_value[i]
 #Ace value can be 11 or 1 depending on the situation
 #makes sure that the 'Ace' value is taken in player's favour
 if total > 21 and ace:
 total -= 10
 return total
#GAMEPLAY
p1 = Player()
d1 = Dealer()
#Game stops only if the player wants to quit
#Or if he runs out of cash
while True:
 p1.cards = p1.pick(2)
 d1.cards = d1.pick(1)
 dealer_won = False
 print_board(p1.cards, d1.cards)
 #Asks for the bet amount till the player enter a valid amount
 while True:
 try:
 bet = int(input("Enter the amount of money you want to bet: "))
 if bet > p1.money:
 print("You dont have sufficient funds!")
 print("Your account balance is: {}".format(p1.money))
 raise Exception
 break
 except Exception as e:
 print("Please enter a valid amount!")
 p1.money -= bet
 #Player turns continues till he passes or bursts
 while True:
 #Asks player choice for action till a valid choice is put
 while True:
 try:
 choice = input("Do you want to hit or pass? [h/p]: ")
 if choice.lower() not in ['h', 'p']:
 raise Exception
 break
 except Exception as e:
 print("Oops! Please enter a valid choice")
 if choice.lower() == 'h':
 p1.cards += p1.pick(1)
 print_board(p1.cards, d1.cards)
 else:
 break
 if find_value(p1.cards) > 21:
 p1.burst = True
 dealer_won = True
 break
 #Dealer only plays if the player is not bursted yet
 if not dealer_won:
 while True:
 d1.cards += d1.pick(1)
 if find_value(d1.cards) > 21:
 d1.burst = True
 break
 if find_value(d1.cards) > find_value(p1.cards):
 dealer_won = True
 break
 print("\n" *50)
 print_board(p1.cards, d1.cards)
 #Winner determination and result printing
 if dealer_won:
 print("_"*50)
 print("Sorry you lost the game and the money.")
 if p1.burst:
 print("Your cards value exceeded 21. Be careful next time!")
 print("_"*50)
 else:
 print("Dealer got very lucky! Better luck next time! :)")
 print("_"*50)
 else:
 p1.money += 3*bet
 print("_"*67)
 print("Bingo! You won the game! Twice the bet is credited to your account.")
 if d1.burst:
 print("Dealer's total card value exceeded 21. You got lucky, well played!")
 print("_"*67)
 print("Your account balance is: {}".format(p1.money))
 #Asks if the player wants to play again
 while True:
 try:
 play_again = input("Do you want to play again? [y/n]: ")
 if play_again.lower() not in ['y', 'n']:
 raise Exception
 break
 except Exception as e:
 print("Oops! Please enter a valid choice")
 if play_again == 'n':
 break
 elif p1.money == 0:
 print("You did not have sufficient to continue playing!")
 break
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked May 20, 2018 at 16:18
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Method and approach

Correctness

These one's I and not sure if it the intended behavior or not, so feel free to ignore these if they are actually correct.

In your function find_value, if the user had the hand Ace, Ace, Ten, your code would say they have the value of 22(1 + 11 + 10) and should bust, however, my understanding of blackjack is that all ace's can be a 1 or an 11, not just the first ace

Also, in blackjack, I am fairly sure the dealer is supposed to win in a tie

if find_value(d1.cards) > find_value(p1.cards):
 dealer_won = True

This indicates that the player wins in a tie to me.

Catching Exception is a bad idea

So, in python ample use of try except is encouraged, however, it is not a good idea to write except Exception, as this will catch more exceptions then you likely want. You can define your own exceptions. In this case I would do something like this

class BadInputException(Exception):
 pass
while True:
 try:
 choice = input("Do you want to hit or pass? [h/p]: ")
 if choice.lower() not in ['h', 'p']:
 raise BadInputException
 break
 except BadInputException:
 print("Oops! Please enter a valid choice")

And I would change all of your input handling sections to use this new exception

Use more functions

The last 100ish lines of code are kind of - no offense - a big blob of while loops. Lots of this could be extracted out into functions. You could have a getBet function for example instead of all these nested loops.

In fact, you already know that the big while loop at the end needs to be split up into several sections, it's clear by where you placed you comments you can see where which section starts and ends, each of those sections I would refactor into a function, then I would take all those function calls into one more function, so your code ends up being a bunch of function and class definitions, and then something like this.

while True:
 play_game()
 if not play_again():
 break

This will make your code easier to follow, as well it will be easier to test and maintain. Your could would benefit from being more modular.

Implementation of OOP

While I am not an expert on blackjack, it seems to me in most card games the player and the dealer would draw from the same deck. If this is what you intended, you will need to structure things a little differently.

You could have a general person class which has a pick method and has a deck field, and have both the player and the dealer inherit from that class, then the player and the dealer could both be passed the desired deck upon instantiation. This way when the dealer draws a card, the player can't draw the same one, or in you case, since there are no suits, both the player and the dealer could not be dealt 3 3's

Styling approach

For styling in python, it is recommended to follow pep8. There are lots of tools out there that will format your code to be pep8 compliant. Autopep8 is a good one, or if you are using an IDE, it likely has something built in to allow this.

Readability

This was pretty well done. Most variable names were descriptive, a few I would recommend changing are:

p1 = Player()
d1 = Dealer()

As well as cards1 and cards2 in the print_board function, as it it clear one is meant to be for the player, and the other is meant to be for the dealer

Your comments were alright for the most part, comments are to serve the purpose not of describing what your code is doing, but rather how and why it is doing that, this makes it easier when you look at this later to figure out what you did.

Your docstrings were all good one line descriptions, however, none of them describe what is returned or the values being passed in. Here is a good answer on how to format your docstrings.

answered May 22, 2018 at 21:17
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.