Click here to Skip to main content
Click here to Skip to main content

A very simple two-player Blackjack game in C

By , 2 Feb 2013
Rate this:
Please Sign up or sign in to vote.

Introduction 

Here's a very simple game of blackjack, written in C. I was asked to write this as an assignment for an interview; but because I want all potentially useful code to be reused, I decided to publish it on CodeProject. 

Using the code  

The code plays a basic two-player game of Blackjack. The AI is very simple, as the dealer will stop hitting at 17 unless the hand is a "soft hand".  The deck must be reshuffled at least every six rounds, as no pointer checks are performed to see if we ran out of cards. Unlike a real game of Blackjack, the player is not allowed to split cards.

Here's the header, which is probably the simplest Blackjack header one would've ever seen.. 

#ifndef _BLACKJACK_H_
# define _BLACKJACK_H_

/* Total number of cards */
# define NUMBER_CARDS (NUMBER_SUITS * NUMBER_RANKS)

/* Number of rounds before shuffling. Should not be higher than 6. */
# define NUMBER_ROUNDS_SHUFFLE 6

/* Maximum of cards possible in a hand (4 aces, 4 twos, 3 threes) */
# define MAXIMUM_HAND 11

/* Suit */
typedef enum
{
    /* Spade, heart, diamond and club */
    SPADE = 0,
    HEART,
    DIAMOND,
    CLUB,

    /* Number of suits */
    NUMBER_SUITS

} Suit;

/* Rank
 *
 * These ranks do not correspond to
 * the actual value within the game.
 */
typedef enum
{
    ACE = 0,
    TWO,
    THREE,
    FOUR,
    FIVE,
    SIX,
    SEVEN,
    EIGHT,
    NINE,
    TEN,
    JACK,
    QUEEN,
    KING,

    /* Number of ranks */
    NUMBER_RANKS

} Rank;

/* A card */
typedef struct
{
    /* Card suit */
    Suit suit;

    /* Card rank */
    Rank rank;

} Card;

/* Unsigned integer */
typedef unsigned int uint;

#endif /* _BLACKJACK_H_ */

As for the implementation itself, I've condensed the whole functionality into main and two helper functions.. 

/* Standard C libraries */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/* Local headers */
#include "blackjack.h"

/* Global variables */
static const char *suit_names[NUMBER_SUITS] = {"Spade", "Heart", "Diamond", "Club"};
static const char *rank_names[NUMBER_RANKS] = {"Ace", "2", "3", "4", "5", "6", "7",
                                               "8", "9", "10", "Jack", "Queen", "King"};

/* Shuffle a deck of cards */
static inline void shuffle(Card *cards)
{
    Card *aCard = cards;
    const Card *cardEnd = cards + NUMBER_CARDS;

    printf("Shuffling... ");

    /* Shuffle the cards */
    srand(time(NULL));
    aCard = cards;
    do
    {
        Card *swapPosition = cards + rand() % NUMBER_CARDS;
        Card swap = *aCard;
        *aCard = *swapPosition;
        *swapPosition = swap;
    }
    while (++aCard != cardEnd);

    printf("done\n");
}

/* Show a player or dealer's hand */
static inline void show_hand(Card **hand, Card **end, uint points)
{
    Card **aCard = hand;

    printf("Hand (%u) - %s of %s", points,
           rank_names[(*aCard)->rank],
           suit_names[(*aCard)->suit]);

    while (++aCard != end)
    {
        printf(", %s of %s",
               rank_names[(*aCard)->rank],
               suit_names[(*aCard)->suit]);
    }

    printf(".\n");
}

int main(void)
{
    Card cards[NUMBER_CARDS];
    Card *aCard = cards;
    uint roundCount = NUMBER_ROUNDS_SHUFFLE, gamesPlayed = 0, gamesWon = 0;
    char buffer[80];

    /* Load the cards */
    uint i = 0;
    do
    {
        uint j = 0;
        do
        {
            aCard->suit = i;
            aCard->rank = j;
            ++aCard;
        }
        while (++j < NUMBER_RANKS);
    }
    while (++i < NUMBER_SUITS);

    /* Play */
    do
    {
        uint aces = 0, playerPoints = 0, dealerPoints = 0;
        Card *playerHand[MAXIMUM_HAND];
        Card **aHandCard = playerHand;

        /* Shuffle the cards after a specific number of rounds */
        if (++roundCount >= NUMBER_ROUNDS_SHUFFLE)
        {
            shuffle(cards);
            aCard = cards;
            roundCount = 0;
        }

        /* Player plays */
        do
        {
            /* Add the card's rank to player point */
            if (aCard->rank == ACE)
            {
                playerPoints += 11;
                ++aces;
            }
            else if (aCard->rank < JACK)
                playerPoints += aCard->rank + 1;
            else
                playerPoints += 10;

            /* Print the new card */
            printf("New Card... %s of %s\n", rank_names[aCard->rank],
                                             suit_names[aCard->suit]);

            *aHandCard = aCard;
            ++aCard;
            assert(aCard != cards + NUMBER_CARDS);

            /* Let the second card be dealt */
            if (aHandCard == playerHand)
            {
                ++aHandCard;
                *buffer = 'h';
                continue;
            }

            ++aHandCard;

            /* Convert Aces to 1 */
            while (playerPoints > 21 && aces > 0)
            {
                playerPoints -= 10;
                --aces;
            }

            /* Print the hand */
            show_hand(playerHand, aHandCard, playerPoints);

            /* On a bust, dealer doesn't need to play.
             * This single "goto" simplifies the code.
             */
            if (playerPoints > 21)
            {
                printf("Bust!\n");
                goto done;
            }

            /* On Blackjack, let the dealer play */
            if (playerPoints == 21)
            {
                printf("Blackjack!\n");
                break;
            }

            /* Hit or stand? */
            do
            {
                printf("Hit or stand? (h/s) : ");
                scanf("%s", buffer);
            }
            while (*buffer != 'h' && *buffer != 'H' && *buffer != 's' && *buffer != 'S');
        }
        while (*buffer == 'h' || *buffer == 'H');

        /* Dealer plays */
        aHandCard = playerHand;
        aces = 0;
        printf("Dealer plays...\n");
        do
        {
            /* Add the card's rank to dealer point */
            if (aCard->rank == ACE)
            {
                dealerPoints += 11;
                ++aces;
            }
            else if (aCard->rank < JACK)
                dealerPoints += aCard->rank + 1;
            else
                dealerPoints += 10;

            /* Print the new card */
            printf("New Card... %s of %s\n", rank_names[aCard->rank],
                                             suit_names[aCard->suit]);

            *aHandCard = aCard;
            ++aCard;
            assert(aCard != cards + NUMBER_CARDS);

            /* Let the second card be dealt */
            if (aHandCard == playerHand)
            {
                ++aHandCard;
                continue;
            }

            ++aHandCard;

            /* Convert Aces to 1 */
            while (dealerPoints > 21 && aces > 0)
            {
                dealerPoints -= 10;
                --aces;
            }

            /* Print the hand */
            show_hand(playerHand, aHandCard, dealerPoints);

            if (dealerPoints > 21)
            {
                printf("Bust!\n");
                break;
            }

            /* Blackjack */
            if (dealerPoints == 21)
            {
                printf("Blackjack!\n");
                break;
            }

            printf("Hit or stand? (h/s) : ");

            /* Dealer must hit on soft 17 */
            if (dealerPoints < 17 || (dealerPoints == 17 && aces))
            {
                printf("hit\n");
                continue;
            }

            printf("stand\n");
            break;
        }
        while (1);
done:

        /* Keep track of win percentage for the player. */
        if (playerPoints <= 21 && (playerPoints > dealerPoints || dealerPoints > 21))
        {
            ++gamesWon;
            printf("Player wins!\n");
        }
        else if (playerPoints <= 21 && playerPoints == dealerPoints)
            printf("Tie!\n");
        else
            printf("Dealer wins!\n");

        ++gamesPlayed;

        printf("Win percentage - %u%%\n", (gamesWon * 100) / gamesPlayed);

        /* Ask to play again. */
        do
        {
            printf("Play again? (y/n) : ");
            scanf("%s", buffer);
        }
        while (*buffer != 'n' && *buffer != 'N' && *buffer != 'y' && *buffer != 'Y');
    }
    while (*buffer == 'y' || *buffer == 'Y');

    return 0;
}   

I'm including with this article the entire code itself. I've tested it under MacOS, but I'm sure it builds and works fine under Linux (make run..). As I did for my Quicksort algorithm, I'm liberating the code of any copyrights in hope this gets used someday.  

Points of Interest

One point of interest here is the length of the main function itself. If the game of Blackjack was slightly more complex, I would've considered refactoring the "player plays" and "dealer plays" parts in two separate functions. 

There's also one goto statement in there, which I would humorously hope is not the reason why I didn't get the position Smile | <img src=. Other than that, I naturally recommend people to bring me feedback as to tell me what would've been a better way to implement this, while keeping it short, simple and not changing the functionality. 

History  

01-02-2013: Initial release to CodeProject.com.

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication

About the Author

Remi Dufour
Software Developer
United States United States
No Biography provided

Comments and Discussions

 
Suggestionimprovements PinmemberMember 104515637-Dec-13 8:05 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140415.2 | Last Updated 2 Feb 2013
Article Copyright 2013 by Remi Dufour
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid