#include <ctime>
#include <cstdlib>
#include <iostream>
#include <list>
#include <string>
#include "hang.h"
using namespace std;
/*
* 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.
*
* Note: In addition, if arguments are given on the command line, they
* are taken as a list of words to be guessed before starting to choose
* random ones. This allows for testing with known words.
*/
/*
* 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();
int main(int argc, char **argv)
{
// Elminate the program name from the argument list.
--argc; ++argv;
// 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.
if(argc > 0) {
hanger.set_word(*argv++);
--argc;
} else {
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_attempted()
<< " word(s) attempted; " << 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()];
}