3

On a character input in the first scanf(), the second one doesn't run. getchar() isn't working either for Try Again input. It skips to take input for Would you like to play again? (Y/N)? It seems that your_choice is supposed to take the character and check it afterward but the character is actually being taken by ch. What is causing it to work like this and how to resolve the issue. I've tried re-initializing the variables but doesn't work.

#include <stdio.h>

void choice(int);

int main() {
    char ch;
    int random, your_choice;

    do {
        srand(time(NULL));
        system("cls");
        printf("** 0 is for Rock **\n");
        printf("** 1 is for Scissors **\n");
        printf("** 2 is for Lizard **\n");
        printf("** 3 is for Paper **\n");
        printf("** 4 is for Spock **\n");

        printf("\nEnter your choice here:");
        scanf("%d", &your_choice);

        random = rand() % 5; //random number between 0 & 4
        if ((your_choice >= 0) && (your_choice <= 4)) {
            //choice printer omitted for this post

            if ((random == ((your_choice + 1) % 5)) || (random == ((your_choice + 2) % 5)))
                printf("\n\n... and you win!!!\n");
            else if ((random == ((your_choice + 3) % 5)) || (random == ((your_choice + 4) % 5)))
                printf("\n\n... and you lose!!!\n");
            else if (random == your_choice)
                printf("\n\nUnfortunately, it's a tie!\n");
        } else
            printf("\nWell, this is wrong! Try again with a number from 0 to 4!!\n");

        printf("\nWould you like to play again? (Y/N)?: ");
        scanf(" %c", &ch);

    } while (ch == 'y' || ch == 'Y');

    return 0;
}
nAiN
  • 55
  • 2
  • 7
  • 1
    for starters, take away your space before the `%c` in the second `scanf` – jiveturkey Nov 11 '16 at 15:45
  • 2
    Check the return value of `scanf`. If there is parse error, the invalid input is left in the input buffer, so you have to get rid of it before trying to `scanf` the same data again. If you ignore return value of `scanf` and don't react to its parse errors, you usually end up with program which behaves totally unexpectedly on invalid input. – hyde Nov 11 '16 at 15:46
  • @hyde upon printing ch, it gives the value entered in choice. – nAiN Nov 11 '16 at 15:54
  • Maybe duplicate with [Yes/No loop in C](http://stackoverflow.com/questions/18627163/yes-no-loop-in-c) – J. Piquard Nov 11 '16 at 16:01
  • Cannot reproduce. Did you change `scanf("%c", &ch);` to `scanf(" %c", &ch);` before posting? – Weather Vane Nov 11 '16 at 16:11
  • 3
    @jnbbender: absolutely not! the space tells `scanf()` to ignore pending spaces, which is necessary to skip the newline character still present in the standard input buffer after the first input was read. – chqrlie Nov 11 '16 at 16:16
  • @nAiN where does it print `ch`? – Weather Vane Nov 11 '16 at 16:17
  • In a test, I printed the character in ch and it was the same as that entered in choice (the invalid one) – nAiN Nov 11 '16 at 16:20
  • This actually works fine for me. – cdcdcd Nov 11 '16 at 16:26
  • @nAiN This problem is harder to solve because it does not contain the _input used_. "getchar() isn't working either. " is insufficient detail. Post the code (good, you did that), the input used, results seen and results expected. You will receive better responses, less chatter and more up-votes. – chux - Reinstate Monica Nov 11 '16 at 16:29
  • @cdcdcd try it with an invalid input. – nAiN Nov 11 '16 at 16:30
  • 1
    put `srand` outside of the loop. [srand() — why call it only once?](http://stackoverflow.com/q/7343833/995714). [How does calling srand more than once affect the quality of randomness?](http://stackoverflow.com/q/27209282/995714) – phuclv Nov 11 '16 at 16:32
  • I'm afraid that won't be the real concern here – nAiN Nov 11 '16 at 16:33
  • @nAiN: That's exactly how it should be. You asked the first `scanf` to read `%d`. It will refuse to read any invalid characters, leaving them for your second `scanf`. Your expectations like "`your_choice` is supposed to take the character and check it afterward" are completely unfounded. `scanf` doesn't work that way. – AnT stands with Russia Nov 11 '16 at 16:37
  • @nAiM this is not my finest moment on stack overflow. But you're right again. Methinks a more sophisticated routine or just use a simple int 0/1 rather than a char. – cdcdcd Nov 11 '16 at 16:37

1 Answers1

6

If the user enters characters that cannot be converted to a number, scanf("%d", &your_choice); returns 0 and your_choice is left unmodified, so it is uninitialized. The behavior is undefined.

You should test for this and skip the offending input this way:

    if (scanf("%d", &your_choice) != 1) {
        int c;
        /* read and ignore the rest of the line */
        while ((c = getchar()) != EOF && c != '\n')
            continue;
        if (c == EOF) {
            /* premature end of file */
            return 1;
        }
        your_choice = -1;
    }

Explanation:

  • scanf() returns the number of successful conversions. If the user types a number, it is converted and stored into your_choice and scanf() returns 1, if the user enters something that is not a number, such as AA, scanf() leaves the offending input in the standard input buffer and returns 0, finally if the end of file is reached (the user types ^Z enter in windows or ^D in unix), scanf() returns EOF.

  • if the input was not converted to a number, we enter the body of the if statement: input is consumed one byte at a time with getchar(), until either the end of file or a linefeed is read.

  • if getchar() returned EOF, we have read the entire input stream, no need to prompt the user for more input, you might want to output an error message before returning an error code.

  • otherwise, set your_choice to -1, an invalid value so the read of the code complains and prompts for further input.

Reading and discarding the offending input is necessary: if you do not do that, the next input statement scanf(" %c", &ch); would read the first character of the offending input instead of waiting for user input in response to the Would you like to play again? (Y/N)?: prompt. This is the explanation for the behavior you observe.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • What exactly `return 1;` does? – nAiN Nov 11 '16 at 16:41
  • It exits from `main` and ends the program. The return value can be caught by (in Windows) a batch file that might have called your program, by checking `errorlevel`. – Weather Vane Nov 11 '16 at 16:44
  • @nAiN it is easier to manage faulty inputs if you first input to a string with `fgets` and then apply `sscanf` to that string (checking the return value from those functions). If the input was incorrect, you just dump that line and try again. – Weather Vane Nov 11 '16 at 16:44
  • @nAiN: Using `fgets()` and `sscanf()` is a good suggestion. I wish C tutorials would take this approach more often. – chqrlie Nov 11 '16 at 16:46