Skip to content

Instantly share code, notes, and snippets.

@seanpianka
Created May 13, 2018 04:48
Show Gist options
  • Save seanpianka/63508ed3ccdd21405c3413cdd0728d77 to your computer and use it in GitHub Desktop.
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.
/*
** 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