Section 3 - Returning to Cards

7B.3.1 Cards

Let's return to our card program.  To get started, we want to place our classes in separate files, so start an empty console application and add two files:  a Card.h header file and a Card.cpp source file.  You will then have three files to work with: these two and your main file (based on the name of your project, mine is still Foothill).

Let's pour our Card and Deck classes into the two Card files:

CardSupport.h

// CS 2B - LOCEFF
// CardSupport.h Header File
#pragma once

#include <iostream>
#include <ctime>
#include <string>
#include <cctype>

using namespace std;

// class Card prototype ----------------------------------------
class Card
{
public:
  enum Suit { clubs, diamonds, hearts, spades };

private:
  char value;
  Suit suit;
  bool errorFlag;

public:
  Card(char value = 'A', Suit suit = spades);
  string toString();
  bool set(char value = 'A', Suit suit = spades);
  char getVal() { return value; }
  Suit getSuit() { return suit; }
  bool getErrorFlag() { return errorFlag; }
  bool equals(Card card);

  // helpers
private:
  bool isValid(char value, Suit suit);
};

// class Hand prototype ----------------------------------------
class Hand
{
public:
  // need this in-line for array declaration
  static const int MAX_CARDS_PER_HAND = 50;   // that should be enough for any game

private:
  Card myCards[MAX_CARDS_PER_HAND];
  int numCards;

public:
  Hand() { resetHand(); }

  // mutators
  void resetHand() { numCards = 0; }
  bool takeCard(Card card);
  Card playCard();

  // accessors
  string toString();
  int getNumCards() { return numCards; }
  Card inspectCard(int k);
};

// class Deck prototype ----------------------------------------
class Deck
{
  // six full decks is enough for about any game
  static const int MAX_CARDS_PER_DECK = 6 * 52;
  static Card masterPack[52];  // one 52-card master for initializing decks

private:
  Card cards[MAX_CARDS_PER_DECK];
  int topCard;
  int numPacks;

public:
  Deck(int numPacks = 1);
  void init(int numPacks = 1);
  int getNumCards() { return topCard; }
  void shuffle();
  Card dealCard();
  Card inspectCard(int k);
  string toString();

private:
  static void allocateMasterPack();
  bool floatLargestToTop(int top);
  void swap(Card &a, Card &b);
};

CardSupport.cpp

// CS 2B - LOCEFF
// CardSupport.cpp source file

#include "CardSupport.h"

// beginning of Card method definitions ----------------------------------------
// constructor
Card::Card(char value, Suit suit)
{
  // because mutator sets errorFlag, we don't have to test
  set(value, suit);
}

// stringizer
string Card::toString()
{
  string retVal;
  char strVal[2];

  if (errorFlag)
  return "** illegal **";

  // convert char to a CString, then string
  strVal[0] = value;
  strVal[1] = '\0';
  retVal = string(strVal);

  if (suit == spades)
    retVal += " of Spades";
  else if (suit == hearts)
    retVal += " of Hearts";
  else if (suit == diamonds)
    retVal += " of Diamonds";
  else if (suit == clubs)
    retVal += " of Clubs";

  return retVal;
}

// mutator
bool Card::set(char value, Suit suit)
{
  char upVal;

  // convert to uppercase to simplify (may need to #include <cctype>)
  upVal = toupper((int)value);

  if ( !isValid(upVal, suit))
  {
    errorFlag = true;
    return false;
  }

  // else implied
  errorFlag = false;
  this->value = upVal;
  this->suit = suit;
  return true;
}

// helper
bool Card::isValid(char value, Suit suit)
{
  char upVal;

  // convert to uppercase to simplify (need #include <cctype>)
  upVal = toupper((int)value);

  // check for validity
  if (
  upVal == 'A' || upVal == 'K'
  || upVal == 'Q' || upVal == 'J'
  || upVal == 'T'
  || (upVal >= '2' && upVal <= '9')
  )
    return true;
  else
    return false;
}

bool Card::equals(Card card)
{
  if (this->value != card.value)
    return false;
  if (this->suit != card.suit)
    return false;
  if (this->errorFlag != card.errorFlag)
    return false;
  return true;
}

// end of Card method definitions ----------------------------------------

// beginning of Hand method definitions ----------------------------------------
bool Hand::takeCard(Card card)
{
  if (numCards >= MAX_CARDS_PER_HAND)
    return false;

  // don't just assign:  mutator assures active/undeleted
  myCards[numCards++].set( card.getVal(), card.getSuit() );
  return true;
}

Card Hand::playCard()
{
  // always play highest card in array.  client will prepare this position.
  // in rare case that client tries to play from a spent hand, return error

  Card errorReturn(0, Card::spades); // in rare cases

  if (numCards == 0)
    return errorReturn;
  else
    return myCards[--numCards];
}

Card Hand::inspectCard(int k)
{
  // return copy of card at position k.
  // if client tries to access out-of-bounds card, return error

  Card errorReturn(0, Card::spades); // force errorFlag in return, in rare cases

  if (k < 0 || k >= numCards)
    return errorReturn;
  else
    return myCards[k];
}

string Hand::toString()
{
  int k;
  string retVal = "Hand =  ( ";

  for (k = 0; k < numCards; k++)
  {
    retVal += myCards[k].toString();
    if (k < numCards - 1)
    retVal += ", ";
  }
  retVal += " )";
  return retVal;
}
// end of Hand method definitions ------------------------------------

// beginning of Deck method definitions ------------------------------
Card Deck::masterPack[52];  // static definition

Deck::Deck(int numPacks)
{
  // place cards in the deck, in perfect order
  allocateMasterPack();
  init(numPacks);
}

void Deck::allocateMasterPack()
{
  int k, j;
  Card::Suit st;
  char val;

  // we're in a static method; only needed once per program:  good for whole class
  static bool firstTime = true;
  if ( !firstTime )
    return;
  firstTime = false;

  // first load an array of cards with values
  for (k = 0; k < 4; k++)
  {
    // set the suit for this loop pass
    st = (Card::Suit)k;

    // now set all the values for this suit
    masterPack[13*k].set('A', st);
    for (val='2', j = 1; val<='9'; val++, j++)
      masterPack[13*k + j].set(val, st);
      masterPack[13*k+9].set('T', st);
      masterPack[13*k+10].set('J', st);
      masterPack[13*k+11].set('Q', st);
      masterPack[13*k+12].set('K', st);
    }
  }
}

// set deck from 1 to 6 packs, perfecly ordered
void Deck::init(int numPacks)
{
  int k, pack;

  if (numPacks < 1 || numPacks > 6)
  numPacks = 1;

  // when initing, reset the random num generator for shuffling
  srand(time(NULL));

  // then transfer these values to our deck
  for (pack = 0; pack < numPacks; pack++)
  for (k = 0; k < 52; k++)
  cards[pack * 52 + k] = masterPack[k];

  this->numPacks = numPacks;
  topCard = numPacks * 52;
}

void Deck::shuffle()
{
  Card tempCard;
  int k, randInt;

  // topCard is size of deck
  for (k = 0; k < topCard; k++)
  {
    randInt = rand() % topCard;

    // swap cards k and randInt (sometimes k == randInt:  okay)
    tempCard = cards[k];
    cards[k] = cards[randInt];
    cards[randInt] = tempCard;
  }
}

Card Deck::dealCard()
{
  // always deal the topCard.
  Card errorReturn(0, Card::spades); // in rare cases

  if (topCard == 0)
    return errorReturn;
  else
    return cards[--topCard];
}

Card Deck::inspectCard(int k)
{
  // return copy of card at position k.
  // if client tries to access out-of-bounds card, return error

  Card errorReturn(0, Card::spades); // force errorFlag in return, in rare cases

  if (k < 0 || k >= topCard)
    return errorReturn;
  else
    return cards[k];
}
// end of Hand method definitions ------------------------------------

Your Main .cpp file

This is just to make sure your project is working before we start monkeying with it:

// CS 2B - LOCEFF
// Client driver for Card, Hand, Deck classes ============

#include "CardSupport.h"

#define MAX_HANDS 10
// main client --------------------------------------------------------
int main()
{
  int k, numHands;
  Deck deck(1);
  Hand hands[MAX_HANDS];

  // get the input from the user --------------------------
  do
  {
    cout << "How many hands? (1 - " << MAX_HANDS << ", please): ";
    cin >> numHands;
  }
  while (numHands < 1 || numHands > MAX_HANDS);

  // deal deck, unshuffled --------------------------------
  while (deck.getNumCards() > 0)
  {
    for (k = 0; k < numHands; k++)
    {
      if (deck.getNumCards() == 0)
      break;
      hands[k].takeCard( deck.dealCard() );
    }
  }

  cout << "Here are our hands, from unshuffled deck:" << endl;
  for (k = 0; k < numHands; k++)
  {
    cout << hands[k].toString() << endl << endl;
  }
  cout << endl;

  // restock, deal deck, shuffled --------------------------
  deck.init(1);
  deck.shuffle();

  // clear hands
  for (k = 0; k < numHands; k++)
    hands[k].resetHand();

  while (deck.getNumCards() > 0)
  {
    for (k = 0; k < numHands; k++)
    {
      if (deck.getNumCards() == 0)
      break;
      hands[k].takeCard( deck.dealCard() );
    }
  }

  cout << "Here are our hands, from SHUFFLED deck:" << endl;
  for (k = 0; k < numHands; k++)
  {
    cout << hands[k].toString() << endl << endl;
  }

  return 0;
}

Get that working first.  Also review it to make sure you remember how it all works.