Hangman Main Program
#include <ctime> #include <cstdlib> #include <iostream> #include <list> #include <string> #include "hang.h" /* * Hangman main program. * * Run the program and type a single letter guess when requested. The * letter prompt also accepts the input !q to quit the program, or !c * to cancel and forfeit the round. (And get the program to tell you what * the )(*!#&(&*# that stupid word is.) * * The program works from a word list given at the bottom of this file. * It seems reasonable, but has no certain virtue. Loading a list from a * file would probably be better. */ /* * Display the current status of the game. */ void show_status(hangman &hanger, string missed, string message, bool show_hang = true); /* * Choose at random and return a word to guess from a list. (The list * is below. */ string next_word(); main() { // Seed the random number generator with the current time so that // we don't always get the same words. srand(time(NULL)); // The hangman game object. hangman hanger; // Repeat rounds until stopped by the user. bool firstword = true; // First word to guess while(true) { // Set the word to guess, and display the initial status. hanger.set_word(next_word()); show_status(hanger, "", firstword ? "Welcome to Hangman!" : "Try another!", firstword); firstword = false; // Loop through the guesses for the particular word. string missed = ""; // String of incorrect letter guesses. bool done = false; // Termination when the round is done. while(!done) { // Prompt for a letter guess. cout << "Enter guess: "; string line; if(!getline(cin, line)) exit(0); // A couple of special responses. if(line.substr(0,2) == "!q") exit(0); if(line.substr(0,2) == "!c") { // Give up (cancel guess). cout << "The word was " << hanger.get_word() << endl; break; } // Send the guess to the hangman object and // respond accordingly. string msg; // Msg describing result to user. switch (hanger.guess(line[0])) { case hangman::FOUND: // Good letter guess, but more to do. msg = "Found " + line.substr(0,1); break; case hangman::MISSED: // Bad letter guess. msg = "No, " + line.substr(0,1) + " is wrong"; missed += " " + line.substr(0,1); break; case hangman::DUP: // Repeated a letter guess. msg = "Already tried " + line.substr(0,1); break; case hangman::WIN: // Successfully guessed the word. msg = "Congratulations!"; done = true; break; case hangman::LOSE: // Too many guesses. Lost the round. msg = "Sorry. The word was " + hanger.get_word(); missed += " " + line.substr(0,1); done = true; break; case hangman::BADCHR: // Can't type. msg = line.substr(0,1) + " is an illegal character"; break; case hangman::ERROR: // Shouldn't happen unless there's a bug // in the object, since we won't call it // here outside the guessing state. msg = "Unexpected error."; break; } // Show the status again, giving the result of // the last guess. show_status(hanger, missed, msg); } } } /* * Draw the hangman figure, according to the number of misses. Parts * should be in the range 0-6. Offset is just some number of blanks to * put at the left of the figure. */ void draw_hang(int offset, int parts) { string left(offset, ' '); // Space at left. int bottomrows = 1; // Controls final loop which draws // the bottom of the support. cout << left << " ________" << endl << left << " | |" << endl; if(parts == 0) { // Zero, no body. bottomrows = 9; } else { // At least one, head. cout << left << " ___ |" << endl << left << " / \\ |" << endl << left << " \\___/ |" << endl; if(parts < 2) { // No more body. bottomrows = 6; } else { // At least trunk. Then // make arms or not cout << left << " | |" << endl; switch(parts) { case 2: cout << left << " | |" << endl; break; case 3: cout << left << "---| |" << endl; break; default: cout << left << "---+--- |" << endl; break; } cout << left << " | |" << endl; // Lets or not. switch(parts) { default: bottomrows = 3; break; case 5: cout << left << " / |" << endl << left << " / |" << endl; break; case 6: cout << left << " / \\ |" << endl << left << " / \\ |" << endl; break; } } } // Draw the support below whatever body parts appear. for(int i = 0; i < bottomrows; ++i) cout << left << " |" << endl; } /* * Display the current status of the game. */ void show_status(hangman &hanger, string missed, string message, bool show_hang) { // Build the word with blanks. string word = hanger.get_word(); string display_word = ""; for(int i = 0; i < word.length(); ++i) { if(display_word.length() > 0) display_word.push_back(' '); if(hanger.is_found(i)) display_word.push_back(word[i]); else display_word.push_back('_'); } // Print the sent message, and (optionally) show the gallows picture. cout << "\n=== " << message << " ===" << endl << endl; if(show_hang) { draw_hang(5, hanger.num_letters_incorrect()); cout << endl; } // Give the stats. cout << " Word: " << display_word << " | Missed: " << missed << endl << " Tried " << hanger.num_letters_guessed() << " letter(s), " << "with " << hanger.num_letters_incorrect() << " incorrect." << endl << " " << hanger.num_words_guessed() << " word(s) guessed; " << hanger.num_words_correct() << " correct." << endl; } /* * This is just a list of words. Would be better to read from a file, but * this is easier and fits our present purposes. */ static vector<string> words = { "abluent", "absents", "acyclic", "adsorbs", "advises", "aflower", "afterward", "agencies", "aircrew", "airtight", "alabaster", "alehouses", "allusions", "alumine", "ambience", "ammoniums", "amputates", "anchoring", "androgyne", "anteceded", "anywise", "apiaries", "apostates", "appriser", "arbors", "archer", "arcsine", "asking", "aspirate", "asserting", "atropine", "backing", "backyard", "baffled", "baignet", "bakeware", "baluster", "banknotes", "baptizers", "barhop", "baseboard", "beanstalk", "bedsheets", "besotment", "betimes", "billfold", "biology", "bismite", "bluenose", "braggarts", "braking", "brasque", "brawnier", "breathe", "bridges", "broadaxe", "brownie", "buzzard", "cachuchas", "calcite", "camels", "candying", "canneries", "cherub", "christian", "chuckles", "cleaning", "clumsier", "codified", "competent", "concerto", "confetti", "consigner", "convoy", "copyedit", "corset", "cosecant", "couldron", "covalency", "crackling", "crooked", "crowfeet", "cruise", "curbstone", "custody", "dampest", "darkling", "daytime", "decadent", "deceive", "decorator", "defiant", "deplete", "desert", "detach", "dictation", "dilute", "disarm", "disquiet", "dissolve", "dominant", "donuts", "downrange", "drinking", "drumstick", "eagerness", "earthborn", "eliminate", "elucidate", "eminent", "emphases", "enamour", "encrust", "entangle", "estranged", "evasion", "evildoer", "excessive", "excursory", "fairly", "fanciest", "fencepost", "ferrule", "fillets", "finalize", "finite", "fishbed", "flushing", "fogbound", "folies", "fondness", "forebears", "forsworn", "fourfold", "gadget", "granola", "grieve", "gunmaker", "habitat", "homegrown", "hootch", "humanoid", "icepick", "idolised", "illuminee", "imbiber", "immature", "indenting", "indisposed", "infinity", "ingrown", "intensify", "jellyfish", "landfill", "lawsuit", "leghorn", "lemonade", "livestock", "luddite", "machinery", "magnetism", "maiden", "mercenary", "messaging", "modulate", "monastic", "moppets", "napalm", "nautilus", "newspaper", "nonaction", "notify", "nuptial", "outrun", "overcast", "overdrive", "packaging", "parasitic", "passport", "pathway", "paving", "pedestal", "pianist", "popcorn", "pricing", "prospect", "railing", "reactive", "recusal", "refrigerator", "repeater", "repugnant", "rerelease", "resculpt", "restrain", "retail", "revolver", "ritual", "rubbish", "sabotage", "sailfin", "shameful", "shapoo", "sidetrack", "smartest", "sojourner", "spastic", "speedway", "squeege", "subcompact", "suffers", "sunburned", "sunset", "superius", "surround", "sweetness", "tabulate", "taxonomy", "teapots", "telegraph", "telephone", "toiled", "townfolk", "unmanaged", "unravel", "urbanists", "uttered", "vacuously", "violate", "wardrobe", "waterfall", "weakling" }; // Return a word from the list chosen at random. string next_word() { return words[rand() % words.size()]; }