# A simple Blackjack game implementation in Python

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
• 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

#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!")
raise Exception
break
except Exception as e:

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)

#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


## 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']:
break
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.

p1 = Player()

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