BlackJack Game

/////////////////////////////////////////////////////////////////////////////
//  This is a simple Blackjack (21) game simulator.                        //
/////////////////////////////////////////////////////////////////////////////

#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <string>
#include <ctime>
using namespace std;

const unsigned int MAX_HAND_CARDS = 10;
const unsigned int MAX_DECK_CARDS = 52;

/////////////////////////////////////////////////////////////////////////////
// This is the Card Class which represents each card in the game.          //
/////////////////////////////////////////////////////////////////////////////

class Card {

public:
    enum rank { ACE = 1, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN,
                JACK, QUEEN, KING };
    // ASCII code of each card
    enum suit { HEARTS = 3, DIAMONDS, CLUBS, SPADES };

    friend ostream& operator<< (ostream& os, const Card aCard);
    Card(rank r = ACE, suit s = SPADES, bool cardfaceup = true);
    ~Card();
    unsigned int GetCardValue() const;
    void FlipCard();
    bool GetFace();

private:
    rank cardRank;
    suit cardSuit;
    bool cardIsFaceUp;
};

// Card class constructor
Card::Card(rank r, suit s, bool cardFaceUp):  cardRank(r), cardSuit(s), cardIsFaceUp(cardFaceUp)
{}

Card::~Card()
{}

// Returns the real value of card if greater than 10
unsigned int Card::GetCardValue() const
{
    int cardValue = 0;

    if (cardIsFaceUp)
    {
        cardValue = cardRank;

        if (cardValue > 10)
            cardValue = 10;
    }

    return cardValue;
}

// For hiding the the first card of dealer
void Card::FlipCard()
{
    cardIsFaceUp = !cardIsFaceUp;
}

// Check if the card is facing up
bool Card::GetFace()
{
    return cardIsFaceUp;
}

/////////////////////////////////////////////////////////////////////////////
// This is the Deck Class which represents a deck of cards.                //
// Each deck in the game can have up to 52 cards maximum.                  //
/////////////////////////////////////////////////////////////////////////////

class Deck {

public:
    Deck(unsigned int dPos = 0);
    ~Deck();
    void ClearDeck();
    void PopulateDeck();
    void ShuffleDeck();
    bool DeckIsEmpty();
    void AddCardToDeck(Card pCard);
    unsigned int CurrentPosition();
    Card GiveCardToPlayer();

private:
    unsigned int deckCurrentPos;
    Card deckCards[MAX_DECK_CARDS];
};

// Deck class constructor
Deck::Deck(unsigned int dPos) : deckCurrentPos(dPos)
{
    ClearDeck();
    PopulateDeck();
    ShuffleDeck();
}

// Empty destructor
Deck::~Deck()
{}

// Current position in the array
unsigned int Deck::CurrentPosition()
{
    return deckCurrentPos;
}

// Reset position of array
void Deck::ClearDeck()
{
    deckCurrentPos = 0;
}

// Create an array of Card objects
void Deck::PopulateDeck()
{
    for (int s = Card::HEARTS; s <= Card::SPADES; s++)
    {
        for (int r = Card::ACE; r <= Card::KING; r++)
        {
            AddCardToDeck(Card(static_cast<Card::rank>(r),
                               static_cast<Card::suit>(s)));
        }
    }
}

// Shuffle the array of cards
void Deck::ShuffleDeck()
{
    srand(static_cast<unsigned int>(time(0)));
    for (int i = 0; i < (MAX_DECK_CARDS - 1); i++)
    {
        int r = i + (rand() % (MAX_DECK_CARDS - i));
        Card tempCard = deckCards[i];
        deckCards[i] = deckCards[r];
        deckCards[r] = tempCard;
    }
}

// Check if we have an empty deck
bool Deck::DeckIsEmpty()
{
    return (deckCurrentPos <= 0);
}

// Helper function to PopulateDeck()
void Deck::AddCardToDeck(Card pCard)
{
    deckCards[deckCurrentPos++] = pCard;
}

// Start dealing cards from the last index
Card Deck::GiveCardToPlayer()
{
    if (!DeckIsEmpty())
        return deckCards[--deckCurrentPos];
    else
    {
        // If cards ran out, we simply
        // re-shuffle the deck
        deckCurrentPos = MAX_DECK_CARDS;
        ShuffleDeck();
        return deckCards[--deckCurrentPos];

    }

}

/////////////////////////////////////////////////////////////////////////////
// This is the Hand Class which represents a hand of cards.                //
// Each hand in the game can have up to 10 cards maximum.                  //
/////////////////////////////////////////////////////////////////////////////

class Hand {

public:
    Hand(unsigned int hPos = 0, unsigned int pPos = 0);
    ~Hand();
    void ClearHand();
    void FlipFirstCard();
    bool HandIsEmpty();
    bool HandIsFull();
    Card RetrieveCard();
    void AddCardToHand(Card handCard);
    unsigned int GetHandTotal() const;
    unsigned int GetHandLastPosition() const;

private:
    unsigned int handCurrentPos;
    unsigned int playerCardPos;
    Card handCards[MAX_HAND_CARDS];
};

// Hand class constructor
Hand::Hand(unsigned int hPos, unsigned int pPos) : handCurrentPos(hPos), playerCardPos(pPos)
{ }

// Empty Hand class destructor
Hand::~Hand()
{}

// Reset hand
void Hand::ClearHand()
{
    playerCardPos = handCurrentPos = 0;
}

// Flip dealer's first card
void Hand::FlipFirstCard()
{
    handCards[0].FlipCard();
}

// Check if we have an empty hand
bool Hand::HandIsEmpty()
{
    return (handCurrentPos < 0);
}

// Check if we have a full hand
bool Hand::HandIsFull()
{
    return (handCurrentPos >= MAX_HAND_CARDS);
}

// Retrieve a card from hand
Card Hand::RetrieveCard()
{
    return handCards[playerCardPos++];
}

// Add a card from deck to hand
void Hand::AddCardToHand(Card handCard)
{
    if (!HandIsFull())
    {
        handCards[handCurrentPos++] = handCard;
    }
}

// Get hand total value
unsigned int Hand::GetHandTotal() const
{
    if (handCards[0].GetCardValue() == 0)
        return 0;

    int total = 0;

    // Start from top most card
    for (int i = GetHandLastPosition(); i >= 0; i--)
    {
        total += handCards[i].GetCardValue();
    }

    // Check if we have an Ace
    bool cardHasAce = false;
    for (int i = GetHandLastPosition(); i >= 0; i--)
    {
        if (handCards[i].GetCardValue() == Card::ACE)
            cardHasAce = true;
    }

    // If we have an ace and our total is <= 11
    // then we make the ace equal to 11
    if (cardHasAce && total <= 11)
        total += 10;

    return total;
}

// Get the hand's last position
unsigned int Hand::GetHandLastPosition() const
{
    return handCurrentPos - 1;
}

/////////////////////////////////////////////////////////////////////////////
// This is the Player Class which represents a player of the game.         //
/////////////////////////////////////////////////////////////////////////////

class Player {

public:
    friend ostream& operator<< (ostream& os, const Player aPlayer);
    Player(const string playerName);
    Player();
    ~Player();
    void Clear();
    string GetPlayerName() const;
    void SetPlayerName(string name);
    bool IsEmpty();
    bool IsHitting() const;
    void FlipDealerFirstCard();
    void ReceiveCard(Card playerCard);
    bool IsBusted() const;
    unsigned int GetCardTotal() const;
    unsigned int GetPosition() const;

private:
    string playerName;
    Card playerCard;
    Hand playerHand;
};

// Player class constructor
Player::Player(const string pName) : playerName(pName)
{}

// Set default name for player
Player::Player()
{
    playerName = "Dealer";
}

// Empty Player class destructor
Player::~Player()
{}

// Get player's name
// Can be used in multi-player game
string Player::GetPlayerName() const
{
    return playerName;
}

// Set player's name
void Player::SetPlayerName(string name)
{
    playerName = name;
}

// Check if player's hand has no card
bool Player::IsEmpty()
{
    return playerHand.HandIsEmpty();
}

// Reset player's hand
void Player::Clear()
{
    playerHand.ClearHand();
}

// Check if the player or dealer is still
// asking for additional cards
// In the dealer's case, if the card value
// is <= 16 then we continue giving additional
// cards
bool Player::IsHitting() const
{
    if (playerName != "Dealer")
    {
        cout << endl << playerName << ", do you want to hit? (y/n): ";
        char response;
        cin >> response;
        return (response == 'y' || response == 'Y');
    }
    else
    {
        return (playerHand.GetHandTotal() <= 16);
    }
}

// Flip dealer's first card
void Player::FlipDealerFirstCard()
{
    if (!(playerHand.HandIsEmpty()))
        playerHand.FlipFirstCard();
}

// Add a new card to player's hand
void Player::ReceiveCard(Card playerCard)
{
    playerHand.AddCardToHand(playerCard);
}

// Check if player or dealer is busted
bool Player::IsBusted() const
{
    return (playerHand.GetHandTotal() > 21);
}

// Get hand total
unsigned int Player::GetCardTotal() const
{
    return playerHand.GetHandTotal();
}

// Get the player's hand's last position
unsigned int Player::GetPosition() const
{
    return playerHand.GetHandLastPosition();
}

/////////////////////////////////////////////////////////////////////////////
// This is the Game Class which represents the game itself.                //
/////////////////////////////////////////////////////////////////////////////

class Game {

public:
    Game(string s);
    ~Game();
    void Play();
    void ShowTable();
    void AnnounceWinner();
    void IfDeckIsEmpty();
    void ClearGame();

private:
    string name;
    Deck *gameDeck;
    Player dealer;
    Player player1;
};

// Game class constructor
// string can be set up as an array of string
// for use with multiple players
Game::Game(string s) : name(s)
{   
    gameDeck = new Deck(0);
    player1.SetPlayerName(name);
}

// Game destructor
Game::~Game()
{
    delete gameDeck;
}

// The blackjack game logic
void Game::Play()
{
    // Always check if we have an empty deck
    if (gameDeck->DeckIsEmpty())
    {
        IfDeckIsEmpty();
    }
    else
    {
        // Deal 2 cards initially
        for (int i = 0; i < 2; i++)
        {
            player1.ReceiveCard(gameDeck->GiveCardToPlayer());
            dealer.ReceiveCard(gameDeck->GiveCardToPlayer());
        }

        // Hide dealer's first card
        dealer.FlipDealerFirstCard();

        // Display everyone's cards
        ShowTable();
    }

    // Get more cards from deck
    while (!player1.IsBusted() && player1.IsHitting())
    {
        if (gameDeck->DeckIsEmpty())
        {
            IfDeckIsEmpty();
        }
        else
        {
            player1.ReceiveCard(gameDeck->GiveCardToPlayer());
            ShowTable();
        }
    }

        // Show dealer's first card
        dealer.FlipDealerFirstCard();

    // Dealer's turn to hit
    while (!dealer.IsBusted() && dealer.IsHitting())
    {
        if (gameDeck->DeckIsEmpty())
        {
            IfDeckIsEmpty();
        }

        dealer.ReceiveCard(gameDeck->GiveCardToPlayer());
    }

    // Show the cards and announce the winner
    if (!gameDeck->DeckIsEmpty())
    {
        ShowTable();
        AnnounceWinner();
        ClearGame();
    }
}

// Show the players and cards
void Game::ShowTable()
{
    cout << string(50, '\n');
    cout << "\tWelcome to Simple Casino's BlackJack Game!" << endl << endl;
    cout << left << setw(10) << dealer.GetPlayerName() << dealer << endl;
    cout << left << setw(10) << player1.GetPlayerName() << player1 << endl;
}

// Who won the game?
void Game::AnnounceWinner()
{
    cout << endl;
    if (player1.GetCardTotal() > 21)
        cout << player1.GetPlayerName() << " busts, Dealer Wins." << endl;
    else if (dealer.GetCardTotal() > 21)
        cout << dealer.GetPlayerName() << " busts, " << player1.GetPlayerName() << " Wins!" << endl;
    else if (player1.GetCardTotal() == 21)
        cout << player1.GetPlayerName() << " hit a BlackJack, " << player1.GetPlayerName() << " Wins!" << endl;
    else if (dealer.GetCardTotal() == 21)
        cout << dealer.GetPlayerName() << " hit a BlackJack, " << player1.GetPlayerName() << " lose." << endl;
    else if (player1.GetCardTotal() > dealer.GetCardTotal())
        cout << player1.GetPlayerName() << " Wins!" << endl;
    else if (dealer.GetCardTotal() > player1.GetCardTotal())
        cout << dealer.GetPlayerName() << " Wins." << endl;
    else
        cout << "It's a tie!" << endl;
}

// This is what we do if we have an empty deck
// Just create a new deck object
void Game::IfDeckIsEmpty()
{
    delete gameDeck;
    gameDeck = new Deck(0);
}

// Reset the game
void Game::ClearGame()
{
    dealer.Clear();
    player1.Clear();
}

/////////////////////////////////////////////////////////////////////////////
//  Client Program                                                         //
/////////////////////////////////////////////////////////////////////////////

// Set the function prototype for the overloaded << operator
ostream& operator<< (ostream& os, const Card aCard);
ostream& operator<< (ostream& os, const Player aPlayer);

int main()
{
    string name;
    char ans = 'y';
    cout << "Enter your name: ";
    cin >> name;

    Game *aGame = new Game(name);
   
    while (ans == 'y' || ans == 'Y')
    {
        aGame->Play();
        cout << "\nPlay Again? (y/n): ";
        cin >> ans;
    }

    delete aGame;

    return 0;
}

// Overloaded << operator for use with the Card object
ostream& operator<< (ostream& os, const Card aCard)
{
    const string RANKS[] = {"0", "A", "2", "3", "4", "5", "6", "7", "8", "9", "10",
                            "J", "Q", "K"};

    if (aCard.cardIsFaceUp)
    {
        os << RANKS[aCard.cardRank] <<  (char) aCard.cardSuit << "\t";
    }
    else
    {
        // Hide card
        os << "XX" << "\t";
    }

    return os;
}

// Overloaded << operator for use with the Player object
ostream& operator<< (ostream& os, Player aPlayer)
{
    if (!aPlayer.IsEmpty())
    {
        for (int i = aPlayer.playerHand.GetHandLastPosition(); i >= 0; i--)
            os << aPlayer.playerHand.RetrieveCard();

        if (aPlayer.GetCardTotal() != 0)
            cout << "[" << aPlayer.GetCardTotal() << "]";
    }
    else
    {
        os << "Error: Empty Hand";
    }

    return os;
}