Chapter 13 Exercise Set 0: Chapter Review

We will be modifying the Card objects we created in the Vectors of objects chapter, using the same Cards.h and Cards.cpp files. The new Deck objects will be added there too.

  1. First, lets write tests for the constructors and a to_string() function to make sure they still work with.

    #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
    #include <doctest.h>
    #include <string>
    #include "Cards.h"
    using namespace std;
    
    TEST_CASE("Test can create and render Cards") {
        Card c1(DIAMONDS, JACK);
        CHECK(c1.to_string() == "Jack of Diamonds");
        Card c2;
        CHECK(c2.to_string() == "Joker");
        Card c3(HEARTS, QUEEN);
        CHECK(c3.to_string() == "Queen of Hearts");
        Card c4(SPADES, SEVEN);
        CHECK(c4.to_string() == "7 of Spades");
    }
    
  2. Now we’ll add the comparision operators.

    TEST_CASE("Test for comparison of Cards") {
        Card c1(HEARTS, QUEEN);
        Card c2(HEARTS, QUEEN);
        Card c3(SPADES, ACE);
        Card c4(HEARTS, FIVE);
        Card c5(HEARTS, FOUR);
        Card c6(DIAMONDS, FOUR);
        CHECK((c1 == c2) == true);
        CHECK((c1 == c3) == false);
        CHECK((c1 != c3) == true);
        CHECK((c1 > c5) == true);
        CHECK((c1 <= c5) == false);
        CHECK((c6 < c5) == true);
        CHECK((c5 >= c6) == true);
    }
    
  3. We are going to need a helper function to be able to swap two random card. Let’s add that now. It’s signature will be void swap_cards(Card*, Card*).

    TEST_CASE("Test swap cards function") {
        Card c1(HEARTS, QUEEN);
        Card c2(SPADES, ACE);
        swap_cards(&c1, &c2);
        CHECK(c1.to_string() == "Ace of Spades");
        CHECK(c2.to_string() == "Queen of Hearts");
    }
    
  4. Let’s now test that we can create a deck. We’ll put the Deck object in the Card.h and Card.cpp files along with the Card object, but we can create a separate test_decks.cpp file to test decks, which can start out like this:

    #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
    #include <doctest.h>
    #include <string>
    #include "Cards.h"
    using namespace std;
    
    TEST_CASE("Test create Deck with two constructors") {
        Deck d1(10);
        CHECK(d1.cards.size() == 10);
        CHECK(d1.cards[0].to_string() == "Joker");
        Deck d2;
        CHECK(d2.cards.size() == 52);
        CHECK(d2.cards[0].to_string() == "2 of Clubs");
        CHECK(d2.cards[51].to_string() == "Ace of Spades");
    }
    
  5. Add a find member function that will return the position of a card in the deck.

    TEST_CASE("Test find Card in Deck") {
        Deck d;
        Card c(HEARTS, QUEEN);
        int pos = d.find(c);
        CHECK(d.cards[pos].to_string() == "Queen of Hearts");
        // Create non-existant card to confirm it isn't in the deck
        Card c2(NONE, QUEEN);
        int pos2 = d.find(c2);
        CHECK(pos2 == -1);
    }
    
  6. Add a swap_cards member function.

    TEST_CASE("Test swap_cards in Deck") {
        Deck d;
        d.swap_cards(0, 51);
        CHECK(d.cards[0].to_string() == "Ace of Spades");
        CHECK(d.cards[51].to_string() == "2 of Clubs");
    }
    
  7. Time to add and remove cards on a deck.

    TEST_CASE("Test remove_card and add_card") {
        Deck deck;
        deck.shuffle();
        CHECK(deck.cards.size() == 52);
        Card c = deck.remove_card();
        CHECK(deck.cards.size() == 51);
        Deck deck2(0);
        CHECK(deck2.cards.size() == 0);
        deck2.add_card(c);
        CHECK(deck2.cards.size() == 1);
        CHECK((deck2.cards[0] == c) == true);
    }
    
  8. We will need to divide a deck into parts to use merge sort.

    TEST_CASE("Test create subdecks") {
        Deck deck;
        Deck subdeck = deck.subdeck(2, 10);
        CHECK(subdeck.cards.size() == 9);
        CHECK(subdeck.cards[0].to_string() == "4 of Clubs");
    }
    
  9. Now we’re ready to sort. We’ll add a single test case for both find_lowest and sort, but we will add the tests in separate steps. First add:

    TEST_CASE("Test find_lowest_between and sort") {
        Deck d(0);
        d.add_card(Card(HEARTS, JACK));   // index 0
        d.add_card(Card(HEARTS, TWO));    // index 1
        d.add_card(Card(HEARTS, NINE));   // index 2
        d.add_card(Card(HEARTS, FOUR));   // index 3
        d.add_card(Card(HEARTS, SEVEN));  // index 4
        d.add_card(Card(HEARTS, SIX));    // index 5
        d.add_card(Card(HEARTS, FIVE));   // index 6
        d.add_card(Card(HEARTS, EIGHT));  // index 7
        d.add_card(Card(HEARTS, THREE));  // index 8
        d.add_card(Card(HEARTS, TEN));    // index 9
        CHECK(d.find_lowest(0, 9) == 1);
        CHECK(d.find_lowest(2, 7) == 3);
        CHECK(d.find_lowest(5, 9) == 8);
    }
    

    Then add a call to sort checks for its effect to the end of the test case:

    d.sort();
    CHECK(d.cards[0].to_string() == "2 of Hearts");
    CHECK(d.cards[1].to_string() == "3 of Hearts");
    CHECK(d.cards[3].to_string() == "5 of Hearts");
    CHECK(d.cards[8].to_string() == "10 of Hearts");
    CHECK(d.cards[9].to_string() == "Jack of Hearts");
    
  10. Time for merge sort.

    TEST_CASE("Test merge_sort") {
        Deck d(0);
        d.add_card(Card(HEARTS, QUEEN));
        d.add_card(Card(SPADES, ACE));
        d.add_card(Card(CLUBS, FOUR));
        d.add_card(Card(DIAMONDS, FOUR));
        d.add_card(Card(CLUBS, FIVE));
        d.add_card(Card(HEARTS, TEN));
        d.add_card(Card(CLUBS, TWO));
        Deck d2 = d.merge_sort();
        CHECK(d2.cards[0].to_string() == "2 of Clubs");
        CHECK(d2.cards[1].to_string() == "4 of Clubs");
        CHECK(d2.cards[5].to_string() == "Queen of Hearts");
        CHECK(d2.cards[6].to_string() == "Ace of Spades");
        // Check d is not changed.
        CHECK(d.cards[0].to_string() == "Queen of Hearts");
        CHECK(d.cards[3].to_string() == "4 of Diamonds");
        CHECK(d.cards[6].to_string() == "2 of Clubs");
    }
    
  11. Create a new test file named test_war.cpp to test the new features needed to implement the War game. It should have the same includes as the test_deck.cpp. Now let’s test we can create WarCards.

    TEST_CASE("Test create WarCards") {
        WarCard wc1(DIAMONDS, JACK);
        WarCard wc2(HEARTS, ACE);
        WarCard wc3;
        CHECK(wc1.to_string() == "Jack of Diamonds");
        CHECK(wc2.to_string() == "Ace of Hearts");
        CHECK(wc3.to_string() == "Joker");
    }
    
  12. Make sure WarCards compare as desired.

    TEST_CASE("Test compare WarCards") {
        WarCard wc1(DIAMONDS, JACK);
        WarCard wc2(CLUBS, JACK);
        WarCard wc3(HEARTS, ACE);
        WarCard wc4;
        WarCard wc5(CLUBS, TEN);
        CHECK((wc1 == wc2) == true);
        CHECK((wc1 != wc2) == false);
        CHECK((wc3 > wc2) == true);
        CHECK((wc4 > wc3) == true);
        CHECK((wc4 <= wc3) == false);
        CHECK((wc2 >= wc4) == false);
        CHECK((wc2 < wc3) == true);
    }
    
  13. Now lets create a Pile.

    TEST_CASE("Test create empty pile") {
        Pile p;
        CHECK(p.size() == 0);
    }
    
  14. And add cards to it and remove cards from it.

    TEST_CASE("Test add and remove cards from pile") {
        Pile p;
        p.add_card(WarCard(HEARTS, QUEEN));
        CHECK(p.size() == 1);
        p.add_card(WarCard(HEARTS, TEN));
        p.add_card(WarCard(HEARTS, TWO));
        CHECK(p.size() == 3);
        WarCard c = p.remove_card();
        CHECK(p.size() == 2);
        // Check FIFO behavior for piles (removed card is Queen of Hearts)
        CHECK(c.to_string() == "Queen of Hearts");
    }
    
  15. Almost home. Let’s create two piles from a 54 card shuffled deck.

    TEST_CASE("Test create piles from subdeck") {
        Deck d;
        Card c;
        Card c2;
        d.add_card(c);
        d.add_card(c2);
        CHECK(d.cards.size() == 54);
        d.shuffle();
        Pile p1(d.subdeck(0, 26));
        Pile p2(d.subdeck(27, 53));
        CHECK(p1.size() == 27);
        CHECK(p2.size() == 27);
    }
    
  16. Last thing we need is to be able to move War cards between piles.

    TEST_CASE("Test move cards from one pile to another") {
        Deck d;
        Pile p1(d.subdeck(0, 9));
        Pile p2(d.subdeck(10, 19));
        CHECK(p1.size() == 10);
        CHECK(p2.size() == 10);
        p1.move_cards(p2);
        CHECK(p1.size() == 20);
        CHECK(p2.size() == 0);
    }
    

We now have all we need to implement the game play. We’ll do that in the next exercise set.