init
This commit is contained in:
		
							
								
								
									
										213
									
								
								src/evilhangman.cpp
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										213
									
								
								src/evilhangman.cpp
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,213 @@
 | 
			
		||||
#include <QCoreApplication>
 | 
			
		||||
#include <QDir>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
const std::string ALPHABET = "abcdefghijklmnopqrstuvwxyz";
 | 
			
		||||
 | 
			
		||||
const char *const FORMAT_UNDERSCORE = "\033[4m";
 | 
			
		||||
const char *const END_FORMATTING = "\033[0m";
 | 
			
		||||
const char *const DICTIONARY_NAME = "../../../dictionary.txt";
 | 
			
		||||
 | 
			
		||||
std::string input(const char *message) {
 | 
			
		||||
    if (message != nullptr)
 | 
			
		||||
        std::cout << message;
 | 
			
		||||
 | 
			
		||||
    std::cout << FORMAT_UNDERSCORE;
 | 
			
		||||
    std::flush(std::cout);
 | 
			
		||||
 | 
			
		||||
    std::string ret;
 | 
			
		||||
    std::getline(std::cin, ret);
 | 
			
		||||
    std::cout << END_FORMATTING;
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int inputNumber(const char *message) {
 | 
			
		||||
    while (true) {
 | 
			
		||||
        std::string userInput = input(message);
 | 
			
		||||
        if (userInput.empty())
 | 
			
		||||
            continue;
 | 
			
		||||
        bool isNumber = std::all_of(userInput.begin(), userInput.end(),
 | 
			
		||||
                                    [](const char c) { return std::isdigit(c); });
 | 
			
		||||
        if (isNumber)
 | 
			
		||||
            return std::stoi(userInput);
 | 
			
		||||
 | 
			
		||||
        std::cout << "The input '" << userInput << "', is not a number"
 | 
			
		||||
                  << std::endl;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string fileRead(const std::string &filePath) {
 | 
			
		||||
    std::ifstream file(filePath, std::ios::binary);
 | 
			
		||||
    if (!file.is_open())
 | 
			
		||||
        throw std::runtime_error("File couldn't be found");
 | 
			
		||||
 | 
			
		||||
    std::string content;
 | 
			
		||||
    const int BUF_LENGTH = 1024;
 | 
			
		||||
    char buf[BUF_LENGTH];
 | 
			
		||||
    while (file.read(buf, BUF_LENGTH))
 | 
			
		||||
        content.append(buf, file.gcount());
 | 
			
		||||
    content.append(buf, file.gcount());
 | 
			
		||||
 | 
			
		||||
    if (!file.eof() && file.fail())
 | 
			
		||||
        throw std::runtime_error("Error: Failed to read the file '" + filePath +
 | 
			
		||||
                                 "'");
 | 
			
		||||
 | 
			
		||||
    return content;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<std::string> readDictionary(size_t wordLength) {
 | 
			
		||||
    std::string dictionaryPath = QDir(QCoreApplication::applicationDirPath())
 | 
			
		||||
                                     .filePath(DICTIONARY_NAME)
 | 
			
		||||
                                     .toStdString();
 | 
			
		||||
    std::string fileContent = fileRead(dictionaryPath);
 | 
			
		||||
    std::vector<std::string> ret;
 | 
			
		||||
    size_t last = 0, idx;
 | 
			
		||||
    while ((idx = fileContent.find('\n', last)) != std::string::npos) {
 | 
			
		||||
        std::string sub = fileContent.substr(last, idx - last);
 | 
			
		||||
        last = idx + 1;
 | 
			
		||||
        if (sub.size() == wordLength)
 | 
			
		||||
            ret.push_back(sub);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret.push_back(fileContent.substr(last));
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
inline void printContainer(std::vector<T> &container, const char *sep) {
 | 
			
		||||
    for (size_t i = 0; i < container.size(); i++) {
 | 
			
		||||
        std::cout << container[i];
 | 
			
		||||
        if (sep != nullptr && i != container.size() - 1)
 | 
			
		||||
            std::cout << sep;
 | 
			
		||||
    }
 | 
			
		||||
    std::cout << std::endl;
 | 
			
		||||
}
 | 
			
		||||
template <typename T> inline void printContainer(std::vector<T> &container) {
 | 
			
		||||
    printContainer<T>(container, nullptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char getGuess() {
 | 
			
		||||
    while (true) {
 | 
			
		||||
        std::string guess = input("Guess a character: ");
 | 
			
		||||
        if (guess.size() != 1) {
 | 
			
		||||
            std::cout << "The guess must be EXATCLY one character!" << std::endl;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        if (!std::isalpha(guess[0])) {
 | 
			
		||||
            std::cout
 | 
			
		||||
                << "The guess cannot be anything else than an alphanumeric character!"
 | 
			
		||||
                << std::endl;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        return guess[0];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unordered_map<std::string, std::vector<std::string>>
 | 
			
		||||
getWordFamilies(const std::vector<std::string> &words,
 | 
			
		||||
                const std::vector<char> &previousGuesses, const char guess) {
 | 
			
		||||
    static const char NO_SELECTION = '-';
 | 
			
		||||
 | 
			
		||||
    std::unordered_map<std::string, std::vector<std::string>> families;
 | 
			
		||||
 | 
			
		||||
    for (const std::string &word : words) {
 | 
			
		||||
        std::string key;
 | 
			
		||||
        key.reserve(word.length());
 | 
			
		||||
        for (size_t i = 0; i < word.length(); i++) {
 | 
			
		||||
            const char at = word[i];
 | 
			
		||||
            bool isGuessedChar = at == guess;
 | 
			
		||||
            bool isPreviouslyGuessed =
 | 
			
		||||
                std::find(previousGuesses.begin(), previousGuesses.end(), at) !=
 | 
			
		||||
                                       std::end(previousGuesses);
 | 
			
		||||
 | 
			
		||||
            key.push_back((isGuessedChar || isPreviouslyGuessed) ? at : NO_SELECTION);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // indexing operator will create a default (empty) vector if it doesn't
 | 
			
		||||
        // exist
 | 
			
		||||
        families[key].push_back(word);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return families;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[]) {
 | 
			
		||||
    QCoreApplication a(argc, argv);
 | 
			
		||||
 | 
			
		||||
    std::cout << "Welcome to Hangman." << std::endl;
 | 
			
		||||
 | 
			
		||||
    int wordLength = inputNumber("How long should the word be: ");
 | 
			
		||||
    size_t maxAttempts = inputNumber("How many guesses do you want? ");
 | 
			
		||||
    bool showRemainingWords;
 | 
			
		||||
    {
 | 
			
		||||
        std::string aux = input("Should I tell you my thoughts? (y/N): ");
 | 
			
		||||
        // To lowercase
 | 
			
		||||
        std::transform(aux.begin(), aux.end(), aux.begin(), ::tolower);
 | 
			
		||||
        showRemainingWords = aux == "y" || aux == "yes";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<std::string> dictionary = readDictionary(wordLength);
 | 
			
		||||
    std::vector<char> guesses(wordLength, '-');
 | 
			
		||||
    std::cout << "So far: ";
 | 
			
		||||
    printContainer(guesses);
 | 
			
		||||
 | 
			
		||||
    // There won't be many chars here, so we benefit from vector being
 | 
			
		||||
    // continous memory
 | 
			
		||||
    std::vector<char> guessedChars;
 | 
			
		||||
    size_t wrongAttempts = 0;
 | 
			
		||||
    bool hasWon = false, hasLost = false;
 | 
			
		||||
    do {
 | 
			
		||||
        char guess;
 | 
			
		||||
        while (true) {
 | 
			
		||||
            std::cout << "(wrong attempts: " << wrongAttempts << '/' << maxAttempts
 | 
			
		||||
                      << ") ";
 | 
			
		||||
            guess = getGuess();
 | 
			
		||||
            bool guessIsUnique = std::find(guessedChars.begin(), guessedChars.end(),
 | 
			
		||||
                                           guess) == std::end(guessedChars);
 | 
			
		||||
            if (guessIsUnique)
 | 
			
		||||
                break;
 | 
			
		||||
            else
 | 
			
		||||
                std::cout << "You've already guessed '" << guess << "'!" << std::endl;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::unordered_map<std::string, std::vector<std::string>> families =
 | 
			
		||||
            getWordFamilies(dictionary, guessedChars, guess);
 | 
			
		||||
 | 
			
		||||
        auto biggestEntry = std::max_element(
 | 
			
		||||
            families.begin(), families.end(),
 | 
			
		||||
            [](const std::pair<std::string, std::vector<std::string>> &a,
 | 
			
		||||
                                                 const std::pair<std::string, std::vector<std::string>> &b) {
 | 
			
		||||
                return a.second.size() < b.second.size();
 | 
			
		||||
            });
 | 
			
		||||
        std::string familyKey = std::move(biggestEntry->first);
 | 
			
		||||
        dictionary = std::move(biggestEntry->second);
 | 
			
		||||
 | 
			
		||||
        if (showRemainingWords) {
 | 
			
		||||
            std::cout << "Possible words: ";
 | 
			
		||||
            printContainer(dictionary, ", ");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bool wasCorrect = familyKey.find(guess) != std::string::npos;
 | 
			
		||||
        if (wasCorrect)
 | 
			
		||||
            std::cout << "Correct!" << std::endl;
 | 
			
		||||
        else {
 | 
			
		||||
            std::cout << "Wrong!" << std::endl;
 | 
			
		||||
            wrongAttempts++;
 | 
			
		||||
        }
 | 
			
		||||
        std::cout << "So far: " << familyKey << std::endl;
 | 
			
		||||
 | 
			
		||||
        guessedChars.push_back(guess);
 | 
			
		||||
        hasWon = dictionary.size() == 1;
 | 
			
		||||
        hasLost = wrongAttempts == maxAttempts;
 | 
			
		||||
    } while (!hasWon && !hasLost);
 | 
			
		||||
 | 
			
		||||
    const char *out = hasWon ? "congartulations! You've won! 🎉" : "you lost.";
 | 
			
		||||
    std::cout << "The word was '" << dictionary[0] << "', " << out << std::endl;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								src/readme.txt
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										3
									
								
								src/readme.txt
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
This directory contains the source code files (*.cpp, *.h)
 | 
			
		||||
that you will write as you complete the assignment.
 | 
			
		||||
We will also put any instructor-provided code here.
 | 
			
		||||
		Reference in New Issue
	
	Block a user