Thread: RPG troubles

  1. #1
    Registered User
    Join Date
    Sep 2009
    Posts
    14

    RPG troubles

    Hey, it's me again. I've got a decent system going for my RPG now, but I have one major issue that ruins the whole thing. The problem comes from two little lines of code:
    Code:
                    cout << "Choose a class: Press '1' for Warrior, '2' for Ranger, or '3' for Mage.\n";
    		cin >> charClass;
    When choosing a class, if the user inputs a value that is greater than 2,147,483,647, the program will infinitely run:
    Code:
                            hitPoints = rand() % 50 + 50;
    			attack = rand() % 15 + 5;
    			defense = rand() % 15 + 5;
    			magic = rand() % 5 + 5;
    			accuracy = rand() % 5 + 10;
    
    			cout << "Name: " << charName << endl;
    			cout << "Class: Warrior\n";
    			cout << "HP: " << hitPoints << endl;
    			cout << "ATK: " << attack << endl;
    			cout << "DEF: " << defense << endl;
    			cout << "MGK: " << magic << endl;
    			cout << "ACC: " << accuracy << endl;
    			cout << "\nIs this information correct? (y/n) ";
    
    			cin >> recreate;
    
    			if (recreate != 'n' && recreate != 'y')
    			{
    				cout << "\nYou have entered an invalid character.";
    				recreate = 'n';
    			}
    Is there any way I can fix the overflow problem? Any help will be greatly appreciated as this problem keeps reoccurring in my programs.

  2. #2
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    When a stream like std::cin tries to read a number, and in doing so would cause overflow, it enters an error state. This is the same sort of error state entered when EOF is reached, for example, and you can check for it in the same way: by seeing if "std::cin >> number" evaluates to NULL or not. (The various operator>> overloads return the stream itself if no error occurs, so that you can chain together statements like "std::cin >> a >> b >> c"; and return a "NULL" object when an error occurs. This "NULL" object evaluates to false if you convert it to a bool, whereas the original stream, which is an instance of a stream class, evaluates to true.) Here's a quick example.
    Code:
    #include <iostream>
    
    int main() {
        int number;
        std::cout << "Enter a number: ";
        while(!(std::cin >> number)) {  // while std::cin evaluates to false (an error state)
            std::cout << "Invalid number, try again: ";
            std::cin.clear();  // clear the error state
        }
        
        std::cout << "You entered " << number << std::endl;
        
        return 0;
    }
    Input/output:
    Code:
    $ ./toolarge
    Enter a number: 555555555555555555555
    Invalid number, try again: -55555555555555555555
    Invalid number, try again: 123
    You entered 123
    $
    As you can see, that's an efficient way to check if a number entered was too large or too small (and would cause overflow or underflow). But it doesn't handle all cases: enter a letter, for example, and that program will enter an infinite loop.

    The trouble is this. When you enter something like "A\n", and ask std::cin to read an int, it will enter an error state because it can't parse a number from your input, and leave the input unchanged. Then you clear the error state, and ask it to parse another int. Again it fails to parse "A\n", and so on in the infinite loop.

    You can get around this in a few ways. Probably the best is to just clear the remaining input until the end of the line when an input operation fails. So if you want an int and the user enters "A\n", you clear the input buffer and then ask again. This is pretty easy to do:
    Code:
    #include <iostream>
    #include <limits>  // for std::numeric_limits
    #include <ios>  // for std::streamsize
    
    int main() {
        int number;
        std::cout << "Enter a number: ";
        while(!(std::cin >> number)) {
            std::cout << "Invalid number, try again: ";
            std::cin.clear();
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        }
        
        std::cout << "You entered " << number << std::endl;
        
        return 0;
    }
    I/O:
    Code:
    $ ./toolarge
    Enter a number: f
    Invalid number, try again: why me?
    Invalid number, try again: 44444444444444443333333333333
    Invalid number, try again: fine, 12.
    Invalid number, try again: 12
    You entered 12
    $
    (There's a bit more in-depth explanation elsewhere, e.g.: why do some programs work on some compliers while others dont? )

    That new line looks a little scary, but here's a breakdown. If you say something like
    Code:
    std::cin.ignore(5, '\n');
    then std::cin will ignore up to five characters or up until a newline (whichever happens first). The std::numeric_limits stuff just gets the absolute maximum value for that integer type, so that you ignore as much as you could possibly need to.

    Anyway, you can wrap something like the above code into a function like get_number(), and use it wherever you need to read numbers from the user.

    By the way: I've used std::cin here instead of just plain cin. You can use the latter, if you use something like "using namespace std". There's another whole story behind this, which you can ignore if you don't particularly care. Search the boards for "using namespace std" or the like for details.

    [Last note: codeform was used to colour that code.]
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  3. #3
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Make you sure you use _NOMINMAX or #undef them or that code will fail under Windows.

  4. #4
    Registered User
    Join Date
    Sep 2009
    Posts
    14
    Thank you very much! It worked perfectly!

    P.S. I already knew about ignore, but I didn't know it could be used that way!
    Last edited by MartinThatcher; 10-02-2009 at 09:53 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. here it is again folks...news on my rpg
    By agerealm in forum Game Programming
    Replies: 3
    Last Post: 05-31-2010, 06:58 PM
  2. problem
    By ExDHaos in forum C++ Programming
    Replies: 12
    Last Post: 05-22-2009, 04:50 AM
  3. (dev) C++ Ascii Rpg
    By kevinawad in forum A Brief History of Cprogramming.com
    Replies: 11
    Last Post: 08-10-2008, 11:10 AM
  4. Strange complier error in simple RPG
    By Marcos in forum Game Programming
    Replies: 4
    Last Post: 03-27-2003, 06:57 AM
  5. Char Variable Probelm
    By Krak in forum C++ Programming
    Replies: 1
    Last Post: 01-26-2003, 12:34 PM