A sample Blackjack project to show off C++:
Casino.h:
// Casino.h
// Clay Foye, August 2022
// Define the Casino namespace
// see the README
#pragma once
#include <bits/stdc++.h>
#include <cstring>
namespace Casino // casino namespace declaration
{
using namespace std;
const int deck_size = 52; // decksize
const int max_hand_size = 22;
// Suit enumeration (scoped):
enum class Suit
{
Hearts,
Diamonds,
Spades,
Clubs
};
// Cast to string:
inline const string suit_to_string(Suit suit) {
switch(suit) {
case Suit::Hearts:
return "Hearts";
case Suit::Diamonds:
return "Diamonds";
case Suit::Spades:
return "Spades";
case Suit::Clubs:
return "Clubs";
default:
return "Unknown Suit";
}
}
// Card strut
struct CARD
{
Suit suit; // the suit of a card
string name; // the full name of a card
int type; // 2-14: the integer representation of card name
int value; // 1-11: the actual blackjack value of a card
int index; // the index of a card in the deck
bool activated_ace = false; // An Ace worth 11?
};
// Deck class
class Deck
{
public:
CARD draw_card(); // draw a card
inline void reshuffle() { the_deck.reset(); }
inline void print_deck() { cout << the_deck << '\n'; }
private:
// private functions
void get_card_name(CARD &card);
void get_card_suit(CARD &card);
void get_card_value(CARD &card);
int draw_card_helper();
inline void get_card_type(CARD &card)
{ card.type = (card.index % 13) + 2; }
inline void randomize_index() { randint = rand() % 51; }
// Vars
bitset<deck_size> the_deck;
int randint;
};
// Person class: can be used as a dealer or as a player
class Person
{
public:
inline void add_card(CARD& card) { hand.push_back(card); }
inline void flush_hand() {
hand_value = 0;
hand.clear();
}
void print_hand();
void bjack_eval();
inline int get_hand_value() { return hand_value; }
private:
vector<CARD> hand;
int hand_value =0;
};
}
deck.cpp:
// Deck class implementation
#include "../include/casino.h"
#include <bits/stdc++.h>
#include <cstring>
using namespace Casino;
using namespace std;
// Assign the suit to a card (assume card_index exists)
void Deck::get_card_suit(CARD &card)
{
if (card.index < 13)
{
card.suit = Suit::Hearts;
return;
}
if (card.index < 26)
{
card.suit = Suit::Diamonds;
return;
}
if (card.index < 39)
{
card.suit = Suit::Spades;
return;
}
card.suit = Suit::Clubs;
}
// Assign the blackjack value to a card (assume card_type exists)
void Deck::get_card_value(CARD &card)
{
card.value = card.type; // default
if (card.type > 10 && card.type < 14) //face cards
{
card.value = 10;
}
if (card.type == 14) // aces
{
card.activated_ace = true;
card.value = 11;
}
}
// Assign the name to a card (assume type and suit exist)
void Deck::get_card_name(CARD &card)
{
switch (card.type)
{
case 14:
card.name = "Ace of " + suit_to_string(card.suit);
break;
case 13:
card.name = "King of " + suit_to_string(card.suit);
break;
case 12:
card.name = "Queen of " + suit_to_string(card.suit);
break;
case 11:
card.name = "Jack of " + suit_to_string(card.suit);
break;
default:
card.name = to_string(card.type) + " of " + suit_to_string(card.suit);
break;
}
}
// Recursive to find a valid card
int Deck::draw_card_helper()
{
if (the_deck[randint] == 0)
{
the_deck[randint] = 1;
return randint;
}
else
{
randint = (randint + 1) % deck_size;
return draw_card_helper();
}
}
CARD Deck::draw_card() {
// Card to return:
CARD card;
// get the card index:
Deck::randomize_index();
card.index = draw_card_helper();
// get the card type:
Deck::get_card_type(card);
// get the card suit:
Deck::get_card_suit(card);
// get the card name:
Deck::get_card_name(card);
// get blackjack value: (can expand this out to different games eventually)
Deck::get_card_value(card);
// return the card
return card;
}
person.cpp:
// Person class implementation
#include "../include/casino.h"
#include <cstring>
using namespace Casino;
using namespace std;
void Person::print_hand() {
for (CARD c : hand){
cout << c.name + '\n';
}
cout << '\n';
}
void Person::bjack_eval() {
// lambda to sum a card value
auto card_count = [](int sum, CARD c) { return sum + c.value; };
// accumulate value
int total = std::accumulate(hand.begin(), hand.end(), 0, card_count);
// Handle aces
if (total > 21) {
// find an activated ace:
auto result = find_if(hand.begin(), hand.end(), [](CARD& c)
{ return c.activated_ace; });
if (result != hand.end()) {
auto pos = distance(hand.begin(), result);
hand[pos].value = 1;
hand[pos].activated_ace = false;
}
total = std::accumulate(hand.begin(), hand.end(), 0, card_count);
}
hand_value = total;
}
game.cpp:
// Run the blackjack game!
#include "../include/casino.h"
#include <cstring>
#include <chrono>
#include <thread>
using namespace Casino;
using namespace std;
// Let the player draw cards
void player_draw(Deck& deck, Person& player, CARD& c) {
string answer = "";
c = deck.draw_card();
player.add_card(c);
cout << "You draw: " + c.name + '\n';
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
c = deck.draw_card();
player.add_card(c);
cout << "You draw: " + c.name + '\n';
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// Player Hand:
player.bjack_eval();
cout << "You show " + to_string(player.get_hand_value()) + '\n';
while (player.get_hand_value() < 21)
{
cout << "Hit or Stand? (h/s)\n";
getline(cin, answer);
if (answer.compare("h") == 0)
{
c = deck.draw_card();
player.add_card(c);
cout << "You draw: " + c.name + '\n';
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
player.bjack_eval();
cout << "You show " + to_string(player.get_hand_value()) + '\n';
}
else if (answer.compare("s") == 0)
{
break;
}
else
{
cout << "Please type 'h' or 's'.\n";
}
player.bjack_eval();
}
}
// Draw the dealer's inital hand
void dealer_initial_draw(Deck& deck, Person& dealer, CARD& c) {
dealer.add_card(c);
cout << "Dealer draws: " + c.name + '\n';
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
dealer.bjack_eval();
c = deck.draw_card();
dealer.add_card(c);
cout << "Dealer draws a card face-down.\n";
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// Dealer hand:
cout << "Dealer shows " + to_string(dealer.get_hand_value()) + '\n';
dealer.bjack_eval();
}
// Draw out the dealer at the end of a hand
void draw_out_dealer(Deck& deck, Person& dealer, CARD& c) {
while (dealer.get_hand_value() < 17)
{
c = deck.draw_card();
dealer.add_card(c);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
cout << "Dealer draws: " + c.name + '\n';
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
cout << "Dealer hand:\n\n";
dealer.print_hand();
std::this_thread::sleep_for(std::chrono::milliseconds(1500));
dealer.bjack_eval();
cout << "Dealer shows: " + to_string(dealer.get_hand_value()) + '\n';
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
if (dealer.get_hand_value() > 21)
{
cout << "Dealer busts!\n";
return;
}
}
// Check for a win/loss using player and dealer hand values
void check_win(Person& player, Person& dealer) {
// Check for win/loss conditions
if (dealer.get_hand_value() > player.get_hand_value())
{
cout << "Dealer wins!\n";
return;
}
else if (dealer.get_hand_value() == player.get_hand_value())
{
cout << "Push!\n";
return;
}
else if (dealer.get_hand_value() < player.get_hand_value())
{
cout << "You win!\n";
return;
}
}
// A hand of blackjack
void blackjack(Deck& deck, Person& player, Person& dealer) {
// Dealer draws
CARD c = deck.draw_card();
dealer_initial_draw(deck, dealer, c);
if (dealer.get_hand_value() == 21) {
cout << "Dealer turns over " + c.name + " for Blackjack.\n";
return;
}
// Draw the player
player_draw(deck, player, c);
// Check for Bust
if (player.get_hand_value() > 21) {
cout << "Bust!\n";
return;
}
// Check for Blackjack
if (player.get_hand_value() == 21) {
cout << "Blackjack!\n";
return;
}
// Draw out the dealer
cout << "Dealer hand:\n\n";
dealer.print_hand();
cout << "Dealer shows: " + to_string(dealer.get_hand_value()) + '\n';
if (dealer.get_hand_value() > player.get_hand_value()) {
cout << "Dealer wins!\n";
return;
}
draw_out_dealer(deck, dealer, c);
// Check for a win or loss
check_win(player, dealer);
}
int main(int argc, char const *argv[])
{
// Seed
srand(time(NULL));
// Store inputs
string answer = "";
// Deck, Player, and Dealer
Deck deck;
Person player, dealer;
// Main Execution Loop
while (true) {
// Ask to play:
cout << "Want to play Blackjack? (y/n)\n";
// Get answer
getline(cin, answer);
// Play the game
if (answer.compare("y") == 0){
blackjack(deck, player, dealer);
cout << "Good Game!\n\n";
deck.reshuffle();
player.flush_hand();
dealer.flush_hand();
}
// Leave execution
else if (answer.compare("n") == 0)
{
cout << "Goodbye!\n";
break;
}
else
{ // handle unknown input
cout << "please type 'y' or 'n' \n";
}
}
return 0;
}
Some Unit Tests:
draw_deck.cpp:
// Unit test the Deck class, CARD struct, and card-drawing functionality
#include "../include/casino.h"
using namespace Casino;
using namespace std;
// draw a card, print a name
int main(int argc, char const *argv[])
{
srand(time(NULL));
Deck deck;
int i = 0;
int j = 3;
while (j < 15) {
cout << "Drawing " + to_string(j) + "s: \n";
while (i < 52)
{
CARD card = deck.draw_card();
if (card.type == j)
{
cout << "Card Num: " + to_string(i + 1) + ": " + card.name << '\n';
}
i++;
}
deck.reshuffle();
i = 0;
j = j + 1;
}
}
test_hand.cpp:
// Unit Test the Person class and the hand functionalities.
#include "../include/casino.h"
using namespace Casino;
using namespace std;
int main(int argc, char const *argv[])
{
// seed
srand(time(NULL));
// deck and player
Deck deck;
Person player;
int i = 0;
// 64 games of Blackjack
while (i < 64) {
// Draw until bust or blackjack
while (player.get_hand_value() < 21) {
cout << "Drawing card!\n";
CARD c = deck.draw_card();
player.add_card(c);
player.bjack_eval();
}
(player.get_hand_value() == 21) ? cout << "BLACKJACK!\n" : cout << "" ;
// Print the hand:
cout << "Printing Hand!\n";
player.print_hand();
// Print Hand value:
cout << "Hand value: " + to_string(player.get_hand_value()) + '\n';
cout << "Flushing hand and reshuffling deck . . .\n\n";
player.flush_hand();
deck.reshuffle();
i++;
}
return 0;
}