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!