Solution to Programming Assignment 5

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;
}


index | lectures | labs | handouts | assignments | solutions | grades | help