Solution to Programming Assignment 5
////
//
// This program contains four functions that will be used in the card game
// playing program of assignment 6. The functions are Shuffle, DealTwoHands,
// CompareTwoCards, and PageInstructions. The complete user-level
// specifications for these functions are defined in the program 5 assignment
// writeup at
//
// http://www.csc.calpoly.edu/~gfisher/classes/101/assignments/program5.html
//
// In addition to the four processing functions, there are a number of
// work-doing subfunctions that the four top-level processing functions call.
//
// Also, there is a companion testing function for each of the four processing
// functions. The testing functions, with the aid of some subfunctions, call
// the processing functions and output the results for inspection.
//
//
// Author: Gene Fisher (gfisher@calpoly.edu)
// Created: 26may99
// Modified: 27may99
//
////
#include <fstream.h>
#include <iostream.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "Boolean.h"
//
// Constant definitions
//
const int DECK_SIZE = 52; // Number of cards in a deck
const int CARD_STR_LEN = 4; // Number of chars in a card string
const int MAX_HAND_SIZE = 20; // Maximum number of cards in any hand
const int SUIT_POSITION = 2; // Position of the suit char in a card
const int MAX_SHUFFLES = 10; // Maximum number of times to test shuffle
const char BLANK_CARD[] = " "; // A blank card in a deck or hand
const int MAX_INSTR_LINES = 500; // Max number of lines in instructions file
const int MAX_INSTR_COLS = 80; // Max number of columns in instructions file
//
// Type Deck is an array of char strings. It represents the card deck used in
// game play.
//
typedef char Deck[DECK_SIZE][CARD_STR_LEN];
// ^^^^^^^^^ ^^^^^^^^^^^^
// ^^^^^^^^^ ^^^^^^^^^^^^ CARD_STR_LEN is the size of each
// ^^^^^^^^^ array element (a card string)
// ^^^^^^^^^
// ^^^^^^^^^ DECK_SIZE is the size of the deck array
//
// Type Hand is an array of char strings. It represents a hand dealt to a game
// player. By convention, there is a blank card immediately after the last
// used element of the hand.
//
typedef char Hand[MAX_HAND_SIZE + 1][CARD_STR_LEN];
//
// Type FileGuts is an array of strings to hold the contents of the
// instructions file.
//
typedef char FileGuts[MAX_INSTR_LINES][MAX_INSTR_COLS + 1];
//
// Type Card is just an alias for string, since cards are represented as string
// values.
//
typedef char* Card;
////
//
// Function Shuffle takes the cards in the given unshuffled_deck and stores
// them in random positions in the given shuffled_deck. The degree of
// randomness is that provided by the UNIX rand function, q.v. Note that
// shuffle does not call srand; therefore, the seeding of rand is outside the
// scope of Shuffle.
//
// Precondition: cards in unshuffled_deck are in order from lowest to highest
//
// Postcondition: cards in shuffled deck are in random order &&
// no cards are missing from shuffled deck &&
// no cards are duplicated in shuffled deck
//
////
void Shuffle(
Deck unshuffled_deck, // Unshuffled input deck
Deck shuffled_deck // Shuffled output deck
);
////
//
// Function DealTwoHands deals two hands from the given deck. The input value
// of the top parameter is the starting position in the deck from which dealing
// begins. The output value of top is the position after the hands have been
// dealt. The num_cards input is how many cards to deal to each hand. The
// cards are dealt successively to each hand until either both hands contain
// num_cards cards, or the deck has run out of cards. If the deck contains
// sufficient cards to fill both hands, then the num_not_dealt return parameter
// is 0, otherwise it is set to the number of cards that were not dealt. By
// convention, there is a blank card in the hand immediately after the last
// card dealt to the hand.
//
// NOTE: The num_not_dealt return value is an advanced feature that was not
// expected in a turned-in solution.
//
////
void DealTwoHands(
Deck deck, // Deck to deal from
int& top, // Index of top card in deck
int num_cards, // Number of cards to deal to each hand
Hand hand1, // 1st hand dealt to
Hand hand2, // 2nd hand dealt to
int& num_not_dealt // Number of cards not dealt
);
////
//
// Function CompareTwoCards returns true if card1 beats card2, by the normal
// rules of ace-high card ordering. Specifically,
//
// a. Ace beats king beats queen beats jack beats any numbered card.
//
// b. A numbered card beats another numbered card with a lower value.
// E.g., a 10 beats a 3.
//
// c. For two cards of the same face value, the suit of the card determines
// order. Specifically, spades beats hearts beats diamonds beats clubs.
//
// The function assumes that card1 and card2 are not equal.
//
// Precondition:
// strcmp(card1, card2) != 0
//
// Postcondition:
// if ComputeFaceValue(card1) > ComputeFaceValue(card2) then {
// return == TRUE
// }
// else if ComputeFaceValue(card1) < ComputeFaceValue(card2) then {
// return == FALSE
// }
// else {
// return == ComputeSuitValue(card1) >
// ComputeSuitValue(card2)
// }
//
////
Boolean CompareTwoCards(
Card card1, // 1st card to compare
Card card2 // 2nd card to compare
);
////
//
// Function ComputeFaceValue returns the ace-high integer face value of the
// given card. Viz., a numeric face value of 2 through 10 is the number
// itself. Jack is 11, queen 12, king 13, and ace 14.
//
////
int ComputeFaceValue(
Card card // Input card
);
////
//
// Function ComputeSuit returns an integer value reflecting the ordinal value
// of the suit of the given card. Viz., spade is 3, heart is 2, diamond is 1,
// and club is 0.
//
////
int ComputeSuitValue(
Card card // Input card
);
////
//
// Function PageInstructions outputs the contents of the given text_file a page
// at a time to the terminal. When the function outputs the file contents, it
// does so in groups of lines_per_page lines at a time. Between each page of
// output, the function prompts the user with the following line:
//
// Enter 'n' to see the next page, 'p' to see the previous page, 'q' to quit:
//
// The function responds to 'n' by showing the next page (or partial last page)
// on the terminal. If 'n' is typed on the last page, the display is
// unchanged, i.e., the last page remains displayed. The function responds to
// 'p' by showing the previous page on the terminal. If 'p' is typed on the
// first page, the display is unchanged, i.e., the first page remains
// displayed. The function responds to 'q' by immediately terminating.
//
////
void PageInstructions(
ifstream& text_file, // Text file to output
int lines_per_page // Number of lines in one page of output
);
////
//
// Function ReadInstructionsFile is a subfunction of PageInstructions that
// reads the contents of the given text_file into the given file_guts array and
// sets the num_lines output parameter to the number of lines read in.
//
////
void ReadInstructionsFile(
ifstream& text_file, // File to read from
FileGuts file_guts, // Full contents of the file
int& num_lines // Number of file lines read in
);
////
//
// Function InputPageCommand prompts for and inputs the page command, returning
// it in the page_command reference parameter. Up to 100 additional chars
// between the single command char and the end of line are ignored.
//
////
void InputPageCommand(
char& page_command // Page command input from the terminal
);
////
//
// Function OutputNextPage is a subfunction of PageInstructions that outputs
// the next full or partial page of instructions in response to the 'n'
// command. The input value of current_pos is the starting line position of
// the page to be output. The output value of current_pos is one past the last
// line of the page just output; this accounts for both full length pages and a
// partial length last page. The lines_per_page input is the number of lines
// in one page of output. The num_lines input is the total number of lines in
// the text file. The FileGuts input is text to be output.
//
////
void OutputNextPage(
int& current_pos, // Current line position input and output
int lines_per_page, // Number of lines in one page
int num_lines, // Total number of lines in the text file
FileGuts file_guts // Complete contents of the text file
);
////
//
// Function OutputPrevPage is a subfunction of PageInstructions that outputs
// the previous full page of instructions in response to the 'p' command. The
// input value of current_pos is one past the ending line of the most recently
// output page. (Note that this same value is interpreted as the starting
// position of the page to be output by OutputNextPage). The output value of
// current_pos is one past the ending line of the page just output, which is
// always a full page. The lines_per_page input is the number of lines in one
// page of output. The num_lines input is the total number of lines in the
// text file. The FileGuts input is text to be output.
//
////
void OutputPrevPage(
int& current_pos, // Current line position input and output
int lines_per_page, // Number of lines in one page
int num_lines, // Total number of lines in the text file
FileGuts file_guts // Complete contents of the text file
);
////
//
// Function TestShuffle calls Shuffle a number of times and outputs the results
// to the terminal in a side-by-side display of each shuffled deck. The
// new_deck input is the starting unshuffled deck. The num_shuffles input is
// the number of times to call Shuffle. Num_shuffles must be less than
// MAX_SHUFFLES, which is the maximum size of the side-by-side display. The
// first of the shuffled decks is returned in the shuffled_deck reference
// parameter.
//
// NOTE: This version of TestShuffle is more advanced than what we expected to
// be turned in. A turned-in version need only call Shuffle two or three
// times, and need not take any parameters.
//
////
void TestShuffle(
Deck new_deck, // New deck to send to shuffle the 1st time
int num_shuffles, // Number of times to call shuffle
Deck shuffled_deck // Return shuffle deck
);
////
//
// Function TestDealTwoHands calls DealTwoHands six times, with hand sizes 0,
// 1, 2, 3, 10, and 26. The deck input is the deck to deal from, starting from
// the 0th position of the deck. The dealt hands for each call are output to
// the terminal. In the last call, the deck runs out of cards, so only partial
// hands are dealt.
//
// NOTE: Testing of dealing past the end of the deck is an advanced feature
// that was not expected in a turned-in solution.
//
////
void TestDealTwoHands(
Deck deck // Deck to start dealing from
);
////
//
// Function DealAndDump is called from TestDealTwoHands to call the dealing
// function once and output the results. The deck, top, and num_cards
// parameters are passed on to DealTwoHands.
//
////
void DealAndDump(
Deck deck, // Deck to deal from
int& top, // Index of top of deck
int num_cards // Number of cards to deal
);
////
//
// Function DumpHand dumps the given hand to the terminal, preceded by the
// given message string.
//
////
void DumpHand(
Hand hand, // Hand to dump
char* message // String to print in front hand cards
);
////
//
// Function TestCompareTwoCards calls CompareTwoCards up to DECK_SIZE * 3
// times. The first DECK_SIZE calls compare the cards of the given
// unshuffled_deck offset up by one card in the 2nd card of the comparison.
// I.e., unshuffled_deck[i] is compared with unshuffled_deck[(i+1) %
// DECK_SIZE], for each 0 <= i < DECK_SIZE. This should yield all false
// comparisons, except for the last. The second DECK_SIZE calls compare the
// cards of unshuffled_deck offset up by one card in the 1st card of the
// comparison. I.e., unshuffled_deck[(i+1) % CARD_SIZE] is compared with
// unshuffled_deck[i]. This should yield all true comparisons, except for the
// last. The final DECK_SIZE (or fewer) calls compare cards of the unshuffled
// and shuffled decks in order, except where the cards are the same.
//
// NOTE: This testing is more sophisticated than was expected for a turned-in
// assignment. For a turn-in, the third round of testing (comparing cards from
// the unshuffled and shuffled decks) is sufficient.
//
////
void TestCompareTwoCards(
Deck unshuffled_deck, // Unshuffled deck to use in testing
Deck shuffled_deck // Shuffled deck to use in testing
);
////
//
// Function TestPageInstructions calls PageInstructions four times. Prior to
// the calls, it outputs the following testing directions to the terminal,
// telling the user how to interact with the paging function to properly test
// it:
//
// Four calls to page instructions follow. For the first call, page
// straight through to the end with 'n', and then type 'q' to quit. For
// the second and third calls, page to the end with 'n', page all the way
// back up to the top with 'p', page down and up a few more times with 'n'
// and 'p', then quit with 'q'. For the last call, type 'q' immediately
// after the first page.
//
// For the first, second, and fourth calls, a file of length 86 and page size
// of 20 are used. For the third call, a file of length 60 and page size of 15
// are used. The purpose of the different file lengths and page sizes is to
// test when the file is exactly divisible by the page size and when it is not.
//
// NOTE: This testing is more sophisticated than was expected for a turned-in
// assignment. For a turn-in, two or three reasonable calls are acceptable.
//
////
void TestPageInstructions();
////
//
// Function DoPageCalls is the work doer for TestPageInstructions.
//
////
void DoPageCalls();
//////
////// NOTE: None of the following testing utility functions was expected in a
////// turned-in solution.
//////
////
//
// Function DumpNDecks is a testing utility that dumps up to NUM_SHUFFLES decks
// in a tab-separated, side-by-side display on the terminal. The decks to dump
// are in the given decks parameter, and the number of decks to dump is in the
// num_decks parameter.
//
////
void DumpNDecks(
Deck* decks, // Array of decks do dump
int num_decks // Number of decks in the array
);
////
//
// Function CopyDeck is a testing utility that copies all of the cards from
// src_deck into dest_deck.
//
////
void CopyDeck(
Deck dest_deck, // Destination deck, to be copied into
Deck src_deck // Source deck, to be copied from
);
////
//
// Function CardAppearsInDeckExactlyOnce is a testing utility that returns true
// if the given card appears exactly one time as an element of the given deck,
// false otherwise. The times_found output parameter is set to the exact
// number of times the card is found in the deck.
//
// NOTE: This testing is more sophisticated than was expected for a turned-in
// assignment. For a turn-in, this testing was not expected at all.
//
////
Boolean CardAppearsInDeckExactlyOnce(
Card card, // Card to check for
Deck deck, // Deck to search in
int& times_found // Counter of number of times
);
////
//
// Function main declares a couple testing card decks, and calls each of the
// four top-level testing functions.
//
////
int main() {
//
// Variable new_deck represents a deck of cards as typically arranged in a
// new package when it's first opened. Note the left padding of a blank in
// all but the 10's cards. This allows the suit to be accessed
// consistently as deck[i][2], for any card i.
//
Deck new_deck = {
" 2C", " 2D", " 2H", " 2S", " 3C", " 3D", " 3H", " 3S",
" 4C", " 4D", " 4H", " 4S", " 5C", " 5D", " 5H", " 5S",
" 6C", " 6D", " 6H", " 6S", " 7C", " 7D", " 7H", " 7S",
" 8C", " 8D", " 8H", " 8S", " 9C", " 9D", " 9H", " 9S",
"10C", "10D", "10H", "10S", " JC", " JD", " JH", " JS",
" QC", " QD", " QH", " QS", " KC", " KD", " KH", " KS",
" AC", " AD", " AH", " AS"
};
Deck shuffled_deck; // Shuffled deck returned from TestShuffle
//
// Test the deck shuffling function.
//
TestShuffle(new_deck, 8, shuffled_deck);
//
// Test the hand dealing function.
//
TestDealTwoHands(new_deck);
//
// Test the card comparison function.
//
TestCompareTwoCards(new_deck, shuffled_deck);
//
// Test the instruction paging function.
//
TestPageInstructions();
//
// Outta here.
//
return 0;
}
void Shuffle(Deck unshuffled_deck, Deck shuffled_deck) {
int i; // Loop and array index
int r; // Random int between 0 and 51, used as index
// to shuffled deck
//
// Initialize all elements of shuffled deck to blank.
//
for (i = 0; i < DECK_SIZE; i++) {
strcpy(shuffled_deck[i], BLANK_CARD);
}
//
// Iterate through the elements of the unshuffled deck, moving each to a
// random position in the shuffled deck.
//
for (i = 0; i < DECK_SIZE; i++) {
//
// Generate a random number r between 0 and 51, such that
// shuffled_deck[r] is blank. That is, loop until the chosen number is
// the index of a blank element in the shuffled_deck. Note that this
// is a "bodiless" for loop, since all of the work is done in the init,
// test, and next parts of the loop statement.
//
for (r = rand() % DECK_SIZE;
strcmp(shuffled_deck[r], BLANK_CARD) != 0;
r = rand() % DECK_SIZE)
;
//
// Copy the ith unshuffled element to the rth position in the shuffled
// deck.
//
strcpy(shuffled_deck[r], unshuffled_deck[i]);
}
}
void DealTwoHands(Deck deck, int& top, int num_cards, Hand hand1, Hand hand2,
int& num_not_dealt) {
int i1, // Index for hand1
i2; // Index for hand2
// NOTE: the reason we use two hand indices is
// to ensure that the ending blank card is put
// in the proper position in the 2nd hand if
// the deck runs out between dealing the 1st
// and 2nd hands. This is a bit subtle.
//
// Compute in advance how many cards, if any, will not be dealt.
//
num_not_dealt = (2 * num_cards) - (DECK_SIZE - top);
// ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
// ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ number of cards left
// ^^^^^^^^^^^^^^^
// ^^^^^^^^^^^^^^^ total number to deal
//
// Treat any negative not-dealt value as 0.
//
if (num_not_dealt < 0) {
num_not_dealt = 0;
}
//
// Loop through the deck starting at top and through each hand starting at
// 0. Stop when either both hands are fully dealt or top goes past the end
// of the deck.
//
for (i1 = 0, i2 = 0; (i1 < num_cards) && (top < DECK_SIZE); i1++) {
//
// Deal to the 1st hand.
//
strcpy(hand1[i1], deck[top]);
//
// Increment top to the next card in the deck.
//
top++;
//
// Deal to the 2nd hand if top is still valid.
//
if (top < DECK_SIZE) {
strcpy(hand2[i2], deck[top]);
i2++;
//
// Increment top again, and continue.
//
top++;
}
}
//
// Put blank cards into the ending positions of each hand.
//
strcpy(hand1[i1], BLANK_CARD);
strcpy(hand2[i2], BLANK_CARD);
}
Boolean CompareTwoCards(Card card1, Card card2) {
int face_value1 // Numeric face value of card 1
= ComputeFaceValue(card1);
int face_value2 // Numeric face value of card 2
= ComputeFaceValue(card2);
//
// If the face value of the cards is the same, return the result of
// comparing the suits.
//
if (face_value1 == face_value2) {
return ComputeSuitValue(card1) > ComputeSuitValue(card2);
}
//
// Otherwise, return the face value comparison.
//
return face_value1 > face_value2;
}
int ComputeFaceValue(Card card) {
//
// If the 2nd char of the card string is between 2 and 9, compute its value
// with a cute trick. Viz., subtract the value of character '0' from the
// char value itself. This will convert the digit character value to its
// numeric integer value. See pages 518-520, and page A30 of the textbook
// for more info.
//
if ((card[1] >= '2') && (card[1] <= '9')) {
return card[1] - '0';
}
//
// If the 2nd char of the card string is ace, king, queen, or, jack, make
// its face value 14, 13, 12, or 11, respectively. Note the use of the
// switch statement here. See pages 460-464 of the book.
//
// BE CAREFUL with this switch statement example. Because each case of the
// switch is a return, no break statements are used. Normally, each case
// ends in a break. See the book for further explanation.
//
switch (card[1]) {
case 'A':
return 14;
case 'K':
return 13;
case 'Q':
return 12;
case 'J':
return 11;
}
//
// What's left is the 10, assuming (as we're allowed to) that the card
// string is well formed.
//
return 10;
}
int ComputeSuitValue(Card card) {
//
// Return an int in the proper suit order. See the BE CAREFUL comment
// above.
//
switch (card[SUIT_POSITION]) {
case 'S':
return 3;
case 'H':
return 2;
case 'D':
return 1;
case 'C':
return 0;
}
}
void PageInstructions(ifstream& text_file, int lines_per_page) {
FileGuts file_guts; // Complete contents of the text file
int num_lines; // Number of file lines actually read
char page_command = ' '; // Paging command input from user
int current_pos = 0; // Current paging position
//
// Read the full file into the file_guts array and set num_lines.
//
ReadInstructionsFile(text_file, file_guts, num_lines);
//
// Unconditionally output the first page. Note that current_pos is a
// return parameter.
//
OutputNextPage(current_pos, lines_per_page, num_lines, file_guts);
//
// Input the first user command.
//
InputPageCommand(page_command);
//
// Loop through the file guts a page at a time, processing a command after
// each page.
//
while (page_command != 'q') {
//
// If the command is 'n' and we're not on the last page, dump the next
// page.
//
if (page_command == 'n' && current_pos < num_lines) {
OutputNextPage(current_pos, lines_per_page, num_lines, file_guts);
}
//
// If the command is 'p' and we're not in a <= one-page file and we're
// not on the first page, dump the previous page.
//
else if (page_command == 'p' && num_lines > lines_per_page &&
current_pos - lines_per_page != 0) {
OutputPrevPage(current_pos, lines_per_page, num_lines, file_guts);
}
else {
//
// Output an error message if illegal command.
//
if (page_command != 'n' && page_command != 'p') {
cout << "Invalid command." << endl;
}
}
//
// Input the next command.
//
InputPageCommand(page_command);
}
}
void ReadInstructionsFile(ifstream& text_file, FileGuts file_guts,
int& num_lines) {
//
// Get the first line of the text file. Note the use of ifstream.get with
// a string parameter and ifstream.ignore, as discussed on pages 673-674 of
// the book.
//
text_file.get(file_guts[0], MAX_INSTR_COLS + 1);
text_file.ignore(1, '\n');
//
// Loop through the text file to its end, reading each line into the
// file_guts array.
//
for (num_lines = 1; text_file; num_lines++) {
text_file.get(file_guts[num_lines], MAX_INSTR_COLS + 1);
text_file.ignore(1, '\n');
}
//
// Decrement the number of lines down to its actual value.
//
num_lines--;
}
void InputPageCommand(char& page_command) {
//
// Prompt.
//
cout << " Enter 'n' to see the next page, 'p' to see the previous page, 'q' to quit: ";
//
// Input one char.
//
cin >> page_command;
//
// Ignore any additional chars (up to 100) before the new line.
//
cin.ignore(100, '\n');
}
void OutputNextPage(int& current_pos, int lines_per_page, int num_lines,
FileGuts file_guts) {
int start_pos = current_pos; // Starting position of output
int i; // Loop and array index
//
// Increment the current position by the page size.
//
current_pos = current_pos + lines_per_page;
//
// If we've come to the last partial page, set the current_position to the
// last actual line position.
//
if (current_pos > num_lines) {
current_pos = num_lines;
}
//
// Output lines from start_pos to current_pos - 1.
//
for (i = start_pos; i < current_pos; i++) {
cout << file_guts[i] << endl;
}
}
void OutputPrevPage(int& current_pos, int lines_per_page, int num_lines,
FileGuts file_guts) {
int i; // Loop and array index
//
// If the current position is at the end of a full page, go back one full
// page, else go back the length of the partial (last) page we're on.
//
if (current_pos % lines_per_page == 0) {
current_pos = current_pos - lines_per_page;
}
else {
current_pos = current_pos - num_lines % lines_per_page;
}
//
// Output lines from current_pos - lines_per page to current_pos - 1. Note
// that we'll only ever be called here with a full page behind us, due to
// the logic in PageInstructions for the 'p' command, q.v.
//
for (i = current_pos - lines_per_page; i < current_pos; i++) {
cout << file_guts[i] << endl;
}
}
void TestShuffle(Deck new_deck, int num_shuffles, Deck shuffled_deck) {
int i; // Loop and array index
Deck decks[MAX_SHUFFLES]; // Array of decks for side-by-side dumping
//
// Output a brief explanatory message.
//
cout << "Results of " << num_shuffles << " test shuffles are: " << endl;
//
// Put the entering new deck in decks[0].
//
CopyDeck(decks[0], new_deck);
//
// Call shuffle num_shuffles times.
//
for (i = 0; i < num_shuffles; i++) {
Shuffle(decks[i], decks[i + 1]);
}
//
// Dump out the original deck and each of the shuffled decks, checking that
// each deck has exactly one occurrence of each card.
//
DumpNDecks(decks, num_shuffles);
cout << endl << endl;
//
// Copy the 1st shuffled deck into the return parameter for use by a later
// test.
//
CopyDeck(shuffled_deck, decks[1]);
}
void TestDealTwoHands(Deck deck) {
int top = 0; // Index of current top of deck
//
// Output a brief explanatory message.
//
cout
<< "Results of dealing six hands of sizes 0, 1, 2, 3, 5, 10, and 26 are:"
<< endl;
//
// Deal and dump hands of size 0, 1, 2, 3, 10, and 26. The last is a
// partial deal.
//
DealAndDump(deck, top, 0);
DealAndDump(deck, top, 1);
DealAndDump(deck, top, 2);
DealAndDump(deck, top, 3);
DealAndDump(deck, top, 10);
DealAndDump(deck, top, 26);
cout << endl;
}
void DealAndDump(Deck deck, int& top, int num_cards) {
Hand hand1; // 1st hand
Hand hand2; // 2nd hand
int num_not_dealt; // Number of cards not dealt
//
// Do the deal.
//
DealTwoHands(deck, top, num_cards, hand1, hand2, num_not_dealt);
//
// Dump each hand.
//
DumpHand(hand1, "Hand 1: ");
DumpHand(hand2, "Hand 2: ");
//
// Output a message if num_not_dealt is non-zero. As noted in the function
// specs, this feature is more advanced than was expected in a turned-in
// solution.
//
if (num_not_dealt > 0) {
cout << "Number of cards not dealt: " << num_not_dealt
<< endl;
}
//
// Finish off with a new line.
//
cout << endl;
}
void DumpHand(Hand hand, char* message) {
int i; // Loop and array index
//
// Output the labeling message.
//
cout << message;
//
// Loop through the hand and dump each card.
//
for (i = 0; strcmp(hand[i], BLANK_CARD) != 0; i++) {
cout << hand[i];
//
// Output a comma separator for all but the last card in the hand.
//
if (strcmp(hand[i + 1], BLANK_CARD) != 0) {
cout << ", ";
}
}
//
// Finish hand dump with new line.
//
cout << endl;
}
void TestCompareTwoCards(Deck unshuffled_deck, Deck shuffled_deck) {
int i; // Loop and array index
//
// Output a brief explanatory message.
//
cout << "Results of up to 52 * 3 card comparisons are:" << endl;
//
// Call the compare function with unshuffled_deck[i] and
// unshuffled_deck[i + 1 % DECK_SIZE], for 0 <= i < DECK_SIZE.
//
for (i = 0; i < DECK_SIZE; i++) {
cout << "Comparison of " << unshuffled_deck[i] << " with "
<< unshuffled_deck[(i + 1) % 52] << " yields: "
<< CompareTwoCards(unshuffled_deck[i],
unshuffled_deck[(i + 1) % 52])
<< endl;
}
cout << endl;
//
// Call the compare function with unshuffled_deck[(i + 1) % DECK_SIZE] and
// unshuffled_deck[i], for 0 <= i < DECK_SIZE.
//
for (i = 0; i < DECK_SIZE; i++) {
cout << "Comparison of " << unshuffled_deck[(i + 1) % DECK_SIZE]
<< " with " << unshuffled_deck[i] << " yields: "
<< CompareTwoCards(unshuffled_deck[(i + 1) % DECK_SIZE],
unshuffled_deck[i])
<< endl;
}
cout << endl;
//
// Call the compare function with unshuffled_deck[i] and shuffled_deck[i],
// for 0 <= i < DECK_SIZE, except where the cards are equal.
//
for (i = 0; i < DECK_SIZE; i++) {
if (strcmp(unshuffled_deck[i], shuffled_deck[i]) != 0) {
cout << "Comparison of " << unshuffled_deck[i] << " with "
<< shuffled_deck[i] << " yields: "
<< CompareTwoCards(unshuffled_deck[i], shuffled_deck[i])
<< endl;
}
}
cout << endl;
}
void TestPageInstructions() {
//
// Output an explanatory message indicating how to interact with the test
// calls.
//
cout << endl << "Four calls to page instructions follow. For the first call, page straight"
<< endl << "through to the end with 'n', and then type 'q' to quit. For the second and"
<< endl << "third calls, page to the end with 'n', page all the way back up to the top"
<< endl << "with 'p', page down and up a few more times with 'n' and 'p', then quit with"
<< endl << "'q'. For the last call, type 'q' immediately after the first page."
<< endl << endl;
//
// Do the four calls, as advertised.
//
DoPageCalls();
cout << endl;
}
void DoPageCalls() {
ifstream text_file; // Instructions file variable
//
// Note the use of text_file.close between calls. This is necessary to
// rewind the file read marker to the beginning of the file.
//
cout << "First call to PageInstructions: " << endl;
text_file.open("instructions86");
PageInstructions(text_file, 20);
text_file.close();
cout << endl << "Second call to PageInstructions: " << endl;
text_file.open("instructions86");
PageInstructions(text_file, 20);
text_file.close();
cout << endl << "Third call to PageInstructions: " << endl;
text_file.open("instructions60");
PageInstructions(text_file, 15);
text_file.close();
cout << endl << "Fourth call to PageInstructions: " << endl;
text_file.open("instructions86");
PageInstructions(text_file, 20);
}
void DumpNDecks(Deck* decks, int num_decks) {
int i, j; // Loop and array indices
int times_found; // Value returned from CardAppears...
//
// Loop through the array of decks, dumping the ith element of each on the
// same line of output, separating each with a tab. Also, check each deck
// for exactly one occurrence of each card.
//
for (i = 0; i < DECK_SIZE; i++) {
for (j = 0; (j < num_decks) && (j < MAX_SHUFFLES); j++) {
cout << decks[j][i];
//
// Output tab after all but last item.
//
if (j < num_decks - 1) {
cout << '\t';
}
//
// Check for exactly one occurrence. As noted in the function
// specs, this feature is more advanced than was expected in a
// turned-in solution.
//
if (! CardAppearsInDeckExactlyOnce(decks[j][i], decks[j],
times_found )) {
cout << "SHUFFLE ERROR: Card " << decks[j][i] << " appears "
<< times_found << " times in shuffled deck no. " << j
<< endl;
}
}
cout << endl;
}
}
void CopyDeck(Deck dest_deck, Deck src_deck) {
int i; // Loop and array index
//
// Loop through the deck, copying each card from source to destination.
//
for (i = 0; i < DECK_SIZE; i++) {
strcpy(dest_deck[i], src_deck[i]);
}
}
Boolean CardAppearsInDeckExactlyOnce(Card card, Deck deck, int& times_found) {
int i; // Loop and array index
//
// Start the times-found counter at 0.
//
times_found = 0;
//
// Search deck for the given card, incrementing times found for each
// occurrence.
//
for (i = 0; i < DECK_SIZE; i++) {
if (strcmp(card, deck[i]) == 0) {
times_found++;
}
}
//
// Return true if times_found = 1, false otherwise.
//
return times_found == 1;
}