Table of contents
1.
Introduction
2.
System Requirements
2.1.
Functional Requirements
2.2.
Non-Functional Requirements
3.
Class Diagram
4.
Use Case Diagram
5.
Code in C++
5.1.
Code Explanation
5.2.
Code Output
6.
Frequently Asked Questions
6.1.
What is redundancy?
6.2.
What is passive redundancy?
6.3.
What is active redundancy?
7.
Conclusion
Last Updated: Mar 27, 2024
Medium

Design Blackjack and a Deck of Cards - Low Level Design

Author Suraj Pandey
1 upvote
Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

Blackjack is a card game involving one or more standard playing cards, each consisting of 52 cards divided into four suits (Spades, Hearts, Diamonds, and Clubs). The game's objective is to have a hand value of 21 or as close to 21 as possible, without exceeding it, to beat the dealer.

illustrative diagram

Each player is dealt two cards at the beginning of the game, while the dealer receives one face-up card and one face-down card. During their turn, players can choose to hit (receive another card) or stand (maintain their current hand value). If a player's hand value exceeds 21, they are deemed to have busted and lose the game.

The dealer must hit until their hand value reaches 17 or higher, at which point they must stand. The player with the highest hand value, without exceeding 21, wins the game. The ranking of cards within each suit is: Ace, King, Queen, Jack, 10, 9, 8, 7, 6, 5, 4, 3, 2. The ranking of suits may vary depending on the game, but in most cases, Spades is the highest-ranking suit, followed by Hearts, Diamonds, and Clubs.

System Requirements

The system requirements are both functional and non-functional requirements, which serve to define the necessary capabilities and characteristics of the system to ensure successful implementation and operation.

Functional Requirements

Functional requirements are the specific tasks, functions or features a software system must perform to fulfil the user's needs and expectations. These requirements define the system's capabilities, inputs, outputs, and interactions with other systems. Some examples of functional requirements in the code are:

  1. Shuffle the deck of cards before each game.
     
  2. Deal two cards to the player and two cards to the dealer.
     
  3. Display the player's cards and the dealer's first card.
     
  4. Allow the player to hit (take another card) or stand (keep their current hand).
     
  5. End the game if the player busts (goes over 21).
     
  6. Reveal the dealer's second card and continue to hit until their hand value is at least 17.
     
  7. Compare the hands of the player and dealer and determine the winner or if the game ends in a tie.
     
  8. Display the result of the game.

Non-Functional Requirements

Non-functional requirements are the desired qualities or characteristics of a system, product, or service. Examples include: performance, scalability, security, usability, reliability, maintainability, compatibility, and portability.

Class Diagram

The class diagram in the code is a visual representation of the program's building blocks, created using Unified Modeling Language (UML). It includes seven classes: card, BlackjackCard, deck, shoe, hand, player, and game. Each class interacts with the others through the use of member variables and method calls, which allow for information to be shared and processed between the classes.

class diagram

For a detailed overview of class diagrams, including their definition and creation process, refer to this article.

Use Case Diagram

A use case diagram for the Blackjack card game can depict the various actors involved in the game and the actions they can perform. The main actors in the game are the player and the dealer. The player can perform actions such as hitting, standing, and busting, while the dealer can perform actions such as hitting and standing. The game also uses a deck of cards as a system in the diagram.

The use case diagram for Blackjack would include the following elements:

  • Actor: Player, Dealer
     
  • Use case: Hit, Stand, Bust
     
  • System: Deck of cards.
     

The use case diagram would show the relationships between the actors, use cases, and systems, clearly representing the game and its mechanics.

use case diagram

Code in C++

#include <vector>
#include <iostream>
#include <random>
#include <algorithm>

class Card {
public:
    int suit, value;
    Card(int s, int v) : suit(s), value(v) {}
};

class BlackjackCard : public Card {
public:
    BlackjackCard(int s, int v) : Card(s, v) {}
    int getValue() {
        if (value >= 10) return 10;
        return value;
    }
};

class Deck {
public:
    std::vector<BlackjackCard> cards;
    Deck() {
        for (int s = 0; s < 4; ++s) {
            for (int v = 1; v <= 11; ++v) {
                cards.push_back(BlackjackCard(s, v));
            }
        }
    }
    void shuffle() {
        std::random_device rd;
        std::mt19937 g(rd());
        std::shuffle(cards.begin(), cards.end(), g);
    }
};

class Shoe {
public:
    std::vector<Deck> decks;
    Shoe(int n) {
        for (int i = 0; i < n; ++i) {
            decks.push_back(Deck());
        }
    }
    void shuffle() {
        for (auto &deck : decks) {
            deck.shuffle();
        }
    }
};

class Hand {
public:
    std::vector<BlackjackCard> cards;
    Hand() {}
    int getHardValue() {
        int sum = 0;
        for (auto &card : cards) {
            sum += card.getValue();
        }
        return sum;
    }
    int getSoftValue() {
        int sum = 0;
        int aces = 0;
        for (auto &card : cards) {
            sum += card.getValue();
            if (card.getValue() == 11) {
                aces++;
            }
        }
        while (sum > 21 && aces > 0) {
            sum -= 10;
            aces--;
        }
        return sum;
    }
};

class Player {
public:
    int stake;
    Hand hand;
    Player() : stake(0) {}
};

class Game {
public:
    Shoe shoe;
    std::vector<Player> players;
    Game(int n) : shoe(n) {}
    void run() {
        shoe.shuffle();
        for (auto &player : players) {
            player.stake = 100;
            player.hand = Hand();
        }
        for (int i = 0; i < 2; ++i) {
            for (auto &player : players) {
                player.hand.cards.push_back(shoe.decks[0].cards.back());
                shoe.decks[0].cards.pop_back();
            }
        }
        for (auto &player : players) {
            std::cout << "Player " << &player - &players[0] + 1 << ": " << player.hand.getHardValue() << std::endl;
        }
        for (auto &player : players) {
            while (player.hand.getSoftValue() < 17) {
                player.hand.cards.push_back(shoe.decks[0].cards.back());
                shoe.decks[0].cards.pop_back();
                std::cout << "Player " << &player - &players[0] + 1 << ": " << player.hand.getHardValue() << std::endl;
            }
        }
        int maxValue = 0;
        for (auto &player : players) {
            if (player.hand.getSoftValue() > maxValue &&
                player.hand.getSoftValue() <= 21) {
                maxValue = player.hand.getSoftValue();
            }
        }
        for (auto &player : players) {
            if (player.hand.getSoftValue() == maxValue) {
                std::cout << "Player " << &player - &players[0] + 1 << " wins" << std::endl;
            }
        }
    }
};

int main() {
    Game game(1);
    game.players.push_back(Player());
    game.players.push_back(Player());
    game.run();
    return 0;
}
You can also try this code with Online C++ Compiler
Run Code

Code Explanation

The code provided simulates a two-player Blackjack card game. The code starts by defining several classes:

  • Card: The base class representing a card with a suit and value.
     
  • BlackjackCard: A subclass of Card, representing a Blackjack card with a getValue() method to retrieve the card value.
     
  • Deck: A class representing a deck of cards. It contains an array of BlackjackCards and has a shuffle() method to rearrange the deck randomly.
     
  • Shoe: A class representing a collection of decks. It has an array of Decks and a shuffle() method to shuffle all the decks in the collection.
     
  • Hand: A class representing a hand of cards. It has an array of BlackjackCards and two methods: getHardValue() and getSoftValue(). The first method calculates the sum of card values as they are, while the second method calculates the sum considering that aces can have a value of 1 or 11.
     
  • Player: A class representing a player with a stake and a hand of cards.
     
  • Game: The main class representing the game. It has a Shoe, an array of Players, and a run() method to simulate a game.
     

In the main function, the code creates a Game object with 1 deck, and two Player objects. The run() method is called to simulate the game, which performs the following steps:

  • Shuffle the shoe (which is a collection of decks).
     
  • Initialize the players' stake to 100 and deal two cards to each player.
     
  • Print the hard value of each player's hand.
     
  • Have each player hit (draw a card) until they have a hand value of at least 17.
     
  • Determine the player with the highest hand value under 21 and print that player as the winner.
     

The code outputs the results of the simulation to the console.

Code Output

Player 1: 6
Player 2: 13
Player 1: 9
Player 1: 19
Player 2: 22
Player 1 wins
You can also try this code with Online C++ Compiler
Run Code

Frequently Asked Questions

What is redundancy?

Redundancy in system design refers to using multiple components or systems in a setup to provide backup in case of a failure or malfunction. The goal of redundancy is to ensure continuous operation of the system, even if one of the components fails. This helps to improve the reliability and availability of the system, and prevent downtime or data loss. 

There are two main types of redundancy in system design active redundancy and passive redundancy.

Redundancy can be applied to various system components, such as hardware, software, or network components, to improve the overall reliability and resilience of the system.

What is passive redundancy?

Passive redundancy in system design means having multiple, identical components that only one is active at a time. In case of failure, the passive component takes over and becomes the active one. It provides a basic level of redundancy, but is simpler and less expensive than active redundancy, which involves multiple active components working together.

What is active redundancy?

Active redundancy in system design refers to a design approach where multiple active components work together in a coordinated way to provide redundancy and ensure continuous system operation in case of a failure or malfunction. 

In this approach, multiple active components are used in a setup, and each component performs its role and provides backup for the others in case of failure. This type of redundancy can improve performance and availability, as the system can continue to function even if one component fails. 

Examples of active redundancy include load balancing, failover clusters, and redundant arrays of independent disks (RAID). 

Conclusion

In conclusion, the design of a blackjack game and a deck of cards involves a combination of functional and non-functional requirements. The system must perform a series of tasks, such as shuffling the deck of cards, dealing the cards, allowing players to hit or stand, determining the winner, and displaying the game results. The design must also meet certain desired qualities, such as performance, scalability, security, usability, reliability, and maintainability. 

The class diagram and the code in C++ provided in this article serve as a guide for implementing a blackjack game and a deck of cards.

Now that you have gained an in-depth understanding of the Blackjack and card game design, you can put your skills to the test by tackling various system design challenges often posed by product-based companies. 

You can also consider our System Design Course to give your career an edge over others.

Your feedback and thoughts are highly valued, so please share them in the comments section.

Live masterclass