2

I'm trying to do a program with a simple game for a user to guess the number. My code is below:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define MAX 30
#define TRYING 5

void guessnumber(int, int, int *);

int main(void) {
    int mytry = 1;

    guessnumber(MAX, TRYING, &mytry);

    if (mytry <= TRYING)
        printf("Congratulations! You got it right in %d tries\n", mytry);
    else
        printf("Unfortunately you could not guess the number in the number of tries predefined\n");

    printf("End\n");

    return EXIT_SUCCESS;
}

void guessnumber(int _n, int _m, int *_mytry) {
    srandom(time(NULL));
    int generated = 0, mynum = 0, test = 0;

    generated = rand() % (_n + 1);

    printf("Welcome to \"Guess the number\" \n");
    printf("A number between 0 and %d was generated\n", _n);
    printf("Guess the number:\n");

    while (*_mytry <= TRYING) {
        test = scanf(" %d", &mynum);

        if (test != 1 || mynum < 0 || mynum > MAX)
            printf("ERROR: please enter a valid number \n");
        else
        if (mynum > generated)
            printf("Wrong! The number your trying to guess is smaller\n");
        else
        if (mynum < generated)
            printf("Wrong ! The number your trying to guess is bigger\n");
        else
            break;
        *_mytry = *_mytry + 1;
    }
}

Okay, now the program is working pretty ok except for one thing: the scanf test. It works if I try to enter a number out of my range (negative or above my upper limit) but it fails if I for example try to enter a letter. What it does is that it prints the message of error _m times and then it prints "Unfortunately you could not guess the number in the number of tries predefined" and "End".

What am I doing wrong and how can I fix this?

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 1
    See [`srand()` — why call it only once?](http://stackoverflow.com/questions/7343833/srand-why-call-it-only-once/) – Jonathan Leffler Mar 20 '17 at 17:46
  • 3
    Starting identifiers with an underscore is considered bad style. Is this a local convention to distinguish argument names from local variable names? – chqrlie Mar 20 '17 at 18:10

2 Answers2

2

In case, a character is entered, you're trying to detect it correctly

  if(test!=1 ......

but you took no action to correct it.

To elaborate, once a character is inputted, it causes a matching failure. So the input is not consumed and the loop falls back to the genesis position, only the loop counter is increased. Now, the previous input being unconsumed, is fed again to the scanf() causing failure once again.

This way, the loop continues, until the loop condition is false. Also, for every hit to scanf(), as unconsumed data is already present in the input buffer, no new prompt is given.

Solution: You need to clean the input buffer of existing contents when you face a failure. You can do something like

while ((c = getchar()) != '\n' && c != EOF);

to clean the buffer off existing contents.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
  • Thanks! Just a quick question: why putting the character different from '\n' and EOF. Also I trying to use, instead of c, the variable mynum. But isn't mynum an integer? Shouldn't it not work? –  Mar 20 '17 at 17:55
  • @GrangerObliviate: The letter `c` is the classic name for variable `int c;` used in I/O loops. It needs to be an `int` and not a `char` because `getchar()` can return any character code and EOF, so it returns an `int`. If you use `char`, you run into problems — either failing to recognize EOF ever, or recognizing an ordinary (albeit rare) character as EOF. You could use `mynum` if you want, but a separate variable `c` might be better. – Jonathan Leffler Mar 20 '17 at 18:13
1

When you enter a letter, scanf() leaves the letter in the input stream since it does not match the %d conversion specifier. The simplest thing to do is use getchar() to remove the unwanted character:

if (test != 1) {
    getchar();
}

A better solution would be to use fgets() to get a line of input, and sscanf() to parse the input:

char buffer[100];
while (*_mytry<=TRYING)
{
    if (fgets(buffer, sizeof buffer, stdin) == NULL) {
        fprintf(stderr, "Error in fgets()");
        exit(EXIT_FAILURE);
    }

    test=sscanf(buffer, "%d", &mynum);

    if(test!=1 || mynum<0 || mynum>MAX)
        printf ("ERROR: please enter a valid number \n");

    else if(mynum>generated)
        printf("Wrong! The number your trying to guess is smaller\n");

    else if(mynum<generated)
        printf("Wrong ! The number your trying to guess is bigger\n");

    else
        break;
    *_mytry=*_mytry+1;
}

In the above code, note that the leading space has been removed from the format string. A leading space in a format string causes scanf() to skip leading whitespaces, including newlines. This is useful when the first conversion specifier is %c, for example, because any previous input may have left a newline behind. But, the %d conversion specifier (and most other conversion specifiers) already skips leading whitespace, so it is not needed here.

Additionally, your code has srandom() instead of srand(); and the call to srand() should be made only once, and probably should be at the beginning of main(). And, identifiers with leading underscores are reserved in C, so you should change the names _m, _n, and _mytry.

ad absurdum
  • 19,498
  • 5
  • 37
  • 60