Design a Blackjack Game Using Object Oriented Principles

Blackjack is a card-based game played at casinos. The participants in this game do not compete with each other but the dealer assigned by the casino. In this article, we will be creating the Blackjack game between a player and a dealer from scratch, that can be played on the terminal.

Rules of Blackjack

We will provide a brief set of rules for readers who have never played Blackjack. The magic number for Blackjack is 21. The values for all the cards dealt to a player are added and if the sum exceeds 21, the player busts and loses instantly.

If a player gets an exact 21, the player wins against the dealer. Otherwise, in order to win, the sum of the player's cards must be more than the sum of the dealer's cards.

Each face card has a definite value of 10, whereas the ace can be counted as 1 or 11 suitable to the player's chances of winning. The value of the rest of the cards is defined by their number.

The dealing of the cards in a game of Blackjack is as follows:

  • A card is dealt to the player facing upwards (visible to everyone).
  • The dealer deals a card to himself visible to everyone.
  • Another card is given to the player facing upwards.
  • The dealer deals a card facing downwards for himself.
  • The player has to decide whether to stand with the current set of cards or get another card.
  • If the player decides to hit, another card is dealt.
  • If the player decides to stand, then the dealer reveals his hidden card.
  • The dealer does not have the authority to decide whether to hit or stand. The general rule is that the dealer needs to keep hitting more cards if the sum of dealer's cards is less than 17.
  • As soon as the sum of dealer's cards is either 17 or more, the dealer is obliged to stand.
  • According to the final sum of the cards, the winner is decided.

The programming of the Blackjack game becomes simple as soon as the rules are understood. Creating a terminal-based game from scratch requires three main components: The Game Design, The Game Logic, and Management of Player Interaction.


Blackjack Game Demo

A Demo of our Blackjack Game

Designing Blackjack in Python

Firstly, we will work on our game's design. Our job is to effectively display a series of cards on the terminal something like the following figure.

Blackjack Terminal Design
Game

We need a function that prints a sequence of cards and is independent of the number of cards. Moreover, it must provide a functionality to print a hidden card if needed.

The following code solves our problem.

# Function to print the cards def print_cards(cards, hidden): 		 	s = "" 	for card in cards: 		s = s + "\t ________________" 	if hidden: 		s += "\t ________________" 	print(s)   	s = "" 	for card in cards: 		s = s + "\t|                |" 	if hidden: 		s += "\t|                |"		 	print(s)  	s = "" 	for card in cards: 		if card.value == '10': 			s = s + "\t|  {}            |".format(card.value) 		else: 			s = s + "\t|  {}             |".format(card.value)	 	if hidden: 		s += "\t|                |"		 	print(s)  	s = "" 	for card in cards: 		s = s + "\t|                |" 	if hidden: 		s += "\t|      * *       |"	 	print(s)	  	s = "" 	for card in cards: 		s = s + "\t|                |" 	if hidden: 		s += "\t|    *     *     |"	 	print(s)	  	s = "" 	for card in cards: 		s = s + "\t|                |" 	if hidden: 		s += "\t|   *       *    |"	 	print(s)	  	s = "" 	for card in cards: 		s = s + "\t|                |" 	if hidden: 		s += "\t|   *       *    |"	 	print(s)	  	s = "" 	for card in cards: 		s = s + "\t|       {}        |".format(card.suit) 	if hidden: 		s += "\t|          *     |"	 	print(s)	  	s = "" 	for card in cards: 		s = s + "\t|                |" 	if hidden: 		s += "\t|         *      |"	 	print(s)	  	s = "" 	for card in cards: 		s = s + "\t|                |" 	if hidden: 		s += "\t|        *       |"	 	print(s)  	s = "" 	for card in cards: 		s = s + "\t|                |" 	if hidden: 		s += "\t|                |"	 	print(s)  	s = "" 	for card in cards: 		s = s + "\t|                |" 	if hidden: 		s += "\t|                |"	 	print(s)	  	s = "" 	for card in cards: 		if card.value == '10': 			s = s + "\t|            {}  |".format(card.value) 		else: 			s = s + "\t|            {}   |".format(card.value) 	if hidden: 		s += "\t|        *       |"			 	print(s)	 		 	s = "" 	for card in cards: 		s = s + "\t|________________|" 	if hidden: 		s += "\t|________________|"	 	print(s)		  	print()                

The details of each Card are stored as a Card Object. The second parameter of the print_cards() function is a boolean value that indicates whether a hidden card is to be displayed or not.


Creating a Card

With the help of classes and objects, we can create an ensemble of suits and values to represent a "playing card". In Blackjack, a card has three properties, its suit, its representing value and its value as score.

All the above properties are maintained within the following Card Class.

# The Card Class definition class Card: 	def __init__(self, suit, value, card_value): 		 		# Suit of the Card like Spades and Clubs 		self.suit = suit  		# Representing Value of the Card like A for Ace, K for King 		self.value = value  		# Score Value for the Card like 10 for King 		self.card_value = card_value                

Using the above class we can create a proper deck of cards containing 52 Card objects.


Some fundamental values

Each game of cards requires fundamental values like the types of suits, the types of cards, and the values for each card.

# The type of suit suits = ["Spades", "Hearts", "Clubs", "Diamonds"]  # The suit value  suits_values = {"Spades":"\u2664", "Hearts":"\u2661", "Clubs": "\u2667", "Diamonds": "\u2662"}  # The type of card cards = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]  # The card value cards_values = {"A": 11, "2":2, "3":3, "4":4, "5":5, "6":6, "7":7, "8":8, "9":9, "10":10, "J":10, "Q":10, "K":10}                

One thing to note here is that, the Ace is initially marked as a 11-point card. The idea behind this strategy is that whenever the player/dealer's score seems to cross 21, we can reduce the score of Ace (if dealt) to 1.

We will see the implementation of the reduction later, in this article.


Generate a deck of playing cards

A normal deck of playing cards consists of 52 cards, each bearing a different combination of suit and value. Using the above fundamental values and the Card Class, we generate a deck of cards.

# The deck of cards deck = []  # Loop for every type of suit for suit in suits:  	# Loop for every type of card in a suit 	for card in cards:  		# Adding card to the deck 		deck.append(Card(suits_values[suit], card, cards_values[card]))                

In reality, a game of Blackjack involves multiple decks, therefore the above set of loops can be re-used for populating multiple decks.

The newly created deck is passed to the function that executes the game.

Let us learn the game logic behind a single iteration of a Blackjack Game between a player and a computer-based dealer.


Declaring Important Game Variables

At any instant of time, we require the following game variables:

  • A list of cards dealt to the player and the dealer.
  • The sum of the card values for each side.
# Function for a single game of blackjack def blackjack_game(deck):  	global cards_values  	# Cards for both dealer and player 	player_cards = [] 	dealer_cards = []  	# Scores for both dealer and player 	player_score = 0 	dealer_score = 0                

These game variables come into play when we design the game logic.


Python Blackjack Game Logic

The entire game logic revolves around the dealing of cards and player's choices for either hitting or standing. As soon as we handle the above two things, we are done for the day.

First Phase of Dealing: Mandatory Cards

The initial dealing involves giving two cards to the player and the dealer. However, the second card for the dealer must remain unknown.

# Initial dealing for player and dealer while len(player_cards) < 2:  	# Randomly dealing a card 	player_card = random.choice(deck) 	player_cards.append(player_card) 	deck.remove(player_card)  	# Updating the player score 	player_score += player_card.card_value  	# In case both the cards are Ace, make the first ace value as 1  	if len(player_cards) == 2: 		if player_cards[0].card_value == 11 and player_cards[1].card_value == 11: 			player_cards[0].card_value = 1 			player_score -= 10  	# Print player cards and score		 	print("PLAYER CARDS: ") 	print_cards(player_cards, False) 	print("PLAYER SCORE = ", player_score)  	input()  	# Randomly dealing a card 	dealer_card = random.choice(deck) 	dealer_cards.append(dealer_card) 	deck.remove(dealer_card)  	# Updating the dealer score 	dealer_score += dealer_card.card_value  	# Print dealer cards and score, keeping in mind to hide the second card and score 	print("DEALER CARDS: ") 	if len(dealer_cards) == 1: 		print_cards(dealer_cards, False) 		print("DEALER SCORE = ", dealer_score) 	else: 		print_cards(dealer_cards[:-1], True)	 		print("DEALER SCORE = ", dealer_score - dealer_cards[-1].card_value)   	# In case both the cards are Ace, make the second ace value as 1  	if len(dealer_cards) == 2: 		if dealer_cards[0].card_value == 11 and dealer_cards[1].card_value == 11: 			dealer_cards[1].card_value = 1 			dealer_score -= 10  	input()  # Player gets a blackjack	 if player_score == 21: 	print("PLAYER HAS A BLACKJACK!!!!") 	print("PLAYER WINS!!!!") 	quit()                

It might be a lot to imbibe for a seemingly simple dealing. Let us understand the process involved in the above code:

  • The main loop runs until the player and the dealer gets two cards each.
  • A card is randomly chosen from the deck and in the next step that card is removed from the deck.
  • The card's value is added to the player's score.
  • Similarly, a card is randomly chosen for the dealer and its value is added to the dealer's score.
  • The player's cards are displayed on the screen normally.
  • The dealer's cards are displayed carefully such that the second card and its value are not revealed.
  • In case, either of the participants gets double Aces, their scores are adjusted such that neither of them busts.
  • After all the above things happen smoothly, we move on to the second stage of dealing.

Note: There is a subtle difference between the adjustment of scores for the player and the dealer. In the former case, the value of the first card is adjusted, whereas, in the latter, the value of the second card is adjusted.

The reason for adjusting the value of the second card is that, had we adjusted the first one, we would have revealed the identity of the hidden card as an Ace.

One final thing that needs to be done here is checking whether the player already has a Blackjack. If he does, the player wins and the game ends.

Note: The input() function pauses the program until the player presses "ENTER". This prevents a quick fall-through of all the game events.

The clear() function is responsible for clearing the terminal giving a clean aesthetic for the game.


Second phase of dealing: Player's Choices

The second stage of dealing depends on the player's decision of either wanting another card for boosting score or standing with the current set of cards.

# Print dealer and player cards print("DEALER CARDS: ") print_cards(dealer_cards[:-1], True) print("DEALER SCORE = ", dealer_score - dealer_cards[-1].card_value)  print()	  print("PLAYER CARDS: ") print_cards(player_cards, False) print("PLAYER SCORE = ", player_score)  # Managing the player moves while player_score < 21: 	choice = input("Enter H to Hit or S to Stand : ")  	# Sanity checks for player's choice 	if len(choice) != 1 or (choice.upper() != 'H' and choice.upper() != 'S'): 		clear() 		print("Wrong choice!! Try Again")  	# If player decides to HIT 	if choice.upper() == 'H':  		# Dealing a new card 		player_card = random.choice(deck) 		player_cards.append(player_card) 		deck.remove(player_card)  		# Updating player score 		player_score += player_card.card_value  		# Updating player score in case player's card have ace in them 		c = 0 		while player_score > 21 and c < len(player_cards): 			if player_cards[c].card_value == 11: 				player_cards[c].card_value = 1 				player_score -= 10 				c += 1 			else: 				c += 1	  		clear()		  		# Print player and dealer cards 		print("DEALER CARDS: ") 		print_cards(dealer_cards[:-1], True) 		print("DEALER SCORE = ", dealer_score - dealer_cards[-1].card_value)  		print()  		print("PLAYER CARDS: ") 		print_cards(player_cards, False) 		print("PLAYER SCORE = ", player_score) 		 	# If player decides to Stand 	if choice.upper() == 'S': 		break  # Check if player has a Blackjack if player_score == 21: 	print("PLAYER HAS A BLACKJACK") 	quit()  # Check if player busts if player_score > 21: 	print("PLAYER BUSTED!!! GAME OVER!!!") 	quit()                

The player decides whether to hit or stand until the score exceeds 21 or the player decides to stand. There is no limit to the number of cards dealt to the player, just on the score.

Every time the player decides to hit, a new card is dealt from the deck and the score is updated. As mentioned before, an Ace can be counted as 1 or 11. A special piece of code converts Ace's value from 11 to 1, in case the score exceeds 21.

The player stands when he is satisfied with the current score. When he does, we move on to the final stage of dealing after making some mandatory checks like a Blackjack or a busting scenario.


Final phase of dealing: Dealer's Cards

In the final stage of dealing, the hidden card of the dealer is revealed and so is the dealer's score. According to the standard Blackjack rules, the dealer has to deal more cards to himself until its score is more than or equal to 17.

# Managing the dealer moves while dealer_score < 17: 	clear()	  	print("DEALER DECIDES TO HIT.....")  	# Dealing card for dealer 	dealer_card = random.choice(deck) 	dealer_cards.append(dealer_card) 	deck.remove(dealer_card)  	# Updating the dealer's score 	dealer_score += dealer_card.card_value  	# Updating player score in case player's card have ace in them 	c = 0 	while dealer_score > 21 and c < len(dealer_cards): 		if dealer_cards[c].card_value == 11: 			dealer_cards[c].card_value = 1 			dealer_score -= 10 			c += 1 		else: 			c += 1  	# print player and dealer cards 	print("PLAYER CARDS: ") 	print_cards(player_cards, False) 	print("PLAYER SCORE = ", player_score)  	print()  	print("DEALER CARDS: ") 	print_cards(dealer_cards, False) 	print("DEALER SCORE = ", dealer_score)  	input()                

The dealer keeps hitting until the score crosses 17 mark. We have a similar implementation of converting card values of Aces from 11 to 1, if needed.


The End Game

When the dealer's score is either 17 or more, we move onto the End Game, which involves comparing of values and nominating the winner of the game. There can be a few scenarios possible:

  • The Dealer Busts – The dealer's score exceeds 21.
  • The Dealer has Blackjack – The dealer has an exact score of 21.
  • A Tie Game – Both the player and the dealer has equal score.
  • The Player Wins – The player's score is more than that of the dealer.
  • The Dealer Wins – The dealer's score is more than that of the player.

We check for each of the above possibilities and declare the winner.

# Dealer busts if dealer_score > 21:		 	print("DEALER BUSTED!!! YOU WIN!!!")  	quit()	  # Dealer gets a blackjack if dealer_score == 21: 	print("DEALER HAS A BLACKJACK!!! PLAYER LOSES") 	quit()  # TIE Game if dealer_score == player_score: 	print("TIE GAME!!!!")  # Player Wins elif player_score > dealer_score: 	print("PLAYER WINS!!!")					  # Dealer Wins else: 	print("DEALER WINS!!!")                

This concludes a single iteration of a game of Blackjack between a player and a dealer.


Complete Python Code for Blackjack Game

import random import os import time  # The Card class definition class Card: 	def __init__(self, suit, value, card_value): 		 		# Suit of the Card like Spades and Clubs 		self.suit = suit  		# Representing Value of the Card like A for Ace, K for King 		self.value = value  		# Score Value for the Card like 10 for King 		self.card_value = card_value  # Clear the terminal def clear(): 	os.system("clear")  # Function to print the cards def print_cards(cards, hidden): 		 	s = "" 	for card in cards: 		s = s + "\t ________________" 	if hidden: 		s += "\t ________________" 	print(s)   	s = "" 	for card in cards: 		s = s + "\t|                |" 	if hidden: 		s += "\t|                |"		 	print(s)  	s = "" 	for card in cards: 		if card.value == '10': 			s = s + "\t|  {}            |".format(card.value) 		else: 			s = s + "\t|  {}             |".format(card.value)	 	if hidden: 		s += "\t|                |"		 	print(s)  	s = "" 	for card in cards: 		s = s + "\t|                |" 	if hidden: 		s += "\t|      * *       |"	 	print(s)	  	s = "" 	for card in cards: 		s = s + "\t|                |" 	if hidden: 		s += "\t|    *     *     |"	 	print(s)	  	s = "" 	for card in cards: 		s = s + "\t|                |" 	if hidden: 		s += "\t|   *       *    |"	 	print(s)	  	s = "" 	for card in cards: 		s = s + "\t|                |" 	if hidden: 		s += "\t|   *       *    |"	 	print(s)	  	s = "" 	for card in cards: 		s = s + "\t|       {}        |".format(card.suit) 	if hidden: 		s += "\t|          *     |"	 	print(s)	  	s = "" 	for card in cards: 		s = s + "\t|                |" 	if hidden: 		s += "\t|         *      |"	 	print(s)	  	s = "" 	for card in cards: 		s = s + "\t|                |" 	if hidden: 		s += "\t|        *       |"	 	print(s)  	s = "" 	for card in cards: 		s = s + "\t|                |" 	if hidden: 		s += "\t|                |"	 	print(s)  	s = "" 	for card in cards: 		s = s + "\t|                |" 	if hidden: 		s += "\t|                |"	 	print(s)	  	s = "" 	for card in cards: 		if card.value == '10': 			s = s + "\t|            {}  |".format(card.value) 		else: 			s = s + "\t|            {}   |".format(card.value) 	if hidden: 		s += "\t|        *       |"			 	print(s)	 		 	s = "" 	for card in cards: 		s = s + "\t|________________|" 	if hidden: 		s += "\t|________________|"	 	print(s)		  	print()   # Function for a single game of blackjack def blackjack_game(deck):  	# Cards for both dealer and player 	player_cards = [] 	dealer_cards = []  	# Scores for both dealer and player 	player_score = 0 	dealer_score = 0  	clear()  	# Initial dealing for player and dealer 	while len(player_cards) < 2:  		# Randomly dealing a card 		player_card = random.choice(deck) 		player_cards.append(player_card) 		deck.remove(player_card)  		# Updating the player score 		player_score += player_card.card_value  		# In case both the cards are Ace, make the first ace value as 1  		if len(player_cards) == 2: 			if player_cards[0].card_value == 11 and player_cards[1].card_value == 11: 				player_cards[0].card_value = 1 				player_score -= 10  		# Print player cards and score		 		print("PLAYER CARDS: ") 		print_cards(player_cards, False) 		print("PLAYER SCORE = ", player_score)  		input()  		# Randomly dealing a card 		dealer_card = random.choice(deck) 		dealer_cards.append(dealer_card) 		deck.remove(dealer_card)  		# Updating the dealer score 		dealer_score += dealer_card.card_value  		# Print dealer cards and score, keeping in mind to hide the second card and score 		print("DEALER CARDS: ") 		if len(dealer_cards) == 1: 			print_cards(dealer_cards, False) 			print("DEALER SCORE = ", dealer_score) 		else: 			print_cards(dealer_cards[:-1], True)	 			print("DEALER SCORE = ", dealer_score - dealer_cards[-1].card_value)   		# In case both the cards are Ace, make the second ace value as 1  		if len(dealer_cards) == 2: 			if dealer_cards[0].card_value == 11 and dealer_cards[1].card_value == 11: 				dealer_cards[1].card_value = 1 				dealer_score -= 10  		input()  	# Player gets a blackjack	 	if player_score == 21: 		print("PLAYER HAS A BLACKJACK!!!!") 		print("PLAYER WINS!!!!") 		quit()  	clear()  	# Print dealer and player cards 	print("DEALER CARDS: ") 	print_cards(dealer_cards[:-1], True) 	print("DEALER SCORE = ", dealer_score - dealer_cards[-1].card_value)  	print()	  	print("PLAYER CARDS: ") 	print_cards(player_cards, False) 	print("PLAYER SCORE = ", player_score)  	# Managing the player moves 	while player_score < 21: 		choice = input("Enter H to Hit or S to Stand : ")  		# Sanity checks for player's choice 		if len(choice) != 1 or (choice.upper() != 'H' and choice.upper() != 'S'): 			clear() 			print("Wrong choice!! Try Again")  		# If player decides to HIT 		if choice.upper() == 'H':  			# Dealing a new card 			player_card = random.choice(deck) 			player_cards.append(player_card) 			deck.remove(player_card)  			# Updating player score 			player_score += player_card.card_value  			# Updating player score in case player's card have ace in them 			c = 0 			while player_score > 21 and c < len(player_cards): 				if player_cards[c].card_value == 11: 					player_cards[c].card_value = 1 					player_score -= 10 					c += 1 				else: 					c += 1	  			clear()		  			# Print player and dealer cards 			print("DEALER CARDS: ") 			print_cards(dealer_cards[:-1], True) 			print("DEALER SCORE = ", dealer_score - dealer_cards[-1].card_value)  			print()  			print("PLAYER CARDS: ") 			print_cards(player_cards, False) 			print("PLAYER SCORE = ", player_score) 			 		# If player decides to Stand 		if choice.upper() == 'S': 			break   	clear()	  	# Print player and dealer cards 	print("PLAYER CARDS: ") 	print_cards(player_cards, False) 	print("PLAYER SCORE = ", player_score)  	print() 	print("DEALER IS REVEALING THE CARDS....")  	print("DEALER CARDS: ") 	print_cards(dealer_cards, False) 	print("DEALER SCORE = ", dealer_score)  	# Check if player has a Blackjack 	if player_score == 21: 		print("PLAYER HAS A BLACKJACK") 		quit()  	# Check if player busts 	if player_score > 21: 		print("PLAYER BUSTED!!! GAME OVER!!!") 		quit()  	input()	  	# Managing the dealer moves 	while dealer_score < 17: 		clear()	  		print("DEALER DECIDES TO HIT.....")  		# Dealing card for dealer 		dealer_card = random.choice(deck) 		dealer_cards.append(dealer_card) 		deck.remove(dealer_card)  		# Updating the dealer's score 		dealer_score += dealer_card.card_value  		# Updating player score in case player's card have ace in them 		c = 0 		while dealer_score > 21 and c < len(dealer_cards): 			if dealer_cards[c].card_value == 11: 				dealer_cards[c].card_value = 1 				dealer_score -= 10 				c += 1 			else: 				c += 1  		# print player and dealer cards 		print("PLAYER CARDS: ") 		print_cards(player_cards, False) 		print("PLAYER SCORE = ", player_score)  		print()  		print("DEALER CARDS: ") 		print_cards(dealer_cards, False) 		print("DEALER SCORE = ", dealer_score)		  		input()  	# Dealer busts 	if dealer_score > 21:		 		print("DEALER BUSTED!!! YOU WIN!!!")  		quit()	  	# Dealer gets a blackjack 	if dealer_score == 21: 		print("DEALER HAS A BLACKJACK!!! PLAYER LOSES") 		quit()  	# TIE Game 	if dealer_score == player_score: 		print("TIE GAME!!!!")  	# Player Wins 	elif player_score > dealer_score: 		print("PLAYER WINS!!!")					  	# Dealer Wins 	else: 		print("DEALER WINS!!!")					  if __name__ == '__main__':  	# The type of suit 	suits = ["Spades", "Hearts", "Clubs", "Diamonds"]  	# The suit value  	suits_values = {"Spades":"\u2664", "Hearts":"\u2661", "Clubs": "\u2667", "Diamonds": "\u2662"}  	# The type of card 	cards = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]  	# The card value 	cards_values = {"A": 11, "2":2, "3":3, "4":4, "5":5, "6":6, "7":7, "8":8, "9":9, "10":10, "J":10, "Q":10, "K":10}  	# The deck of cards 	deck = []  	# Loop for every type of suit 	for suit in suits:  		# Loop for every type of card in a suit 		for card in cards:  			# Adding card to the deck 			deck.append(Card(suits_values[suit], card, cards_values[card])) 	 	blackjack_game(deck)                

The reader is not obliged to follow the entire coding sequence. There can be various amends made to the above code, by adding the facility of multiple players against the dealer.


Conclusion

A game of Blackjack may seem simple and random at first, but only when the players follow certain strategies like Card Counting, the game becomes complex.

There are many versions of Blackjack floating across the world like Swedish Pub Blackjack and Home Game Blackjack. Curious readers can learn about these variants and try to implement them using the knowledge gained in this article.

Thank you for reading. Feel free to check out how to develop a Mastermind game in Python.

Design a Blackjack Game Using Object Oriented Principles

Source: https://www.askpython.com/python/examples/blackjack-game-using-python

0 Response to "Design a Blackjack Game Using Object Oriented Principles"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel