init
This commit is contained in:
		
							
								
								
									
										12
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										12
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Executable file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					*~
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.DS_Store
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*.pro.user.*
 | 
				
			||||||
 | 
					*.pro.user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					build-*/
 | 
				
			||||||
 | 
					*.app
 | 
				
			||||||
 | 
					*.exe
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					build
 | 
				
			||||||
							
								
								
									
										44
									
								
								EvilHangman.pro
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										44
									
								
								EvilHangman.pro
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					TEMPLATE = app
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIG += console
 | 
				
			||||||
 | 
					CONFIG += no_include_pwd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SOURCES = $$PWD/src/*.cpp
 | 
				
			||||||
 | 
					#SOURCES += $$PWD/lib/*.cpp
 | 
				
			||||||
 | 
					HEADERS = $$PWD/src/*.h
 | 
				
			||||||
 | 
					HEADERS += $$PWD/lib/*.h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QMAKE_CXXFLAGS += -std=c++11
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INCLUDEPATH += $$PWD/lib/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Copies the given files to the destination directory
 | 
				
			||||||
 | 
					defineTest(copyToDestdir) {
 | 
				
			||||||
 | 
					    files = $$1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for(FILE, files) {
 | 
				
			||||||
 | 
					        DDIR = $$OUT_PWD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Replace slashes in paths with backslashes for Windows
 | 
				
			||||||
 | 
					        win32:FILE ~= s,/,\\,g
 | 
				
			||||||
 | 
					        win32:DDIR ~= s,/,\\,g
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        !win32 {
 | 
				
			||||||
 | 
					            QMAKE_POST_LINK += cp -r '"'$$FILE'"' '"'$$DDIR'"' $$escape_expand(\\n\\t)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        win32 {
 | 
				
			||||||
 | 
					            QMAKE_POST_LINK += xcopy '"'$$FILE'"' '"'$$DDIR'"' /e /y $$escape_expand(\\n\\t)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    export(QMAKE_POST_LINK)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					!win32 {
 | 
				
			||||||
 | 
					    copyToDestdir($$files($$PWD/res/*))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					win32 {
 | 
				
			||||||
 | 
					    copyToDestdir($$PWD/res)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					macx {
 | 
				
			||||||
 | 
					    cache()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										2
									
								
								lib/readme.txt
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										2
									
								
								lib/readme.txt
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					This directory contains any libraries that should be linked
 | 
				
			||||||
 | 
					to your project when it is built.
 | 
				
			||||||
							
								
								
									
										9
									
								
								res/di.txt
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										9
									
								
								res/di.txt
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					ally
 | 
				
			||||||
 | 
					beta
 | 
				
			||||||
 | 
					cool
 | 
				
			||||||
 | 
					deal
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
 | 
					flew
 | 
				
			||||||
 | 
					good
 | 
				
			||||||
 | 
					hope
 | 
				
			||||||
 | 
					ibex
 | 
				
			||||||
							
								
								
									
										268885
									
								
								res/dictionary.txt
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										268885
									
								
								res/dictionary.txt
									
									
									
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										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