init
This commit is contained in:
commit
16551b327f
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.
|
Loading…
x
Reference in New Issue
Block a user