Incorrect output behavior when function calls itself

I am making a program that gives the user a certain number of tries to guess a hidden word. The compiler outputs the expected behavior except when it comes to the askToPlayAgain() function in "main.cpp".

After the user has exceeded their number of guesses, the program asks the user if they want to play again. If they select "yes", the game, playGame(), resets. If they select "no", the program quits. When the user inputs any string other than "yes" or "no", the program asks the user to please select either "yes" or "no" and then gives them an opportunity to do so again. For all three of these options, the program behaves as expected. However, if the user selects an input other than "yes" or "no" and THEN selects "no" after being prompted to input a valid string, the program does not quit as expected. Instead, the game resets.

Does anyone know why this is happening? Does it have something to do with askToPlayAgain() calling itself? Something to do with how the subroutine is loaded in to memory and the initial subroutine being denied the requirement of returning a boolean?

Note: I compiled these files in a Unix terminal.

Here are my files:

main.cpp
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
#include <iostream>
#include <string>
#include "BullCowGame.h"

using namespace std;

void printIntro();
string getGuess();
void playGame();
bool askToPlayAgain();

int main()
{
	printIntro();

	playGame();
	
	return 0;
}


void printIntro()
{
	constexpr int WORD_LENGTH = 4;

	cout << "Welcome to Bulls and Cows, a fun word game.\n" << endl;
	cout << "Can you guess the " << WORD_LENGTH << " letter isogram I am thinking of?\n" << endl;
}

void playGame()
{
	constexpr int NUM_OF_TURNS = 3;
	string answer = "milk";

	cout << endl;

	for (int i = 0; i < NUM_OF_TURNS; i++)
	{
		BullCowGame bullCowGame;

		string guess = getGuess(); 

		bullCowGame.setNumOfCows(guess, answer);
		bullCowGame.setNumOfBulls(guess, answer);

		cout << "Number of Bulls: " << bullCowGame.getNumOfBulls() << 
"  Number of Cows: " << bullCowGame.getNumOfCows() << endl;
		cout << endl;

	}

	bool playAgain = askToPlayAgain();

	//if user selects to play again, recurse and play the game again
	if (playAgain == true)
	{
		playGame();
	}

}

string getGuess()
{
	string guess = "";

	cout << "Enter your guess: ";

	getline(cin, guess);
	cout << endl;

	return guess; 
}

bool askToPlayAgain()
{
	cout << "Do you want to play again?";
	string response = "";
	getline(cin, response);

	if (response == "yes" || response == "Yes")
	{
		return true; 
	}
	else if (response == "no" ||  response == "No")
	{
		return false; 
	}
	else
	{
		//if incorrect input is given, ask for answer again
		cout << "Please input either 'yes' or 'no'\n" << endl;
		askToPlayAgain();
	}
	
}



BullCowGame.h
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
#ifndef BULL_COW_GAME_H
#define BULL_COW_GAME_H

#include <string>

using namespace std; 

class BullCowGame
{
public:
	BullCowGame() = default;
	BullCowGame(int initialBull, int initialCow);
	
	void setNumOfCows(string guess, string answer);
	int getNumOfCows();
	void setNumOfBulls(string guess, string answer);
	int getNumOfBulls();


		
private:
	int storedBulls = 0;
	int storedCows = 0;
	
	
};

#endif	 


BullCowGame.cpp
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
#include <string>
#include <iostream>
#include <algorithm>
#include <vector>
#include "BullCowGame.h"

using namespace std;

BullCowGame::BullCowGame(int initialBull = 0, int initialCow = 0)
	:storedBulls{ initialBull }, storedCows{ initialCow } {}

void BullCowGame::setNumOfCows(string guess, string answer)
{
	/*examines each character index location in both strings
	to see if there are two characters that are common
	in both words, albeit located at different index locations*/
	for (unsigned int i = 0; i < guess.size(); i++)
	{
		if (guess[i] != answer[i])
		{
			for (unsigned int j = 0; j < guess.size(); j++)
			{
				if (guess[i] == answer[j])
				{
					storedCows++;
				}

			}
		}
	}

}

int BullCowGame::getNumOfCows()
{
	return storedCows;
}

void BullCowGame::setNumOfBulls(string guess, string answer)
{
	for (unsigned int i = 0; i < answer.size(); i++)
	{
		if (guess[i] == answer[i])
		{
			storedBulls++;
		}
	}
}

int BullCowGame::getNumOfBulls()
{
	return storedBulls;
}
Last edited on
You forgot to return anything if the code reaches the else part of the askToPlayAgain function.

The compiler should be able to warn you about errors like this. If you're using GCC you should at least compile with the -Wall compiler flag. https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
Last edited on
I didn't forget. I just thought that if I returned something in the else condition, then the subroutine would end and wouldn't repeat.
Isn't that what would happen?
You always need to make sure to return something from a function that has a non-void return type.

In this case it looks like you want to return the return value of askToPlayAgain().

1
2
3
4
5
6
else
{
	//if incorrect input is given, ask for answer again
	cout << "Please input either 'yes' or 'no'\n" << endl;
	return askToPlayAgain();
}


The above code should work (I haven't tested it), but it might not be the "best" solution. In theory you could run out of stack space if the user inputs too many invalid strings. The "solution" would be to implement this in terms of a loop instead of recursion.
Last edited on
Okay. I understand. I was going to use a while loop but having the function recurse just looked cleaner. But the stack space not being freed up would definitely be a problem.
The "solution" would be to implement this in terms of a loop instead of recursion.


Recursion is usually going to be harder to read/write/debug/ modify/ maintain / etc. I avoid it if possible. There are a handful of algorithms where the recursion is the better choice, but those are uncommon in general and rare if not dealing with some specific data structures or sub disciplines.
Topic archived. No new replies allowed.