Thread: Validating an input to be a number for guessing game

  1. #1
    Registered User
    Join Date
    May 2007
    Posts
    77

    Validating an input to be a number for guessing game

    Finally, an update to the number guessing game. I've come back to it after all this time, and had to start from scratch (because of upgrading to Vista, which I'm not sure if I regret or not). However, I think I got a better result anyway, as this time, I not only managed the loop for replay, I put in a minor menu, added difficulty settings, and put in a number validation sequence to make sure the number input is in the given range. Here's what I've got:
    Code:
    #include <cstdlib>
    #include <ctime>
    #include <iostream>
    
    using namespace std;
    //returns value between 0 and RAND_MAX
    //rand();
    //srand (time(0));
    
    int x,w,random,guess,tries,option;
    char play;
    
    int game (){
        do {
            cout << "\nEnter a difficulty level: \n1. Easy\n2. Medium\n3. Hard\n4. Very Hard\n5. Insane!\n\n";//States possible difficulties
            cin >> w;
            if (w==1) x = 50;
            if (w==2) x = 100;
            if (w==3) x = 500;
            if (w==4) x = 1000;
            if (w==5) x = 10000;//Determines upper limit for random generator
            srand(time(0));//Random seed
            random = rand() &#37; x + 1;//Random number generation
            cout << "\nNumber chosen between 1 and " << x << ".\n";
            cout << "Enter your guess.\n";
            cin >> guess;
            tries = 1;//Sets tries to one after first guess before going into the game loop
            while (guess != random){
                if ((guess > x) && (guess < 1)) {
                    cout << "\nIllegal guess.  Your guess must be between 1 and " << x << ".\n";//Keeps player from inputting a number that is not available
                    --tries;//Sets counter back to keep track of legal guesses only
                }
                else if (guess > random)
                    cout << "\nYou guessed too high.\n";
                else
                    cout << "\nYou guessed too low.\n";//The actual process of guessing the number
        	    cout << "Enter your guess.\n";
        	    cin >> guess;
        	    ++tries;
            }
        	cout << "\nYou Guessed It!\n";
        	cout << "You took " << tries << " guesses.\n";//Displays number of legal guesses required to win current round
        	cout << "Would you like to play again (Y/N)?\n";//Asks if player wants to play again, feeds into do...while loop parameter
        	cin >> play;
        } while (play == 'y');
        return 0;
    }
    
    int menu(){
        cout << "Main Menu: \n1. New Game \n2. Quit\n";
        cin >> option;
        if (option == 1)
            game();
        else
            return 0;
    }
    
    int main(){
    
        cout << "Welcome to Guess It, the number guessing game.\n";
    	menu();
    	return 0;
    }
    Now, right now I have two problems.
    1) I can't find any reasonable resource for validating the input as a number, instead of a letter or punctuation. I've tried something like this:
    Code:
    cin.ignore(numeric_limits<int>::max(), '\n');
            while (guess != random){
                if (!cin || cin.gcount() != 1){
                    cout << "\nIllegal guess.  Your guess must be a number.\n";
    As the first if... statement in the game loop, but although it works and it displays the "Illegal guess" line and the "enter your guess" line later, it does it on an infinite loop, and I can't get it to do it just once and accept another input.
    2) I don't know how to make it go back to the menu after the player says "no" to the play again question.

    Any and all help will be greatly appreciated.

    Edit: OK, I got the menu to work. All I needed to do was add a parameter (I chose on) to tell whether or not the program was supposed to still be running. I then put a do...while loop in main() to keep going back to the menu as long as on == 1. I then put an extra line in menu() to say if the player doesn't want to start a new game, but instead wants to quit, on is set to 0. So, the changes look like so:
    Code:
    int menu(){
        cout << "Main Menu: \n1. New Game \n2. Quit\n";
        cin >> option;
        if (option == 1)
            game();
        else
            on = 0;
        return 0;
    }
    
    int main(){
    
        cout << "Welcome to Guess It, the number guessing game.\n";
    	do {
                menu();
            } while (on == 1);
    	return 0;
    }
    Still need help on the letter elimination problem...
    Last edited by Molokai; 11-02-2007 at 12:18 PM. Reason: Problem 2 solved

  2. #2
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    This is how I normally check input to see if its a number:
    Code:
    while (!(cin >> guess))
    {
      cin.clear(); // clear fail state
      cin.ignore(numeric_limits<int>::max(), '\n'); // ignore bad input
      // prompt again here.
    }
    You can add to the while control more checks. For example, to error on 123abc add a check for cin.get() != '\n', and to check for numbers in your range add those as well.
    Code:
    while (!(cin >> guess) || cin.get() != '\n' || (guess > x) || (guess < 1))
    Note that I used logical OR, not AND, since you want to enter the loop if any of the reasons for error are true (rather than only entering the loop if all of them are).

    >> 2) I don't know how to make it go back to the menu after the player says "no" to the play again question.
    You need a loop around the menu code.

  3. #3
    Registered User
    Join Date
    May 2007
    Posts
    77
    That's what I did. And, just to clarify, the only reason I wanted a menu was because I wanted to start using a high scores page, and I have no idea how to do that at all. Can't find anything online about doing that. Any help there would be useful, too.

    By the way, Daved, that worked almost perfectly, except that now it doesn't recognize any odd number...

  4. #4
    Registered User
    Join Date
    May 2007
    Posts
    77

    Unhappy

    Never mind, it's not that it doesn't recognize odd numbers, it just doesn't recognize every other input. Say I put in 25 for my first guess. it tells me whether I'm high or low. Then I enter my next guess, and whether it's 15 or 36, it just stays blank until I put another guess in.
    And, on top of that, it doesn't add to the number of tries for each blank response. It only adds to it when it recognizes an input and gives the try again output. And, if you guess the right number on what should be an unrecognized guess input, it recognizes it and you win the game...
    Last edited by Molokai; 11-02-2007 at 12:36 PM.

  5. #5
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    Can you show what you tried?

    Note that I moved the input into the while control, so you should remove the cin >> guess line in your current code.

  6. #6
    Registered User
    Join Date
    May 2007
    Posts
    77
    Oh, I didn't realize that that is what that did.
    I've been fidgeting with the code, and came up with this:
    Code:
    int game (){
        do {
            cout << "\nEnter a difficulty level: \n1. Easy\n2. Medium\n3. Hard\n4. Very Hard\n5. Insane!\n\n";//States possible difficulties
            cin >> w;
            if (w==1) x = 50;
            if (w==2) x = 100;
            if (w==3) x = 500;
            if (w==4) x = 1000;
            if (w==5) x = 10000;//Determines upper limit for random generator
            srand(time(0));//Random seed
            random = rand() % x + 1;//Random number generation
            cout << "\nNumber chosen between 1 and " << x << ".\n";
            cout << "Enter your guess.\n";
            tries = 0;
            while (guess != random){
                if (!(cin >> guess)) {
                    cin.clear(); // clear fail state
                    cin.ignore(numeric_limits<int>::max(), '\n'); // ignore bad input
                    cout << "\nIllegal guess.  Your guess must be a number.\n";
                    --tries;
                }
                else if ((guess > x) || (guess < 1)) {
                    cout << "\nIllegal guess.  Your guess must be between 1 and " << x << ".\n";//Keeps player from inputting a number that is not available
                    --tries;//Sets counter back to keep track of legal guesses only
                }
            	else if (guess > random)
                    cout << "\nYou guessed too high.\n";
            	else
                    cout << "\nYou guessed too low.\n";//The actual process of guessing the number
        		cout << "Enter your guess.\n";
        		++tries;
            }
        	cout << "\nYou Guessed It!\n";
        	cout << "You took " << tries << " guesses.\n";//Displays number of legal guesses required to win the round
        	cout << "Would you like to play again (Y/N)?\n";//Asks if player wants to play again, feeds into do...while loop parameter
        	cin >> play;
        } while (play == 'y');
        return 0;
    }
    You'll notice I had to define tries as 0 instead of 1, since no guesses had yet been made, and I had to declare it within the game, otherwise it just kept adding to the previous successful total guesses. I also remembered to remove the "cin >> guess;" from within the loop itself, to make sure there was only one.

    I also had to made the while into an if, and put the rest int else...if's or else's. The reason for this is because if it was a while, it just kept subtracting from the number of tries, without the "++tries;" at the end of the loop balancing it out, resulting in skewed tries numbers.

    Now, while all of this now works perfectly, there is still one problem: If someone puts in a decimal, such as 1.5, it registers as a legal number at first, and gives the ++tries; then, when it gets back to the beginning of the loop, it says the same guess was illegal, and then does both the --tries; and the ++tries; resulting in an added try, despite the fact it should be illegal. What can I do about that? Maybe a parameter I could put in the while loop to make it look for decimals in the middle of the numbers? Or to look for floats?

  7. #7
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    Code:
            	else if (guess > random)
                    cout << "\nYou guessed too high.\n";
            	else
                    cout << "\nYou guessed too low.\n";//The actual process of guessing the number
        		cout << "Enter your guess.\n";
        		++tries;
    
    Despite your excellent indentation, the two red lines are NOT part of the final else.

    ALWAYS use braces to make your intention explicit, then you'll never fall into the trap of changing one statement into several statements, and the code breaking because you forgot the now compulsory braces.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  8. #8
    Registered User
    Join Date
    May 2007
    Posts
    77
    Those last two lines aren't meant to be part of the last else. They're meant to do exactly what I programmed them to do, which is to execute no matter which of the if's was executed. And it's supposed to look like this:
    Code:
            else if (guess > random)
                    cout << "\nYou guessed too high.\n";
            else
                    cout << "\nYou guessed too low.\n";//The actual process of guessing the number
        	cout << "Enter your guess.\n";
        	++tries;
    Last edited by Molokai; 11-02-2007 at 01:06 PM.

  9. #9
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    Putting it into the if instead of a while the way you did is fine (and you did a good job of taking the example and adapting it to your purpose).

    Now, you have two similar issues. What if the user types 1.5, and what if the user types 123abc. Either way, the code will read in a number, then stop at the '.' or the 'a' (or any non-numeric character), which will break it the next time through.

    You have two choices to handle this. Do you want to allow the guess and ignore the rest of the line, or do you want to give an error message. For example, if the user inputs 1.5, you could read in the 1 and ignore the rest, or you could just make the whole thing an error. I prefer the second option, but it's up to you.

    For the first option, just always add the ignore() line of code no matter what (take it out of the first if and move it to the bottom outside the if/else if/else statements. That way it will always ignore leftover characters.

    The second option is to add a new else if (probably immediately after the if) that checks for cin.get() != '\n'. This will be true if there is extra data in the input, and you can provide an error message and add the ignore() line there to ignore the bad data.

  10. #10
    Registered User
    Join Date
    May 2007
    Posts
    77

    Talking

    Daved, that was exactly what I needed. It works absolutely perfect now! Here's the code:
    Code:
    #include <cstdlib>
    #include <ctime>
    #include <iostream>
    #include <ctype.h>
    
    using namespace std;
    //returns value between 0 and RAND_MAX
    //rand();
    //srand (time(0));
    
    int x,w,random,guess,option,number,tries;
    int on = 1;
    char play;
    
    int game (){
        do {
            cout << "\nEnter a difficulty level: \n1. Easy\n2. Medium\n3. Hard\n4. Very Hard\n5. Insane!\n\n";//States possible difficulties
            cin >> w;
            if (w==1) x = 50;
            if (w==2) x = 100;
            if (w==3) x = 500;
            if (w==4) x = 1000;
            if (w==5) x = 10000;//Determines upper limit for random generator
            srand(time(0));//Random seed
            random = rand() % x + 1;//Random number generation
            cout << "\nNumber chosen between 1 and " << x << ".\n";
            cout << "Enter your guess.\n";
            tries = 0;
            while (guess != random){
                if (!(cin >> guess) || (cin.get() != '\n')) {
                    cin.clear(); // clear fail state
                    cin.ignore(numeric_limits<int>::max(), '\n'); // ignore bad input
                    cout << "\nIllegal guess.  Your guess must be a whole number.\n";
                    --tries;
                }
                else if ((guess > x) || (guess < 1)) {
                    cout << "\nIllegal guess.  Your guess must be between 1 and " << x << ".\n";//Keeps player from inputting a number that is not available
                    --tries;//Sets counter back to keep track of legal guesses only
                }
            	else if (guess > random)
                    cout << "\nYou guessed too high.\n";
            	else
                    cout << "\nYou guessed too low.\n";//The actual process of guessing the number
        		cout << "Enter your guess.\n";
        		++tries;
            }
        	cout << "\nYou Guessed It!\n";
        	cout << "You took " << tries << " guesses.\n";//Displays number of legal guesses required to win the round
        	cout << "Would you like to play again (Y/N)?\n";//Asks if player wants to play again, feeds into do...while loop parameter
        	cin >> play;
        } while ((play == 'y') || (play == 'Y'));
        return 0;
    }
    
    int menu(){
        cout << "Main Menu: \n1. New Game \n2. Quit\n";
        cin >> option;
        if (option == 1)
            game();
        else
            on = 0;
        return 0;
    }
    
    int main(){
    
        cout << "Welcome to Guess It, the number guessing game.\n";
    	do {
            menu();
        } while (on == 1);
    	return 0;
    }
    Anyone can feel free to compile it and run it if they like. However, now I'm faced with a completely new problem: the High Scores. Anyone?

  11. #11
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    Do you want them saved over time, or just while the program is running and then have them reset the next time you start?

    If you want them saved, you'll need to save them in a file, which means using ofstream and ifstream to read and write the scores. If you only want the top score or two, you'll need variables to remember them. If you want top five or ten or more, then you'll also want some sort of container like vector to remember the top scores.

    You'll also need to read in strings to get the player's name.

    So if you know about strings, fstreams or vectors already, then you can get started. Otherwise I'd take it slow. Perhaps start by keeping a variable that holds the high score for the current session and updating it as appropriate. Then maybe add a string variable to hold the user's name that has the high score. Then perhaps learn vector and how to hold multiple high scores. And then maybe save them out to a file. Each step along the way you'll have a new feature that enhances the game, but you'll be taking it slowly and not trying too much at a time.

  12. #12
    Registered User
    Join Date
    May 2007
    Posts
    77
    Alright, so I suppose this isn't really a surprise, but when I was finally able to put this together on Visual C++, it came up with two errors where it compiled perfectly fine on Dev-C++. I also got the normal warning about possible data loss in converting from time_t (for seeding the random number). Anyway, here are the errors:
    Code:
    1>.\GuessIt.cpp(33) : error C2065: 'numeric_limits' : undeclared identifier
    1>.\GuessIt.cpp(33) : error C2062: type 'int' unexpected
    Here is the line it is referring to:
    Code:
    cin.ignore(numeric_limits<int>::max(), '\n'); // ignore bad input
    And I have the following includes:
    Code:
    #include <cstdlib>
    #include <ctime>
    #include <iostream>
    #include <ctype.h>
    Anyone know how to fix this? Are there additional include paths I need to put into VS that are already in Dev-C++?

  13. #13
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    numeric_limits is found in <limits>.

    Sometimes when you forget to #include the right header, some compiler's will still compile the code. You shouldn't rely on this behavior, though, so when you use something from a library you should look up which header it is in and make sure you include that.

  14. #14
    Registered User
    Join Date
    May 2007
    Posts
    77
    Thanks, that did it. Guess I should either bookmark an include website or get a reference...

    So, now I'm working on the basic high score, not saved. I'm trying to make it in an array, which I successfully declared, and I'm trying to have the array address of the high score determined by the difficulty level. So, here's what I tried (the red colored lines are the ones key to the high score):
    Code:
    #include <stdafx.h>
    #include <cstdlib>
    #include <ctime>
    #include <iostream>
    #include <limits>
    
    using namespace std;
    //returns value between 0 and RAND_MAX
    //rand();
    //srand (time(0));
    
    int x,w,t,random,guess,option,number,tries;
    int on = 1;
    char play;
    int highscore[6];
    
    int game (){
        do {
            cout << "\nEnter a difficulty level: \n1. Easy\n2. Medium\n3. Hard\n4. Very Hard\n5. Insane!\n\n";//States possible difficulties
            cin >> w;
            if (w==1) x = 50;
            if (w==2) x = 100;
            if (w==3) x = 500;
            if (w==4) x = 1000;
            if (w==5) x = 10000;//Determines upper limit for random generator
            srand(time(0));//Random seed
            random = rand() % x + 1;//Random number generation
    	if (highscore[w] == 0)
                highscore[w] = x;
            if ((highscore[w] != 0) && (highscore[w] < x))
                cout << "\nCurrent high score: " << highscore[w];
            cout << "\nNumber chosen between 1 and " << x << ".\n";
            cout << "Enter your guess.\n";
            tries = 0;
            while (guess != random){
                if (!(cin >> guess) || (cin.get() != '\n')) {
                    cin.clear(); // clear fail state
                    cin.ignore(numeric_limits<int>::max(), '\n'); // ignore bad input
                    cout << "\nIllegal guess.  Your guess must be a whole number.\n";
                    --tries;
                }
                else if ((guess > x) || (guess < 1)) {
                    cout << "\nIllegal guess.  Your guess must be between 1 and " << x << ".\n";//Keeps player from inputting a number that is not available
                    --tries;//Sets counter back to keep track of legal guesses only
                }
            	else if (guess > random)
                    cout << "\nYou guessed too high.\n";
            	else
                    cout << "\nYou guessed too low.\n";//The actual process of guessing the number
        		cout << "Enter your guess.\n";
        		++tries;
            }
        	cout << "\nYou Guessed It!\n";
        	cout << "You took " << tries << " guesses.\n";//Displays number of legal guesses required to win the round
    	if (tries < highscore[w]){
    		highscore[w] = tries;
    		cout << "\nNew High Score!  New score: " << highscore[w] <<"!\n";
    	}
        	cout << "Would you like to play again (Y/N)?\n";//Asks if player wants to play again, feeds into do...while loop parameter
        	cin >> play;
        } while ((play == 'y') || (play == 'Y'));
        return 0;
    }
    Now, how can I apply this to displaying them, such as in a menu option? I don't want them to be close, but I don't want to have to put in a few dozen spaces to have them separated. I think I remember reading somewhere that there is a possible I/O stream command to make an output left-aligned or right-aligned. Would that work?

  15. #15
    Registered User
    Join Date
    May 2007
    Posts
    77
    Oh, and does anyone know a good way to set the console prompt size in the code of the program right from the start?

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. About aes
    By gumit in forum C Programming
    Replies: 13
    Last Post: 10-24-2006, 03:42 PM
  2. Logical errors with seach function
    By Taka in forum C Programming
    Replies: 4
    Last Post: 09-18-2006, 05:20 AM
  3. Game Engine Link Prob
    By swgh in forum Game Programming
    Replies: 2
    Last Post: 01-26-2006, 12:14 AM
  4. Guess the number game
    By Ninestar in forum C Programming
    Replies: 12
    Last Post: 12-08-2005, 11:30 AM
  5. Array of boolean
    By DMaxJ in forum C++ Programming
    Replies: 11
    Last Post: 10-25-2001, 11:45 PM