Difficulty Check works but breaks when passing to reset

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!

So I solved this:

I moved BCgame.Reset(Difficulty); to the GetValidDifficulty() function. Then I initialized MyHiddenWord in FBullCowGame.cpp.

This allows me to callGetValidDifficulty() and have it ready for PlayGame() without trying to pass parameters where I cant!

It is so satisfying to get it to work :slight_smile: