Level 1 Python projects are projects you can build in 30 to 45 minutes. These projects are more logically complex than the Super Simple Python projects and/or use multiple libraries. In this post, we’ll be building out a simple version of the game of Blackjack. The only library we’ll need in this project is the random
library which we’ve explored several times through projects like our password generator, the high low guessing game, and the dice roll simulator.
I actually struggle with whether or not this should go into level 1 or level 2. I generally consider level 2 programs to be a little bit more complex than this, but this is more complex than most level 1 programs. To be honest this is a level 1.5 project, or a late level 1 project.
Generating the Deck
I went over the details of this part in the Super Simple Python: Generate a Deck of Cards post. We’ll do a quick run through here. In order to generate a standard deck of 52 cards, we’ve got to define the card values and suits. Note there are some changes here. We won’t assign the 11, 12, 13, 14 values to the J, Q, K, A values in the face_cards
dictionary here. We’ll re-assign the face card values later.
All we do in the generate_cards
method is loop through the suits and values and combine them into a card. In the Generate a Deck post we used a Card
class, here we’re just substituting that with a tuple.
import random
# 11 = J, 12 = Q, 13 = K, 14 = A
card_values = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
suits = ["clubs", "diamonds", "hearts", "spades"]
face_cards = {
11: "J",
12: "Q",
13: "K",
14: "A"
}
def generate_cards():
cards = []
for value in card_values:
for suit in suits:
if value in face_cards:
_card = (face_cards[value], suit)
else:
_card = (value, suit)
cards.append(_card)
return cards
cards = generate_cards()
Methods to Deal Cards
Just like we did in the Level 1 Python: War game and the Level 2 Python: Texas HoldEm game, we’ll start off with some ways to deal the cards. In Blackjack, we have to deal two hands, one to you and one to the dealer. In further iterations, (perhaps a level 2 version!) we can add new players to the game. We’ll create a deal_card
function to deal one card and deal2
function to deal two cards. Both functions will take one parameter, the list of cards.
For deal_card
we’ll just pick a random integer between 0 and the length of the cards minus 1. These are the available indices to pick from in our list of cards. After getting a random integer, we pick that entry from the index of the list of cards and then pop the index. At the end of the deal_card
function we’ll return the card we popped and the modified deck of cards. Now let’s build the deal2
function. This function will call deal_card
twice to deal two cards. Then we’ll put the two cards into a list representing your hand and return your hand and the modified card deck.
Note that instead of picking a random integer for dealing a card we could “deal” a card another way too. We could instead shuffle the deck and get the top card like we did in War.
def deal_card(cards):
i = random.randint(0, len(cards)-1)
card = cards[i]
cards.pop(i)
return card, cards
def deal2(cards):
card1, cards = deal_card(cards)
card2, cards = deal_card(cards)
you = [card1, card2]
return you, cards
your_hand, cards = deal2(cards)
dealer_hand, cards = deal2(cards)
Defining Face Card Values for Blackjack
As I was saying above in the section on generating the deck, instead of assigning the values 11, 12, 13, 14 to J, Q, K, A as we did in the face_cards
dictionary in the original deck generation post, we’ll assign different values. Blackjack has a different face card value pattern. All face cards are 10 except for Ace which can be 1 or 11. The value of an Ace is the optimal value for the player, for example a Jack and an Ace is 21 (Blackjack), but two Jacks and an Ace is also 21 (but not a Blackjack).
fc_vals = {
"J": 10,
"Q": 10,
"K": 10,
"A": (1, 11)
}
Getting the Value of a Blackjack Hand
Now that we’ve got ways to generate the deck, deal the cards, and define the face card values, let’s make a way to get the value of a hand. The get_hand_val
function takes one parameter, the list of cards in a hand. At the start of the function, we initiate the value of the hand to 0. Then we loop through each card in the hand. In this loop, we need to handle adding the values in the hand, whether that be a number value or a face card. We need to handle the face cards first, so we don’t try to add any letters to our integer val
variable. In handling face cards, we need to also handle which value of Ace to add to our hand by checking for the value after adding either 1 or 10.
def get_hand_val(hand):
val = 0
for card in hand:
if card[0] in fc_vals:
if card[0] != 'A':
val += fc_vals[card[0]]
else:
if val + fc_vals[card[0]][1] > 21:
val += fc_vals[card[0]][0]
else:
val += fc_vals[card[0]][1]
else:
val += card[0]
return val
Hit me! Method
Hit me! This is one of the core mechanics of Blackjack. After the deal, you’ll see two cards, and at that point you can make the decision to either “hit” or get another card, or “stay” or not be dealt another card. The hit_me
method will take two lists, the representation of your hand and the representation of the cards in the deck. We will call deal_cards
on the list of cards to get a new card and then add that to the hand. After adding a new card to the hand, we have to reevaluate the value of the hand. At the end of the function, we’ll return the new hand, the new hand’s value, and the modified deck of cards.
def hit_me(hand, cards):
hit, cards = deal_card(cards)
hand.append(hit)
new_hand_val = get_hand_val(hand)
return hand, new_hand_val, cards
Playing Blackjack: Initial Deal
Nice, everything is set up. Let’s play Blackjack. Earlier when we created the methods to deal the cards, we dealt the player and dealer hands so we already have them. Note that it doesn’t really matter that we dealt the cards up there, we could deal them here too. Since the cards are already dealt, we’ll print out your hand, call the get_hand_val
function to get the value of your hand and then print out the value of your hand. If you have a 21 straight on the deal you have a Blackjack and you win and we’ll simply exit the program here. Next let’s check the dealer’s initial hand, if the dealer has a 21, then they have Blackjack and we should exit the program here.
print(f"Your hand: {your_hand}")
your_hand_val = get_hand_val(your_hand)
print(your_hand_val)
if your_hand_val == 21:
print("Blackjack!")
exit()
print(f"Dealer hand: {dealer_hand[0]}")
dealer_hand_val = get_hand_val(dealer_hand)
if dealer_hand_val == 21:
print("Dealer Blackjack!")
exit()
Playing Blackjack: To Hit or Not to Hit?
If we’ve gotten this far, that means no one got a 21 on the deal. Since you don’t have a 21, you have the option of hitting or not. So, we need to ask the user whether they want to hit or not. If the user wants to hit, we’ll call the hit_me
function and then re-print the new hand and the new value of the hand. If you get 21 this time around, you also win! However, if you get over 21, you “bust” and you lose. After a deal, if you neither have 21 or have gone over 21, you have the option to hit again or not.
hit_or_no_hit = input("Do you wish to hit?(y/n) ")
while hit_or_no_hit == "y":
your_hand, your_hand_val, cards = hit_me(your_hand, cards)
print(your_hand)
print(your_hand_val)
if your_hand_val == 21:
print("You win!")
exit()
elif your_hand_val > 21:
print("You busted :(")
exit()
hit_or_no_hit = input("Do you wish to hit?(y/n) ")
Handling the Dealer Hand
After the user is done hitting or not hitting, we move on to the dealer. Note that this is not how the game necessarily works in real life. Of course, everyone’s rules of casual Blackjack are different, but as long as we use an expected set of rules, or define the rules beforehand it’s all fair. For this version we’ll deal with the dealer hand after the user hand. Instead of prompting if the dealer wants to hit, we’ll follow the classic Blackjack rules and hit while the dealer hand is below 16. We treat the dealer’s hand the same way as we treat the player’s hand. If the dealer hits 21, the dealer wins, if the dealer goes over 21, they bust and lose.
print(f"Dealer hand val: {dealer_hand_val}")
while dealer_hand_val <= 16:
dealer_hand, dealer_hand_val, cards = hit_me(dealer_hand, cards)
print("Dealer hits")
print(dealer_hand)
print(f"Dealer hand val: {dealer_hand_val}")
if dealer_hand_val == 21:
print("Dealer wins :(")
exit()
elif dealer_hand_val > 21:
print("Dealer busted!")
exit()
Comparing Hand Values
If we’ve gotten here that means neither you nor the dealer has won or lost yet. That means both player hand values should be under 21. We’ll add a check just for a failsafe. At this point, we just compare the values of the hands and whoever has the higher hand wins. If it’s a tie, then we print out that it’s a tie.
if your_hand_val < 21 and dealer_hand_val < 21:
if your_hand_val > dealer_hand_val:
print("You win!")
elif dealer_hand_val > your_hand_val:
print("Dealer wins ...")
else:
print("Tie!")
Example
Let’s see some examples of the game when we play. We can play this from our command line. In the below example you hit and stay under. Then the dealer hits and busts.
In this example below you stay and the dealer hits twice and stays under, beating you by 2 points.
One thought on “Level 1 Python: Blackjack”