Created
May 13, 2018 04:48
-
-
Save seanpianka/63508ed3ccdd21405c3413cdd0728d77 to your computer and use it in GitHub Desktop.
A hacky, partially-working implementation of the Fallout video-game series hacking mini-game.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
** Game designed according to this challenge: | |
** https://www.reddit.com/r/dailyprogrammer/comments/3qjnil/20151028_challenge_238_intermediate_fallout/ | |
*/ | |
/* | |
************************************************************ | |
// HEADER FILES | |
************************************************************ | |
*/ | |
#include <iostream> | |
#include <fstream> | |
#include <cstring> | |
#include <cstdlib> | |
#include <ctime> | |
#include <random> | |
#include <cmath> | |
#include <iomanip> | |
/* | |
************************************************************ | |
// ENUM DEFINITIONS | |
************************************************************ | |
*/ | |
enum class guessResult | |
{ | |
INVALIDGUESS, // 0 | |
VALIDGUESS // 1 | |
}; | |
/* | |
************************************************************ | |
// STRUCT DEFINITIONS | |
************************************************************ | |
*/ | |
struct Dictionary | |
{ | |
std::ifstream dictionary; // Input stream object for enable1.txt | |
int fileLineCount = 0; // Number of lines (words) in the dictionary file | |
char** wordList; // List of words to choose from for game passwords | |
}; | |
struct Passwords | |
{ | |
const int maxPasswords = 10; // Number of passwords the user must guess from | |
int gamePasswordLength; // Length of terminal passwords (determined by difficulty) | |
int gamePasswordLineNums[10]; // Selected line numbers for passwords (within dictionary file) | |
char* gamePasswordList[10]; // Array of pointers to C-Strings containing usable passwords | |
char *gameCorrectPassword; // Correct terminal game password | |
}; | |
struct gameSettings | |
{ | |
int attempts; // Number of guess attempts | |
int gameDifficulty; // Difficulty level, 1 to 5 | |
bool gameRepeat = false; | |
Dictionary dictionary; | |
Passwords passwords; | |
}; | |
/* | |
************************************************************ | |
// FUNCTION PROTOTYPES | |
************************************************************ | |
*/ | |
// Game settings | |
void setGameSettings(gameSettings*&, std::mt19937&); | |
int getPasswordLength(const int&, std::mt19937&); | |
int getGameDifficulty(); | |
void getPossiblePasswords(gameSettings*&); | |
void getTerminalPasswords(gameSettings*&, std::mt19937&); | |
void getCorrectPassword(gameSettings*&, std::mt19937&); | |
// Terminal display and formatting | |
void printMainTerminal(gameSettings*&, std::mt19937&); | |
void printTerminalHeader(int&); | |
void printAttemptsRemaining(int&); | |
void printTerminalPasswords(gameSettings*&, std::mt19937&); | |
void printGameChoices(gameSettings*&); | |
void printSym(std::mt19937&, const int); | |
// Game functions | |
guessResult playerGuessWord(gameSettings*&); | |
int getLikeness(gameSettings*&, char*&); | |
bool repeatGame(gameSettings*&); | |
// Misc functions | |
void clearBuffer(char*&, const unsigned int); | |
void pause(); | |
int checkInt(); | |
int getPow2(int); | |
/* | |
************************************************************ | |
// MAIN FUNCTION | |
************************************************************ | |
*/ | |
int main() | |
{ | |
static std::mt19937 randGen(static_cast<int>(time(0))); // Random number generation engine | |
bool isPlaying; // Game-loop and program-loop | |
guessResult correctGuess; | |
gameSettings gameSettings, *gameSettingsPtr = &gameSettings; | |
// Program-loop Init | |
do | |
{ | |
// Initial game settings | |
correctGuess = guessResult::INVALIDGUESS; | |
isPlaying = true; | |
setGameSettings(gameSettingsPtr, randGen); | |
// Game-loop Init | |
while (correctGuess != guessResult::VALIDGUESS && gameSettingsPtr->attempts >= 0) | |
{ | |
printMainTerminal(gameSettingsPtr, randGen); | |
correctGuess = playerGuessWord(gameSettingsPtr); | |
pause(); | |
} | |
isPlaying = repeatGame(gameSettingsPtr); | |
} while (isPlaying); | |
/* | |
for (int i = 0; i < gameSettingsPtr->dictionary.fileLineCount; ++i) | |
{ | |
delete[] gameSettingsPtr->dictionary.wordList[i]; | |
} | |
delete[] gameSettingsPtr->dictionary.wordList; | |
*/ | |
return 0; | |
} | |
/* | |
************************************************************ | |
// FUNC: DETERMINES INITIAL GAME SETTINGS | |
************************************************************ | |
*/ | |
void setGameSettings(gameSettings *&gameSettingsPtr, std::mt19937 &randGen) | |
{ | |
// std::cout << "\nConfiguring game settings...\n"; | |
gameSettingsPtr->attempts = 4; | |
gameSettingsPtr->gameDifficulty = getGameDifficulty(); | |
gameSettingsPtr->passwords.gamePasswordLength = getPasswordLength(gameSettingsPtr->gameDifficulty, randGen); | |
std::cout << "\nLoading..."; | |
if (!gameSettingsPtr->gameRepeat) | |
{ | |
getPossiblePasswords(gameSettingsPtr); | |
} | |
getTerminalPasswords(gameSettingsPtr, randGen); | |
getCorrectPassword(gameSettingsPtr, randGen); | |
// std::cout << "\nGame settings configured.\n"; | |
} | |
/* | |
************************************************************ | |
// FUNC: SETS GAME DIFFICULTY | |
************************************************************ | |
*/ | |
int getGameDifficulty() | |
{ | |
int difficulty; | |
std::cout << "|| SELECT TERMINAL DIFFICULTY ||\n\n" | |
<< "1 -> Very Easy\n" | |
<< "2 -> Easy\n" | |
<< "3 -> Average \n" | |
<< "4 -> Hard\n" | |
<< "5 -> Very Hard\n\n" | |
<< "Difficulty (1-5) -> "; | |
difficulty = checkInt(); | |
return difficulty; | |
} | |
/* | |
************************************************************ | |
// FUNC: DETERMINE GAME PASSWORD LENGTH | |
************************************************************ | |
*/ | |
int getPasswordLength(const int &gameDifficulty, std::mt19937 &randGen) | |
{ | |
std::uniform_int_distribution<int> randNumber(1, 10); | |
int randNumDifficulty = randNumber(randGen); | |
switch (gameDifficulty) | |
{ | |
case 1: // very easy 3, 4 | |
if (randNumDifficulty <= 5) | |
return 3; | |
else | |
return 4; | |
case 2: // easy 5, 6 | |
if (randNumDifficulty <= 5) | |
return 5; | |
else | |
return 6; | |
case 3: // average 7, 8 | |
if (randNumDifficulty <= 5) | |
return 7; | |
else | |
return 8; | |
case 4: // hard 10, 11 | |
if (randNumDifficulty <= 5) | |
return 10; | |
else | |
return 11; | |
case 5: // very hard 13, 14 | |
if (randNumDifficulty <= 5) | |
return 13; | |
else | |
return 14; | |
default: | |
return 5; | |
} | |
} | |
/* | |
************************************************************ | |
// FUNC: POPULATES LIST OF GAME PASSWORDS | |
************************************************************ | |
*/ | |
void getPossiblePasswords(gameSettings *&gameSettingsPtr) | |
{ | |
int itBuffer = 0, power = 1; // running count of characters in buffer | |
char buffer[255], bufferChar, *bufferPtr = buffer; // bufferPtr used for clearing buffer | |
char** tmp_wordList; | |
gameSettingsPtr->dictionary.dictionary.open("wldict.dat"); | |
gameSettingsPtr->dictionary.wordList = new char*[getPow2(power)]; // initial size of wordList | |
// set all of buffer to null byte | |
clearBuffer(bufferPtr, 255); | |
while (!gameSettingsPtr->dictionary.dictionary.eof()) | |
{ | |
// read characters into buffer | |
gameSettingsPtr->dictionary.dictionary.get(bufferChar); | |
if (bufferChar != '\n') | |
{ | |
buffer[itBuffer++] = bufferChar; | |
} | |
// load buffer into wordList | |
else if (bufferChar == '\n') | |
{ | |
if (gameSettingsPtr->dictionary.fileLineCount > getPow2(power)) | |
{ | |
tmp_wordList = new char*[getPow2(++power)]; | |
for (int i = 0; i < gameSettingsPtr->dictionary.fileLineCount - 1; ++i) | |
{ | |
tmp_wordList[i] = gameSettingsPtr->dictionary.wordList[i]; | |
} | |
// deallocate gameSettingsPtr->dictionary.wordList | |
gameSettingsPtr->dictionary.wordList = tmp_wordList; | |
tmp_wordList = nullptr; | |
} | |
gameSettingsPtr->dictionary.wordList[gameSettingsPtr->dictionary.fileLineCount] = new char[itBuffer++]; | |
// Store words in uppercase | |
for (int i = 0; i < itBuffer; ++i) | |
{ | |
(gameSettingsPtr->dictionary.wordList[gameSettingsPtr->dictionary.fileLineCount])[i] = toupper(buffer[i]); | |
} | |
// Reset read-in conditions | |
++gameSettingsPtr->dictionary.fileLineCount; | |
clearBuffer(bufferPtr, itBuffer); | |
itBuffer = 0; | |
} | |
} | |
gameSettingsPtr->dictionary.dictionary.close(); | |
} | |
/* | |
************************************************************ | |
// FUNC: GETS LIST OF 10 TERMINAL PASSWORDS | |
************************************************************ | |
*/ | |
void getTerminalPasswords(gameSettings *&gameSettingsPtr, std::mt19937 &randGen) | |
{ | |
int lineNumBuffer, selectedLineNums[10]; | |
bool uniqueLineNum = true, validWordLength = true; | |
std::uniform_int_distribution<int> lineNumGen(0, gameSettingsPtr->dictionary.fileLineCount); | |
// passList has max size of 10 | |
for (int i = 0; i < gameSettingsPtr->passwords.maxPasswords; ++i) | |
{ | |
do | |
{ | |
lineNumBuffer = lineNumGen(randGen); | |
// checking for uniqueness, no repeated words | |
for (int j = 0; j <= gameSettingsPtr->passwords.maxPasswords; ++j) | |
{ | |
if (selectedLineNums[j] == lineNumBuffer) | |
uniqueLineNum = false; | |
else | |
{ | |
uniqueLineNum = true; | |
selectedLineNums[i] = lineNumBuffer; | |
} | |
} | |
if (strlen(gameSettingsPtr->dictionary.wordList[lineNumBuffer]) != gameSettingsPtr->passwords.gamePasswordLength) | |
validWordLength = false; | |
else | |
{ | |
validWordLength = true; | |
} | |
} while (validWordLength == false || uniqueLineNum == false); | |
gameSettingsPtr->passwords.gamePasswordList[i] = gameSettingsPtr->dictionary.wordList[lineNumBuffer]; | |
} | |
} | |
/* | |
************************************************************ | |
// FUNC: DETERMINES CORRECT PASSWORD FOR GAME | |
************************************************************ | |
*/ | |
void getCorrectPassword(gameSettings *&gameSettingsPtr, std::mt19937 &randGen) | |
{ | |
int correctLineIndex; | |
std::uniform_int_distribution<int> lineNumGen(0, 9); | |
correctLineIndex = lineNumGen(randGen); | |
gameSettingsPtr->passwords.gameCorrectPassword = gameSettingsPtr->passwords.gamePasswordList[correctLineIndex]; | |
} | |
/* | |
************************************************************ | |
// FUNC: CLEAR INPUT BUFFER FOR C-STRING TO \0 | |
************************************************************ | |
*/ | |
void clearBuffer(char* &buffer, const unsigned int length) | |
{ | |
std::fill(buffer, buffer + length, '\0'); | |
} | |
/* | |
************************************************************ | |
// FUNC: GET POWER OF TWO RESULT | |
************************************************************ | |
*/ | |
int getPow2(int power) | |
{ | |
return static_cast<int>((pow(2, power))); | |
} | |
/* | |
************************************************************ | |
// FUNC: DISPLAY MAIN TERMINAL GAME SCREEN | |
************************************************************ | |
*/ | |
void printMainTerminal(gameSettings *&gameSettingsPtr, std::mt19937& randGen) | |
{ | |
printTerminalHeader(gameSettingsPtr->attempts); | |
printAttemptsRemaining(gameSettingsPtr->attempts); | |
printTerminalPasswords(gameSettingsPtr, randGen); | |
// printGameChoices(gameSettingsPtr); // only for testing | |
} | |
/* | |
************************************************************ | |
// FUNC: DISPLAYS TERMINAL HEADER | |
************************************************************ | |
*/ | |
void printTerminalHeader(int &attempts) | |
{ | |
std::cout << "***********************************************************************\n" | |
<< "ROBCO INDUSTRIES (TM) TERMLINK PROTOCOL\n"; | |
if (attempts > 1) | |
std::cout << "Password Required\n\n"; | |
else if (attempts == 1) | |
std::cout << "!!! WARNING: LOCKOUT IMMINENT !!!\n\n"; | |
else if (attempts == 0) | |
std::cout << "YOU HAVE BEEN LOCKED OUT\n\n"; | |
} | |
/* | |
************************************************************ | |
// FUNC: DISPLAYS ATTEMPTS REMAINING | |
************************************************************ | |
*/ | |
void printAttemptsRemaining(int &attempts) | |
{ | |
const unsigned char block = 219; | |
std::cout << attempts << " ATTEMPT(S) LEFT: "; | |
for (int i = 0; i < attempts; ++i) | |
{ | |
std::cout << block; | |
if (i != attempts - 1) | |
std::cout << ' '; | |
} | |
std::cout << std::endl; | |
} | |
/* | |
************************************************************ | |
// FUNC: DISPLAYS TERMINAL GAME PASSWORDS | |
************************************************************ | |
*/ | |
void printTerminalPasswords(gameSettings *&gameSettingsPtr, std::mt19937 &randGen) | |
{ | |
std::uniform_int_distribution<int> randNumber(4096, 65536); | |
int fakeHex = randNumber(randGen), fakeHex2; | |
int terminalLineLength = gameSettingsPtr->passwords.gamePasswordLength + 5; | |
int wordNumber = 0; | |
// five main blocks of text, two terminal passwords words per block, 3 lines per block | |
for (int terminalTextBlock = 0; terminalTextBlock < (gameSettingsPtr->passwords.maxPasswords) / 2; ++terminalTextBlock) | |
{ | |
for (int terminalBlockLineNum = 0; terminalBlockLineNum < 3; ++terminalBlockLineNum, fakeHex += 4) | |
{ | |
// hex for left side | |
std::cout << "0x" << std::hex << std::uppercase << fakeHex << ' '; | |
if (terminalBlockLineNum == 0) | |
{ | |
// 1st line, first word | |
printSym(randGen, 2); | |
std::cout << (gameSettingsPtr->passwords.gamePasswordList[wordNumber++]); | |
printSym(randGen, 3); | |
// 1st line, symbols | |
fakeHex2 = fakeHex + 3375; | |
std::cout << " 0x" << std::hex << std::uppercase << fakeHex2 << ' '; | |
printSym(randGen, terminalLineLength); | |
std::cout << '\n'; | |
} | |
else if (terminalBlockLineNum == 1) | |
{ | |
// 2nd line, symbols | |
printSym(randGen, terminalLineLength); | |
// 2nd line, symbols | |
fakeHex2 = fakeHex + 3375; | |
std::cout << " 0x" << std::hex << std::uppercase << fakeHex2 << ' '; | |
printSym(randGen, terminalLineLength); | |
std::cout << '\n'; | |
} | |
else if (terminalBlockLineNum == 2) | |
{ | |
// 3rd line, symbols | |
printSym(randGen, terminalLineLength); | |
// 3rd line, second word | |
fakeHex2 = fakeHex + 3375; | |
std::cout << " 0x" << std::hex << std::uppercase << fakeHex2 << ' '; | |
printSym(randGen, 2); | |
std::cout << (gameSettingsPtr->passwords.gamePasswordList[wordNumber++]); | |
printSym(randGen, 3); | |
std::cout << '\n'; | |
} | |
} | |
} | |
std::cout << std::dec; | |
} | |
/* | |
************************************************************ | |
// FUNC: DISPLAYS USER CHOICES, FOR TESTING ONLY | |
************************************************************ | |
*/ | |
void printGameChoices(gameSettings *&gameSettingsPtr) | |
{ | |
for (int i = 0; i < 10; i++) | |
{ | |
std::cout << i + 1 << ") " << gameSettingsPtr->passwords.gamePasswordList[i] << ' '; | |
if (i == 4) | |
std::cout << '\n'; | |
} | |
} | |
/* | |
************************************************************ | |
// FUNC: PRINT RANDOM SYMBOL | |
************************************************************ | |
*/ | |
void printSym(std::mt19937 &randGen, const int symbolAmount) | |
{ | |
std::uniform_int_distribution<int> symbolGen(0, 10); | |
int randNum; | |
char symbol; | |
for (int i = 0; i < symbolAmount; ++i) | |
{ | |
randNum = symbolGen(randGen); | |
switch (randNum) | |
{ | |
case 0: | |
symbol = '%'; | |
break; | |
case 1: | |
symbol = '@'; | |
break; | |
case 2: | |
symbol = '/'; | |
break; | |
case 3: | |
symbol = ']'; | |
break; | |
case 4: | |
symbol = '['; | |
break; | |
case 5: | |
symbol = ';'; | |
break; | |
case 6: | |
symbol = ')'; | |
break; | |
case 7: | |
symbol = '('; | |
break; | |
case 8: | |
symbol = '^'; | |
break; | |
case 9: | |
symbol = '#'; | |
break; | |
case 10: | |
symbol = '*'; | |
break; | |
default: | |
symbol = '&'; | |
break; | |
} | |
std::cout << symbol; | |
} | |
} | |
/* | |
************************************************************ | |
// FUNC: ALLOWS PLAYER TO GUESS WORD | |
************************************************************ | |
*/ | |
guessResult playerGuessWord(gameSettings *&gameSettingsPtr) | |
{ | |
int likeness; | |
char *choice = new char[gameSettingsPtr->passwords.gamePasswordLength + 1]; | |
std::cout << "\n>"; // formatting | |
if (gameSettingsPtr->attempts > 0) | |
{ | |
// clear standard input stream | |
// get user input | |
std::cin.sync(); | |
std::cin.getline(choice, gameSettingsPtr->passwords.gamePasswordLength + 1); | |
// convert user input to uppercase for easy matching using strcmp | |
// set null terminator to end of string | |
for (int i = 0; i < gameSettingsPtr->passwords.gamePasswordLength; ++i) | |
{ | |
choice[i] = toupper(choice[i]); | |
} | |
choice[gameSettingsPtr->passwords.gamePasswordLength] = '\0'; | |
likeness = getLikeness(gameSettingsPtr, choice); | |
// if user did not guess correct word: | |
// print likeness rating and decrement number of attempts remaining | |
if (likeness != gameSettingsPtr->passwords.gamePasswordLength) | |
{ | |
std::cout << ">Entry denied\n>Likeness = " << likeness << '\n'; | |
--gameSettingsPtr->attempts; | |
delete[] choice; | |
return guessResult::INVALIDGUESS; | |
} | |
else | |
{ | |
std::cout << ">Password valid\n>Accessing maglock controls...\n"; | |
delete[] choice; | |
return guessResult::VALIDGUESS; | |
} | |
} | |
else | |
{ | |
std::cout << "### ACCESS DENIED ###\n"; | |
--gameSettingsPtr->attempts; | |
return guessResult::INVALIDGUESS; // | |
} | |
} | |
/* | |
************************************************************ | |
// FUNC: DETERMINES CHARACTER SIMILARITY, "LIKENESS" | |
************************************************************ | |
*/ | |
int getLikeness(gameSettings *&gameSettingsPtr, char* &choice) | |
{ | |
int correctCharPos = 0; | |
for (int i = 0; i < gameSettingsPtr->passwords.gamePasswordLength; ++i) | |
{ | |
if (choice[i] == gameSettingsPtr->passwords.gameCorrectPassword[i]) | |
++correctCharPos; | |
} | |
return correctCharPos; | |
} | |
/* | |
************************************************************ | |
// FUNC: ALLOWS USER TO REPEAT GAME | |
************************************************************ | |
*/ | |
bool repeatGame(gameSettings *&gameSettingsPtr) | |
{ | |
char choice; | |
bool repeat; | |
std::cout << "Would you like to play again (Y/N) -> "; | |
do | |
{ | |
std::cin.sync(); | |
std::cin.clear(); | |
std::cin >> choice; | |
if (choice == 'N' || choice == 'n') | |
{ | |
repeat = false; | |
} | |
else if (choice == 'Y' || choice == 'y') | |
{ | |
repeat = true; | |
} | |
else | |
{ | |
std::cout << "Invalid input, retry with 'Y' or 'N' -> "; | |
} | |
} while (repeat != false && repeat != true); | |
gameSettingsPtr->gameRepeat = true; | |
return repeat; | |
} | |
/* | |
************************************************************** | |
// FUNC: VERIFY USER INPUT IS OF TYPE INT | |
************************************************************** | |
*/ | |
int checkInt() | |
{ | |
int value; | |
// run indefinitely unless cin doesn't fail to store input into variable of type int | |
while (1) | |
{ | |
// this will return a true flag if the storing process was successful, false flag if unsuccessful | |
if (std::cin >> value) | |
break; | |
else | |
{ | |
std::cout << "Invalid, retry (1 - 5) -> "; | |
// clear input stream + flags | |
std::cin.sync(); | |
std::cin.clear(); | |
} | |
} | |
return value; | |
} | |
/* | |
************************************************************** | |
// FUNC: PAUSES CONSOLE | |
************************************************************** | |
*/ | |
void pause() | |
{ | |
std::cin.sync(); | |
std::cout << "\nPress key.\n"; | |
std::cin.get(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment