This exercise is an exercise to ingrain in your mind why scanf is generally a bad choice for taking mixed user input! You can do it, but you must be very careful to account for any characters that remain in the input buffer (i.e. stdin) -- especially when taking character input... Why?
When you enter a value that is read by scanf, the '\n' will always remain in the input buffer (unless accounted for in your format string). Further, on a failed conversion -- all characters will remain in the input buffer. Further, the user can do something stupid like entering "4 is my guess" when prompted leaving is my guess\n for you to deal with.
Further, what if the user cancels input by pressing ctrl + d (or ctrl + z on windoze) generating a manual EOF? You must account for all possibilities for each and every input.
You must also use the correct format specifier to read input. You are not going to read 'y' or 'n' with %d or %i. When you want to read an int use %d when you want to read a char, use %c. You must also take into account that %c never skips leading whitespace.
(you beginning to understand why it's better to use fgets and then call sscanf for user input??)
How do you handle the characters that remain in the input buffer? Well generally you will use getchar() to read until you have read '\n' (generated by pressing Enter) or until EOF is encountered. You can make it easy on yourself by writing a short function like the following:
/* empty characters that remain in stdin */ void fflushstdin () { for (int c = getchar(); c != '\n' && c != EOF; c = getchar()) {} }
If you call fflushstdin after each input, you will always take care of any characters that remain. If you know chars remain from a prior input that have not been removed, then call it before taking input.
Don't use magic numbers in your code (e.g. 1, 5, 200), instead define any needed constants at the beginning of your code and use the constants in your code. Why? If they change, then you have a single readily accessible place to change them and you don't have to go picking though your code to find them. You can use a #define or an enum like the following:
enum {LOW = 1, TRIES = 5, HIGH = 200 };
The remainder of your problems are simply logic problems that you can work out. Incorporating the above, you can handle (what I think you are attempting to do) as follows:
#include <stdio.h> #include <stdlib.h> #include <time.h> enum {LOW = 1, TRIES = 5, HIGH = 200 }; /* empty characters that remain in stdin */ void fflushstdin () { for (int c = getchar(); c != '\n' && c != EOF; c = getchar()) {} } int main (void) { int i, number, guess, ret; char answer; printf ("Welcome to the game of Guess It!\n" "I will choose a number between %d and %d.\n" "You will try to guess that number.\n" "I will tell you if you guessed too high or too low.\n" "You have %d tries to get the number.\n\n" "OK, I am thinking of a number. Try to guess it.\n\n", LOW, HIGH, TRIES); srand(time(NULL)); while (1) { /* outer loop until user quits */ number = rand() % HIGH + 1; /* set number INSIDE loop */ for (i = 0; i< TRIES; i++) { /* loop for set number of TRIES */ while (1) { /* validate user guess, handle cancelation */ printf ("Your guess no. %d? ", i + 1); /* prompt */ if ((ret = scanf (" %d", &guess)) != 1) { /* chk return */ if (ret == EOF) { /* check for cancelation */ printf ("input canceled, exiting.\n"); return 0; } fprintf (stderr, " error: invalid input.\n"); fflushstdin(); /* empty chars remaining in stdin */ continue; } if (guess < LOW || guess > HIGH) /* check limits */ printf("Illegal guess. Your guess must be between " "%d and %d.\nTry again. Your guess?", LOW, HIGH); break; } if (guess == number) { /* correct answer */ printf ("\n**** CORRECT ****\n\nWant to play again(y/n) "); fflushstdin(); /* validate answer, you are reading a `char` NOT `int` */ while ((ret = scanf (" %c", &answer)) != 1 || (answer != 'y' && answer != 'n')) { fprintf (stderr, "error: invalid answer, play again (y/n) "); if (ret == EOF) { /* check for cancelation */ printf ("input canceled, exiting.\n"); return 0; } fflushstdin(); /* empty chars remaining in stdin */ } if (answer == 'y') /* use goto for breaking nested loops */ goto done; printf ("Goodbye, It was fun. Play again soon.\n"); /* no */ return 0; } if (guess > number) /* provide > and < feedback */ printf ("Too high!\n"); if (guess < number) printf("Too low!\n"); } printf ("Sorry, you exhausted all your tries, number was: %d\n" "play again (y/n) ", number); fflushstdin(); /* validate answer, you are reading a `char` NOT `int` */ while ((ret = scanf (" %c", &answer)) != 1 || (answer != 'y' && answer != 'n')) { fprintf (stderr, "error: invalid answer, play again (y/n) "); if (ret == EOF) { printf ("input canceled, exiting.\n"); return 0; } fflushstdin(); } if (answer != 'y') break; done:; /* goto lable to play again after correct asnwer */ } return 0; }
Example Use/Output
$ ./bin/guess Welcome to the game of Guess It! I will choose a number between 1 and 200. You will try to guess that number. I will tell you if you guessed too high or too low. You have 5 tries to get the number. OK, I am thinking of a number. Try to guess it. Your guess no. 1? onehundred error: invalid input. Your guess no. 1? 100 Too low! Your guess no. 2? 150 Too high! Your guess no. 3? 125 Too low! Your guess no. 4? 137 Too high! Your guess no. 5? 131 Too low! Sorry, you exhausted all your tries, number was: 132 play again (y/n) y Your guess no. 1? 100 Too low! Your guess no. 2? 150 Too low! Your guess no. 3? 175 Too low! Your guess no. 4? 187 Too high! Your guess no. 5? 181 **** CORRECT **** Want to play again(y/n) y Your guess no. 1? 100 Too low! Your guess no. 2? 150 Too high! Your guess no. 3? 125 Too high! Your guess no. 4? 112 Too high! Your guess no. 5? 106 Too low! Sorry, you exhausted all your tries, number was: 110 play again (y/n) n
Note, the above handles stupid user input (like onehundred) and adds number to the failure output to let the user know what he missed.
Look things over and let me know if you have further questions.
whileloop there, no one is preventing you to do that. Or do you mean that you don't know how to use awhileloop? In that case, take a book about C and try learning with it, you'll have more success that way.guessok = 0;flag and do something likewhile (!guessok) { printf ("\n your guess? "); if (scanf("%i",&guess) != 1) {--handle error..; continue;} ... test guess values ...; guessok = 1; }. You can also use awhile (1)orfor (;;)and just break when all good guess conditions are satisfied.elsedoesn't have a condition.%iis for converting integers, not characters.return (i=0);doesn't make much sense, asiis a local variable, it's the same asreturn 0-- what do you think you achieve with that?