C number guessing game with isdigit() verification - c

I'm working on a challenge problem from my textbook where I'm supposed to generate a random number between 1-10, let the user guess, and validate their response with isdigit(). I (mostly) got the program to work with the code below.
The main issue I ran into is that using isdigit() required the input to be stored as a char, which I then had to convert before the comparison so the actual number was compared and not the ASCII code for the number.
So my question is, since this conversion only works for numbers 0 - 9, how can I change the code to allow for the user to successfully guess 10 when that's the number that is generated? Or what if I wanted the game to have a range of 1-100 - how would I then accomplish this? Can I not verify the input with isdigit() if I'm using a possible range greater than 0-9? What is a better way to verify user input?
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
int main(void) {
char buffer[10];
char cGuess;
char iNum;
srand(time(NULL));
iNum = (rand() % 10) + 1;
printf("%d\n", iNum);
printf("Please enter your guess: ");
fgets(buffer, sizeof(buffer), stdin);
sscanf(buffer, "%c", &cGuess);
if (isdigit(cGuess))
{
cGuess = cGuess - '0';
if (cGuess == iNum)
printf("You guessed correctly!");
else
{
if (cGuess > 0 && cGuess < 11)
printf("You guessed wrong.");
else
printf("You did not enter a valid number.");
}
}
else
printf("You did not enter a correct number.");
return(0);
}

You can use the return value of scanf to determine whether the read was successful. So, there are two paths in your program, successful reading and failed reading:
int guess;
if (scanf("%d", &guess) == 1)
{
/* guess is read */
}
else
{
/* guess is not read */
}
In the first case, you do whatever your program logic says. In the else case, you have to figure out "what was the problem", and "what to do about it":
int guess;
if (scanf("%d", &guess) == 1)
{
/* guess is read */
}
else
{
if (feof(stdin) || ferror(stdin))
{
fprintf(stderr, "Unexpected end of input or I/O error\n");
return EXIT_FAILURE;
}
/* if not file error, then the input wasn't a number */
/* let's skip the current line. */
while (!feof(stdin) && fgetc(stdin) != '\n');
}

Related

Why is my call to strcmp not working as expected?

I'm new to C and programming in general. It's a guessing game where the user has a certain amount of guesses to guess whatever the hidden message is.
Unfortunately, even if I enter the correct word, it does not register it as being correct.
#include <stdio.h>
#include <stdlib.h>
Lost(){
printf("You lost");
}
Won(){
printf("You won");
}
int main(void)
{
char HiddenMessage[7] = "Cameron";
char Guess[50] = "";
int Tries = 0;
int MaxTries = 5;
while(Tries != MaxTries && strcmp(HiddenMessage, Guess) != 0){
printf("Guess the secret word:");
scanf("%s", Guess);
Tries++;
if (Tries == MaxTries){
Lost();
}
else if (strcmp(HiddenMessage, Guess) == 0){
Won();
}
}
return 0;
}
How do I structure this to ensure that if a Guess occurs correctly within the number of tries it runs Won();
You should have received a warning about:
char HiddenMessage[7] = "Cameron";
if your compiler was doing its job properly :-)
Seven characters is not enough to store Cameron and the end-of-string marker. Try it again with:
char HiddenMessage[] = "Cameron";
This will make sure enough characters are set aside for the full string.
If you're interested, this is covered in C11 6.7.9 Initialization /14 (emphasis added):
An array of character type may be initialized by a character string literal or UTF−8 string literal, optionally enclosed in braces. Successive bytes of the string literal (including the terminating null character if there is room or if the array is of unknown size) initialize the elements of the array.
So what your code will give you is the sequence of characters { 'C', 'a', 'm', 'e', 'r', 'o', 'n' } but with no \0 at the end to make it a C string. That means strcmp cannot be guaranteed to give you the results you want (it will possibly run off the end of the array during the comparison).
And, as an aside, scanf with the unbounded %s format specifier is considered rather dangerous in non-trivial code, since there's no way to protect against buffer overflow.
If you're looking for a fairly bullet-proof user input function that can detect and mitigate this, have a look at this earlier answer of mine.
It allows prompting, prevents buffer overflow, properly handles line-based user input (no half lines delivered to user) and so on.
It's what I use for simple console applications (where no better user input method is available).
There are, essentially, four errors in your code:
As paxdiablo has stated, your HiddenMessage array is not large enough to hold the text you have given plus the nul terminator.
You need to define (or at least declare) your Lost and Won functions before you use them.
The definitions of these two functions are incorrect, as you have not specified a return type: void is suitable for functions that don't return anything.
You must include stdio.h to get the definitions of printf and scanf
Here's a fixed (and working) version of your code with the corrections made:
#include <stdlib.h>
#include <stdio.h> // MUST include this for "printf" and "scanf" functions
// You can keep these DEFINITIONS after "main" if you like, but then you'll need to
// have "pre-declarations" of them (the two commented lines immediately below).
// void Lost();
// void Won();
void Lost() {
printf("You lost");
}
void Won() {
printf("You won");
}
int main()
{
char HiddenMessage[8] = "Cameron"; // Added enough space for the nul terminator
char Guess[50] = "";
int Tries = 0;
int MaxTries = 5;
while (Tries != MaxTries && strcmp(HiddenMessage, Guess) != 0) {
printf("Guess the secret word:");
scanf("%s", Guess);
Tries++;
if (Tries == MaxTries) {
Lost();
}
else if (strcmp(HiddenMessage, Guess) == 0) {
Won();
}
}
return 0;
}
Feel free to ask for further clarification and/or explanation.
Another significant problem is your failure to validate any of the input. Any time (means ANY time) you take input, you must validate the input succeeds before relying on the data. Failure to validate invites undefined behavior.
Validations are simple, if you read input with a function check the return to validate you got all the input you were expecting. For example:
printf (" try no. %d - Guess the seceret word: ", tries + 1);
if (scanf ("%49s", guess) != 1) { /* read/validate word */
fputs ("(error: EOF encountered reading guess.)\n", stderr);
return 1;
}
(note: the use of the field-width modifier to limit the number of characters that can be read. Failing to control the number of characters than can be input will read to attempt to write beyond the bounds of your array if the user enters 50 (or more) characters.)
Your logic is a bit awkward. All you need to do is loop continually until the user either wins the game or exhausts the number of tries available. Rearranging your logic slightly, you could do something like:
(Edited to empty-stdin)
printf ("HANGMAN - you have %d tries to guess the word.\n\n", MAXTRIES);
for (;;) { /* loop continually until win or tries exhausted */
printf (" try no. %d - Guess the seceret word: ", tries + 1);
if (scanf ("%49s", guess) != 1) { /* read/validate word */
fputs ("(error: EOF encountered reading guess.)\n", stderr);
return 1;
}
if (strcmp (hiddenmsg, guess) == 0) /* compare hiddenmsg & guess */
break; /* if matched, break loop */
if (++tries == MAXTRIES) { /* test if all tries exhausted */
fputs ("\nyou have exhausted all guesses - you lost :(\n", stderr);
return 1;
}
/* empty stdin */
for (int c = getchar(); c != EOF && c != '\n'; c = getchar()) {}
}
puts ("\ncongratulations - you won.\n"); /* issue congrats & exit */
Putting it altogether in an example, you could do:
#include <stdio.h>
#include <string.h>
#define MAXTRIES 5
#define MAXGUESS 50
int main (void) {
char hiddenmsg[] = "Cameron",
guess[MAXGUESS] = "";
int tries = 0;
printf ("HANGMAN - you have %d tries to guess the word.\n\n", MAXTRIES);
for (;;) { /* loop continually until win or tries exhausted */
printf (" try no. %d - Guess the seceret word: ", tries + 1);
if (scanf ("%49s", guess) != 1) { /* read/validate word */
fputs ("(error: EOF encountered reading guess.)\n", stderr);
return 1;
}
if (strcmp (hiddenmsg, guess) == 0) /* compare hiddenmsg & guess */
break; /* if matched, break loop */
if (++tries == MAXTRIES) { /* test if all tries exhausted */
fputs ("\nyou have exhausted all guesses - you lost :(\n", stderr);
return 1;
}
/* empty stdin */
for (int c = getchar(); c != EOF && c != '\n'; c = getchar()) {}
}
puts ("\ncongratulations - you won.\n"); /* issue congrats & exit */
}
Example Use/Output
Typical try and fail game:
$ ./bin/hangman
HANGMAN - you have 5 tries to guess the word.
try no. 1 - Guess the seceret word: Alligator
try no. 2 - Guess the seceret word: Campers
try no. 3 - Guess the seceret word: Scam
try no. 4 - Guess the seceret word: Horses
try no. 5 - Guess the seceret word: Bananna
you have exhausted all guesses - you lost :(
A lucky win:
$ ./bin/hangman
HANGMAN - you have 5 tries to guess the word.
try no. 1 - Guess the seceret word: Lightning
try no. 2 - Guess the seceret word: Thunder
try no. 3 - Guess the seceret word: Geroge
try no. 4 - Guess the seceret word: Cameron
congratulations - you won.
(Testcase After Edit w/Example from Comment)
$./bin/hangman
HANGMAN - you have 5 tries to guess the word.
try no. 1 - Guess the seceret word: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxCameron
try no. 2 - Guess the seceret word: foo
try no. 3 - Guess the seceret word: Cameron
congratulations - you won.
Look things over and let me know if you have any further questions.
As strcmp compares the difference between each character till the end of string that is \0 So your buffer needs to be large enough to hold your string i.e. char HiddenMessage[8] = "Cameron";
PS you can avoid a lot of lengthy code
#include <stdlib.h>
int main()
{
char HiddenMessage[8] = "Cameron";
char Guess[50] = "";
int Tries = 0;
int MaxTries = 5;
while (Tries != MaxTries)
{
printf("Guess the secret word:");
scanf("%s", Guess);
Tries++;
if (strcmp(HiddenMessage, Guess) == 0)
{
Won();
return 0;
}
}
Lost();
return 0;
}
void Lost()
{
printf("You lost");
}
void Won()
{
printf("You won");
}
There are multiple problems with your code. The size of HiddenMessage reported by #paxdiablo is just one of them.
You should use fgets instead of scanf because scanf won't consume the newline. You will be stuck on the second iteration.
You increment Tries and test it against MaxTries before testing if the guess was correct. As a consequence the program will tell that the user lost before testing the validity of the last guess.
Once the user guessed the word and won, you must break from the while loop and terminate the program. With your code, after the program reported that the user won, it will ask for another guess if it wasn't the last guess.

C Program - How to deny any non-numerical input

I've just started learning the language of C, and would love your help in cleaning up / simplifying my code if you know a better way to reach the following.
I want a program to ask for a number, and if that is found then proceed to print and end, however if anything else is put in (e.g. a letter key), then I want the program to loop asking for a number until one is given.
I started off by using a simple scanf input command, but this seemed to go into an infinite loop when I tried to check if a valid number (as we define them) was put in.
So instead I have ended up with this, from playing around / looking online, but I would love to know if there is any more efficient way!
//
// Name & Age Program
// Created by Ben Warren on 1/3/18.
//
#include <stdio.h>
int main (void)
{
//Setting up variables
int num;
char line[10]; /* this is for input */
//Collecting input
printf("Please enter any number? \t");
scanf("%d", &num);
//If Invalid input
while (num==0)
{
printf("\nTry again:\t");
fgets(line, 10, stdin); //turning input into line array
sscanf(line, "%d",&num); //scaning for number inside line and storing it as 'num'
if (num==0) printf("\nThat's not an number!");
}
//If Valid input
{
printf("\n%d is nice number, thank you! \n\n", num);
*}*
return 0;
}
Instead of checking if the value is different to 0, check the return value of
sscanf. It returns the number of conversions it made. In your case it should be 1. Unless the return value is 1, keep asking for a number.
#include <stdio.h>
int main(void)
{
int ret, num;
char line[1024];
do {
printf("Enter a number: ");
fflush(stdout);
if(fgets(line, sizeof line, stdin) == NULL)
{
fprintf(stderr, "Cannot read from stdin anymore\n");
return 1;
}
ret = sscanf(line, "%d", &num);
if(ret != 1)
fprintf(stderr, "That was not a number! Try again.\n");
} while(ret != 1);
printf("The number you entered is: %d\n", num);
return 0;
}
That is not a bad approach for someone new to C. One small improvement would be to actually check the return value of scanf(), since it returns the number of arguments successfully retrieved. Then you could get away from relying on num being 0 to indicate the input was valid. Unless you do want to specifically flag 0 as invalid input.
int ret = scanf("%d", &num);
ret == 1 would mean an integer was succesffully read into num, ret == 0 would mean it was not.
Consider using strtol to parse a string for a long int. This also allows you to detect trailing characters. In this example if the trailing character is not a newline, the input can be rejected. strtol can also detect overflow values. Read the documentation to see how that works.
#include <stdio.h>
#include <stdlib.h>
int main (void)
{
//Setting up variables
long int num = 0;
char line[40] = ""; /* this is for input */
char *parsed = NULL;
printf("Please enter any number? \t");
fflush ( stdout);
while ( fgets(line, 40, stdin))
{
parsed = line;//set parsed to point to start of line
num = strtol ( line, &parsed, 10);
if ( parsed == line) {//if parsed equals start of line there was no integer
printf("Please enter a number? \t");
printf("\nTry again:\t");
fflush ( stdout);
continue;
}
if ( '\n' != *parsed) {//if the last character is not a newline reject the input
printf("Please enter only a number? \t");
printf("\nTry again:\t");
fflush ( stdout);
}
else {
break;
}
}
if ( !parsed || '\n' != *parsed) {
fprintf ( stderr, "problem fgets\n");
return 0;
}
printf("\n%ld is nice number, thank you! \n\n", num);
return 0;
}
0 (zero) is a number...
But I see what you want to do...
You can check for a valid number, using isdigit or a combination of similar functions
I think its also important to follow the advice of other answers to use the return value from scanf using code such as:
int ret = scanf("%d", &num);
and examining ret for success or failure of scanf.

check input program gets stuck in an infinte loop

I'm trying to create a program that asks to type something and check if it is an integer. If it is an integer, then print "the integer is ...". Else, print "try again" and waits for another input. However, the program prints an infinite number of "try again" if you type in a character. Here's the source code:
#include <stdio.h>
#include <stdbool.h>
int main()
{
int inp;
bool t = 1;
printf("type an integer\n");
while (t) {
if (scanf("%i", &inp) == 1) {
printf("The integer is %i", inp);
t = 0;
} else {
printf("try again");
scanf("%i", &inp);
}
}
}
OP's code fail to consume the offending non-numeric input. It remains in stdin, for the next input function. As it is unfortunately just another scanf("%i", &inp) which fails the same way - infinite loop.
After attempting to read an int, read the rest of the line.
#include <stdio.h>
#include <stdbool.h>
int main() {
int inp;
int scan_count;
printf("Type an integer\n");
do {
scan_count = scanf("%i", &inp); // 1, 0, or EOF
// consume rest of line
int ch;
while ((ch == fgetchar()) != '\n' && ch != EOF) {
;
}
} while (scan_count == 0);
if (scan_count == 1) {
printf("The integer is %i\n", inp);
} else {
puts("End of file or error");
}
}
An even better approach would read the line of user input with fgets(). Example
When you entered a char, the variable inp in scanf("%d", &inp) would get null, since the input that doesn't match the format string. And the character you input would remain in the buffer, so that's the reason both your scanf would not stop.
A simplest way to fix this is modify your second scanf("%i", &inp); to scanf("%c", &c); (don't forget to declare a char c in your main function).
check here while(t) its in an infinite loop because you have to set a condition for t something like while(t==1) or while(t>1) or (t<1) something like that. saying while(t) means that t can be anything and it will continue to run.
There is nothing in to break the while loop.
consider getting rid of the boolean, and simply using a while (1) loop with a break. Also you should be using "%d" to indicate an integer in scanf/printf. And there is no need for the scanf call in the else, since your program would loop back and call scanf again anyway.
#include <stdio.h>
int main() {
int inp = 0;
printf("type an integer\n");
while (1) {
if (scanf("%d", &inp) == 1) {
printf("The integer is %d", inp);
break;
}
else {
printf("try again");
}
}
return 0;
}
I hope this helped.

How to check if the user input an integer using scanf

I created a program to make a diamond out of *'s. I am looking for a way to check if the type of input is an integer in the C language. If the input is not an integer I would like it to print a message.
This is what I have thus far:
if(scanf("%i", &n) != 1)
printf("must enter integer");
However it does not display the message if it's not an integer. Any help/guidance with this issue would be greatly appreciated!
you can scan your input in a string then check its characters one by one, this example displays result :
0 if it's not digit
1 if it is digit
you can play with it to make your desired output
char n[10];
int i=0;
scanf("%s", n);
while(n[i] != '\0')
{
printf("%d", isdigit(n[i]));
i++;
}
Example:
#include <stdio.h>
#include <string.h>
main()
{
char n[10];
int i=0, flag=1;
scanf("%s", n);
while(n[i] != '\0'){
flag = isdigit(n[i]);
if (!flag) break;
i++;
}
if(flag)
{
i=atoi(n);
printf("%d", i);
}
else
{
printf("it's not integer");
}
}
Use fgets() followed by strtol() or sscanf(..."%d"...).
Robust code needs to handle IO and parsing issues. IMO, these are best done separately.
char buf[50];
fgets(buf, sizeof buf, stdin);
int n;
int end = 0; // use to note end of scanning and catch trailing junk
if (sscanf(buf, "%d %n", &n, &end) != 1 || buf[end] != '\0') {
printf("must enter integer");
}
else {
good_input(n);
}
Note:
strtol() is a better approach, but a few more steps are needed. Example
Additional error checks include testing the result of fgets() and insuring the range of n is reasonable for the code.
Note:
Avoid mixing fgets() and scanf() in the same code.
{ I said scanf() here and not sscanf(). }
Recommend not to use scanf() at all.
strtol
The returned endPtr will point past the last character used in the conversion.
Though this does require using something like fgets to retrieve the input string.
Personal preference is that scanf is for machine generated input not human generated.
Try adding
fflush(stdout);
after the printf. Alternatively, have the printf output a string ending in \n.
Assuming this has been done, the code you've posted actually would display the message if and only if an integer was not entered. You don't need to replace this line with fgets or anything.
If it really seems to be not working as you expect, the problem must be elsewhere. For example, perhaps there are characters left in the buffer from input prior to this line. Please post a complete program that shows the problem, along with the input you gave.
Try:
#include <stdio.h>
#define MAX_LEN 64
int main(void)
{ bool act = true;
char input_string[MAX_LEN]; /* character array to store the string */
int i;
printf("Enter a string:\n");
fgets(input_string,sizeof(input_string),stdin); /* read the string */
/* print the string by printing each element of the array */
for(i=0; input_string[i] != 10; i++) // \0 = 10 = new line feed
{ //the number in each digits can be only 0-9.[ASCII 48-57]
if (input_string[i] >= 48 and input_string[i] <= 57)
continue;
else //must include newline feed
{ act = false; //0
break;
}
}
if (act == false)
printf("\nTHIS IS NOT INTEGER!");
else
printf("\nTHIS IS INTEGER");
return 0;
}
[===>] First we received input using fgets.Then it's will start pulling each digits out from input(starting from digits 0) to check whether it's number 0-9 or not[ASCII 48-57],if it successful looping and non is characters -- boolean variable 'act' still remain true.Thus returning it's integer.

C programming minor issue with interactive menu when input a floating value

My program works well with invalid inputs such as char, number out of range, but a problem happens when a floating point value such as 1.2 is entered. The program prints menu again, and asks user for input before printing error message. What I try to fix is don't print menu again, but still struggle. For example,
Make your selection: 1.1
[Here is menu content]
Make your selection: That selection isn't valid. Please try again.
#include <stdio.h>
#define QUIT 0
int menu();
int main(void)
{
int choice;
char c;
choice = menu();
while(choice != QUIT) //execute so long as choice is not equal to QUIT
{
choice = menu();
}
}
int menu(void)
{
int option;
printf("Text Encoder Service\n\n");
printf("1.\tEnter name of input file (currently 'Secret.txt')\n");
printf("2.\tEnter name of output file (currently not set)\n");
printf("3.\tEnter number of characters data should be shifted (currently +7)\n");
printf("4.\tEncode the text\n\n");
printf("0.\tQuit\n\n");
printf("Make your selection: ");
while( (scanf(" %d", &option) != 1) /* non-numeric input */
|| (option < 0) /* number too small */
|| (option > 4)) /* number too large */
{
fflush(stdin); /* clear bad data from buffer */
printf("That selection isn't valid. Please try again.\n\n");
printf("Your choice? ");
}
return option;
}
I finally could validate floating input. Thanks your advices so much! This is my new code. What else do you think an invalid input?
int menu(void)
{
int option, parsed_inputs;
char overcount_char;
parsed_inputs = 1;
printf("Text Encoder Service\n\n");
printf("1.\tEnter name of input file (currently 'Secret.txt')\n");
printf("2.\tEnter name of output file (currently not set)\n");
printf("3.\tEnter number of characters data should be shifted (currently +7)\n");
printf("4.\tEncode the text\n\n");
printf("0.\tQuit\n\n");
printf("Make your selection: ");
parsed_inputs = scanf_s("%d%c", &option, &overcount_char);
while( parsed_inputs > 1 ) /* number too large */
{
if((overcount_char != '\n') || (option < 0) || (option > 4))
{
fflush(stdin); /* clear bad data from buffer */
printf("That selection isn't valid. Please try again.\n\n");
printf("Your choice? ");
scanf_s("%d%c", &option, &overcount_char);
}
else
break;
}
return option;
}
An input of 1.1 leads to the following:
The string is read into an internal buffer.
It is then matched against the given format string.
On the first non-match, the scanf() call is stopped and it returns the number of successfully scanned values.
Let's test it:
#include <stdio.h>
int main(int argc, char ** argv)
{
while (1) {
int option;
int n = scanf(" %d", &option);
printf("%d %d\n", n, option);
if (n <= 0) break;
}
}
This program reads one line.
Suppose I enter 123 132.
Then the following happens:
* As the format string starts with a space, all leading whitespace is consumed. In this case, there is none.
* Then the 123 is consumed and put into option.
* As the format string is over now, parsing is stopped, n=1 and option=123.
* In the next loop run, the same happens, giving n=1 and option=123.
But: Suppose I enter 123.321 or 123#321.
Then the following happens:
* As the format string starts with a space, all leading whitespace is consumed. In this case, there is none.
* Then the 123 is consumed and put into option.
* As the format string is over now, parsing is stopped, n=1 and option=123.
* In the next loop run, there is no whitespace to skip. .321 resp. #321 is tried to be matched to %d, but these are no valid ints. Thus, we get n=0 and option keeps its old value.
* As no characters are consumed from the input stream (the one used is put back again), the same happens over and over again - that's why I put if (n <= 0) break;.
So you see that the behaviour has nothing to do with floating point, as it doesn't matter if we use . or # to "disturb".
We change our program to
#include <stdio.h>
int main(int argc, char ** argv)
{
while (1) {
int option; char c;
int n = scanf("%d%c", &option, &c);
printf("%d %d %d %c\n", n, option, c, c);
if (n <= 0) break;
}
}
and run it, inputting 4.235#6x7.
Then we get
* n=2, option=4 and c='.' at the first run
* n=2, option=235 and c='#' at the 2nd run
* n=2, option=6 and c='x' at the 3rd run
* n=2, option=7 and c='\n' (newline) at the 3rd run
and are prompted for further input.
This makes you open to the option
(parsed_inputs = scanf("%d%c", &option, &overcount_char)) < 1
and then check what overcount_char contains, whenever parsed_inputs is > 1.
I think you should put scanf() before the while loop and explicitly check "option" variable in the while loop.
What happening is that, here scanf() will always return the value 1, because scanf() returns no. of arguments read successfully. Hence this while loop will run forever. For further information check this link->http://www.cplusplus.com/reference/cstdio/scanf/
Hope this helps!

Resources