Program calls function one too many times

I am currently working on a Bull Cow Game game in Visual Studio wherein the user tries to guess a psuedo-randomly selected Secret Word and the console outputs how many letters that were in the right place (Cows) and how many were in the wrong place but were the right letters (Bulls). In my main file, the function PlayGame() and creates an object based on the class definition in FBullCowGame.h. Within the PlayGame() function there is a for loop which continually asks for guesses from the user until the user has exhausted their guess limit. Within that for loop, the following is printed:


Try: 1
Enter your guess (enter 'exit' to leave the game):
Your guess was:


The problem is that this is outputted to the console and doesn't allow the user to input a guess after the line "Enter your guess:". Instead, the above is outputted as is- without anything after "Enter your guess:" or "Your guess was:". It is only after this is outputted that on "Try: 2" that the user is allowed to enter their guess and the program outputs what the guess was after "your guess was:".

My question is: why is the console doing this? When I look at my code I can't find anything that would cause it to output the above three lines without asking for user input before then asking for user input.

Note: My solution is not currently finished.

Here are the relevant files of my solution:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
  // main.cpp
#include "FBullCowGame.h"
#include "ToLowerCase.h"

void PrintIntro();
void PlayGame();
FText GetGuess();
int32 GetCurrentTry();
bool AskToPlayAgain();


int main(){

	PrintIntro();
	
	bool bPlayAgain = true;
	do {
		PlayGame();
	} while (AskToPlayAgain() == true);

	return 0; // exit the application

};



void PrintIntro(){

	//constexpr int WORLD_LENGTH = 9;
	std::cout << "Welcome to Bulls and Cows, a fun word game.\n";
	//std::cout << "Can you guess the " << WORLD_LENGTH;
	//std::cout << " letter isogram I'm thinking of?\n";
	std::cout << std::endl;

	//return;
};

 
void PlayGame() {

	FBullCowGame BCGame; // instantiate a new game

	//variable "Difficulty" is assigned difficulty based on user input
	FText Difficulty = BCGame.DifficultySelect();

	//sets the max tries based on the difficulty user selected
	BCGame.SetMaxTries(Difficulty);

	int32 MaxTries = BCGame.GetMaxTries();

	//randomly selects a word from file "ListOfWords.txt" and assigns it to the "SecretWord" variable
	BCGame.SetSecretWord();

	FText SecretWord = BCGame.GetSecretWord();

	for (int32 count = 0; count <= MaxTries; count++) {
		int32 CurrentTry = BCGame.GetCurrentTry();
		std::cout << "Try: " << CurrentTry << std::endl;
		FText Guess = GetGuess();
		if (Guess == "exit"){
			count = MaxTries;
		}
		std::cout << "Your guess was: " << Guess << std::endl;
		std::cout << std::endl;
		BCGame.IterateCurrentTry(CurrentTry);
	}

	//return;
};

FText GetGuess() {

	//get a guess from the player
	std::cout << "Enter your guess (enter 'exit' to leave game): " << std::endl;
	FText Guess = "";
	std::getline(std::cin, Guess);

	//converts all characters to lower case
	Guess = ToLowerCase(Guess);

	return Guess;
}

bool AskToPlayAgain(){

		std::cout << "Do you want to play again (yes/no)? ";
		FText Response = "";
		std::getline(std::cin, Response);

		//converts all characters of string in to lower case characters
		Response = ToLowerCase(Response);

		bool validAnswer = false;
		bool loopExit = false;

		//do-while loop ensures that user enters valid response before loop exits
		do {
			if (Response == "yes" || Response == "y") {
				validAnswer = true;
				loopExit = true;
			}
			else if (Response == "no" || Response == "n") {
				validAnswer = false;
				loopExit = true;
			}
			else {
				std::cout << "Please enter a valid response (yes/no): " << std::endl;
				std::getline(std::cin, Response);
				Response = ToLowerCase(Response);
			}
		} while (loopExit == false);

		return validAnswer;

};


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//FBullCowGame.h
#ifndef __FBULLCOWGAME_H_INCLUDED__
#define __FBULLCOWGAME_H_INCLUDED__

#include <string>
#include <iostream>
#include <fstream>
#include <time.h>

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

//values intitialized to zero
struct BullCowCount {
	int32 Bulls = 0;
	int32 Cows = 0;

};

class FBullCowGame {

	public:
		//void Reset(); //TO DO: Make a more rich return value
		FText DifficultySelect(); //[mine]
		void SetMaxTries(FText Difficulty);
		int32 GetMaxTries() const;
		int32 GetCurrentTry();
		void IterateCurrentTry(int32 CurrentTry); //[mine]
		void SetSecretWord(); //[mine]
		FText GetSecretWord(); //[mine]
		//bool IsGameWon() const;
		FText StringToLowerCase(FText Word); //[mine]
		//bool GuessCheckValidity(FText); TO DO: Make a more rich return value
		//FText SubmitGuess(FText);

	private:  
		int32 MyMaxTries = 0;
		FText SecretWord = "";
		int32 RandomNumber = 0;
		int32 MyCurrentTrys = 1;


};

#endif 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#include "FBullCowGame.h"

FText FBullCowGame::DifficultySelect() {

	std::cout << "Please select a difficulty (easy, medium, hard, brutal): ";
	FText Difficulty = "";
	std::cin >> Difficulty;

	Difficulty = StringToLowerCase(Difficulty);

	bool ValidAnswer = false;

	//makes sure user inputs valid difficulty
	do {
		if (Difficulty == "easy" || "medium" || "hard" || "brutal") {
			ValidAnswer = true;
		}
		else {
			std::cout << "Please enter valid choice (easy, medium, hard, brutal): ";
			std::cin >> Difficulty;
			Difficulty = StringToLowerCase(Difficulty);
		}

	} while (ValidAnswer == false);

	return Difficulty;


};

void FBullCowGame::SetMaxTries(FText Difficulty) {

	if (Difficulty == "easy") {
		MyMaxTries = 15;
	}
	else if (Difficulty == "medium") {
		MyMaxTries = 10;
	}
	else if (Difficulty == "hard") {
		MyMaxTries = 5;
	}
	else if (Difficulty == "brutal") {
		MyMaxTries = 3;
	}

	return;

};

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

void FBullCowGame::SetSecretWord(){
	
	std::ifstream inFile;

	//opens ListOfWords.txt file which has 100 words to select from
	inFile.open("ListOfWords.txt");

	if (inFile.fail()) {
		std::cerr << "Error opening file" << std::endl;
		exit(1);
	}

	//makes program generate a new set of values every new game
	srand(time(NULL));

	//pick a random number from 0 to 99
	int32 RandomNumber = rand() % 100;

	int32 Count = 0;

	FText CurrentWord = "";

	inFile >> CurrentWord;

	//conditional expression searches for word in text file until the Count matches
	if (Count == RandomNumber) {
		SecretWord = CurrentWord;
	}
	else {
		while (Count != RandomNumber) {
			Count++;
			inFile >> CurrentWord;
		}
		SecretWord = CurrentWord;
	}

	inFile.close();
	
};

FText FBullCowGame::GetSecretWord() {

	return SecretWord;
};

void FBullCowGame::IterateCurrentTry(int32 CurrentTry) {

	MyCurrentTrys = CurrentTry + 1;
	

}

int32 FBullCowGame::GetCurrentTry() {

	return MyCurrentTrys;

};

/*void FBullCowGame::Reset() {
	constexpr int32 MAX_TRIES = 8;
	MyMaxTries = MAX_TRIES;

	const FString HIDDEN_WORD = "Planet";
	MyHiddenWord = HIDDEN_WORD;

	MyHiddenWord = HIDDEN_WORD;

	MyCurrentTry = 1;

	return;
}*/

/*bool FBullCowGame::IsGameWon() const {
	return false;
}*/

/*identical function to the "ToLowerCase.h"'s function but
for the FBullCowGame object; "main.cpp" uses the header file*/
FText FBullCowGame::StringToLowerCase(FText Word){
	
	int32 i = 0;

	//iterate through the word and make every letter lower case
	for (i = 0; i < (int32)(Word.length()); i++) {
		//condition met if character is uppercase
		if ((Word[i] >= 65) && (Word[i] <= 90)) {
			//character is then transformed from uppercase to lower case
			Word[i] = Word[i] + 32;
		}
	}

	return Word;

};

/*bool FBullCowGame::GuessCheckValidity(FText) {
	return false;
};*/

/*BullCowCount FBullCowGame::SubmitGuess(FString){

	//increment the turn number
	MyCurrentTry++;

	//setup a return variable
	BullCowCount BullCowCount;
	//loop through the letters in the guess
		//compare letters against the hidden word

	return BullCowCount();
}*/

Hello vaderboi,

I tried to compile your program, found the header file "ToLowerCase.h" was not in the code you posted.

Two other things I noticed are:

1. The "main" and "FBullCowGame..cpp" files both had ";"s at the end of the functions. They should not be there.

2. Both ".cpp" files are missing The needed include files like: "<iostream>" and others.

I have no idea what you did in the "ToLowerCase.h" file, so it is hard to guess for a work around.

I have a guess at one problem, but can not test it yet. The "PlayGame()" function calls the "GetGuess()" function where "std::getline" is used for input from the user. My guess is that before this "std::getline" in "GetGuess" there was a "std::cin >> someVariable" used leaving a new line character in the input buffer and then the "getline" is extracting this new line from the input buffer and not from the user. Not knowing where the "std::cin >>" is I would try this just before the "getline" at line 76 in main.
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // <--- Requires header file <limits>. and see what happens.

Hope that helps,

Andy
Hello vaderboi,

As I started working with your program I found that "SetSecretWord()" member function opens the file "ListOfWords.txt", but I have no file to open or any idea what it would contain.

Would you please post this file or at least part of it so I could test the program better.

Andy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
//ListOfWords.txt
corneal
wiggeries
resourcefully
bedesman
unassuageable
melodia
magnetics
circumambulate
instarred
gadgetry
avoid
capouch
psychotechnics
hippocrene
semifuturistic
pasqualina
depletive
siang
peeler
gomuti
biro
rudolf
jugum
pyrope
hyponitrous
outback
suez
bisymmetric
dimorph
silvius
pistoling
faq
eclipsed
rfc
shoveled
navy
psychogenesis
unpalled
barotseland
bandage
unstudded
graze
gaugeable
humanizing
ostracoderm
neurosurgeon
myrica
hieroglyphist
postbaptismal
ithiel
ethiopian
unitable
sovietise
studio
dexterous
euphorion
kimchee
mediatory
succeedingly
doziest
fade
tearlessness
consonantism
requalification
stola
cauterising
caponised
rasputin
abominated
gink
gantleted
intolerant
lapwing
quiverer
villosity
hallroom
preforgave
decinormal
pajamaed
walkabout
demagogue
yipe
arvin
symphystic
exert
avail
metaphyte
supercomplexity
atmolysis
mampara
inspire
silva
unbeloved
smaragd
interpledged
platyhelminth
aggrandizer
belatedly
unstormed
naivetivet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//ToLowerCase.h
#ifndef __TOLOWERCASE_H_INCLUDED__
#define __TOLOWERCASE_H_INCLUDED__

FText ToLowerCase(FText word);

//turns all characters of a String in to lower case
FText ToLowerCase(FText word) {

    int32 i = 0;

    //iterate through the word and make every letter lower case
    for(i = 0; i < (int32)(word.length()); i++) {
        //condition met if character is uppercase
        if((word[i] >= 65) && (word[i] <= 90)){
            //character is then transformed from uppercase to lower case
            word[i] = word[i] + 32;
        }
    }

    return word;


};

#endif 
Sorry, I thought the other two files wouldn't be relevant. Attached them here.
Hello vaderboi,

Thank you and sorry for the delay. The code and file will better help test the program.

Refer to my earlier message http://www.cplusplus.com/forum/beginner/232711/#msg1047403 about the use of "cin.ignore()". I achieved a working program and found the this ignore solved the problem. Later I found the "std::cin >> something" where it would be better to use the ignore after the "std::cin >>"s.

Hope that helps,

Andy
Hello vaderboi,

Now that I have everything to work with I see things ahat are half righr and some things that are completely wrong.

Main is a ".cpp" file and should start this way:
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <string>
#include <iostream>
//#include <fstream>  // <--- Not needed in main.
//#include <ctime>  // <--- Should be ctime not time.h and not needed in main.

#include "FBullCowGame.h"
#include "ToLowerCase.h"

void PrintIntro();
void PlayGame();
FText GetGuess();
int32 GetCurrentTry();  // <--- No function definition for this function.
bool AskToPlayAgain();

Putting the header files "iostream" and "string" in a header file is the wrong place. Header files should never contain "#include" files. Although there are some rare cases.

Notice my note for line 12. Not a problem right now, but is "int32 GetCurrentTry()" a member function or a stand alone function?

The rest of main looks like it works OK for now.

In the "FBullCowGame.h" file:
The header files are not needed here nor is it the best way to program this. See
http://www.lonecpluspluscoder.com/2012/09/22/i-dont-want-to-see-another-using-namespace-xxx-in-a-header-file-ever-again/ This should give you an idea of what not to do.

"using FText = std::string;" is OK to give "std::string" a shorter name.
"using int32 = int;" just gives "int" a new name. The "32" has no meaning or changes the size of an "int" its just a new name.

Defining the struct here is OK, but the class has problems.

In the class in the "public" section the ctor and detor is missing. These should be:
1
2
FBullCowGame();
~FBullCowGame();

In the "private" section:
Although I applaud you for initializeing your variables do not initialize your variables here. This should be done in the ctor. Now when you define these functions in the ".cpp" file the ctor is where you set the variables equal to zero. The string does not need any action because it is already empty.

The rest of the class looks OK for now and the functions that are used do work.

In the "FBullCowGame.cpp" file :

Being a ".cpp" file you should start with the needed include files. Most of the time order does not make any difference, but I like to start with the "#include" files followed with any header files like "FBullCowGame.h". I changed "time.h" to "ctime" and added the header files "limits", "chrono" and "thread" more on the last two shortly.

In the "SetSecretWord()" member function I added the middle line to allow the program to payse long enough to read the message before the program exits.
1
2
3
4
5
6
if (inFile.fail())
{
	std::cerr << "Error opening file" << std::endl;
	std::this_thread::sleep_for(std::chrono::seconds(2));  // Requires header files "chrono" and "thread"
	exit(1);
}

The rest of the functions look OK for now.

For the file "ToLowerCase.h":

First this contains a function and should be a ".cpp" file not a header file.

Line 5 should be in main with all the other prototypes not here. I generally take all these prototypes and put them in a header file I call "proto.hpp". I have been using the ".hpp" extension to go with the ".cpp" extension to keep with being a C++ file. The ".h" extension works just fine too.

In the function. I would define "i" as an unsigned int and remove the type cast in the for loop and it will work just fine. Since "word.length()" returns an unsigned int and "word[i]" can only use a positive number, this works better than all that type casting.

If you include the header file "<cctype>" you can shorten the if statement to just: word[i]=std::tolower(word[i]). if the character is a lower case letter then nothing happens otherwise an upper case letter is changed to lower case. The same could be used in the class member function.

Some suggestions that might be useful:

in the class member function "SetSecretWord()". Instead of reading the file each time you need a word, read the file once and put the words into an array or vector, I prefer the vector, this way you do not have to open and close the file each time you need a new word.The random number will become the subscript to the array or vector to retrive the word.Another thought is that a vector could hold 50, 100, 150 or anything in brtween or greater.Then you couse usevectorName.size()[/ code] to retrieve the number of elements in the vector, i.e., the number of entries.Then you could use[code]rand() % vectorName.size(); making this a more generic bit of code that could use any number of secert words. At the least I would put the vector in main and pass it to where it is needed, but I am leaning more twards putting the vector in the class where it would work better with class member function "SetSecretWord()".

I would put "srand()"at the beginning of main because it only needs to be done once. putting it in the function may not always generate a good random number. Since you are using "time" for the seed, if not enough time has passed between calls to "srand" you could end up with the same number twice.


This last point is really your choice, but consider this:
Your code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void FBullCowGame::SetMaxTries(FText Difficulty) {

	if (Difficulty == "easy") {
		MyMaxTries = 15;
	}
	else if (Difficulty == "medium") {
		MyMaxTries = 10;
	}
	else if (Difficulty == "hard") {
		MyMaxTries = 5;
	}
	else if (Difficulty == "brutal") {
		MyMaxTries = 3;
	}

	return;

};  // <--- The ";" is not needed here. 


My code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void FBullCowGame::SetMaxTries(FText Difficulty)
{

	if (Difficulty == "easy")
	{
		MyMaxTries = 15;
	}
	else if (Difficulty == "medium")
	{
		MyMaxTries = 10;
	}
	else if (Difficulty == "hard")
	{
		MyMaxTries = 5;
	}
	else if (Difficulty == "brutal")
	{
		MyMaxTries = 3;
	}

	return;
}

Although white space makes no difference to the compiler my use of the{}s along with proper indenting makes the code easier to read.ANd if you conside a set of{}s that are not on the same screen having them in the same column makes searching for the mate easier.Some day when you write some code that ends with several }s on different lines and have to find the one that is missing it helps when{}s all line up in the same column.

Two last thing for now.When you have an if, else if, while loop or for loop that only has one line of code the{}s are not really needed, but OK if you use them.

2. When I first started working with the program I notice the you put a; after the closing } of the functions. This is not needed at the end of a function and will cause a compile error.

You have a good start to your program. For now there are a few things that need fixed.

Sorry for being so long as I was trying to cover alot at one time.

Hope that helps,

Andy
Thank you for being so helpful, Andy! I just want to let you know I have been going over your suggestions but because of school I have not been able to comprehensively respond yet. I am getting around to it. However, it will probably take some time.
Topic archived. No new replies allowed.