Skip to content

Instantly share code, notes, and snippets.

@telmotrooper
Last active January 27, 2017 00:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save telmotrooper/4508023e7f9ffae7c613c61cac30d7ed to your computer and use it in GitHub Desktop.
Save telmotrooper/4508023e7f9ffae7c613c61cac30d7ed to your computer and use it in GitHub Desktop.
BullCowGame with difficulty settings
/*
This is the implementation of the FBullCowGame header.
*/
#pragma once
#include "FBullCowGame.h"
#include <map>
#define TMap std::map
FBullCowGame::FBullCowGame() {Reset();}
int32 FBullCowGame::getCurrentTry() const { return myCurrentTry;}
int32 FBullCowGame::getHiddenWordLength() const {return static_cast<int>(myHiddenWord.length());}
void FBullCowGame::setHiddenWordBasedOnLength(int32 length) {
switch(length) {
case 3: myHiddenWord = "bat"; break;
case 4: myHiddenWord = "lane"; break;
case 5: myHiddenWord = "ocean"; break;
case 6: myHiddenWord = "planet"; break;
case 7: myHiddenWord = "panther"; break;
}
}
bool FBullCowGame::isGameWon() const {return bGameIsWon;}
int32 FBullCowGame::getMaxTries() const {
TMap<int32, int32> wordLengthToMaxTries{{3,4}, {4,7}, {5,10}, {6,15}, {7,20}};
return static_cast<int>(wordLengthToMaxTries[myHiddenWord.length()]);
}
void FBullCowGame::Reset() {
myCurrentTry = 1;
bGameIsWon = false;
}
bool FBullCowGame::isIsogram(FString guess) const {
if (guess.length() <= 1) {return true;}
TMap<char, bool> letterSeen;
for (auto letter : guess) {
letter = tolower(letter);
if (letterSeen[letter]) { // if the letter has been seen before, the guess is not an isogram
return false;
} else {
letterSeen[letter] = true;
}
}
return true;
}
bool FBullCowGame::isLowercase(FString guess) const {
for (auto letter : guess) {
if (!islower(letter)) {return false;}
}
return true;
}
EGuessStatus FBullCowGame::checkGuessValidity(FString guess) const {
if(!isIsogram(guess)) {
return EGuessStatus::NOT_ISOGRAM;
} else if (!isLowercase(guess)) {
return EGuessStatus::NOT_LOWERCASE;
} else if (guess.length() != getHiddenWordLength()) {
return EGuessStatus::WRONG_LENGTH;
} else {
return EGuessStatus::OK;
}
}
// receives a valid guess, increments turn and returns count
FBullCowCount FBullCowGame::submitValidGuess(FString guess) {
myCurrentTry++;
FBullCowCount bullCowCount;
int32 hiddenWordLength = getHiddenWordLength();
for (int32 i = 0; i < guess.length(); i++) {
for(int32 j = 0; j < hiddenWordLength; j++) {
if(guess[i] == myHiddenWord[j]) {
if (i == j) {
bullCowCount.Bulls++;
} else {
bullCowCount.Cows++;
}
}
}
}
if(bullCowCount.Bulls == hiddenWordLength) {bGameIsWon = true;}
return bullCowCount;
}
/*
This acts as the model in a MVC pattern and is responsible
for managing data, logic and rules of the application.
*/
#pragma once
#include <string>
using FString = std::string;
using int32 = int;
// all values initialized to zero
struct FBullCowCount {
int32 Bulls = 0;
int32 Cows = 0;
};
enum class EGuessStatus {
INVALID_STATUS,
OK,
NOT_ISOGRAM,
WRONG_LENGTH,
NOT_LOWERCASE
};
class FBullCowGame {
public:
FBullCowGame();
int32 getMaxTries() const;
int32 getCurrentTry() const;
int32 getHiddenWordLength() const;
void setHiddenWordBasedOnLength(int32);
bool isGameWon() const;
void Reset();
EGuessStatus checkGuessValidity(FString) const;
// provide a method for counting bulls and cows, and incrementing try number
// count bulls & cows and increase try number, assuming a valid guess
FBullCowCount submitValidGuess(FString);
// ^^ Please try and ignore this and focus on the interface above ^^
private:
int32 myCurrentTry;
FString myHiddenWord;
bool bGameIsWon;
bool isIsogram(FString) const;
bool isLowercase(FString) const;
};
/*
This is the console executable that makes use of the BullCow class.
This acts as the view in a MVC pattern and is responsible for all
user interaction. For game logic see the FBullCowGame class.
*/
#pragma once
#include <iostream>
#include <string>
#include "FBullCowGame.h"
#define cout std::cout
#define cin std::cin
#define endl std::endl
// to make syntax Unreal friendly
using FText = std::string;
using int32 = int;
void printIntro();
void setDifficulty();
void playGame();
void printGameSummary();
FText getValidGuess();
bool askToPlayAgain();
FBullCowGame BCGame;
bool bPlayAgain = false;
int main() {
do {
printIntro();
setDifficulty();
playGame();
bPlayAgain = askToPlayAgain();
} while (bPlayAgain);
return 0;
}
void printIntro() {
cout << "Welcome to Bulls and Cows, a fun word game.\n\n";
cout << " } { ___ " << endl;
cout << " (o o) (o o) " << endl;
cout << " /-------\\ / \\ /-------\\ " << endl;
cout << " / | BULL |O O| COW | \\ " << endl;
cout << " * |-,--- | |------| * " << endl;
cout << " ^ ^ ^ ^ " << "\n\n";
}
void setDifficulty() {
FText difficultyInput = "0";
int32 difficultyValue = 0;
bool loop = true;
do {
cout << "Choose the number of letters of the isogram (between 3 and 7): ";
getline(cin, difficultyInput);
cout << endl;
try {
difficultyValue = stoi(difficultyInput); // try to convert input to integer
if (difficultyValue < 3 || difficultyValue > 7) {
cout << "\"" << difficultyInput << "\"" << " is not between 3 and 7.\n\n";
} else {
loop = false; // exit loop
}
} catch(std::invalid_argument&) { // if a non-numeric value is entered
cout << "\"" << difficultyInput << "\"" << " is not a number.\n\n";
}
} while (loop);
BCGame.setHiddenWordBasedOnLength(difficultyValue);
cout << "Can you guess the " << BCGame.getHiddenWordLength();
cout << " letter isogram I'm thinking of?\n\n";
}
void playGame() {
BCGame.Reset();
int32 maxTries = BCGame.getMaxTries();
// loop for the number of turns asking for guesses
while(!BCGame.isGameWon() && BCGame.getCurrentTry() <= maxTries) {
//for (int32 count = 1; count <= maxTries; count++) {
FText guess = getValidGuess();
// submit valid guess to the game and receive count
FBullCowCount bullCowCount = BCGame.submitValidGuess(guess);
cout << "Bulls: " << bullCowCount.Bulls;
cout << " Cows: " << bullCowCount.Cows << "\n\n";
}
printGameSummary();
}
void printGameSummary() {
if (BCGame.isGameWon()) {
cout << "You won!\n\n";
} else {
cout << "Bad luck!\n\n";
}
}
FText getValidGuess() {
EGuessStatus status = EGuessStatus::INVALID_STATUS;
FText guess = "";
do {
int32 currentTry = BCGame.getCurrentTry();
cout << "Try " << currentTry << " of " << BCGame.getMaxTries();
cout << ". Enter your guess: ";
getline(cin, guess);
status = BCGame.checkGuessValidity(guess);
switch (status) {
case EGuessStatus::WRONG_LENGTH:
cout << "Please enter a " << BCGame.getHiddenWordLength() << " letter word.\n\n";
break;
case EGuessStatus::NOT_ISOGRAM:
cout << "Please enter a word without repeating letters.\n\n";
break;
case EGuessStatus::NOT_LOWERCASE:
cout << "Please enter all lowercase letters.\n\n";
break;
}
} while(status != EGuessStatus::OK);
return guess;
}
bool askToPlayAgain() {
cout << "Do you wanna play again (y/n)? ";
FText response = "";
getline(cin, response);
cout << endl;
return (response[0] == 'y' || response[0] == 'Y');
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment