12.6. Vectors of cards

The reason I chose Cards as the objects for this chapter is that there is an obvious use for a vector of cards—a deck. Here is some code that creates a new deck of 52 cards:

std::vector<playing_card> deck (52);

Here is the state diagram for this object:

image

The three dots represent the 48 cards I didn’t feel like drawing. Keep In mind that we haven’t initialized the instance variables of the cards yet. In some environments, they will get initialized to zero, as shown In the figure, but in others they could contain any possible value.

One way to initialize them would be to pass a playing_card as a second argument to the constructor:

playing_card ace_of_spades (3, 1);
std::vector<playing_card> deck (52, ace_of_spades);

This code builds a deck with 52 identical cards, like a special deck for a magic trick. Of course, it makes more sense to build a deck with 52 different cards in it. To do that we use a nested loop.

The outer loop enumerates the suits, from 0 to 3. For each suit, the inner loop enumerates the ranks, from 1 to 13. Since the outer loop iterates 4 times, and the inner loop iterates 13 times, the total number of times the body is executed is 52 (13 times 4).

std::size_t i = 0;
for (int suit = 0; suit <= 3; suit++) {
  for (int rank = 1; rank <= 13; rank++) {
    deck[i].suit = suit;
    deck[i].rank = rank;
    i++;
  }
}

I used the variable i to keep track of where in the deck the next card should go.

Notice that we can compose the syntax for selecting an element from an array (the [] operator) with the syntax for selecting an instance variable from an object (the dot operator). The expression deck[i].suit means “the suit of the ith card in the deck”.

This deck-building code is encapsulated in a function called build_deck that takes no parameters and that returns a fully-populated vector of playing_cards.

Take a look at the active code below, which includes the implementation of the build_deck function.

Example c192_12_6
 1#include <cstddef>
 2#include <iostream>
 3#include <string>
 4#include <vector>
 5
 6struct playing_card {
 7    int suit, rank;
 8
 9    playing_card ();
10    playing_card (int s, int r);
11    void print () const;
12};
13
14std::vector<playing_card> build_deck() {
15    std::vector<playing_card> deck (52);
16    std::size_t i = 0;
17    for (int suit = 0; suit <= 3; suit++) {
18        for (int rank = 1; rank <= 13; rank++) {
19            deck[i].suit = suit;
20            deck[i].rank = rank;
21            i++;
22        }
23    }
24    return deck;
25}
26
27int main() {
28    std::vector<playing_card> deck = build_deck();
29    std::cout << "We just created our deck of 52 cards. We can access an individual card by indexing." << std::endl;
30    std::cout << "For example, the first card in the deck is: ";
31    deck[0].print();
32}

Take a look at the code below. What can we say about the deck that is created?

vector<Card> createDeck() {
   vector<Card> deck (16);
   int i = 0;
   for (int suit = 0; suit <= 1; suit++) {
      for (int rank = 4; rank <= 11; rank++) {
         deck[i].suit = suit;
         deck[i].rank = rank;
         i++;
      }
   }
   return deck;
}

If we actually created the deck in the previous question, what is printed after the following code runs?

deck[11].print();

Type your answer exactly as it would appear in the terminal!