0

So I am trying to build a Rock, Paper, Scissors, Lizard, Spock Game. That basicall prints this:

------------------------------
Enter Move
1 - Rock
2 - Paper
3 - Scissors
4 - Lizard
5 - Spock       : 1 // user inputted value
Rock crushes lizard. You win!
------------------------------
Enter Move
1 Rock
2 Paper
3 Scissors
4 Lizard
5 Spock         : 40 // user inputted value
Error, invalid input.
------------------------------
Enter Move
1 - Rock
2 - Paper
3 - Scissors
4 - Lizard
5 - Spock       : P // user inputted value
Error, invalid input.
----------------------------

and before I start building statements to define the winner of a game, I am trying to ensure that the game does not accept any invalid user inputs. So I got it to accept the proper decimal value, such as 1, and it rejects a number out of range like 40... but when I enter in a character as above say "P" it goes into an infinite loop, and I do not know how to stop this. here is my code:

    #include <cstdlib>
    #include <iostream>
    #include <string>
    #include "rpslsType.h"

    using namespace std;

    int main() {
        // stores user input
        int u;

        // outputs options
        cout << "---------------------------" << endl;
        cout << "Enter Move" << endl;
        cout << "1 - Rock" << endl;
        cout << "2 - Paper" << endl;
        cout << "3 - Scissors" << endl;
        cout << "4 - Lizard" << endl;
        cout << "5 - Spock" << "        : "; // inputs here

        //prompts user input, stores value
        cin >> u;

        // and when input is not in range of 1 t0 5, print error
        while (!((u <= 5) && (u >= 1))) {
            printf("Error, invalid input.\n"); // error message

            // re-enter user input
            // outputs options
            cout << "---------------------------" << endl;
            cout << "Enter Move" << endl;
            cout << "1 - Rock" << endl;
            cout << "2 - Paper" << endl;
            cout << "3 - Scissors" << endl;
            cout << "4 - Lizard" << endl;
            cout << "5 - Spock" << "        : "; // inputs here

            //prompts user input, stores value
            cin >> u;
        }
    return 0;
    }

what am I doing wrong?

2 Answers2

1

The reason is that when the user inputs something that is not an integer, std::cin fails to extract it into the integer variable 'u', so the input is stuck in the input buffer forever, and std::cin will go into a failed state.

What you need to do is to clear the input buffer every time and clear the failed state of std::cin, which can be achieved with cin.ignore() and cin.clear() respectively. The working code is as follows:

#include <cstdlib>
#include <iostream>
#include <string>
#include <limits>
#include "rpslsType.h"

using namespace std;

int main() {
    // stores user input
    int u = 0;

    // outputs options
    cout << "---------------------------" << endl;
    cout << "Enter Move" << endl;
    cout << "1 - Rock" << endl;
    cout << "2 - Paper" << endl;
    cout << "3 - Scissors" << endl;
    cout << "4 - Lizard" << endl;
    cout << "5 - Spock" << "        : "; // inputs here

    //prompts user input, stores value
    cin >> u;
    cin.clear();
    cin.ignore(numeric_limits<int>::max(), '\n');

    // and when input is not in range of 1 t0 5, print error
    while (!((u <= 5) && (u >= 1))) {
        printf("Error, invalid input.\n"); // error message

        // re-enter user input
        // outputs options
        cout << "---------------------------" << endl;
        cout << "Enter Move" << endl;
        cout << "1 - Rock" << endl;
        cout << "2 - Paper" << endl;
        cout << "3 - Scissors" << endl;
        cout << "4 - Lizard" << endl;
        cout << "5 - Spock" << "        : "; // inputs here
        //prompts user input, stores value
        cin >> u;
        if (cin.fail()) {
            cin.clear(); //clear the fail state
            cin.ignore(numeric_limits<int>::max(), '\n'); //clear the buffer
            u = 0;
        }
    }
    return 0;
}

Also consider using a do while loop instead of a while loop if you need to do input validation. Example code:

#include <cstdlib>
#include <iostream>
#include <string>
#include <limits>
#include "rpslsType.h"

using namespace std;

int main() {
    // stores user input
    int u = 0; 

    do {
        printf("Error, invalid input.\n"); // error message

        // re-enter user input
        // outputs options
        cout << "---------------------------" << endl;
        cout << "Enter Move" << endl;
        cout << "1 - Rock" << endl;
        cout << "2 - Paper" << endl;
        cout << "3 - Scissors" << endl;
        cout << "4 - Lizard" << endl;
        cout << "5 - Spock" << "        : "; // inputs here
        //prompts user input, stores value
        cin >> u;
        if (cin.fail()) {
            cin.clear(); //clear the fail state
            cin.ignore(numeric_limits<int>::max(), '\n'); //clear the buffer
            u = 0;
        }
    }  while (!((u <= 5) && (u >= 1)));
    return 0;
}

A good resource for learning input validation in c++: https://www.learncpp.com/cpp-tutorial/5-10-stdcin-extraction-and-dealing-with-invalid-text-input/

TYeung
  • 2,579
  • 2
  • 15
  • 30
  • Is the value of u defined if cin.operator >> failed? Seems like it should be but if not the while condition is UB. – HerrJoebob Feb 06 '20 at 17:03
  • 1
    Thanks for pointing this out. According to http://www.cplusplus.com/doc/tutorial/basic_io/, "And this, by default, lets the program continue without setting a value for variable", so I think if u is initialised to 0, it won't cause UB? – TYeung Feb 06 '20 at 17:41
  • 1
    @HerrJoebob: It depends on the version of C++ that your standard library follows. Why not just write `u = 0;` inside the failure-handling block? Then you not only know `u` is set, you know to what value. – Ben Voigt Feb 06 '20 at 17:43
  • OK I will edit my post – TYeung Feb 06 '20 at 17:44
0

You can input a string and then check if it is a valid integer.

#include <iostream>
#include <string>

int GetUserInput() {
#define INVALID_INPUT()                     \
    std::cout << "Error, invalid input.\n"; \
    continue                               

  do {
    std::string s;
    std::cin >> s;
    try {
      int u = std::stoi(s);
      if (u <= 0 || u >= 6) {
        INVALID_INPUT();
      }
      return u;
    } catch (...) {
      INVALID_INPUT();
    }
  } while (true);
  return -1;

#undef INVALID_INPUT
}
tigertang
  • 445
  • 1
  • 6
  • 18
  • You wrote more code to avoid duplicating the invalid input processing... and it would have been even easier to just put it at the end of the loop, where you always hit it unless you did return a valid value. – Ben Voigt Feb 06 '20 at 17:45