From aa9d7f6543982618e0b4c72cfbfd3ebfeda293c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Tue, 15 Sep 2020 13:47:27 +0200 Subject: TDDDD86 labb 2 redovisning --- labb2/evilhangman/src/evilhangman.cpp | 179 ++++++++++++++++++++++++---------- 1 file changed, 127 insertions(+), 52 deletions(-) (limited to 'labb2/evilhangman/src/evilhangman.cpp') diff --git a/labb2/evilhangman/src/evilhangman.cpp b/labb2/evilhangman/src/evilhangman.cpp index ce30ad5..c57995b 100644 --- a/labb2/evilhangman/src/evilhangman.cpp +++ b/labb2/evilhangman/src/evilhangman.cpp @@ -1,14 +1,20 @@ #include #include #include - #include #include #include using namespace std; -void openDictionary(map> &wordsByLength, string path) { +static const string ALPHABET = "abcdefghijklmnopqrstuvwxyz"; + +/* + * Populate `wordsByLength` with lists of strings from file `path`. + * + * `wordsByLength` is "indexed" by the length of the words in the list. + */ +void openDictionary(map> &wordsByLength, const string &path) { ifstream input; input.open(path); string word; @@ -23,9 +29,9 @@ void openDictionary(map> &wordsByLength, string path) { } /* - * Given a word and a previous prototype, update the prototype with the char `guess`. + * Update the prototype with the char `guess` to match `word`. */ -void getWordPrototype(string &wordPrototype, string &word, char guess) { +void getWordPrototype(string &wordPrototype, const string &word, const char guess) { for (int i = 0; i < word.size(); i++) { if (word[i] == guess) { wordPrototype[i] = guess; @@ -34,22 +40,23 @@ void getWordPrototype(string &wordPrototype, string &word, char guess) { } /* - * Populate `wordGroup` with the largest word group after a `guess` was made, - * given a previous word group. Returns whether the prototype has changed or not. + * Update `wordGroup` with the largest word group after `guess` was made, + * given a previous word group and prototype. Returns whether the guess + * was correct or not and updates the prototype. */ -bool getWordGroup(list &wordGroup, string &wordPrototype, char guess) { - map> wordGroups; // { prototype: [ match1, match2, .. ] } - for (string &word: wordGroup) { - if (word.find(guess) == string::npos) { - // word doesn't contain guess +bool getWordGroup(list &wordGroup, string &wordPrototype, const char guess) { + map> wordGroups; // { prototype: [ match1, match2, .. ], .. } + for (string &word : wordGroup) { + if (word.find(guess) == string::npos) { // word doesn't contain guess + // initial empty list if (wordGroups.count(wordPrototype) == 0) { wordGroups[wordPrototype] = list(); } wordGroups[wordPrototype].push_back(word); - } else { - // word contains guess at least once + } else { // word contains guess at least once string newWordPrototype(wordPrototype); getWordPrototype(newWordPrototype, word, guess); + // initial empty list if (wordGroups.count(newWordPrototype) == 0) { wordGroups[newWordPrototype] = list(); } @@ -67,6 +74,8 @@ bool getWordGroup(list &wordGroup, string &wordPrototype, char guess) { maxPrototype = n.first; } } + + // set wordgroup and prototype wordGroup = wordGroups[maxPrototype]; if (wordPrototype == maxPrototype) { return false; @@ -76,65 +85,131 @@ bool getWordGroup(list &wordGroup, string &wordPrototype, char guess) { } } -int main() { - cout << "Welcome to Hangman." << endl; - - map> wordsByLength; - openDictionary(wordsByLength, "res/dictionary.txt"); - - int wordLength; - while (cout << "Choose a word length to guess: " && cin >> wordLength) { - if (wordsByLength.count(wordLength) == 0) { - cout << "No words of that length found." << endl; - } else { - break; - } - } - int guesses; - cout << "Choose number of guesses: "; - cin >> guesses; - - bool showWordsLeft; - { - char response; - cout << "Would you like to see the number of words left? [Y/n]: "; - cin >> response; - showWordsLeft = (response != 'n'); - } - +/* + * Play (evil) hangman with the given parameters. + * + * `wordLength` is the length of the word to guess. + * `guesses` is the total amount of guesses. + * `showWordsLeft` is whether to print how many valid words are left in each stage. + * `wordGroup` is the initial group of valid words. + */ +bool hangman(const int wordLength, const int guesses, const bool showWordsLeft, list &wordGroup) { string wordPrototype; for (int i = 0; i < wordLength; i++) { wordPrototype.push_back('-'); } - list wordGroup = wordsByLength[wordLength]; set guessedChars; + int guesses_left = guesses; + string line; bool won = false; - while (guesses > 0 && !won) { + while (guesses_left > 0 && !won) { + // print status if (showWordsLeft) { cout << "(" << wordGroup.size() << ") "; } - cout << wordPrototype << " with " << guesses << " guesses left. Guessed characters: "; - for (char c: guessedChars) { + cout << wordPrototype << " with " << guesses_left << " guesses left. Guessed characters: "; + for (char c : guessedChars) { cout << c; } cout << endl; - cout << "Guess a letter: "; + + // ask for guess char guess; - cin >> guess; + bool done = false; + while (!done) { + cout << "Guess a letter: "; + getline(cin, line); + if (line.size() != 1) { + cout << "Please input one character only." << endl; + continue; + } + guess = line[0]; + if (ALPHABET.find(guess) == string::npos) { + cout << "Please input a valid (lower-case) character." << endl; + continue; + } + if (guessedChars.count(guess)) { + cout << "You have already guessed this character." << endl; + continue; + } + done = true; + } + guessedChars.insert(guess); + + // check guess if (getWordGroup(wordGroup, wordPrototype, guess)) { - // new prototype, correct guess + // correct guess, new prototype won = (wordPrototype.find('-') == string::npos); } else { // wrong guess - guessedChars.insert(guess); - guesses--; + guesses_left--; } } - if (won) { - cout << "Well done!" << endl; - } else { - cout << "The word was '" << wordGroup.front() << "'. Better luck next time." << endl; + return won; +} + +int main() { + cout << "Welcome to Hangman." << endl; + + cout << "Reading dictionary... " << endl; + map> wordsByLength; + openDictionary(wordsByLength, "res/dictionary.txt"); + cout << "done" << endl; + + bool play = true; + while (play) { + string line; + int wordLength; + bool done = false; + while (!done) { + cout << "Choose a word length to guess: "; + getline(cin, line); + try { + wordLength = stoi(line); + } catch (const invalid_argument &) { + cout << "Not a number." << endl; + continue; + } + if (wordsByLength.count(wordLength) == 0) { + cout << "No words of that length found." << endl; + } else { + done = true; + } + } + int guesses; + done = false; + while (!done) { + cout << "Choose number of guesses: "; + getline(cin, line); + try { + guesses = stoi(line); + } catch (const invalid_argument &) { + cout << "Not a number." << endl; + continue; + } + if (guesses <= 0) { + cout << "Non-positive number of guesses doesn't make sense." << endl; + continue; + } + done = true; + } + + cout << "Would you like to see the number of words left? [Y/n]: "; + getline(cin, line); + bool showWordsLeft = (line != "n"); + + list wordGroup = wordsByLength[wordLength]; + + if (hangman(wordLength, guesses, showWordsLeft, wordGroup)) { + cout << "Well done!" << endl; + } else { + cout << "The word was '" << wordGroup.front() << "'. Better luck next time." << endl; + } + cout << "Play again? [Y/n]: "; + getline(cin, line); + play = (line != "n"); } + cout << "Goodbye" << endl; } -- cgit v1.2.1