Hey guys,
I am using Imap to add a difficulty setting to my game. The error handling seems to be working, but when I pass the Difficulty String to my reset function, it seems to be breaking the game. I keep getting this error that says:
std::_String_alloc<std::_String_base_types<char,std::allocator<char> > >::_Mysize(...) returned 0x19.
Below is my code:
main.cpp:
#pragma once
#include <iostream>
#include <string>
#include "FBullCowGame.h"
// to make syntax Unreal friendly
using FText = std::string;
using int32 = int;
// function prototypes as outside a class
void PrintIntro();
void PlayGame(FString Difficulty);
FText GetValidDifficulty();
FText GetValidGuess();
bool AskToPlayAgain();
void PrintGameSummary();
FBullCowGame BCGame; // instantiate a new game, which we re-use across plays
// the entry point for our application
int main(FString Difficulty)
{
bool bPlayAgain = false;
do {
PrintIntro();
GetValidDifficulty();
BCGame.Reset(Difficulty);
PlayGame(Difficulty);
bPlayAgain = AskToPlayAgain();
}
while (bPlayAgain);
return 0; // exit the application
}
void PrintIntro()
{
std::cout << "Welcome to Bulls and Cows, a fun word game.\n";
std::cout << std::endl;
std::cout << " } { ___ " << std::endl;
std::cout << " (o o) (o o) " << std::endl;
std::cout << " /-------\\ / \\ /-------\\ " << std::endl;
std::cout << " / | BULL |O O| COW | \\ " << std::endl;
std::cout << " * |-,--- | |------| * " << std::endl;
std::cout << " ^ ^ ^ ^ " << std::endl;
std::cout << "Can you guess the " << BCGame.GetHiddenWordLength();
std::cout << " letter isogram I'm thinking of?\n";
std::cout << std::endl;
return;
}
// plays a single game to completion
void PlayGame(FString Difficulty)
{
//BCGame.Reset(Difficulty);
int32 MaxTries = BCGame.GetMaxTries();
// loop asking for guesses while the game
// is NOT won and there are still tries remaining
while (!BCGame.IsGameWon() && BCGame.GetCurrentTry() <= MaxTries) {
FText Guess = GetValidGuess();
// submit valid guess to the game, and receive counts
FBullCowCount BullCowCount = BCGame.SubmitValidGuess(Guess);
std::cout << "Bulls = " << BullCowCount.Bulls;
std::cout << ". Cows = " << BullCowCount.Cows << "\n\n";
}
PrintGameSummary();
return;
}
FText GetValidDifficulty()
{
FText Difficulty = "";
EDifficultyStatus DStatus = EDifficultyStatus::Invalid_Status;
do
{
std::cout << "Please enter a difficulty level between 1(easy) and 5(impossible): ";
std::getline(std::cin, Difficulty);
DStatus = BCGame.CheckDifficultyValidity(Difficulty);
switch (DStatus)
{
case EDifficultyStatus::Not_Number:
std::cout << "Please enter only a valid number. \n\n";
break;
case EDifficultyStatus::Not_Range:
std::cout << "Please enter a number within range. \n\n";
break;
default:
break;
}
} while (DStatus != EDifficultyStatus::OK);
return Difficulty;
}
// loop continually until the user gives a valid guess
FText GetValidGuess()
{
FText Guess = "";
EGuessStatus Status = EGuessStatus::Invalid_Status;
do {
// get a guess from the player
int32 CurrentTry = BCGame.GetCurrentTry();
std::cout << "Try " << CurrentTry << " of " << BCGame.GetMaxTries();
std::cout << ". Enter your guess: ";
std::getline(std::cin, Guess);
// check status and give feedback
Status = BCGame.CheckGuessValidity(Guess);
switch (Status) {
case EGuessStatus::Wrong_Length:
std::cout << "Please enter a " << BCGame.GetHiddenWordLength() << " letter word.\n\n";
break;
case EGuessStatus::Not_Isogram:
std::cout << "Please enter a word witout repeating letters.\n\n";
break;
case EGuessStatus::Not_Lowercase:
std::cout << "Please enter all lowercase letters.\n\n";
break;
default:
// assume the guess is valid
break;
}
} while (Status != EGuessStatus::OK); // keep looping until we get no errors
return Guess;
}
bool AskToPlayAgain()
{
std::cout << "Do you want to play again with the same hidden word (y/n)? ";
FText Response = "";
std::getline(std::cin, Response);
return (Response[0] == 'y') || (Response[0] == 'Y');
}
void PrintGameSummary()
{
if (BCGame.IsGameWon())
{
std::cout << "WELL DONE - YOU WIN!\n";
}
else
{
std::cout << "Better luck next time!\n";
}
}
FBullCOwGame.h:
#pragma once
#include <string>
// to make syntax Unreal friendly
using FString = std::string;
using int32 = int;
struct FBullCowCount
{
int32 Bulls = 0;
int32 Cows = 0;
};
enum class EDifficultyStatus
{
Invalid_Status,
OK,
Not_Number,
Not_Range
};
enum class EGuessStatus
{
Invalid_Status,
OK,
Not_Isogram,
Wrong_Length,
Not_Lowercase
};
class FBullCowGame
{
public:
FBullCowGame(); // constructor
int32 GetMaxTries() const;
int32 GetCurrentTry() const;
int32 GetHiddenWordLength() const;
bool IsGameWon() const;
FString GetHiddenWord(FString Difficulty);
EDifficultyStatus CheckDifficultyValidity(FString) const;
EGuessStatus CheckGuessValidity(FString) const;
void Reset(FString Difficulty);
FBullCowCount SubmitValidGuess(FString);
// ^^ Please try and ignore this and focus on the interface above ^^
private:
// see constructor for initialisation
int32 MyCurrentTry;
FString MyHiddenWord;
bool bGameIsWon;
bool IsIsogram(FString) const;
bool IsLowercase(FString) const;
bool IsNumber(FString Difficulty) const;
bool IsRange(int32 Difficulty) const;
};
FBullCowGame.cpp:
#pragma once
#include "FBullCowGame.h"
#include <map>
// to make syntax Unreal friendly
#define TMap std::map
using int32 = int;
FString Difficulty = "";
FBullCowGame::FBullCowGame() { Reset(Difficulty); } // default constructor
int32 FBullCowGame::GetCurrentTry() const { return MyCurrentTry; }
int32 FBullCowGame::GetHiddenWordLength() const { return MyHiddenWord.length(); }
bool FBullCowGame::IsGameWon() const { return bGameIsWon; }
FString FBullCowGame::GetHiddenWord(FString Difficulty)
{
TMap<FString, FString> GetHiddenWord{ { "3", "pan" },{ "4", "base" },{ "5", "start" },{ "6", "drawer" },{ "7", "kitchen" } };
return GetHiddenWord[Difficulty];
}
int32 FBullCowGame::GetMaxTries() const
{
TMap<int32, int32> WordLengthToMaxTries{ {3,4}, {4,7}, {5,10}, {6,16}, {7,20} };
return WordLengthToMaxTries[MyHiddenWord.length()];
}
void FBullCowGame::Reset(FString Difficulty)
{
//
//const FString HIDDEN_WORD = "plane"; // this MUST be an isogram
MyHiddenWord = GetHiddenWord(Difficulty);//GetHiddenWord(Difficulty);
MyCurrentTry = 1;
bGameIsWon = false;
return ;
}
EDifficultyStatus FBullCowGame::CheckDifficultyValidity(FString Difficulty) const
{
if (!IsNumber((Difficulty)))
{
return EDifficultyStatus::Not_Number;
}
else if(!IsRange((std::stoi(Difficulty))))
{
return EDifficultyStatus::Not_Range;
}
else
{
return EDifficultyStatus::OK;
}
}
EGuessStatus FBullCowGame::CheckGuessValidity(FString Guess) const
{
if (!IsIsogram(Guess)) // if the guess isn't an isogram
{
return EGuessStatus::Not_Isogram;
}
else if (!IsLowercase(Guess)) // if the guess isn't all lowercase
{
return EGuessStatus::Not_Lowercase;
}
else if (Guess.length() != GetHiddenWordLength()) // if the guess length is wrong
{
return EGuessStatus::Wrong_Length;
}
else
{
return EGuessStatus::OK;
}
}
// receives a VALID guess, incriments turn, and returns count
FBullCowCount FBullCowGame::SubmitValidGuess(FString Guess)
{
MyCurrentTry++;
FBullCowCount BullCowCount;
int32 WordLength = MyHiddenWord.length(); // assuming same length as guess
// loop through all letters in the hidden word
for (int32 MHWChar = 0; MHWChar < WordLength; MHWChar++) {
// compare letters against the guess
for (int32 GChar = 0; GChar < WordLength; GChar++) {
// if they match then
if (Guess[GChar] == MyHiddenWord[MHWChar]) {
if (MHWChar == GChar) { // if they're in the same place
BullCowCount.Bulls++; // incriment bulls
}
else {
BullCowCount.Cows++; // must be a cow
}
}
}
}
if (BullCowCount.Bulls == WordLength) {
bGameIsWon = true;
}
else
{
bGameIsWon = false;
}
return BullCowCount;
}
bool FBullCowGame::IsIsogram(FString Word) const
{
// treat 0 and 1 letter words as isograms
if (Word.length() <= 1) { return true; }
TMap<char, bool> LetterSeen; // setup our map
for (auto Letter : Word) // for all letters of the word
{
Letter = tolower(Letter); // handle mixed case
if (LetterSeen[Letter]) {// if the letter is in the map
return false; // we do NOT have an isogram
} else {
LetterSeen[Letter] = true;// add the letter to the map
}
}
return true; // for example in cases where /0 is entered
}
bool FBullCowGame::IsLowercase(FString Word) const
{
for (auto Letter : Word)
{
if (!islower(Letter)) // if not a lowercase letter
{
return false;
}
}
return true;
}
bool FBullCowGame::IsNumber(FString Difficulty) const
{
char *cstr = &Difficulty[0u];
if (isdigit(*cstr))
{
return true;
}
else
{
return false;
}
return false;
}
bool FBullCowGame::IsRange(int32 Difficulty) const
{
if ((Difficulty >= 1) && (Difficulty <= 5))
{
return true;
}
else
{
return false;
}
return false;
}
Thanks!
