2

I make a game that the user need to guess the number,

the number is generated with rand function.

If the user wrote an invalid number or a character, print error message.

My problem is that cin.fail() does not working well for me, for example, when I enter a character as input, my program is always printing "Too Low!", maybe because it calculates the value of the character ( ASCII TALBE ).

Any suggestions ?

My Code:

void Game()
{
    srand(time(0));

    int iGuess;
    const unsigned int iNum = (rand() % 1000 + 1);

    Start:
    system("cls");

    cout << "\n Guess the Num: "; cin >> iGuess;
    if (iGuess == iNum) {
        system("color A");
        cout << "\n\n Good Job! You Won!";
        exit(0);
    }
    if (iGuess > iNum) {
        cout << "\n\n Too High!";
        Sleep(3000);
        goto Start;
    }
    if (iGuess < iNum) {
        cout << "\n\n Too Low!";
        Sleep(3000);
        goto Start;
    }
    if (cin.fail()) {
        cout << "Input has failed! & Error Code: " << GetLastError();
        Sleep(3000);
        goto Start;
    }
}
nakE
  • 362
  • 1
  • 13
  • 2
    Try checking if the user input failed *before* using that input. Edit : You'll also need to clear the error state before the user can try again : [How to clear cin Buffer in c++](https://stackoverflow.com/questions/40003190/how-to-clear-cin-buffer-in-c) – François Andrieux Dec 10 '19 at 16:07
  • You could take input from the user as a character string, then use `std::strtol()` to convert it to a number. You can check the returned value in the second argument to see if there was an error converting (i.e. if the string is not a valid number). – Adrian Mole Dec 10 '19 at 16:07
  • The check for `cin.fail()` should be the first after input. If input has failed the value in `iGuess` is useless. – Scheff's Cat Dec 10 '19 at 16:07
  • And, if the input failed you have to reset it and to consume the garbage. You may have a look onto this: [SO: C++ Beginner Infinite Loop When Input Wrong Data Type and Help Evaluate My Code](https://stackoverflow.com/a/52906099/7478597) - it's for a `float` value but the principle is similar. – Scheff's Cat Dec 10 '19 at 16:08
  • I wonder that nobody mentioned the terrible usage of `goto Start;` yet... ;-) – Scheff's Cat Dec 10 '19 at 16:13
  • IOStream is not as usable as it tries to make believe. Since you want to read 1 number per line (user enters a number, then hits enter), you might want to look at: https://en.cppreference.com/w/cpp/string/basic_string/getline But even the example code there looks wrong, because they do not (cannot) check if ``std::stoi()`` actually succeeds. – BitTickler Dec 10 '19 at 16:38

3 Answers3

1

First you can check that for streams, boolean conversion (explicit operator bool() const) is equivalent to !failed() (see https://en.cppreference.com/w/cpp/io/basic_ios/operator_bool).

This allows you to write:

void Game()
{
  srand(time(0));

  int iGuess;
  const unsigned int iNum = (rand() % 1000 + 1);

Start:
  system("cls");

  cout << "\n Guess the Num: ";
  if (cin >> iGuess)
  {
    if (iGuess == iNum)
    {
      system("color A");
      cout << "\n\n Good Job! You Won!";
      exit(0);
    }
    if (iGuess > iNum)
    {
      cout << "\n\n Too High!";
      Sleep(3000);
      goto Start;
    }
    if (iGuess < iNum)
    {
      cout << "\n\n Too Low!";
      Sleep(3000);
      goto Start;
    }
  }
  else
  {
    cin.clear();                        // clear error flags
    cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // empty buffer 
    assert(cin);                        // check that we are still in a good state

    cout << "Input has failed! & Error Code: " << GetLastError();
    Sleep(3000);
    goto Start;
  }
}

If an error happened it is important not to forget these steps:

Clear error flags:

cin.clear(); 

Remove all the data previously stored in the buffer (and which could not be interpreted as an integer)

cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); 

Check that we are still in a good state (defensive programming, another error could have happened)

assert(cin); 

Update: it is certainly better to use ignore, my first version was

while (cin.get() != '\n') continue; 

my update is:

cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); 
Picaud Vincent
  • 10,518
  • 5
  • 31
  • 70
0

I recommend utilizing the do while format to get your input.

#include <stdlib.h>
#include <time.h>
#include <iostream>
#ifdef _WIN32
#include <windows.h>

void sleep(unsigned milliseconds)
{
    Sleep(milliseconds);
}
#else
#include <unistd.h>

void sleep(unsigned milliseconds)
{
    usleep(milliseconds * 1000); // takes microseconds
}
#endif
using namespace std;

int main()
{
    srand(time(0));

    int iGuess;
    const unsigned int iNum = (rand() % 1000 + 1);

Start:
    system("cls");

    do
    {       
        if (!std::cin)
        {
            std::cin.clear();
            std::cin.ignore(10000, '\n');
            std::cout << "\nFailed input";
        }
        std::cout << "\n Guess the Num: ";
    } while (!(std::cin >> iGuess));


    if (iGuess == iNum) {
        system("color A");
        cout << "\n\n Good Job! You Won!";
        exit(0);
    }
    if (iGuess > iNum) {
        cout << "\n\n Too High!";
        Sleep(3000);
        goto Start;
    }
    if (iGuess < iNum) {
        cout << "\n\n Too Low!";
        Sleep(3000);
        goto Start;
    }
}
Sean Brookins
  • 574
  • 4
  • 12
0

The simplest way to parse the input string using std::string_view and std::isdigit().

char str[] = "12abc12"; 

int alphabet = 0, number = 0, i; 
for (i=0; str[i]!= '\0'; i++) 
{ 
    // check for alphabets 
    if (isalpha(str[i]) != 0) 
        alphabet++; 

    // check for decimal digits 
    else if (isdigit(str[i]) != 0) 
        number++; 
} 

https://www.geeksforgeeks.org/isalpha-isdigit-functions-c-example/

Build Succeeded
  • 1,153
  • 1
  • 10
  • 24