Please help me fix my std::cin issue (Expression: string subscript out of range error)

Okay, So I decided to take a different approach on the Bulls Cows game by including the option for the user to input a ‘single character’ and a ‘position’ and having the program search through the hidden word, if the character at the specified ‘position’ of hidden word wasn’t a bull, for any potential cows.

I’ve been having issues trying to find a efficient way of checking the ‘position’ variable for the appropriate data type, so I looked online for a solution. Prior to implementing the code into my program everything compiled and I could play the game multiple times, now when I try to play the game multiple times , I get this error: Expression: string subscript out of range, on visual studio. I’ve been trying to go through the debugger for 2 days now (off and on) trying to figure out what exactly was going wrong in my program to prompt this string subscript out of range error. Originally I thought the issue was with the logic of the code I implemented from an online source for checking the cin datatype:

while (!(std::cin >> Pos) || (test > (GetHiddenWordLength() - 1))) {

		std::cout << "\n\nThe isogram position you provide must be an integer number.\n\n" << test << std::endl;
		std::cout << "Also the isogram position has to be less than or equal to '" << (GetHiddenWordLength() - 1) << "'. Try again. \n\n";
		std::cin.clear();
		std::cout << "\n\nGuess is:" << Guess << "\n\n";
		std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
		std::cout << "\n\nGuess is:" << Guess << "\n\n";
		
	}

However, after checking the variable values with the debugger, I found out that that wasn’t the reason. Eventually, I think I found the real culprit. For some reason, my program isn’t prompting me to enter another guess once a valid guess has been submitted and processed. The bug occurs around the second try of the game. Instead of prompting me to define an input value for ‘Guess’, I think it’s null/default value is being passed into ‘SubmitValidGuess()’ and ‘CheckValidGuess()’ which both use loops based on the ‘Guess’ value. That is why I’m getting an out of bounds exception. I’m not even entirely sure if that’s the issue exactly, but if that is the issue, I still wouldn’t know how to fix it.
Please, I need help. I can’t progress without finding out how to debug my program, and I really want to advance through this section. So if anyone could help me figure out what’s going wrong with my program, I would be grateful. Also, there’s another bug with my program, concerning rem_tries (since I decided to implement that function instead of current_try). I’m not too worried about that one. I think that’s easier to fix. My main issue is with the out of bounds exception that I’m getting. I’m just mentioning that bug in case you think I’m not aware of it.

Here is my program:

/FBullCowGame.h/

#pragma once
#include
#include

using FString = std::string;
using int32 = int;

//all values initialised to zero
struct FBullCowCount
{
int32 Bulls = 0;
int32 Cows = 0;

};

enum class EGuessStatus
{
Guess_invalid,
Guess_valid,
Guess_not_isogram,
Guess_isogram_not_of_equal_length,
Guess_not_char_nor_equal_length_isogram,
Guess_position_not_specified,
Guess_not_lowercase,
Guess_not_entered
};

class FBullCowGame
{
public:

FBullCowGame(); //constructor 

int32 GetDifficulty() const;
int32 GetGuessPos(FString) const;
int32 GetMaxTries() const;
int32 GetRemainingTries();
int32 GetHiddenWordLength() const;
void SetDifficulty();
void SetMaxTries();
bool IsBull(FString) const;
bool IsCow(FString) const;
bool IsGameWon() const;
void DisplayLettersUsed(FString);
void DisplayWordInGrid(FString);
EGuessStatus CheckGuessValidity(FString Guess) const;
FBullCowCount SubmitValidGuess(FString);
void Reset(); //TODO make more rich return value.

private:
//see constructor for initialization
int32 MaxTries;
int32 RemTries;
FString MyHiddenWord;
bool bGameWon;
};

/FBullCowGame.cpp/

#include “FBullCowGame.h”

FBullCowGame::FBullCowGame() { Reset(); }

int32 FBullCowGame::GetDifficulty() const { return 0; }

int32 FBullCowGame::GetGuessPos(FString) const { return 0; }

int32 FBullCowGame::GetMaxTries() const { return MaxTries; }

int32 FBullCowGame::GetRemainingTries() { return --RemTries; }

int32 FBullCowGame::GetHiddenWordLength() const
{
int32 HiddenWordLength = MyHiddenWord.length();
return HiddenWordLength;
}

void FBullCowGame::SetDifficulty() { }

void FBullCowGame::SetMaxTries()
{
constexpr int32 MAX_TRIES = 8;

MaxTries = MAX_TRIES;
}

bool FBullCowGame::IsBull(FString) const { return false; }

bool FBullCowGame::IsCow(FString) const { return false; }
//loop asking for guesses while the game is NOT won
//and there are still tries remaining
bool FBullCowGame::IsGameWon() const { return bGameWon; }

void FBullCowGame::DisplayLettersUsed(FString) { }

void FBullCowGame::DisplayWordInGrid(FString) { }

EGuessStatus FBullCowGame::CheckGuessValidity(FString Guess) const
{
if (false)//if the guess isn’t an isogram,
{
return EGuessStatus::Guess_not_isogram;
}
else if (Guess.length() > GetHiddenWordLength())
{
return EGuessStatus::Guess_isogram_not_of_equal_length;
}
else if (1 < Guess.length() && Guess.length() < GetHiddenWordLength())
{
return EGuessStatus::Guess_not_char_nor_equal_length_isogram;
}
else if (Guess.length() < 1) {
return EGuessStatus::Guess_not_entered;
}
else if (false)
{
return EGuessStatus::Guess_position_not_specified;
}
else if (false)
{
return EGuessStatus::Guess_not_lowercase;
}
else
{
return EGuessStatus::Guess_valid;
}
//return an error
//if guess is too long,
//return and error
//if position is not specified,
//return an error
//if guess isn’t all lowercase,
//return an error
//otherwise
//return OK

}

FBullCowCount FBullCowGame::SubmitValidGuess(FString Guess)
{
/counts bulls and cows and increments
try no.; assuming valid guess
/
//increments the turn no.
int MyCurrentTry = (MaxTries - RemTries);

MyCurrentTry++;
//setup return var.
FBullCowCount BullCowCount;
//loops through all letters in guess
int32 WordLength = MyHiddenWord.length(); //assuming same length as guess

if (Guess.length() == 1)
{
	FString StrPos = "";

	std::cout << Guess << "\n\n";

	std::cout << "Enter a postion within the hidden isogram";

	std::cout << " at which you think your letter guess may match\n\n";

	std::cout << "e.g (0, 1, 2, 3, etc.). Keep in mind that first ";

	std::cout << "letter of the hidden isogram is at '0' and the last is at";

	std::cout << "'" << (GetHiddenWordLength() - 1) << "' \n\n\n";

	std::cout << "Enter a position that is at least, but no more than " << (GetHiddenWordLength() - 1) << " here:  ";

	

	std::getline(std::cin, StrPos);

	int32 Pos;
		
	int32 test = atoi(StrPos.c_str());

	
	

	while (!(std::cin >> Pos) || (test > (GetHiddenWordLength() - 1))) {
		
		
		std::cout << "\n\nThe isogram position you provide must be an integer number.\n\n" << test << std::endl;
		std::cout << "Also the isogram position has to be less than or equal to '" << (GetHiddenWordLength() - 1) << "'. Try again. \n\n";
		std::cin.clear();
		std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
		
		
	}

	Pos = atoi(StrPos.c_str());
	
	if(Guess[0] == MyHiddenWord[Pos])

	{ 
		BullCowCount.Bulls++;

	}

	else if (Guess[0] != MyHiddenWord[Pos])
	{
		for (int32 MHWChar = 0; MHWChar < WordLength; MHWChar++)
		{
			if (Guess[0] == MyHiddenWord[MHWChar])
			{
				BullCowCount.Cows++;
			}
		}
	}
}
else
{
	//loop though all letters in the hidden word
	for (int32 MHWChar = 0; MHWChar < WordLength; MHWChar++)
	{
		//compare letters against hidden wor 1 at a time
		for (int32 GChar = 0; GChar < WordLength; GChar++)
		{
			//if match then
			if (Guess[GChar] == MyHiddenWord[MHWChar])
			{
				//increment bullls if they're in the same place
				if (MHWChar == GChar) {

					BullCowCount.Bulls++; //only if on same increment
					//return position
				}
				else {
					BullCowCount.Cows++; //must be cow if not on same increment
					//return position
				}
				//increment cows if they're not
			}
		}
	}
}
if (BullCowCount.Bulls == GetHiddenWordLength())
{
	bGameWon = true;
}

else
{
	bGameWon = false;
}
return BullCowCount;

}

void FBullCowGame::Reset()
{
SetMaxTries();

RemTries = GetMaxTries();

const FString HIDDEN_WORD = “flower”;

MyHiddenWord = HIDDEN_WORD;
}

/main.cpp/

/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 int32eraction. For game logic see the FBullCowGame class.
/

#include
#include
#include “FBullCowGame.h”

using FText = std::string;

void print_intro();
FText get_valid_guess(int32 rem_tries);
void play_game();
bool ask_to_play_again();

FBullCowGame BCGame;

int main()
{
bool bplay_again = false;

do {
	

	print_intro();

	play_game();

	bplay_again = ask_to_play_again();

	ask_to_play_again();

	
} 

while (bplay_again);

return 0;

}

void print_intro()
{

int32 word_length = BCGame.GetHiddenWordLength();

//introducing the game

std::cout << "Welcome to the Bulls and Cows game!" << std::endl << std::endl;
std::cout << "In this game you will be required to guess an isogram that is " << word_length;
std::cout << " characters in length; however, you will only be alloted a limited number of ";
std::cout << "letter or word guesses, so chose wisely. Goodluck! I hope you have fun! :)";
std::cout << std::endl << std::endl;


//getting a guess from the player

std::cout << "Again, the word length is " << word_length << " What is your first letter or ";
std::cout << "word guess?\n\n";

return; 

}

//repeat the guess back to them

//loop continually until user gives valid guess
FText get_valid_guess(int32 rem_tries)
{
EGuessStatus Status = EGuessStatus::Guess_invalid;

do {
	//get guess from player
	
	FText Guess = "";

		std::cout << "Enter your guess here: ";

		std::getline(std::cin, Guess);
		
		EGuessStatus Status = BCGame.CheckGuessValidity(Guess);

		if (Status == EGuessStatus::Guess_valid)
		{
			 

			std::cout << "\n\nYou have " << rem_tries << " more tries.\n\n\n";
		}

	switch (Status)
	{
	case EGuessStatus::Guess_not_isogram:
		std::cout << "\nPlease enter an isogram. An isogram is a word with no repeating ";
		std::cout << "characters: e.g. 'hemlock'.\n\n";
		break;

	case EGuessStatus::Guess_isogram_not_of_equal_length:
		std::cout << "\nPlease enter a " << BCGame.GetHiddenWordLength() << " letter isogram word.\n\n ";
		break;

	case EGuessStatus::Guess_not_char_nor_equal_length_isogram:
		std::cout << "\nPlease enter a one letter character or a " << BCGame.GetHiddenWordLength() << " letter isogram word.\n\n";
		break;

	case EGuessStatus::Guess_position_not_specified:
		std::cout << "\nPlease enter a position for your letter guess that is a number: e.g. 1,2,3.\n\n";
		break;

	case EGuessStatus::Guess_not_lowercase:
		std::cout << "\nPlease make sure your that your letter or isogram guesses are all lowercase.\n\n";
		break;

	default:
		return Guess;
	}

	std::cout << std::endl;


} while (Status != EGuessStatus::Guess_valid);
//keep looping till we get no errors

}

void play_game()
{
BCGame.Reset();

// Submit valid guess to the game
// Print32 number of bulls and cows
std::cout << "\nYou have " << BCGame.GetMaxTries() << " total tries. Goodluck!\n\n";

// TODO change from FOR to WHILE loop once we are validating tries
int32 rem_tries = BCGame.GetRemainingTries();

while(!BCGame.IsGameWon() && rem_tries > 0)
{
	FText Guess = get_valid_guess(rem_tries);
	
	

		FBullCowCount BullCowCount = BCGame.SubmitValidGuess(Guess);
		//print number of bulls and cows
		

		std::cout << "\nOkay, so your guess was " "'" << Guess << "'";
		std::cout << " right?\nLet's see if we have a match.";
		std::cout << "\n\n.  .  .\n\n";
		std::cout << "Bulls = " << BullCowCount.Bulls;
		std::cout << "\n\nCows = " << BullCowCount.Cows << "\n\n";
	

	
	
}

//TODO add a game summary

std::cout << "\n\n\nCongratulations! You've won the Game!\n\n\n";

}

bool ask_to_play_again()
{
FText Response = “”;
std::cout << "Would you like to play again? Answer ‘y’ or ‘n’: ";
std::getline(std::cin, Response);
std::cout << std::endl;
std::cout << std::endl;
return (Response[0] == ‘y’) || (Response[0] == ‘Y’);
}

Thanks in advance guys!

Sorry, I didn’t have a chance to look at all the code and try to figure through it, but just wanted to mention that it sounds like an initialization problem, ie. your guess (or whatever) is not being re-initialized at the start of the game, so still has a string length = to the last guess, which may not be the same length (hence out of range as it steps off the end of your string)

Only a guess form your Topic description though!

Good luck!

1 Like

Haven’t gone over it fully but your problem lies here, I also assume you don’t intend to get input twice here but you do.

std::getline(std::cin, StrPos); //getting input 

int32 Pos;

int32 test = atoi(StrPos.c_str());

while (!(std::cin >> Pos) || (test > (GetHiddenWordLength() - 1))) { //getting another input within while loop condition
    std::cout << "\n\nThe isogram position you provide must be an integer number.\n\n" << test << std::endl;
    std::cout << "Also the isogram position has to be less than or equal to '" << (GetHiddenWordLength() - 1) << "'. Try again. \n\n";
    std::cin.clear();
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
//missing
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
//

On your second input you only clear the input stream within the loop and not after it. So when someone enters a number and hits enter the input stream would be"1\n" 1 will go into Pos and you are left with “\n” in the input stream, and the next time you use std::cin (where you are getting guess with std::getline) you will be using “\n” and not getting an input from the user, and since getline reads up to and including a “\n” then discards the “\n” you are left with an empty string, then somewhere down the line (I think in the B/C count loop) you try to access an element in the empty string causing the out of bounds error.

1 Like

Did @DanM solve this for you?

1 Like

Thanks so much for helping me understand the problem. I’m so sorry that I wasn’t able to get back to you all sooner, but I’ve actually been pretty busy so I haven’t had time to mess around with my program. But I really appreciate both of you guys helping me. I’ll going to give fixing it a shot right now. Thanks again.

And yes @sampattuzzi, @DanM helped me quite a bit.

Yea, I see now that asking for input twice, especially with one occurrence being in a loop, was problem. But the issue is that I’m trying to perform two separate tests on the user’s input, but I can’t really think of a way of both checking the type of the user’s input and making sure, if that type is indeed an ‘int’, that the size of the int doesn’t exceed the (wordlength -1). Actuallly I think I just answered my own question. Perhaps I can’t use a while loop after all to solve this problem. But then again, perhaps I can. I’ll just have to think over it some more. I’ll get back to you if it all works out. Thanks again for being so generous to help me.

You can use a while loop, the solution is pretty much already written and you just have to remove a couple things. If you want the answer just ask, but based on your comment it sounds like you want to solve it yourself.

Ohmergerd it finally works! Here’s what I did:

int32 Pos;

	while (!(std::cin >> Pos) || Pos  > (GetHiddenWordLength() - 1)) {
		
		std::cout << "\n\nThe isogram position you provide must be an integer number.\n\n";
		std::cout << "Also the isogram position has to be less than or equal to '" << (GetHiddenWordLength() - 1) << "'. Try again. \n\n";
		std::cin.clear();
		std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
		
		
	}

I didn’t know what the operator ‘>>’ did at first, but it’s just a redirect. All this time I was trying to figure out a way how evaluate the user input if the variable wasn’t assigned, when Pos was the variable that was assigned to the user input. Therefore, I can use Pos again in the other condition to test the same user input.

I’m still a little confused as to why assigning std::cin to Pos didn’t cast the string number into an ASCII int. But at least the code works. Thanks a bunch for all the help. Thanks to you I was able to solve it.
Cheers!

It’s actually the bitwise shift right operator. But the iostreams have overloaded them to basically mean “put into” (<<) and “extract out of” (>>)

Note that the ordering of the if condition matters, conditional statements are short-circuited so in your case the right hand side of your or will only be evaluated if std::cin successfully inserted into the int.

Not sure what you mean, as in given the following

int x;
std::cin >> x; //input is 1

Why isn’t x 49?

Privacy & Terms