Blackjack Simulation Code

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 12

28 March 2017

C++ Code
Visual Studio 2017

Deck.h

// this file declares and defines what a "deck" is for the program to use in the future

#ifndef _DECK_H
#define _DECK_H

enum CardType { Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King, Ace
}; // declaring each card type, also assigning a preliminary value to each (note: value for each
card is 2 less than it should be)

struct Card
{
CardType type;
short value; // stating that each card will eventually be given a value
};

class Deck
{
private:
Card* m_pCards; // Array to hold the cards
int m_iNumCards; // Number of cards left in the deck
int m_uiMaxCards; // Max Cards the deck array can hold

void CreateNewDeckArray(unsigned int newSize);


void AddFullSetOfCardsToDeck();

public:
Deck();
~Deck();

void AddCardsToDeck(unsigned int numberOfFullDecksToAdd);


void Shuffle();

bool DrawCard(Card& card);


};

#endif

Game.h

// this file labels all the actions that take place in one round, in addition to all the variables we
want to keep track of, like the win count, how many decks we've used, etc

#ifndef _GAME_H
#define _GAME_H

#include "Deck.h"
#include <vector>

class Game
{
private:
Deck m_dDeck; // The main deck of cards
std::vector<Card> m_aPlayerCards; // The player's hand
std::vector<Card> m_aDealerCards; // The dealer's hand

unsigned int m_uiNumGamesPlayed; // The total number of games played


unsigned int m_uiNumTimesFillingDeck; // The number of times the deck was refilled
unsigned int m_uiMaxDeckFills; // The max number of times to refill the deck
unsigned int m_uiDecksToFillWith; // The number of decks to use when refilling the main
deck
unsigned int m_uiPlayerWins; // The number of wins the player had
unsigned int m_uiPlayerBlackjackWins; // The number of wins which were won with the
player having Blackjack
unsigned int m_uiNumTieGames; // The number of games which ended in a tie

void RefillDeck();
void DrawCard(std::vector<Card>& hand);
unsigned int EvaluateHand(const std::vector<Card>& hand) const;

// Standard round actions


void InitialDeal();
void PlayerTurn();
void DealerTurn();
void EvaluateRound();
void ResetRound();

public:
Game(unsigned int numberOfFullDecksPerRefill = 2, unsigned int numberOfRefills = 12);
~Game();

void Setup();
void Play();

// declaring the totals for each of the previously assigned variables - these are what will
get displayed in our final results
unsigned int TotalGamesPlayed() const;
unsigned int TotalPlayerWins() const;
unsigned int TotalPlayerBlackjackWins() const;
unsigned int TotalTieGames() const;
float PlayerWinPercentage() const;
};

#endif

Deck.cpp

// this file actually assigns values to a lot of the variables declared in deck.h

#include "Deck.h"

#include <stdlib.h>

Deck::Deck()
{
m_iNumCards = 0;
m_pCards = nullptr;
CreateNewDeckArray(104); // a standard deck has 52 cards, but we're using two decks at
once, so we use an array length of 104
}
Deck::~Deck()
{
delete m_pCards;
}

void Deck::CreateNewDeckArray(unsigned int newSize) // generates a new deck array each time
the old one runs out of cards that doesn't exceed our specified size (104)
{
Card* temp = new Card[newSize];

if (m_pCards != nullptr)
{
int copyCount = newSize > m_iNumCards ? m_iNumCards : newSize;
for (int i = 0; i < copyCount; i++)
temp[i] = m_pCards[i];

delete m_pCards;
}

m_pCards = temp;
m_uiMaxCards = newSize;
}

void Deck::AddFullSetOfCardsToDeck() // adds ONE full deck of cards each time we run out
while playing (this function gets called twice, so we actually use two decks each time we reset)
{
if (m_iNumCards + 52 > m_uiMaxCards)
CreateNewDeckArray(m_uiMaxCards + 52);

int index = 0;
for (int suit = 0; suit < 4; suit++) // telling the program that there are four of each card type per
deck
{
for (int value = 0; value < 13; value++, index++)
{
Card newCard;
newCard.type = (CardType)value;
if (value < Ten)
newCard.value = value + 2; // this off-sets the values of the numbered cards being less
than they should have been
else if (value == Ace)
newCard.value = 11;
else
newCard.value = 10;

m_pCards[m_iNumCards + index] = newCard;


}
}

m_iNumCards += 52;
}

void Deck::AddCardsToDeck(unsigned int numberOfFullDecksToAdd) // runs a loop of adding


new decks whenever we run out, made so that we could add as many decks as we wanted - 2, 6,
8, etc
{
for (unsigned int i = 0; i < numberOfFullDecksToAdd; i++)
AddFullSetOfCardsToDeck();
}
void Deck::Shuffle() // rudimentary shuffle function, takes two random cards and swaps their
location, does this 1000 times
{
for (int i = 0; i < 1000; i++)
{
int card1 = rand() % m_iNumCards;
int card2 = rand() % m_iNumCards;

if (card1 == card2) // double-checks that no card ends up in the spot where it started
{
if (card2 == m_iNumCards - 1)
card2--;
else
card2++;
}

// actually swapping the cards


Card tmp = m_pCards[card1];
m_pCards[card1] = m_pCards[card2];
m_pCards[card2] = tmp;
}
}

// defining what "drawing a card" means to the program - takes the top card off the deck and
reduces the number of cards in the deck by one
bool Deck::DrawCard(Card& card)
{
if (m_iNumCards == 0)
return false;

card = m_pCards[--m_iNumCards];
return true;
}

#include "Game.h"

Game::Game(unsigned int numberOfFullDecksPerRefill, unsigned int numberOfRefills) //


calling .h functions for use
{
// initializing variables that will be used
m_uiDecksToFillWith = numberOfFullDecksPerRefill;
m_uiMaxDeckFills = numberOfRefills;
m_uiNumGamesPlayed = 0;
m_uiNumTimesFillingDeck = 0;
m_uiPlayerBlackjackWins = 0;
m_uiPlayerWins = 0;
m_uiNumTieGames = 0;
}
Game::~Game()
{
}

void Game::RefillDeck() // telling the program what needs to be done when we want to refill the
deck
{
m_dDeck.AddCardsToDeck(m_uiDecksToFillWith);
m_dDeck.Shuffle();
m_uiNumTimesFillingDeck++;
}
void Game::DrawCard(std::vector<Card>& hand) // if we are unable to draw a card, it means the
deck is empty and we need to refill it
{
Card drawnCard;

if (m_dDeck.DrawCard(drawnCard) == false)
{
RefillDeck();
m_dDeck.DrawCard(drawnCard);
}

hand.push_back(drawnCard);
}
unsigned int Game::EvaluateHand(const std::vector<Card>& hand) const // determining whether
to count an ace as an 11 or a 1
{
int val = 0;
int numAces = 0;
for (size_t i = 0; i < hand.size(); i++)
{
if (hand[i].type == Ace)
numAces++;

val += hand[i].value;
}

while (val > 21 && numAces > 0)


{
val -= 10;
numAces--;
}

return val;
}
void Game::InitialDeal() // starting a new round
{
m_uiNumGamesPlayed++;

DrawCard(m_aPlayerCards);
DrawCard(m_aDealerCards);
DrawCard(m_aPlayerCards);
DrawCard(m_aDealerCards);

unsigned int dealerScore = EvaluateHand(m_aDealerCards);


unsigned int playerScore = EvaluateHand(m_aPlayerCards);

if (dealerScore == 21 || playerScore == 21) // if either player gets blackjack


{
if (dealerScore < playerScore) // the player wins if they got it but the dealer didn't
{
m_uiPlayerBlackjackWins++;
m_uiPlayerWins++;
}
else if (dealerScore == playerScore) // the dealer and player tie if they both got it
m_uiNumTieGames++;

// we don't need to compute dealer blackjacks, because they don't matter in terms
of our total winnings - we lose the same amount whether they get blackjack or not

ResetRound();
InitialDeal();
}
}
void Game::PlayerTurn() // player logic - we choose to hit unless we have 16 or lower
{
while (EvaluateHand(m_aPlayerCards) < 17)
DrawCard(m_aPlayerCards);

if (EvaluateHand(m_aPlayerCards) > 21) // checks if the player busted before switching


to the dealer's turn
{
ResetRound();
InitialDeal();
}
}
void Game::DealerTurn() // dealer logic - they choose to hit unless they have 16 or lower
{
if (EvaluateHand(m_aPlayerCards) <= 21)
{
while (EvaluateHand(m_aDealerCards) < 17)
DrawCard(m_aDealerCards);
}
// the dealer's bust check takes place in the "evaluateround" function below
}
void Game::EvaluateRound() // this function determines who has the winning hand
{
unsigned int dealerScore = EvaluateHand(m_aDealerCards);
unsigned int playerScore = EvaluateHand(m_aPlayerCards);

if (dealerScore == playerScore || (dealerScore > 21 && playerScore > 21)) // if the player
and dealer are tied, or if both bust
m_uiNumTieGames++;
else if ((playerScore > dealerScore || dealerScore > 21) && playerScore <= 21) // if the
player has more than the dealer, or if the dealer busts - the player cannot bust here
m_uiPlayerWins++;
}
void Game::ResetRound() // this empties the players' hands
{
m_aDealerCards.clear();
m_aPlayerCards.clear();
}

void Game::Setup() // refills the decks if they run out


{
RefillDeck();
}
void Game::Play() // makes one big function to call all the smaller functions we just described
{
while (m_uiNumTimesFillingDeck <= m_uiMaxDeckFills)
{
InitialDeal();
PlayerTurn();
DealerTurn();
EvaluateRound();
ResetRound();
}
}

// sending the game counts to the main source function


unsigned int Game::TotalGamesPlayed() const { return m_uiNumGamesPlayed; }
unsigned int Game::TotalPlayerWins() const { return m_uiPlayerWins; }
unsigned int Game::TotalPlayerBlackjackWins() const { return m_uiPlayerBlackjackWins; }
unsigned int Game::TotalTieGames() const { return m_uiNumTieGames; }
float Game::PlayerWinPercentage() const { return (float)(m_uiPlayerWins) /
m_uiNumGamesPlayed; }

Source.cpp

// this is the parent function that references everything declared in the other functions. It calls the
results from game.cpp and displays the output.

#include <iostream>
#include <time.h>
#include "Game.h"
#include <iomanip>

using namespace std;

void main()
{
// declaring output variables and giving them an initial value
srand(static_cast<unsigned int>(time(0)));
int totalOutcome = 0;
int TotalGamesPlayed = 0;
int TotalPlayerWins = 0;
int TotalPlayerBlackjackWins = 0;
int TotalTieGames = 0;

// looping the calculations and output for each round, making only 12 rounds
for (unsigned int games = 0; games < 1000000; games++)
{
Game game(2, 1);
game.Setup();
game.Play();

int outcome = (game.TotalGamesPlayed() * -2) + ((game.TotalPlayerWins() -


game.TotalPlayerBlackjackWins()) * 4) +
(game.TotalPlayerBlackjackWins() * 5) + (game.TotalTieGames() * 2); //
this is the return rate for playing - 2 to 1 for a regular win, 3 to 2 for a blackjack win, even for
ties

// calculating outcome and game counts based on stored data


totalOutcome += outcome;
TotalGamesPlayed += game.TotalGamesPlayed();
TotalPlayerWins += game.TotalPlayerWins();
TotalPlayerBlackjackWins += game.TotalPlayerBlackjackWins();
TotalTieGames += game.TotalTieGames();

// output
if (outcome < 0)
cout << "Deck " << games + 1 << " Outcome: -$" << abs(outcome) <<
'\n';
else
cout << "Deck " << games + 1 << " Outcome: $" << outcome << '\n';
}

cout << " " << '\n';

// displaying the total outcome for all 12 rounds


if (totalOutcome < 0)
{
cout << "Total Outcome: -$" << abs(totalOutcome) << '\n';
cout << "Average Outcome per Deck: -$";
cout << fixed << setprecision(2) << float(abs(totalOutcome)) / 1000000 << '\n';
cout << setprecision(0);
}
else
{
cout << "Total Outcome: $" << totalOutcome << '\n';
cout << "Average Outcome per Deck: $";
cout << fixed << setprecision(2) << float(totalOutcome) / 1000000 << '\n';
cout << setprecision(0);
}

// displaying number of games played / won


cout << "Total Games Played: " << TotalGamesPlayed << '\n'
<< "Total Player Wins: " << TotalPlayerWins << '\n'
<< "Total Player BlackJacks: " << TotalPlayerBlackjackWins << '\n'
<< "Total Dealer Wins: " << TotalGamesPlayed - TotalPlayerWins -
TotalTieGames << '\n'
<< "Total Ties: " << TotalTieGames << "\n\n";

system("PAUSE");
return;
}

You might also like