#include #include #include #include #include #include // returns true if string "str" contains character "ch" // and false otherwise. lower and upper case characters are // considered equivalent (i.e. the string "ABC" contains 'a'). bool letter_present (const char str[], char ch) { int i; for (i = 0; str[i] != '\0'; i++) { if (toupper(str[i]) == toupper(ch)) { return true; } } return false; } // displays the target string. matched letters (letters which have // been guessed) are displayed normally, as are all characters which // are not letters (i.e. numbers, spaces, punctuation, and so on). // unmatched letters are displayed as stars. returns the number of // unmatched letters in the target string (i.e. the number of stars output). int display_target (const char target[], const char guesses[]) { int i, letters_left = 0; cout << "Your target is: "; for (i = 0; target[i] != '\0'; i++) { if (!isalpha(target[i])) { cout << target[i]; } else if (letter_present (guesses, target[i])) { cout << target[i]; } else{ cout << '*'; letters_left++; } } cout << endl; return letters_left; } // displays a graphical representation of how many mistakes have // been made and also outputs a message giving the same information. // returns true if the maximum allowable number of mistakes have been // made and false otherwise. bool display_gallows (int mistakes) { // ouputting this string draws the basic gallows char pattern[] = " =====\n | |\n | |\n | \n | \n | \n" " | \n=========\n"; // if there has been one mistake, string position 26 should be replaced // with an 'O' before the string is output. in general, array "locations" // contains the positions at which replsacements should occur, and array // "drop_ins" contains the replacement characters. const int locations[6] = { 26, 33, 32, 34, 40, 42 }; const char drop_ins[6] = { 'O', '|', '/', '\\', '/', '\\' }; int i; // adjust the standard pattern as required by replacing characters. for (i = 0; i < mistakes; i++) { pattern[locations[i]] = drop_ins[i]; } // now output the adjusted pattern followed by a text message. cout << pattern << "\nSo far you have made " << mistakes << " mistakes.\n"; return (bool) (mistakes == 6); } // prompts the player for their next guess and keeps at it until a // valid guess is entered. inputs which are nor letters are rejected, // as are guess which match a previous guess. returns the valid guess. char get_valid_guess (const char guesses[] /* previous guesses */) { char guess; for (;;) { cout << "Please enter your choice of letter: "; cin >> guess; cin.ignore (INT_MAX, '\n'); if (!isalpha(guess)) { cout << "Your guess must be a letter.\n"; } else if (letter_present(guesses, guess)) { cout << "You've already guessed that letter.\n"; } else { // we have an acceptable guess return guess; } } } // plays a single game of hangman using the target string supllied. void play_game (const char target[]) { // string "guesses" contains all guess made. initially this string // is empty (the terminator is at guesses[0]). guesses are added to // the string as the game proceeds. char guesses[27] = "", // we can't possible have more than 26 guesses guess; int guess_count = 0, mistakes = 0; for (;;) { clrscr (); // display the target and see if the player has won if (display_target (target, guesses) == 0) { cout << "\nCongratulations - you've have worked out the target.\n"; return; } cout << endl; // display the situation and see if the player has lost if (display_gallows (mistakes)) { cout << "\nYou're dead.\nThe target was \"" << target << "\".\n"; return; } cout << endl; // get the player's next guess guess = get_valid_guess(guesses); // add the guess to the guess string guesses[guess_count++] = guess; guesses[guess_count] = '\0'; // must keep the string terminated // if the guess is wrong, we've got one more mistake if (!letter_present (target, guess)) { mistakes++; } } } int main () { const int max_filename_length = 80, max_target_length = 50; // including the terminator char target[max_target_length], filename[max_filename_length], reply; ifstream fin; for (;;) { cout << "Please enter name of file containing the targets: "; cin >> filename; fin.open(filename); if (!fin.fail()) { break; // file successfully opened } cout << "Unable to open file <" << filename << ">.\n"; } do { // the "ws" manipulator discards "whitespace" characters (blanks, // tabs and newlines). one effect of doing this here is that, if // any file line contains leading blanks, they will be ignored. // also any completely blank lines will also be ignored. fin >> ws; // "ws" is a manipulator (like setw) and not a variable!! // read the next target fin.getline (target, max_target_length); if (fin.fail()) { if (fin.eof()) { cout << "Sorry - the end of the target file has been reached.\n"; } else { // most unlikely as all possible file contents are valid. // might happy if a floppy became corrupted or something similar. cout << "An error occurred in reading the target file.\n"; } return 0; } play_game (target); cout << "\nDo you want to play again (Y/N): "; cin >> reply; // ignore any extra characters that may have been entered cin.ignore (INT_MAX, '\n'); } while (toupper(reply) == 'Y'); return 0; }