Play blackjack with Python

Python programming language - logo

In this new post, play blackjack with Python, I show how to create a simple application to play this popular game.

Blackjack, also known as 21, is a card game where players try to get as close to 21 points as possible without going over. This program uses images drawn with text characters, called ASCII art. American Standard Code for Information Interchange (ASCII) is a mapping of text characters to numeric codes that computers used before Unicode replaced it.

Blackjack in action with Visual Studio Code - Play blackjack with Python
Blackjack in action with Visual Studio Code

Start the code

First think to do is to import the module sys and random. I will use the sys to exit the while loop and random module to shuffle the desk of cards.

import sys, random

Then, I have to setup the constants for the deck using the chr function and assign to a variable the correspondent character.

Set up the main function

Then, I define the new constants for the suits and use them later in the code.

HEARTS = chr(9829) # Character 9829 is '♥'.
DIAMONDS = chr(9830) # Character 9830 is '♦'.
SPADES = chr(9824) # Character 9824 is '♠'.
CLUBS = chr(9827) # Character 9827 is '♣'.
BACKSIDE = 'backside'

Then, I’m going to define the main function printing same text and set up the money variable.

def main():
    print('''Blackjack

    Rules:
    Try to get as close to 21 without going over.
    Kings, Queens, and Jacks are worth 10 points.
    Aces are worth 1 or 11 points.
    Cards 2 through 10 are worth their face value.
    
    (H)it to take another card.
    (S)tand to stop taking cards.
    
    On your first play, you can (D)ouble down to increase your bet
    but must hit exactly one more time before standing.
    
    In case of a tie, the bet is returned to the player.
    The dealer stops hitting at 17.''')

    money = 5000

The application can start only if I call the main function. For this reason, I have to call the main function at the end of the application with this code

if __name__ == '__main__':
    main()

Shuffle the deck

After that, I want to create a function that returns the deck: from 2 to 10 values and the face and aces cards for each suit. The """ is the comment to the function that it will show if you call the help function for this function.

def getDeck():
    """Return a list of cards from the deck."""
    deck = []
    for suit in (HEARTS, DIAMONDS, SPADES, CLUBS):
        for rank in range(2, 11):
            # add the numbered cards
            deck.append((str(rank), suit))
        for rank in ('J', 'Q', 'K', 'A'):
            # add the face and ace cards
            deck.append((rank, suit))
    random.shuffle(deck)
    return deck

Getting the bet

So, the next step it to write a function that takes the bet from the player. If the player texts “QUIT” the application will terminate invoking sys.exit(). input is waiting for the user to type a value. Then, the application will check if the value is a valid bet. If it is, the function will print out an error and wait for another input. If the value is accepted, the function will return the value of the bet. I like to point out that I use the function isdecimal to check if the value is a decimal number. Also, in the if I like to syntax to verify if the value is a decimal number between 1 and the maxBet.

def getBet(maxBet):
    """Ask the player how much money they want to bet for this round."""
    while True:
        # keep asking until they enter a valid amount
        print('How much do you bet? (1-{}, or QUIT)'.format(maxBet))
        bet = input('> ').upper().strip()
        if bet == 'QUIT':
            print('Thanks for playing!')
            sys.exit()

        if not bet.isdecimal():
            # if the player didn't enter a number, ask again
            print('Please enter a number.\n')
            continue

        bet = int(bet)
        if 1 <= bet <= maxBet:
            # player entered a valid bet
            return bet

Now, in the main function, I add the following code to read the bet from the user and then call the getDeck() to have the full deck of cards shuffled.

while True:
    # check if player has run out of money
    if money <= 0:
        print('You have run out of money.')
        print('You lose.')
        print('Thanks for playing.')
        sys.exit()

    # let the player enter their bet for this round
    print('Money: ', money)
    bet = getBet(money)
    
    # give the dealer and player two cards from the deck each
    deck = getDeck()

Values of the card for each player

Now, we have to create a function that returns the value of the cards for each player. Here you have the code.

def getHandValue(cards):
    """Return the value of the cards. Face cards are worth 10, Ace cards are worth 11 or 1
    (this function picks the most suitable ace value)"""
    value = 0
    numberOfAces = 0

    # add the value for the non-ace cards
    for card in cards:
        # card is a tuple like (rank, suit)
        rank = card[0]
        if rank == 'A':
            numberOfAces += 1
        elif rank in ('K', 'Q', 'J'):
            # face cards are worth 10 points
            value += 10
        else:
            # numbered cards are woth their number
            value += int(rank)
    
    # add the value for the ace cards
    value += numberOfAces
    for i in range(numberOfAces):
        # if another 10 can be added with busting, do so
        if value + 10 <= 21:
            value += 10
    
    return value

Display the cards

So, this is a console application and we don’t have any graphics but we can create, in an old style manner, the cards. Simple but efficient.

def displayCards(cards):
    """Display all the cards in the cards list."""
    rows = ['', '', '', '', '']  # The text to display on each row.

    for i, card in enumerate(cards):
        rows[0] += ' ___  '  # Print the top line of the card.
        if card == BACKSIDE:
            # Print a card's back:
            rows[1] += '|## | '
            rows[2] += '|###| '
            rows[3] += '|_##| '
        else:
            # Print the card's front:
            rank, suit = card  # The card is a tuple data structure.
            rows[1] += '|{} | '.format(rank.ljust(2))
            rows[2] += '| {} | '.format(suit)
            rows[3] += '|_{}| '.format(rank.rjust(2, '_'))
        
        # Print each row on the screen:
        for row in rows:
            print(row)

Next move

Now, the last function has to check the options for the next move for the player.

def getMove(playerHand, money):
    """Asks the player for their move and returns'H' fir hit, 'S' stand, or 'D' double down."""

    # keep asking until they enter 'H' or 'S' or 'D'
    while True:
        # determine the player's move
        moves = ['(H)it', '(S)tand']

        # the player can double down on their first move, which we can
        # tell because they will be exactly teo cards
        if len(playerHand) == 2 and money > 0:
            moves.append('(D)ouble down')

        # get the player's move
        movePrompt = ', '.join(moves) + ' > '
        move = input(movePrompt).upper()
        if move in ('H', 'S'):
            # player has entered a valid move
            return move
        if move == 'D' and '(D)ouble down' in moves:
            # player has entered a valid move
            return move

Full code

Last part is to implement the main function with all the functionalities to play the game.

import random, sys

# Set up constants
HEARTS   = chr(9829) # Character 9829 is '♥'.
DIAMONDS = chr(9830) # Character 9830 is '♦'.
SPADES   = chr(9824) # Character 9824 is '♠'.
CLUBS    = chr(9827) # Character 9827 is '♣'.
BACKSIDE = 'backside'

def main():
    print('''Blackjack

    Rules:
    Try to get as close to 21 without going over.
    Kings, Queens, and Jacks are worth 10 points.
    Aces are worth 1 or 11 points.
    Cards 2 through 10 are worth their face value.
    
    (H)it to take another card.
    (S)tand to stop taking cards.
    
    On your first play, you can (D)ouble down to increase your bet
    but must hit exactly one more time before standing.
    
    In case of a tie, the bet is returned to the player.
    The dealer stops hitting at 17.''')

    money = 5000

    # main game loop
    while True:
        # check if player has run out of money
        if money <= 0:
            print('You have run out of money.')
            print('You lose.')
            print('Thanks for playing.')
            sys.exit()

        # let the player enter their bet for this round
        print('Money: ', money)
        bet = getBet(money)
        
        # give the dealer and player two cards from the deck each
        deck = getDeck()
        print(deck)
        dealerHand = [deck.pop(), deck.pop()]
        playerHand = [deck.pop(), deck.pop()]

        # handle player actions
        print('Bet: ', bet)
        while True:
            # keep looping until player stands or busts
            displayHands(playerHand, dealerHand, False)
            print()

            # check if the player has bust
            if getHandValue(playerHand) > 21:
                break

            # get the player's move, either H, S or D
            move = getMove(playerHand, money - bet)

            # handle player actions
            if move == 'D':
                # player is doubled down, they can increase their bet
                additionalBet = getBet(min(bet, (money - bet)))
                bet += additionalBet
                print('Bet increased to {}.'.format(bet))
                print('Bet: ', bet)

            if move in ('H', 'D'):
                # hit/doubling down takes another card
                newCard = deck.pop()
                rank, suit = newCard
                print('You drew a {} of {}.'.format(rank, suit))
                playerHand.append(newCard)

                if getHandValue(playerHand) > 21:
                    # the player has busted
                    continue
            
            if move in ('S', 'D'):
                # stand/doubling down stops the player's turn
                break
        
        # handle the dealer's actions
        if getHandValue(playerHand) <= 21:
            while getHandValue(dealerHand) < 17:
                # the dealer hits
                print('Dealer hits...')
                dealerHand.append(deck.pop())
                displayHands(playerHand, dealerHand, False)

                if getHandValue(playerHand) > 21:
                    # the dealer has busted
                    break

                input('Press Enter to continue...')
                print('\n\n')

        # show the final hands
        displayHands(playerHand, dealerHand, True)

        playerValue = getHandValue(playerHand)
        dealerValue = getHandValue(dealerHand)

        # handle wheter the player won, lost or tied
        if dealerValue > 21:
            print('Dealer busts! You win £{}!'.format(bet))
            money += bet
        elif (playerValue > 21) or (playerValue < dealerValue):
            print('You lost!')
            money -= bet
        elif playerValue > dealerValue:
            print('You won £{}!'.format(bet))
            money += bet
        elif playerValue == dealerValue:
            print('It\'s a tie, the bet is returned to you')

        input('Press Enter to continue...')
        print('\n\n')

def getBet(maxBet):
    """Ask the player how much money they want to bet for this round."""
    while True:
        # keep asking until they enter a valid amount
        print('How much do you bet? (1-{}, or QUIT)'.format(maxBet))
        bet = input('> ').upper().strip()
        if bet == 'QUIT':
            print('Thanks for playing!')
            sys.exit()

        if not bet.isdecimal():
            # if the player didn't enter a number, ask again
            print('Please enter a number.\n')
            continue

        bet = int(bet)
        if 1 <= bet <= maxBet:
            # player entered a valid bet
            return bet

def getDeck():
    """Return a list of cards from the deck."""
    deck = []
    for suit in (HEARTS, DIAMONDS, SPADES, CLUBS):
        for rank in range(2, 11):
            # add the numbered cards
            deck.append((str(rank), suit))
        for rank in ('J', 'Q', 'K', 'A'):
            # add the face and ace cards
            deck.append((rank, suit))
    random.shuffle(deck)
    return deck

def displayHands(playerHand, dealerHand, showDealerHand):
    """Show the player's hand and the dealer's cards. Hide the dealer's first card if showDealerHand is False."""
    print()
    if showDealerHand:
        print('DEALER:', getHandValue(dealerHand))
        displayCards(dealerHand)
    else:
        print('DEALER: ???')
        # Hide the dealer's first card
        displayCards([BACKSIDE] + dealerHand[1:])

    # show the player's hand
    print('PLAYER:', getHandValue(playerHand))
    displayCards(playerHand)

def getHandValue(cards):
    """Return the value of the cards. Face cards are worth 10, Ace cards are worth 11 or 1
    (this function picks the most suitable ace value)"""
    value = 0
    numberOfAces = 0

    # add the value for the non-ace cards
    for card in cards:
        # card is a tuple like (rank, suit)
        rank = card[0]
        if rank == 'A':
            numberOfAces += 1
        elif rank in ('K', 'Q', 'J'):
            # face cards are worth 10 points
            value += 10
        else:
            # numbered cards are woth their number
            value += int(rank)
    
    # add the value for the ace cards
    value += numberOfAces
    for i in range(numberOfAces):
        # if another 10 can be added with busting, do so
        if value + 10 <= 21:
            value += 10
    
    return value

def displayCards(cards):
    """Display all the cards in the cards list."""
    rows = ['', '', '', '', '']  # The text to display on each row.

    for i, card in enumerate(cards):
        rows[0] += ' ___  '  # Print the top line of the card.
        if card == BACKSIDE:
            # Print a card's back:
            rows[1] += '|## | '
            rows[2] += '|###| '
            rows[3] += '|_##| '
        else:
            # Print the card's front:
            rank, suit = card  # The card is a tuple data structure.
            rows[1] += '|{} | '.format(rank.ljust(2))
            rows[2] += '| {} | '.format(suit)
            rows[3] += '|_{}| '.format(rank.rjust(2, '_'))
        
        # Print each row on the screen:
        for row in rows:
            print(row)

def getMove(playerHand, money):
    """Asks the player for their move and returns'H' fir hit, 'S' stand, or 'D' double down."""

    # keep asking until they enter 'H' or 'S' or 'D'
    while True:
        # determine the player's move
        moves = ['(H)it', '(S)tand']

        # the player can double down on their first move, which we can
        # tell because they will be exactly teo cards
        if len(playerHand) == 2 and money > 0:
            moves.append('(D)ouble down')

        # get the player's move
        movePrompt = ', '.join(moves) + ' > '
        move = input(movePrompt).upper()
        if move in ('H', 'S'):
            # player has entered a valid move
            return move
        if move == 'D' and '(D)ouble down' in moves:
            # player has entered a valid move
            return move

# if the program is run (instead of imported), run the game
if __name__ == '__main__':
    main()

Wrap up

For other examples in Python, here the links:

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.