Terminal-based Hi-Lo Game in Python

Hi Lo Featured Image

This article revolves around creating our own Hi-Low Card Game that can be played on the command line.

Hi-Lo is very simple, yet famous game in casinos, where the objective of the player is to guess whether the next card in the deck is high or low. The card ranking starts at the Ace, as the lowest rank to the King, as the highest rank.

Hi-Lo Game Demo


Looks simple, right? Let us quickly move on to the design section of the game.

Game Design

The most creative part of game development is the game design. Being a terminal-based card game, there are not many degrees of freedom for a programmer.

At a particular instant of the game, three cards are shown, the previous one, the current one, and the bottom-faced next card in the deck. Our game design looks like:

Hi Lo Display
Cards Display

On the screen, we can see, ace of hearts, seven of diamonds and an unknown next card. This is done by:

def print_cards(prev_card, current_card):
	
	print()
	print("\t ________________      ________________      ________________")
	print("\t|                |    |                |    |                |")
	if prev_card.value == '10' and current_card.value == '10':
		print("\t|  {}            |    |  {}            |    |                |".format(prev_card.value,current_card.value))
	elif prev_card.value == '10': 
		print("\t|  {}            |    |  {}             |    |                |".format(prev_card.value,current_card.value))	
	elif current_card.value == '10':
		print("\t|  {}             |    |  {}            |    |                |".format(prev_card.value,current_card.value))	
	else:
		print("\t|  {}             |    |  {}             |    |                |".format(prev_card.value,current_card.value))	
	print("\t|                |    |                |    |      * *       |")
	print("\t|                |    |                |    |    *     *     |")
	print("\t|                |    |                |    |   *       *    |")
	print("\t|                |    |                |    |   *       *    |")
	print("\t|       {}        |    |       {}        |    |          *     |".format(prev_card.suit, current_card.suit))
	print("\t|                |    |                |    |         *      |")
	print("\t|                |    |                |    |        *       |")
	print("\t|                |    |                |    |                |")
	print("\t|                |    |                |    |                |")
	if prev_card.value == '10' and current_card.value == '10':
		print("\t|            {}  |    |            {}  |    |        *       |".format(prev_card.value,current_card.value))
	elif prev_card.value == '10': 
		print("\t|            {}  |    |            {}   |    |        *       |".format(prev_card.value,current_card.value))	
	elif current_card.value == '10':
		print("\t|            {}   |    |            {}  |    |        *       |".format(prev_card.value,current_card.value))	
	else:
		print("\t|            {}   |    |            {}   |    |        *       |".format(prev_card.value,current_card.value))	
	print("\t|________________|    |________________|    |________________|")
	print()

The trick part of printing the cards is the alignment of card borders, which becomes an issue for a 10-valued card since it has two characters instead of one. Using simple conditional statements, the issue is resolved.

The print_cards() function accepts two arguments, that are Card objects.


Creating a Card

The best way to represent a “playing card” is by using objects. We create a Card class.

class Card:
	def __init__(self, suit, value):
		self.suit = suit
		self.value = value

Any card has two characteristics:

  • Suit – The type of suit of the card, for example, Spades
  • Value – The face value of the card, for example, Ace or Nine.

To learn more of Classes and objects in Python, visit here.


Suits and Values

We need certain data structures to store the types of suits and cards.

# 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": 1, "2":2, "3":3, "4":4, "5":5, "6":6, "7":7, "8":8, "9":9, "10":10, "J":11, "Q":12, "K":13}

Each of these data structures play some role in the smooth functioning of the game.


Create a deck of cards

A deck of cards contain 52 cards, each with a different combination of suit and value. Using a list of objects, we store all the 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))

After all the preparations are done, it is time to start the game.

hi_lo_game(deck)

The function hi_lo_game() is responsible for the running of a game. It requires a deck of cards for its working.


Set up game variables

Before we move onto the game logic, we need set up some game variables:

  • Previous Card – We need to initialize the previous card with at an empty card.
  • Current Card – Initialize the current card
    • A standard rule of Hi-Lo game requires the starting card to not be the lowest card or the highest card.
    • Remove the current card from the deck of cards
  • Score – The count of correct guesses.
  • Chances – The number of chances left for an incorrect guess.
def hi_lo_game(deck):

	global cards_values

	# Initialize the previous card
	prev_card = Card(" ", " ")

	# Initialize the current card
	current_card = random.choice(deck)

	# The starting card cannot be lowest or highest
	while current_card.value == "A" or current_card.value == "K":
		current_card = random.choice(deck)

	# Remove the card from the deck	
	deck.remove(current_card)

	# Number of chances left
	chances = 3

	# The current
	score = 0

The global keyword before card_values in the first line of function, is responsible for obtaining the global variable card_values defined outside the scope of the current function hi_lo_game().


The Game Loop

One of the key components of game logic is the Game Loop. In the context of our version of Hi-Lo game, the game loop depends on the number of chances left for the player. Therefore:

# The GAME LOOP
while chances:

The while loop runs until the chances left for the player is not zero.


Display Scoreboard

There are two things that need to be displayed in our scoreboard: Score and Chances.

# Function to print the scorebaord	
def print_scoreboard(score, chances):
	print("\t\t\t     ____________________")
	print("\t\t\t    |                    |")
	if score >= 10:
		print("\t\t\t    |     Score = {}     |".format(score))
	else:	
		print("\t\t\t    |     Score = {}      |".format(score))
	print("\t\t\t    |  Chances Left = {}  |".format(chances))	
	print("\t\t\t    |____________________|")

The Game Menu

Our game menu is the interface required for accepting player input. During the gameplay, the menu looks like this:

Hi Lo Gameplay
Game Menu

This game menu is created by:

print_scoreboard(score, chances)
print_cards(prev_card, current_card)

print("\t\t   ------------------------------------")
print("\t\t\t\tGAME MENU")
print("\t\t   ------------------------------------")
print()
print("\t\t      Enter 1 to bet for a high card")
print("\t\t      Enter 0 to bet for a low card")
print()

Accept Player Input

The only thing that a player does in this game is guess “High” or “Low”. In the game menu, we have already allotted 1 to “High”, whereas 0 to “Low”.

# Try block for player input error
try:
	choice = int(input("\t\t\t  Enter your choice = "))
except ValueError:
	clear()
	print("\t\t\tWrong Input!! Try Again.")
	continue	

# Some wrong choice
if choice > 1 or choice < 0:
	clear()
	print("\t\t\tWrong Input!! Try Again.")
	continue		

There is try block for suppressing the errors, and channeling them through the except section.


Switch the cards

The game logic for our Hi-Lo game is very easy. We need to switch the current card to the previous one and the unknown card as the current card.

# Switch the current card to the previous card
prev_card = current_card

# Choose the new current card
current_card = random.choice(deck)

# Remove the new card from the deck
deck.remove(current_card)

Check the round result

After the new card is selected, we can check for the result, that is, high or low.

# A high card
if cards_values[current_card.value] > cards_values[prev_card.value]:
	result = 1

# A low card	
elif cards_values[current_card.value] < cards_values[prev_card.value]:
	result = 0

# Same value card	
else:
	result = -1	

Manage game variables

The game variables like score and chances need to be updated according to the result.

# A Tie Round
if result == -1:
	clear()
	print("\t\t\t TIE GAME!! Play Again")

# Round won
elif choice == result:
	clear()
	print("\t\t\t YOU WIN!!! Play Again")
	score = score + 1	

# Round Lost	
else:
	if chances == 1:
		clear()
		print("\t\t\t\tGAME OVER")
		print_cards(prev_card, current_card)
		print("\t\t        Your Final Score =", score)
		print("\t\t      Thank you for playing!!!")
		break	
	clear()
	print("\t\t\t YOU LOSE!! Play Again")
	chances = chances - 1

There is no issue until the player is winning. When the player loses, we need to manage the end game as well.

As soon as the player consumes the last chance, the game displays the final state that involves printing the final score.

This sums up the tutorial for creating our own Hi-Lo game in Python Language.


The Complete Code

import random
import os

# Function to clear the terminal
def clear():
	os.system("clear")

# Function to print the scorebaord	
def print_scoreboard(score, chances):
	print("\t\t\t     ____________________")
	print("\t\t\t    |                    |")
	if score >= 10:
		print("\t\t\t    |     Score = {}     |".format(score))
	else:	
		print("\t\t\t    |     Score = {}      |".format(score))
	print("\t\t\t    |  Chances Left = {}  |".format(chances))	
	print("\t\t\t    |____________________|")

# Function to print the cards
def print_cards(prev_card, current_card):
	
	print()
	print("\t ________________      ________________      ________________")
	print("\t|                |    |                |    |                |")
	if prev_card.value == '10' and current_card.value == '10':
		print("\t|  {}            |    |  {}            |    |                |".format(prev_card.value,current_card.value))
	elif prev_card.value == '10': 
		print("\t|  {}            |    |  {}             |    |                |".format(prev_card.value,current_card.value))	
	elif current_card.value == '10':
		print("\t|  {}             |    |  {}            |    |                |".format(prev_card.value,current_card.value))	
	else:
		print("\t|  {}             |    |  {}             |    |                |".format(prev_card.value,current_card.value))	
	print("\t|                |    |                |    |      * *       |")
	print("\t|                |    |                |    |    *     *     |")
	print("\t|                |    |                |    |   *       *    |")
	print("\t|                |    |                |    |   *       *    |")
	print("\t|       {}        |    |       {}        |    |          *     |".format(prev_card.suit, current_card.suit))
	print("\t|                |    |                |    |         *      |")
	print("\t|                |    |                |    |        *       |")
	print("\t|                |    |                |    |                |")
	print("\t|                |    |                |    |                |")
	if prev_card.value == '10' and current_card.value == '10':
		print("\t|            {}  |    |            {}  |    |        *       |".format(prev_card.value,current_card.value))
	elif prev_card.value == '10': 
		print("\t|            {}  |    |            {}   |    |        *       |".format(prev_card.value,current_card.value))	
	elif current_card.value == '10':
		print("\t|            {}   |    |            {}  |    |        *       |".format(prev_card.value,current_card.value))	
	else:
		print("\t|            {}   |    |            {}   |    |        *       |".format(prev_card.value,current_card.value))	
	print("\t|________________|    |________________|    |________________|")
	print()


# The Card class definition
class Card:
	def __init__(self, suit, value):
		self.suit = suit
		self.value = value

def hi_lo_game(deck):

	global cards_values

	# Initialize the previous card
	prev_card = Card(" ", " ")

	# Initialize the current card
	current_card = random.choice(deck)

	# The starting card cannot be lowest or highest
	while current_card.value == "A" or current_card.value == "K":
		current_card = random.choice(deck)

	# Remove the card from the deck	
	deck.remove(current_card)

	# Number of chances left
	chances = 3

	# The current
	score = 0

	# The GAME LOOP
	while chances:

		print_scoreboard(score, chances)
		print_cards(prev_card, current_card)

		print("\t\t   ------------------------------------")
		print("\t\t\t\tGAME MENU")
		print("\t\t   ------------------------------------")
		print()
		print("\t\t      Enter 1 to bet for a high card")
		print("\t\t      Enter 0 to bet for a low card")
		print()
		
		# Check if we reached the end of the deck
		if len(deck) == 0:
			clear()
			print_cards(prev_card, current_card)
			print("\t\t    YOU HAVE REACHED THE END OF THE DECK!")
			print("\t\t           Congratulations!!!")
			print()
			print("\t\t          Your Final Score =", score)
			print("\t\t        Thank you for playing!!!")
			break

		# Try block for player input error
		try:
			choice = int(input("\t\t\t  Enter your choice = "))
		except ValueError:
			clear()
			print("\t\t\tWrong Input!! Try Again.")
			continue	

		# Some wrong choice
		if choice > 1 or choice < 0:
			clear()
			print("\t\t\tWrong Input!! Try Again.")
			continue		

		# Switch the current card to the previous card
		prev_card = current_card

		# Choose the new current card
		current_card = random.choice(deck)

		# Remove the new card from the deck
		deck.remove(current_card)

		# A high card
		if cards_values[current_card.value] > cards_values[prev_card.value]:
			result = 1

		# A low card	
		elif cards_values[current_card.value] < cards_values[prev_card.value]:
			result = 0

		# Same value card	
		else:
			result = -1	 	

		# A Tie Round
		if result == -1:
			clear()
			print("\t\t\t TIE GAME!! Play Again")

		# Round won
		elif choice == result:
			clear()
			print("\t\t\t YOU WIN!!! Play Again")
			score = score + 1	

		# Round Lost	
		else:
			if chances == 1:
				clear()
				print("\t\t\t\tGAME OVER")
				print_cards(prev_card, current_card)
				print("\t\t        Your Final Score =", score)
				print("\t\t      Thank you for playing!!!")
				break	
			clear()
			print("\t\t\t YOU LOSE!! Play Again")
			chances = chances - 1


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": 1, "2":2, "3":3, "4":4, "5":5, "6":6, "7":7, "8":8, "9":9, "10":10, "J":11, "Q":12, "K":13}

	# 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))

	hi_lo_game(deck)

Conclusion

Hi-Lo game in Python is a very simple game to create. We hope that the underlying concepts of creating a simple terminal-based game were clear to the reader.

If you have any queries or suggestions, then you can drop us a line in the comments section below.