home *** CD-ROM | disk | FTP | other *** search
/ Beginning C++ Through Gam…rogramming (2nd Edition) / BCGP2E.ISO / source / chapter10 / blackjack.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2006-11-02  |  11.0 KB  |  494 lines

  1. //Blackjack
  2. //Plays a simple version of the casino game of blackjack; for 1 - 7 players
  3.  
  4. #include <iostream>
  5. #include <string>
  6. #include <vector>
  7. #include <algorithm>
  8. #include <ctime>
  9.  
  10. using namespace std;
  11.  
  12. class Card
  13. {
  14. public:
  15.     enum rank {ACE = 1, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN,
  16.                JACK, QUEEN, KING};           
  17.     enum suit {CLUBS, DIAMONDS, HEARTS, SPADES};
  18.  
  19.     //overloading << operator so can send Card object to standard output 
  20.     friend ostream& operator<<(ostream& os, const Card& aCard);
  21.     
  22.     Card(rank r = ACE, suit s = SPADES, bool ifu = true); 
  23.  
  24.     //returns the value of a card, 1 - 11
  25.     int GetValue() const;
  26.  
  27.     //flips a card; if face up, becomes face down and vice versa
  28.     void Flip();
  29.     
  30. private:
  31.     rank m_Rank;
  32.     suit m_Suit;
  33.     bool m_IsFaceUp;
  34. };
  35.  
  36. Card::Card(rank r, suit s, bool ifu):  m_Rank(r), m_Suit(s), m_IsFaceUp(ifu)
  37. {} 
  38.  
  39. int Card::GetValue() const
  40. {
  41.     //if a cards is face down, its value is 0
  42.     int value = 0;
  43.     if (m_IsFaceUp)
  44.     {
  45.         //value is number showing on card
  46.         value = m_Rank;
  47.         //value is 10 for face cards
  48.         if (value > 10)
  49.             value = 10;
  50.     }
  51.     return value;
  52. }
  53.  
  54. void Card::Flip()
  55. {
  56.     m_IsFaceUp = !(m_IsFaceUp);
  57. }
  58.  
  59. class Hand
  60. {
  61. public:
  62.     Hand();
  63.     
  64.     virtual ~Hand();
  65.  
  66.     //adds a card to the hand
  67.     void Add(Card* pCard);
  68.  
  69.     //clears hand of all cards
  70.     void Clear();
  71.  
  72.     //gets hand total value, intelligently treats aces as 1 or 11
  73.     int GetTotal() const;
  74.  
  75. protected:
  76.     vector<Card*> m_Cards;
  77. };
  78.  
  79. Hand::Hand()
  80. {
  81.     m_Cards.reserve(7);
  82. }
  83.  
  84. Hand::~Hand()  
  85. {
  86.     Clear();
  87. }
  88.  
  89. void Hand::Add(Card* pCard)
  90. {
  91.     m_Cards.push_back(pCard);
  92. }
  93.  
  94. void Hand::Clear()
  95. {
  96.     //iterate through vector, freeing all memory on the heap
  97.     vector<Card*>::iterator iter = m_Cards.begin();
  98.     for (iter = m_Cards.begin(); iter != m_Cards.end(); ++iter)
  99.     {
  100.         delete *iter;
  101.         *iter = 0;
  102.     }
  103.     //clear vector of pointers
  104.     m_Cards.clear();
  105. }
  106.  
  107. int Hand::GetTotal() const
  108. {
  109.     //if no cards in hand, return 0
  110.     if (m_Cards.empty())
  111.         return 0;
  112.   
  113.     //if a first card has value of 0, then card is face down; return 0
  114.     if (m_Cards[0]->GetValue() == 0)
  115.         return 0;
  116.     
  117.     //add up card values, treat each Ace as 1
  118.     int total = 0;
  119.     vector<Card*>::const_iterator iter;
  120.     for (iter = m_Cards.begin(); iter != m_Cards.end(); ++iter)
  121.         total += (*iter)->GetValue();
  122.                   
  123.     //determine if hand contains an Ace
  124.     bool containsAce = false;
  125.     for (iter = m_Cards.begin(); iter != m_Cards.end(); ++iter)
  126.         if ((*iter)->GetValue() == Card::ACE)
  127.             containsAce = true;
  128.           
  129.     //if hand contains Ace and total is low enough, treat Ace as 11
  130.     if (containsAce && total <= 11)
  131.         //add only 10 since we've already added 1 for the Ace
  132.         total += 10;   
  133.             
  134.     return total;
  135. }
  136.  
  137. class GenericPlayer : public Hand
  138. {
  139.     friend ostream& operator<<(ostream& os, const GenericPlayer& aGenericPlayer);
  140.  
  141. public:
  142.     GenericPlayer(const string& name = "");
  143.     
  144.     virtual ~GenericPlayer();
  145.  
  146.     //indicates whether or not generic player wants to keep hitting
  147.     virtual bool IsHitting() const = 0;
  148.  
  149.     //returns whether generic player has busted - has a total greater than 21
  150.     bool IsBusted() const;
  151.  
  152.     //announces that the generic player busts
  153.     void Bust() const;
  154.  
  155. protected:
  156.     string m_Name;
  157. };
  158.  
  159. GenericPlayer::GenericPlayer(const string& name): 
  160.     m_Name(name)
  161. {}
  162.  
  163. GenericPlayer::~GenericPlayer()  
  164. {}
  165.  
  166. bool GenericPlayer::IsBusted() const
  167.     return (GetTotal() > 21);
  168. }
  169.  
  170. void GenericPlayer::Bust() const
  171. {
  172.     cout << m_Name << " busts.\n";
  173. }
  174.  
  175. class Player : public GenericPlayer
  176. {
  177. public:
  178.     Player(const string& name = "");
  179.  
  180.     virtual ~Player();
  181.  
  182.     //returns whether or not the player wants another hit       
  183.     virtual bool IsHitting() const;
  184.  
  185.     //announces that the player wins
  186.     void Win() const;
  187.  
  188.     //announces that the player loses
  189.     void Lose() const;
  190.  
  191.     //announces that the player pushes
  192.     void Push() const;
  193. };
  194.  
  195. Player::Player(const string& name): 
  196.     GenericPlayer(name)
  197. {}
  198.  
  199. Player::~Player()
  200. {}
  201.     
  202. bool Player::IsHitting() const
  203. {
  204.     cout << m_Name << ", do you want a hit? (Y/N): ";
  205.     char response;
  206.     cin >> response;
  207.     return (response == 'y' || response == 'Y');
  208. }
  209.  
  210. void Player::Win() const
  211. {
  212.     cout << m_Name <<  " wins.\n";
  213. }
  214.  
  215. void Player::Lose() const
  216. {
  217.     cout << m_Name <<  " loses.\n";
  218. }
  219.  
  220. void Player::Push() const
  221. {
  222.     cout << m_Name <<  " pushes.\n";
  223. }
  224.  
  225. class House : public GenericPlayer
  226. {
  227. public:
  228.     House(const string& name = "House");
  229.  
  230.     virtual ~House();
  231.  
  232.     //indicates whether house is hitting - will always hit on 16 or less
  233.     virtual bool IsHitting() const;
  234.  
  235.     //flips over first card
  236.     void FlipFirstCard();
  237. };
  238.  
  239. House::House(const string& name): 
  240.     GenericPlayer(name)
  241. {}
  242.  
  243. House::~House()
  244. {}
  245.  
  246. bool House::IsHitting() const
  247. {
  248.     return (GetTotal() <= 16);
  249. }   
  250.  
  251. void House::FlipFirstCard()
  252. {
  253.     if (!(m_Cards.empty()))
  254.         m_Cards[0]->Flip();
  255.     else cout << "No card to flip!\n";
  256. }
  257.  
  258. class Deck : public Hand
  259. {
  260. public:
  261.     Deck();
  262.     
  263.     virtual ~Deck();
  264.  
  265.     //create a standard deck of 52 cards
  266.     void Populate();
  267.  
  268.     //shuffle cards
  269.     void Shuffle();
  270.  
  271.     //deal one card to a hand
  272.     void Deal(Hand& aHand);
  273.  
  274.     //give additional cards to a generic player 
  275.     void AdditionalCards(GenericPlayer& aGenericPlayer);
  276. };
  277.  
  278. Deck::Deck()
  279.     m_Cards.reserve(52);
  280.     Populate();
  281. }
  282.  
  283. Deck::~Deck()
  284. {}
  285.  
  286. void Deck::Populate()
  287. {
  288.     Clear();
  289.     //create standard deck
  290.     for (int s = Card::CLUBS; s <= Card::SPADES; ++s)
  291.             for (int r = Card::ACE; r <= Card::KING; ++r)
  292.                 Add(new Card(static_cast<Card::rank>(r), static_cast<Card::suit>(s)));
  293. }
  294.  
  295. void Deck::Shuffle()
  296. {
  297.     random_shuffle(m_Cards.begin(), m_Cards.end());
  298. }
  299.  
  300. void Deck::Deal(Hand& aHand)
  301. {
  302.     if (!m_Cards.empty())
  303.     {
  304.         aHand.Add(m_Cards.back());
  305.         m_Cards.pop_back();
  306.     }
  307.     else
  308.     {
  309.         cout << "Out of cards. Unable to deal.";
  310.     }
  311. }
  312.  
  313. void Deck::AdditionalCards(GenericPlayer& aGenericPlayer)
  314. {
  315.     cout << endl;
  316.     //continue to deal a card as long as generic player isn't busted and
  317.     //wants another hit
  318.     while ( !(aGenericPlayer.IsBusted()) && aGenericPlayer.IsHitting() )
  319.     {
  320.         Deal(aGenericPlayer);
  321.         cout << aGenericPlayer << endl;
  322.         
  323.         if (aGenericPlayer.IsBusted())
  324.             aGenericPlayer.Bust();
  325.     }
  326.  
  327. class Game
  328. {
  329. public:
  330.     Game(const vector<string>& names);
  331.     
  332.     ~Game();
  333.     
  334.     //plays the game of blackjack    
  335.     void Play();
  336.  
  337. private:
  338.     Deck m_Deck;
  339.     House m_House;
  340.     vector<Player> m_Players;  
  341. };
  342.  
  343. Game::Game(const vector<string>& names)
  344. {
  345.     //create a vector of players from a vector of names       
  346.     vector<string>::const_iterator pName;
  347.     for (pName = names.begin(); pName != names.end(); ++pName)      
  348.         m_Players.push_back(Player(*pName));
  349.  
  350.     srand(time(0));    //seed the random number generator
  351.     m_Deck.Populate();
  352.     m_Deck.Shuffle();
  353. }
  354.  
  355. Game::~Game()
  356. {}
  357.  
  358. void Game::Play()
  359. {         
  360.     //deal initial 2 cards to everyone
  361.     vector<Player>::iterator pPlayer;
  362.     for (int i = 0; i < 2; ++i)
  363.     {
  364.         for (pPlayer = m_Players.begin(); pPlayer != m_Players.end(); ++pPlayer)      
  365.             m_Deck.Deal(*pPlayer);
  366.         m_Deck.Deal(m_House);
  367.     }
  368.     
  369.     //hide house's first card
  370.     m_House.FlipFirstCard();    
  371.     
  372.     //display everyone's hand
  373.     for (pPlayer = m_Players.begin(); pPlayer != m_Players.end(); ++pPlayer)      
  374.         cout << *pPlayer << endl;
  375.     cout << m_House << endl;
  376.  
  377.     //deal additional cards to players
  378.     for (pPlayer = m_Players.begin(); pPlayer != m_Players.end(); ++pPlayer)
  379.         m_Deck.AdditionalCards(*pPlayer);    
  380.  
  381.     //reveal house's first card
  382.     m_House.FlipFirstCard();    
  383.     cout << endl << m_House; 
  384.   
  385.     //deal additional cards to house
  386.     m_Deck.AdditionalCards(m_House);
  387.  
  388.     if (m_House.IsBusted())
  389.     {
  390.         //everyone still playing wins
  391.         for (pPlayer = m_Players.begin(); pPlayer != m_Players.end(); ++pPlayer)      
  392.             if ( !(pPlayer->IsBusted()) )
  393.                 pPlayer->Win();
  394.     }
  395.     else
  396.     {
  397.          //compare each player still playing to house
  398.         for (pPlayer = m_Players.begin(); pPlayer != m_Players.end(); ++pPlayer)      
  399.             if ( !(pPlayer->IsBusted()) )
  400.             {
  401.                 if (pPlayer->GetTotal() > m_House.GetTotal())
  402.                     pPlayer->Win();
  403.                 else if (pPlayer->GetTotal() < m_House.GetTotal())
  404.                     pPlayer->Lose();
  405.                 else
  406.                     pPlayer->Push();
  407.             }
  408.     }
  409.  
  410.     //remove everyone's cards
  411.     for (pPlayer = m_Players.begin(); pPlayer != m_Players.end(); ++pPlayer)      
  412.         pPlayer->Clear();
  413.     m_House.Clear();
  414. }
  415.  
  416. //function prototypes
  417. ostream& operator<<(ostream& os, const Card& aCard);
  418. ostream& operator<<(ostream& os, const GenericPlayer& aGenericPlayer);
  419.  
  420. int main()
  421. {
  422.     cout << "\t\tWelcome to Blackjack!\n\n";
  423.     
  424.     int numPlayers = 0;
  425.     while (numPlayers < 1 || numPlayers > 7)
  426.     {
  427.         cout << "How many players? (1 - 7): ";
  428.         cin >> numPlayers;
  429.     }   
  430.  
  431.     vector<string> names;
  432.     string name;
  433.     for (int i = 0; i < numPlayers; ++i)
  434.     {
  435.         cout << "Enter player name: ";
  436.         cin >> name;
  437.         names.push_back(name);
  438.     }
  439.     cout << endl;
  440.  
  441.     //the game loop        
  442.     Game aGame(names);
  443.     char again = 'y';
  444.     while (again != 'n' && again != 'N')
  445.     {
  446.         aGame.Play();
  447.         cout << "\nDo you want to play again? (Y/N): ";
  448.         cin >> again;
  449.     } 
  450.  
  451.     return 0;
  452. }
  453.  
  454. //overloads << operator so Card object can be sent to cout
  455. ostream& operator<<(ostream& os, const Card& aCard)
  456. {
  457.     const string RANKS[] = {"0", "A", "2", "3", "4", "5", "6", "7", "8", "9", 
  458.                             "10", "J", "Q", "K"};
  459.     const string SUITS[] = {"c", "d", "h", "s"};
  460.  
  461.     if (aCard.m_IsFaceUp)
  462.         os << RANKS[aCard.m_Rank] << SUITS[aCard.m_Suit];
  463.     else
  464.         os << "XX";
  465.  
  466.     return os;
  467. }
  468.  
  469. //overloads << operator so a GenericPlayer object can be sent to cout
  470. ostream& operator<<(ostream& os, const GenericPlayer& aGenericPlayer)
  471. {
  472.     os << aGenericPlayer.m_Name << ":\t";
  473.     
  474.     vector<Card*>::const_iterator pCard;
  475.     if (!aGenericPlayer.m_Cards.empty())
  476.     {
  477.         for (pCard = aGenericPlayer.m_Cards.begin(); 
  478.              pCard != aGenericPlayer.m_Cards.end(); ++pCard)
  479.             os << *(*pCard) << "\t";
  480.  
  481.         if (aGenericPlayer.GetTotal() != 0)
  482.             cout << "(" << aGenericPlayer.GetTotal() << ")";
  483.     }
  484.     else
  485.     {
  486.         os << "<empty>";
  487.     }
  488.         
  489.     return os;
  490. }
  491.