Setting up a loop waiting for user input (C++) - c++

I'm trying to get a simple tic-tac-toe program to function in the console as a way to make sure I understand loops and arrays.
It compiles, and runs as expected, with the exception that if a user inputs something that isn't a number the program races through the first if statement infinitely without a chance to add a new input. I really can't see how to fix this.
I think the issue is that chosenSquare is an integer as it needs to be compared to values, but cin can take anything in. Expected behaviour would be to check if the input is an integer between 0 and 8 (the 9 spaces on the board), and if not return a message and repeat, waiting for a new input.
Is there a simple fix for this? I'm trying to avoid specialist packages and namespaces for now while I grok the basics. I've looked at similar problems but don't follow them.
Thanks.
Code snippet:
// Input loop
bool valid = false;
while (valid != true)
{
int chosenSquare = 0;
cout << "Player " << currentPlayer << ", enter a number between 1 and 9:" << endl;
cin >> chosenSquare;
chosenSquare--; // For array indexing
if ((chosenSquare < 0) || (chosenSquare > 8)) // <--- PROBLEM IS THIS LOOP
{
cout << "Invalid input. Try again." << endl;
continue;
}
else if ((board[chosenSquare] == currentPlayer) || (board[chosenSquare] == lastPlayer))
{
cout << "Square not availible. Try again." << endl;
continue;
}
else
{
board[chosenSquare] = currentPlayer;
valid = true;
break;
}
}

There are a couple of things culminating causing this.
The first is that when an alpha character is put into the console, the error bit is set, and 0 is written to the variable you're writing to:
The behavior you want to observe changed in 2011. Until then:
If extraction fails (e.g. if a letter was entered where a digit is expected), value is left unmodified and failbit is set.
But since C++11:
If extraction fails, zero is written to value and failbit is set. [...]
(From cppr.)
That means chosenSquare is 0 after the read, so chosenSquare-- makes it -1. -1, as you know, is less than 0, so the first if-statement is true.
As to why it stays this way infinitely, you need to clear the fail-bit.

So I changed the first loop after a bit of reading and experiementation:
if (cin.fail()) // <--- PROBLEM IS THIS LOOP
{
cout << "Invalid input. Try again." << endl;
cin.clear();
cin.ignore();
continue;
}
This works, but I can't figure out what it's actually doing.
Could anyone elaborate?

Related

Understanding why unwanted iterations of a cout statement occur [duplicate]

This question already has answers here:
Why does std::getline() skip input after a formatted extraction?
(5 answers)
Closed 3 years ago.
I am working with some C++ code. I have a while-loop set up to allow me to run through some code x-number of times. The while loop terminates once the user indicates that they do not want to run through the code again.
#include <iostream>
#include <string>
using namespace std;
char request;
int main() {
while (request != 'N')
{
string getCode = "";
while (getCode.length() != 3)
{
cout << "Please enter your container's region code (A or B followed by two-number identification)" << endl;
getline(cin, getCode);
if (getCode.length() != 3)
{
cout << "Error" << endl;
}
}
//clear the screen
system("cls");
//get letter
if (getCode.at(0) == 'A' || getCode.at(0) == 'B')
{
if ((getCode.at(1) >= '0' && getCode.at(1) <= '9') && (getCode.at(2) >= '0' && getCode.at(2) <= '9'))
{
if (getCode.at(0) == 'A')
{
cout << "The shipping charge is $25" << endl;
}
else if (getCode.at(0) == 'B')
{
cout << "The shipping charge is $30" << endl;
}
}
else
{
cout << "Error" << endl;
}
}
else
{
cout << "Error...Please enter the code as A or B followed by two numbers" << endl;
}
//Again?
cout << "Would you like to enter in another shipping identification number?" << endl;
cin >> request;
}
cout << "Thank you" << endl;
//End Program
system("pause");
return 0;
}
When I indicated that yes (entering 'Y' to the 'Would you like to enter in another shipping identification number question') I would like to run through the code again, the program outputs an unwanted 'Please enter your container's region code (A or B followed by two-number identification' and 'error' statement. Also please note, the code is inside 'int main()' and that I have properly formatted my 'include' statements.
Your question is to understand why this is happening, so here's the explanation. The code you wrote states thusly:
string getCode = "";
while (getCode.length() != 3)
{
cout << "Please enter your container's region code...
As you see, getCode is always initialized to an empty string. Immediately afterwards, if its length is not 3, this question is outputted.
You need to understand that your computer will always do exactly what you tell it to do. Your computer will not do what you want it to do, but only what you tell it to do. The above is what you told your computer to do, and your computer will always obediently follow its strict instructions, every time it runs this code. That's pretty much the explanation, and there's nothing more to understand.
This section of code is inside another loop, and you indicated that you do not wish the prompt to appear on second and subsequent iteration of the loop, only on the initial one.
However, there's nothing in your instructions to your computer, above, that specify this. You didn't tell your computer that this is what it should do, so why do you expect your computer to do that, entirely on its own? Every time your computer executes these statements shown above, this is exactly what will happen. Nothing more, nothing less. Whether it's the first time inside the outer while loop, or on each subsequent time the while loop iterates, it doesn't matter. The code always does exactly the same thing: getCode gets created and set to an empty string, and because its length is not 3, the inner while loop runs, prints the prompt and calls std::getline to read a line of text from std::cin. At the end of your while loop, if your instructions to your computer indicate that it should run the code in the while loop again, from the beginning (because that's what the while loop does), then the above instructions get executed.
If you now understand why your computer does this (because that's what you told it to do), then you should easily figure out what to tell your computer so it doesn't do this. If you want your computer to print the prompt only the first time it executes the while loop, then this is exactly what you need to tell your computer: set a flag before the while loop, print the prompt only if the flag is set (with all other existing logic remaining the same), and then clear this flag afterwards, so the next time the while loop runs, your computer will do exactly what you told it to do, and not print the prompt.
when I indicate 'Y' to the prompt 'Would you like to enter in another shipping identification number?', it outputs the following: 'Please enter your container's region code (A or B followed by two-number identification)' 'error' 'Please enter your container's region code (A or B followed by two-number identification' . When I input 'Y' I only want it to output 'Please enter your container's region code (A or B followed by two-number identification)'...I only want it to output once
Now that I understand your question, what's happening is an newline (\n) is getting added to the std::cin buffer at these lines right here:
//Again?
cout << "Would you like to enter in another shipping identification number?" << endl;
cin >> request;
This makes even more sense especially when combined with your other comment:
Before int main() there should be a 'char request;
So request a single char. That means when you type something like this:
Y
The newline is added to std::cin as well. That can't be stored in a single char, and the >> may not remove it either. That means it's just sitting here.
What this does is when you get to your if statement at the beginning of the loop again:
while (request != 'N')
{
string getCode = "";
while (getCode.length() != 3)
{
cout << "Please enter your container's region code (A or B followed by two-number identification)" << endl;
getline(cin, getCode);
if (getCode.length() != 3)
{
cout << "Error" << endl;
}
}
getline() sees the newline you added previously and instantly returns an empty string. Empty strings have a length of 0, so it fails your if statement, which prints the error.
The solution is simple, just tell std::cin to ignore the newline:
//Again?
cout << "Would you like to enter in another shipping identification number?" << endl;
cin >> request;
cin.ignore(1, '\n');

C++ special-value-type loop has to exit when a negative value is entered without using break statement

I just completed a program that has to quit when a negative value is entered as input. Everything is working good except for only one issue, it quits the program after the second time a negative value is entered. After some research I noticed the use of break, however the samples I have to guide the assignment use only if and else statement.
#include <iostream>
using namespace std;
int main()
// insert code here...
// create a variable named "pounds" that can be used to store an integer.
// wait for the user to type in a value and put that value into the variable ounces
{
int poundsTotal;
int ouncesTotal;
while (poundsTotal >= 0)
{
cout << "Enter pounds or a negative number to quit: ";
cin >> poundsTotal;
ouncesTotal = poundsTotal * 16;
cout << poundsTotal << " pouds is " << ouncesTotal << " ounces." <<endl;
cout << " Enter pounds or a negative number to quit ";
cin >> poundsTotal;
poundsTotal++;
}
if (poundsTotal == 0){
cout <<"you enter a zero value" <<"Try onemore time";
}
else {
cout << "you chose to quit the program" <<poundsTotal;
}
}
The condition of a while loop is evaluated after the body has been executed. Then it is determined whether the body will be run again. Change your code and add an if statement inside the loop.
if(poundsTotal < 0) break;
And yes, a break statement is useful in a loop. Otherwise you can't stop the loop before your test condition is evaluated to false.
In your case, I find using a break would be a simple option.
When the program first reaches while (poundsTotal >= 0), poundsTotal has no defined value. This puts you at the mercy of the gods as to whether the program will work as expected or not, and Gods are notoriously unreliable. For more information, look up the term Undefined Behaviour.
The solution to this is ask the user for poundsTotal before the loop and once more at the end of the loop.
If you want to get really posh and do this without repeating code (and stay DRY) , make a function that gets poundsTotal from the user and call this function in the while loop's condition. For example,
while ((poundsTotal = getPoundsTotal()) >= 0)
{
...
}

While loop carries on forever (C++)

I'm still quite new to c++ and just experimenting with the language.
I have recently created a 'tictactoe' game.
I have created this simple looking function to get the users board position (from 1 to 9). It seems to work fine but I found a weird bug so to call it;
Whenever I enter a 12 digit number or higher the loop just carries on forever printing 'Invalid position!' and on to the next line 'Choose your position 1-9: '.
I am using visual studio to write code. Is it something with the code or is it perfectly fine? I'm eager to find out to learn from it.
Here's the function:
int getUserPosition()
{
int position;
while (true)
{
cout << " Choose your position 1-9: " << endl;
cin >> position;
if (position < 1 or position > 9)
{
cout << "Invalid position!" << endl;
}
else if (board[position - 1] == 'X')
{
cout << "This position has been taken!" << endl;
}
else
{
return position;
break;
}
}
}
I finally understand the actual behavior here. This is not invoking undefined behavior but rather defined behavior you don't want.
cin >> position;
This tried to read your 12 digit number, which didn't fit into an int, so the read failed. Because it failed on a format error, the 12 digit number remained in the buffer, so the next pass around the loop tried to read it again.
Always use cin::getline() when you intend to read keyboard input. The error/cleanup logic in cin is not designed to resync keyboard input, but rather to resync input piped from a generator that can't read your error messages. In the future, when you try to use cin with a pipe, the best solution is to check cin::fail() and bail the program if it ever gets set.

Program terminates instead of continuing integer input validation loop

I'm asking the user to select a choice between items in a backpack by entering an integer corresponding to the item. But despite my current integer input validation code, the whole program terminates instead of redisplaying the choices and asking the user to enter a choice again. Is there anything problematic in the code below that may be causing this?
int num;
do{
std::cout << "Choose item to use." << std::endl;
for(int i = 0; i < backpack->size(); i++){
std::cout << i+1 << ". " << backpack->at(i) << std::endl;
};
std::cin >> num;
if(!std::cin.fail()){
if(num < 0 || num > (backpack->size())){
std::cout << "Plese enter an integer in range." <<std::endl;
}else{
break;
};
}else{
std::cin.clear();
std::cin.ignore(80, '\n');
std::cout << "Invalid input. Please enter an integer." << std::endl;
};
}while(std::cin.fail() || (num<0 || num > (backpack->size())));
You have to assign an initial value to num. If you don't, it will contain a garbage value, which is possibly less than 0 or greater than backpack->size()-1, which will make the condition true.
int num = 0;
Edit: Scraped last answer
The following clear the cin.fail() flag:
std::cin.clear();
std::cin.ignore(80,'\n');
So upon reaching the loop condition, cin.fail() returns false and the unitialized num most likely contains 0. Thus, your continuation condition does not pass and the loop returns.
As suggested by #Eddge, you should initialize num to an invalid value in regards to your condition, like -1.
After looking over the code a few times, I don't immediately see a problem with the logic, though I could be wrong. But from what I understand is the intention of the code, the condition in the while statement is redundant and unnecessary. You are already checking those same conditions in the if statements and breaking out of the loop when needed, so try using while(true) as your while statement and see if it fixes your problem.
I just found that changing "std::cin.fail()" to "!(std::cin>>num)" in the while condition did the trick. Why do you think that is? By the way, thanks for all of your input!

while statement is unable to read the correct char input

hi i am new to c++ and i dont understand why my while statement doesnt work now. it was working when i tried to do it earlier.
Full code is available at: http://pastebin.com/aeH5fKwh
basically here is the while loop (i excluded all the unnecessary parts, i left the inside of the while loop intact for viewing purpose)
int main()
{
unsigned int seed;
char input;
bool done;
for (int round = 0; round < 5; round++)
{
done = false;
cout << "\nEnter seed: ";
cin >> seed;
cout << "\nRound 1" << endl;
while(!done)
{
cout << "\nDo you wish to draw another card [y][n]: ";
cin >> input;
while (input != 'y' && input != 'n')
{
cout << "Invalid input! Please enter [y][n]!" << endl;
cin >> input;
}
if (input == 'y')
{
dealExtra(playerHand, deck, gameInfo);
cout << "Your cards are ";
printHand(playerHand, gameInfo.playerCardCount);
}
else
done = true;
}
}
cout << endl;
return 0;
}
when i try entering anything that is not 'y', 'n', it will tell me that my input is invalid. But when i try to enter 'y' or 'n', it kinda just ignored it and nothing else happened.. i checked with cout statement and found that it manage to get into the if (input == 'y') statement, but it doesnt seem like it is doing anything else. Everything was fine till 20 minutes ago and i really couldnt figure out whats wrong.
Edit: i ran another test using "cout << '[' << input << ']' << endl;".. it seems like the program is able to get my first input, but then it just hangs there afterwards.. what i get is something like:
Do you wish to draw another card [y][n]: y
[y]
y
y
y
y
I compiled this on linux terminal using g++
if extra codes is needed, i'll edit and add them.. thanks!
When you ask for input from the console, most implementations buffer characters until a newline key is pressed.
After the newline is received, the first character of the buffer is returned. The newline still remains in the buffer as well as any extra characters.
In your case, the second cin >> input statement will read the newline from the buffer.
As an experiment, try entering "frog" and single step through your program. This should illustrate the case of residual characters in the buffer.
Try cin.ignore(1000, '\n') after the first cin >> input. The ignore method will eat up any remaining characters in the buffer until the newline is found.
Make below statements inactive
dealExtra(playerHand, deck, gameInfo);
printHand(playerHand, gameInfo.playerCardCount);
and check if it works, then try making one of the above statements active alternately to find out in which function the flow is getting lost. And so on.
If you feel lazy to run a debugger, and plan to use cout<< statements to find a hanging call, you should flush you cout:
( cout << "I am here and going to hang" ).flush() ;
Otherwise you can't see recent output just because it's still in the output buffer. Try this and you well might see what call hangs your program.
You have an infinite loop inside checkComputerHand:
bool done = false;
while(!done)
{
if(sum == 11 && checkAce == true)
{
computerHand[aceLocation].value = 11;
done = true;
}
if(sum > 11 && checkAce == true)
{
computerHand[aceLocation].value = 1;
done = true;
}
// What if checkAce wasn't true? Infinite loop!
}
Also, the first two lines of newGame do not make any sense:
void newGame(Card playerHand[], Card computerHand[], Statistics &gameInfo)
{
playerHand = '\0';
computerHand = '\0';
// ...
}
Array parameters are silently rewritten by the compiler as pointer parameters. So all you're doing is assigning the null pointer to those local pointers. Probably not what you intended...