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.
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"); }
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); }
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"); }
Let’s now test that we can create a deck. We’ll put the
Deck
object in theCard.h
andCard.cpp
files along with theCard
object, but we can create a separatetest_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"); }
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); }
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"); }
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); }
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"); }
Now we’re ready to sort. We’ll add a single test case for both
find_lowest
andsort
, 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");
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"); }
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 thetest_deck.cpp
. Now let’s test we can createWarCard
s.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"); }
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); }
Now lets create a
Pile
.TEST_CASE("Test create empty pile") { Pile p; CHECK(p.size() == 0); }
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"); }
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); }
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.