13.8. Subdecks¶
How should we represent a hand or some other subset of a full deck? One
easy choice is to make a card_deck object that has fewer than 52 cards.
We might want a function, subdeck, that takes a vector of cards and
a range of indices, and that returns a new vector of cards that contains
the specified subset of the deck:
card_deck card_deck::subdeck (std::ptrdiff_t low, std::ptrdiff_t high) const {
card_deck sub (high - low + 1);
for (std::size_t i = 0; i < sub.cards.size(); i++) {
sub.cards[i] = cards[low + i];
}
return sub;
}
To create the local variable named sub, we use the card_deck
constructor that takes a size and default-initializes its cards.
We then assign the corresponding cards
from the original deck over those defaults.
The length of the subdeck is high - low + 1 because both the low card
and high card are included.
The endpoints use std::ptrdiff_t because an empty range can end at
low - 1, including -1 when low is zero. Require
0 <= low <= std::ssize(cards) and
low - 1 <= high < std::ssize(cards) before subtracting or indexing.
The complete example below checks these conditions and rejects an invalid
range with std::out_of_range. Its allocation size uses std::size_t.
Warning
This sort of computation can be confusing and can lead to “off-by-one” errors. Drawing a picture is usually the best way to avoid them.
As an exercise, write a version of find_bisect that takes a subdeck
as an argument, rather than a deck and an index range. Which version is
more error-prone? Which version do you think is more efficient?
Try writing the find_bisect function in the commented section
of the active code below. If done correctly, the program should output that
the Seven of Clubs is at index 6 and the King of Diamonds is at index -1.
If you get stuck, you can reveal the extra problem at the end for help.
1#include <iterator>
2#include <stdexcept>
3#include <cstddef>
4#include <random>
5#include <iostream>
6#include <string>
7#include <vector>
8#include <cstdlib>
9using std::cout;
10
11enum card_suit { clubs, diamonds, hearts, spades };
12
13enum card_rank { ace=1, two, three, four, five, six, seven, eight, nine,
14ten, jack, queen, king };
15
16std::size_t random_int(std::size_t low, std::size_t high);
17
18struct playing_card {
19 card_rank rank;
20 card_suit suit;
21 playing_card ();
22 playing_card (card_suit s, card_rank r);
23 void print () const;
24 bool is_greater (const playing_card& c2) const;
25 bool equals (const playing_card& c2) const;
26};
27
28struct card_deck {
29 std::vector<playing_card> cards;
30 card_deck ();
31 card_deck (std::size_t n);
32 void print () const;
33 void swap_cards (std::size_t index1, std::size_t index2);
34 std::size_t find_lowest_card (std::size_t index);
35 void shuffle_deck ();
36 void sort_deck ();
37 card_deck subdeck (std::ptrdiff_t low, std::ptrdiff_t high) const;
38};
39
40std::ptrdiff_t find_bisect (card_deck subdeck, playing_card card) {
41 // ``find_bisect`` should search through the subdeck and
42 // return the location of card. If card is not found in
43 // subdeck, it should return -1.
44 // Delete the return 0 and write your implementation here.
45 return 0;
46}
47
48int main() {
49 card_deck deck;
50 card_deck club_cards = deck.subdeck(0, 12);
51 club_cards.print();
52 playing_card card1 (clubs, seven);
53 playing_card card2 (diamonds, king);
54 cout << std::endl;
55 cout << "The Seven of Clubs is at index " << find_bisect (club_cards, card1) << std::endl;
56 cout << "The King of Diamonds is at index " << find_bisect (club_cards, card2) << std::endl;
57}
find_bisect Help
Return the index of the card in this sorted subdeck, or -1 if it is absent. The subdeck function accepts inclusive signed endpoints.
-
return found == -1 ? -1 : mid + 1 + found; } -
const std::ptrdiff_t found = find_bisect( subdeck.subdeck(mid + 1, std::ssize(subdeck.cards) - 1), card); -
const std::ptrdiff_t mid = std::ssize(subdeck.cards) / 2; -
if (subdeck.cards.empty()) return -1; -
if (subdeck.cards[mid].equals(card)) return mid; -
if (subdeck.cards[mid].is_greater(card)) { return find_bisect(subdeck.subdeck(0, mid - 1), card); } -
std::ptrdiff_t find_bisect(card_deck subdeck, playing_card card) {