#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <ctype.h>
#define MAXGUESSES 5
void Instructions();
int PlayGuess(char solution);
char GetLetter();
int CompareLetters(char guess, char solution);
int main()
{
int i = 0;
int numgames = 0;
char solution;
char guess;
int compareletter(char guess, char solution);
FILE *inp;
inp = fopen("letterList.txt", "r");
fscanf(inp, "%c", &solution);
Instructions();
//get number of games the user wants to play
printf("Please enter the number of games you want to play\n");
scanf("%d", &numgames);
for (i = 1; i <= numgames; i++)
//print current game (value of i)
{
//get letter to guess from file
fscanf(inp, "%c", &solution);
PlayGuess(solution);
printf("\nThe letter is %c\n", solution);
}
fclose(inp);
}
void Instructions()
{
printf("Welcome to Letter Guess\n");
printf("To begin you will enter the number of games you want to
play(1 – 4 games)\n");
printf("You have 5 chances to guess each letter\n");
printf("Let's begin\n");
}
int PlayGuess(char solution) //player defined guesses.
{
int numGuesses = 0;
int winOrLose = 0;
while (numGuesses < MAXGUESSES)
{
GetLetter();
numGuesses = numGuesses + 1;
if (numGuesses>MAXGUESSES)
{
printf("You have run out of guesses\n");
}
}
return 0;
}
//get the guess from the user (call GetLetter function)
//call compareLetters function
char GetLetter()
{
char guess = 0;
char solution;
printf("Enter a guess:", guess);
scanf(" %c", &guess);
CompareLetters(guess, solution);
return guess;
}
//compare the guess and the solution
//return a 1 if they are the same
// message based on before or after alphabetically
//return a 0 if the guess and answer are not the same
int CompareLetters(char guess, char solution)
{
if (guess == solution) //if answer is correct
{ printf("Thats it!\n");
return 1;
}
else
if (guess<solution)
{
printf("The letter you are trying to guess comes after %c\n", guess);
printf("\nTry again\n");
GetLetter();
return 0;
}
else
if (guess>solution)
{ printf("The letter you are trying to guess comes before %c", guess);
printf("\nTry again\n");
GetLetter();
return 0;
}
}
Sorry if the code is a bit messy.
Problem #1: The variable "solution" is uninitialized but i do not know how to fix it. I have this problem a lot, if possible i could use an explanation.
Problem #2: When i launch the program and enter the amount of games I want to play it ignores it and gives me endless guesses, the program never stops.
Thanks.
Just put char solution = '\0';. It's enough to get rid of warning.
Try printing the value of numgames you've just read in scanf() function. It seems you're getting some invalid value for some reason...
Upd: I see your problem: you're calling GetLetter() from PlayGuess(); and you're calling CompareLetters() from GetLetter(); and then you call GetLetter() from CompareLetters() so you create endless recursion. Remove calls GetLetter() from CompareLetters().
Continuing from my comments, the key takeaways from your code are (1) you cannot learn C by guessing at syntax, compiling, over and over again, (2) validate ALL input to your program by checking the return of all input functions and validating the value you receive, (3) enable compiler warnings and then read, understand and correct each warning before attempting to compile again, and (4) do not accept code until it compiles cleanly, without warning.
Since your code includes #define _CRT_SECURE_NO_WARNINGS, it is apparent you are on windows using cl.exe (either from cmd.exe or from VS-Code). For learning basic programming, close VS-Code, open the Command Line provided by your VS (or SDK) install, and don't worry about using the IDE again, until you have mastered compiling from the command line and understand your compiler options. See cl.exe C/C++ Compiler Options, or type cl /? at the command prompt.
From the command line, your basic compile string should be similar to:
cl.exe /nologo /W3 /Ox /Tc mysource.c
(/W3 enable most warnings, /Ox enable all optimizations)
I find it helpful to not clutter my c-source directory with .obj and .exe files so I create two additional directories /obj and /bin for the object and executable files. You then use the /Fo and /Fe options to tell the compiler to put the object files and exe files in the proper directories, e.g.
cl /nologo /W3 /Ox /Foobj/mysource /Febin/mysource /Tc mysource.c
That will put mysource.obj in the obj directory and mysource.exe in the bin directory.
You must have the logic for your code clear in your head before you sit behind the keyboard and start pecking away. (See: (1) above). The easiest way to keep it straight by drawing a simple logic diagram for your code and identify what values you will handle in main() and then what will be handled in each function(). You don't need anything fancy, an 8.5x11 sheet of paper and pencil will do. After you have a clear road map for what each part of your code will do, then sit down and start pecking away.
Putting that logic to test, you can rework your code so it makes a lot more sense than it currently does, e.g.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <ctype.h>
#define MAXGUESSES 5
void Instructions();
int PlayGuess (char solution);
char GetLetter();
int CompareLetters (char guess, char solution);
int main (void)
{
int i = 0,
numgames = 0;
char solution;
FILE *inp = fopen ("letterList.txt", "r");
if (inp == NULL) {
fprintf (stderr, "error: file open failed 'letterList.txt'.\n");
return 1;
}
Instructions(); /* give instructions */
/* get number of games the user wants to play */
printf("Please enter the number of games you want to play: ");
if (scanf ("%d", &numgames) != 1) {
fprintf (stderr, "error: invalid input - numgames.\n");
return 1;
}
putchar ('\n');
for (i = 0; i < numgames; i++)
{
/* get letter to guess from file */
if (fscanf (inp, " %c", &solution) == EOF || solution < ' '
|| '~' < solution) {
fprintf (stderr, "error: invalid character - solution.\n");
return 1;
}
printf (" ==> Game %d <==\n\n", i + 1);
PlayGuess (solution);
printf("The letter was '%c'!\n\n", solution);
}
fclose (inp);
return 0; /* main() is type int and returns a value */
}
void Instructions()
{
printf ("Welcome to Letter Guess\n"
"To begin you will enter the number of games you want "
"to play (1 – 4 games)\n"
"You have 5 chances to guess each letter\n"
"Let's begin\n\n");
}
int PlayGuess (char solution)
{
int numGuesses = 0;
char guess;
while (numGuesses < MAXGUESSES)
{
guess = GetLetter();
if (CompareLetters (guess, solution))
return 1;
numGuesses = numGuesses + 1;
}
printf ("You have run out of guesses\n");
return 0;
}
/* get a letter and validate it is good */
char GetLetter()
{
char guess = 0,
tmp;
printf ("Enter a guess: ");
if (scanf (" %c", &tmp) != EOF && ' ' <= tmp && tmp <= '~')
guess = tmp;
return guess;
}
/* compare the guess and the solution
* return a 1 if they are the same
* message based on before or after alphabetically
* return a 0 if the guess and answer are not the same
*/
int CompareLetters(char guess, char solution)
{
if (guess == solution) /* answer is correct */
{
printf ("Thats it!\n\n");
return 1;
}
if (guess < solution)
printf ("The letter you are trying to guess comes after '%c'\n",
guess);
else
printf ("The letter you are trying to guess comes before '%c'\n",
guess);
printf ("Try again\n\n");
return 0;
}
Example Compile String for cl.exe (VS)
>cl /nologo /W3 /Ox /Foobj/guessletter /Febin/guessletter /Tc guessletter.c
Example Use/Output
> bin\guessletter.exe
Welcome to Letter Guess
To begin you will enter the number of games you want to play (1 – 4 games)
You have 5 chances to guess each letter
Let's begin
Please enter the number of games you want to play: 2
==> Game 1 <==
Enter a guess: k
The letter you are trying to guess comes before 'k'
Try again
Enter a guess: c
The letter you are trying to guess comes after 'c'
Try again
Enter a guess: d
Thats it!
The letter was 'd'!
==> Game 2 <==
Enter a guess: e
The letter you are trying to guess comes after 'e'
Try again
Enter a guess: g
The letter you are trying to guess comes before 'g'
Try again
Enter a guess: f
Thats it!
The letter was 'f'!
Look things over and think about how to approach programming in C. It is an exact language. It is up to you to account for all characters in all input buffers as well as your memory use. If you don't know what parameters a library function takes, or what type and value it will return, or how to use it, look it up. The man pages are available at, e.g. msdn fscanf, fwscanf or scanf(3): input format conversion)
Let me know if you have further questions.
Accepting Input In Any Case, Converting to Lowercase
To accept input in any case and convert the value to lowercase so that guess is always lowercase in your code, you need to change only one-line:
/* get a letter and validate it is good
* (convert letter to lowercase)
*/
char GetLetter()
{
char guess = 0,
tmp;
printf ("Enter a guess: ");
if (scanf (" %c", &tmp) != EOF && ' ' <= tmp && tmp <= '~')
guess = tolower (tmp);
return guess;
}
note: For ASCII characters, the 6th-bit is the 'case bit', if it is 1, the character is lowercase, 0 uppercase. tolower can simply be written as:
unsigned c_tolower (unsigned c)
{
if ('A' <= c && c <= 'Z')
c ^= (1 << 5);
return c;
}
You have to initialize guess and solution to something before you use them in a function.
And what does this line
int compareletter(char guess, char solution);
indicate, there is no function with same name nor it is a call to any function.
In case for dummy initialization you can use
solution = '\0';
Related
I converted a code that I know how to construct in python in C Language, but everytime I run the program in CodeBlocks, the program crashes! And I have NO idea why this is happening, can someone help me?
The program is suppose to guess a person's number (between 0 - 100), using binary search.
For example, if my number is 66, the program asks if my number is 50, since 66 is higher than 50, the number 50 becomes the lower boundary while 100 remains to be the higher boundary, and so on...
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main()
{
int x;
printf("Please think of a number between 0 and 100\n\n");
x = binarysearch();
printf("%d", x);
}
int binarysearch()
{
int hi,lo,guess;
hi = 100;
lo = 0;
char user_inp;
while (1){
guess = round(((hi + lo)/2));
printf("Is your secret number %d?\n\n", guess);
printf("Enter 'h' to indicate the guess is too high. \nEnter 'l' to indicate the guess is too low.\nEnter 'c' to indicate I guessed correctly. \n");
scanf("%c", &user_inp);
if (strcmp(user_inp, "c") == 0){
break;
}
else if (strcmp(user_inp, "h")==0){
hi = guess;
}
else if (strcmp(user_inp, "l")==0){
lo = guess;
}
else{
printf("Sorry, I did not understand your input.");
continue;
}
}
printf("Game over. Your secret number was");
return guess;
}
As per the comments, the problem was very likely the incorrect use of strcmp:
char *string = "fish";
char not_a_string = 'f';
if (0 == strcmp( not_a_string, string ))
...
The character 'f' has ASCII value 0x66. strcmp would blindly use this as a pointer (expecting it to point to a valid string) which would cause a crash as you access memory that's not yours (a segmentation fault).
You would have got away with strcmp( ¬_a_string, string ) in this case, but that's good fortune, not correct code.
To compare the user's character input with another character, you can just use a straightforward equality (since they're both really integers):
if ( user_inp == 'c' ) ...
So that's your code fixed, but how did you ever get to run it in the first place? For me GCC immediately complained:
In function 'int binarysearch()': so.cpp:17:29: error: invalid conversion from 'char' to 'const char*' [-fpermissive]
if (strcmp(user_inp, "c") == 0){
and didn't produce an output. It's telling you the same thing I just did (albeit fractionally more cryptically).
Lessons to learn: listen to your compiler's complaints (and make your compiler as complainy as possible)
#pmg also noted:
add a space before the conversion specifier: scanf(" %c", &user_inp)
Without it, every time you hit Enter:
Sorry, I did not understand your input.Is your secret number 25?
ie you get a spurious complaint. But with the space it works as desired.
(I hate scanf, so have no idea why this works ;) )
Your binary search is incorrect, you need to swap the check of 'h' and 'l'.
Because you compare chars and not strings, use == and not strcmp().
You don't need to include <math.h> because guess is an int, so it'll automatically round floats.
You can use getchar() to clear the buffer after the scanf()
You need to declare your function before main (possibly by defining the function before main).
#include <stdio.h>
#include <stdlib.h>
// WITHOUT <MATH.H>
int binarysearch(void);
int main(void)
{
int x;
printf("Please think of a number between 0 and 100\n\n");
x = binarysearch();
printf("%d", x);
return 0; // RETRUN 0
}
int binarysearch(void)
{
int hi,lo,guess;
hi = 100;
lo = 0;
char user_inp;
int flag = 1; // USE FLAG, NOT BREAK AND CONTINUE
while (flag){
guess = ((hi + lo)/2); // WITHOUT ROUND
printf("Is your secret number %d?\n\n", guess);
printf("Enter 'h' to indicate the guess is too high. \nEnter 'l' to indicate the guess is too low.\nEnter 'c' to indicate I guessed correctly. \n");
scanf("%c", &user_inp);
getchar(); // CLEAR THE BUFFER
if (user_inp == 'c'){ // MAKE FLAG 0
flag = 0;
}
// USE '==', NOT STRCMP
else if (user_inp == 'l'){ // YOU NEED TO SWAP 'L' & 'H'
hi = guess;
}
else if (user_inp == 'h'){
lo = guess;
}
else{
printf("Sorry, I did not understand your input.");
}
}
printf("Game over. Your secret number was ");
return guess;
}
So my code does the following:
Ask what's the option
If option is 1: Scan some numbers
If option is 2: Print those numbers
After each option, ask if user wanted to continue choosing (Y/N)
This is my main code
while(yesnocheck==1)
{
printf("What's your option?: ");
scanf("%d",&b);
switch(b){
case 1:
printf("How many numbers?: ");
scanf(" %d",&n);
a=(struct sv*)malloc(n*sizeof(struct sv));
for(int i=0;i<n;i++)
scanf("%d",&((a+i)->num));
break;
case 2:
for(int i=0;i<n;i++)
printf("%d\n",(a+i)->num);
break;
}
yesnocheck==yesnochecker();
}
And this is the yesnochecker function:
int yesnochecker()
{
char yesorno;
printf("Do you want to continue? (Y/N)");
while(scanf("%s",&yesorno))
{
if(yesorno=='Y')
return 1;
if(yesorno='N')
return 0;
printf("*Wrong input. Please reenter (Y/N): ");
}
}
So on dev C++, my code won't run correctly. After it's done option 1, when I enter "Y" then choose option 2, case 2 will display some weird numbers. However it works well on online C compilers.
And then, when I change the char yesorno in yesnochecker() function to char yesorno[2] and treat it as a string, the code does work.
Can someone shed some light?
It is a bad idea to read a char c with scanf("%s", &c);. "%s" requires a buffer to store a string. The only string which fits into a char is an empty string (consisting only of a terminator '\0' – not very useful). Every string with 1 character requires 2 chars of storage – 1 for the character, 1 for the terminator ('\0'). Providing a char for storage is Undefined Behavior.
So, the first hint was to use the proper formatter instead – "%c".
This is better as it removes the Undefined Behavior. However, it doesn't solve another problem as the following sample shows:
#include <stdio.h>
int cont()
{
char c; do {
printf("Continue (y/n): ");
scanf("%c", &c);
printf("Input %c\n", c);
} while (c != 'y' && c != 'n');
return c == 'y';
}
int main()
{
int i = 0;
do {
printf("Loop iteration %d.\n", ++i);
} while (cont());
/* done */
return 0;
}
Output:
Loop iteration 1.
Continue (y/n): y↵
Input 'y'
Loop iteration 2.
Continue (y/n):
Input '
'
Continue (y/n): n↵
Input 'n'
Live Demo on ideone
WTH?
The scanf("%c") consumes one character from input. The other character (inserted for the ENTER key) stays in input buffer until next call of any input function.
Too bad, without ENTER it is hard to confirm input on console.
A possible solution is to read characters until the ENTER key is received (or input fails for any reasons). (And, btw., getc() or fgetc() can be used as well to read a single character.):
#include <stdio.h>
int cont()
{
int c;
do {
int d;
printf("Continue (y/n): ");
if ((c = fgetc(stdin)) < 0) {
fprintf(stderr, "Input failed!\n"); return 0;
}
printf("Input '%c'\n", c);
for (d = c; d != '\n';) {
if ((d = fgetc(stdin)) < 0) {
fprintf(stderr, "Input failed!\n"); return 0;
}
}
} while (c != 'y' && c != 'n');
return c == 'y';
}
int main()
{
int i = 0;
do {
printf("Loop iteration %d.\n", ++i);
} while (cont());
/* done */
return 0;
}
Output:
Loop iteration 1.
Continue (y/n): y↵
Input 'y'
Loop iteration 2.
Continue (y/n): Hello↵
Input 'H'
Continue (y/n): n↵
Input 'n'
Live Demo on ideone
Please, note, that I changed the type for the read character to int. This is because getc()/fgetc() return an int which is capable to store any of the 256 possible char values as well as -1 which is returned in case of failing.
However, it isn't any problem to compare an int with a character constant (e.g. 'y'). In C, the type of character constants is just int (SO: Type of character constant).
I got up to here, but I still need to use while loop somehow. "want to play again(y/n)" and "Illegal guess. Your guess must be between 1 and 200.Try again. Your guess?" don't seem to work. Please help me with the while/do-while loop and fix my two problems above. Thank you.
#include <stdio.h>
int main()
{
int i,number,guess,tries=5,answer;
printf("Welcome to the game of Guess It!\nI will choose a number between 1 and 200.");
printf("\nYou will try to guess that number.If you guess wrong, I will tell you if you guessed too high or too low.");
printf("\nYou have 5 tries to get the number.\n\nOK, I am thinking of a number. Try to guess it.");
srand(time(NULL));
number = rand() % 200 + 1;
for (i=0;i<tries;i++) {
printf("\n\nYour guess? ");
scanf("%i",&guess);
if (guess==number) {
printf("**** CORRECT ****\n\nWant to play again(y/n) ");
scanf("%i",&answer);
if (answer=='y') {
return (i=0);
}
else (answer=='n'); {
printf("Goodbye, It was fun. Play again soon.");
}
}
else if (guess>number) {
printf("Too high!");
}
else if (guess<number) {
printf("Too low!");
}
else (guess>200); {
printf("Illegal guess. Your guess must be between 1 and 200.\nTry again. Your guess?");
}
}
printf("\n\nSorry, you ran out of tries.\n\nWant to play again?(y/n) ");
scanf("%i",&answer);
if (answer=='y') {
return (i=0);
}
else if (answer=='n'); {
printf("Goodbye, It was fun. Play again soon.");
}
return 0;
}
First, and most important, turn on warnings. You have several elementary mistakes in your code that would be caught with compiler warnings. They're unfortunately off by default. -Wall turns on the basic warnings. It's not "all" warnings, because this is C! -fsanitize=address -Wall -Wshadow -Wwrite-strings -Wextra -Wconversion -std=c99 -pedantic is a good set of warnings to work with.
You could put a loop around the loop, but that rapidly gets hard to maintain. Instead, put the game into a function and loop around that.
void do_game(int tries) {
int number = rand() % 200 + 1;
for (int i=0; i < tries; i++) {
int guess;
printf("\n\nYour guess? ");
scanf("%i",&guess);
if (guess == number) {
printf("**** CORRECT ****\n\n");
return;
}
else if (guess > number) {
printf("Too high!");
}
else if (guess < number) {
printf("Too low!");
}
else if (guess > 200) {
printf("Illegal guess. Your guess must be between 1 and 200.\nTry again. Your guess?");
}
}
puts("\n\nSorry, you ran out of tries.\n\n");
return;
}
Note how the game only has to concern itself with the game. No other logic or questions about playing another game. And it can immediately return when the game is over.
Then the rest of the program is pretty simple. Run the game in an infinite loop, break out of it when you're done.
int main() {
printf("Welcome to the game of Guess It!\nI will choose a number between 1 and 200.");
printf("\nYou will try to guess that number.If you guess wrong, I will tell you if you guessed too high or too low.");
printf("\nYou have 5 tries to get the number.\n\nOK, I am thinking of a number. Try to guess it.");
srand(time(NULL));
while(1) {
do_game(5);
char answer;
printf("Want to play again?(y/n) ");
scanf("%c",&answer);
if (answer == 'n') {
printf("Goodbye, It was fun. Play again soon.");
break;
}
}
return 0;
}
There's a problem, and it's scanf. It's always scanf. scanf is such a problem, there's a whole FAQ for it.
scanf("%i") reads a single integer but not the following newline. That newline, and any other extra input, hangs around on stdin. A later scanf("%c", &answer); might then read that newline instead of their answer.
scanf("%i\n") does not solve the problem. That tells scanf to read an integer, then a newline, then look for another non-whitespace character. scanf is weird.
You're much better off reading the whole line with fgets and parsing it with sscanf. You can write a little utility function for that which gets into variadic arguments.
void line_scanf( const char *fmt, ... ) {
// Get the list of arguments.
va_list args;
va_start(args, fmt);
// Read a line.
char line[256];
fgets(line, sizeof(line), stdin);
// Scan the line like sscanf() but with a va_list.
vsscanf( line, fmt, args );
// Close the list of arguments.
va_end(args);
}
Then use it just like scanf. It guarantees to read the whole line and not leave newlines or partial input on the buffer.
int guess;
printf("\n\nYour guess? ");
line_scanf("%i",&guess);
This is only a partial answer, but it can be a starting point. You really should have a reliable input function. Your scanf() won't do, even if you fix the obvious errors trying to get a character using %i, which is for integers. I won't go into details here, I wrote a document on this. (Basically, you will at least run into problems with unparsable input that scanf() will just leave unread.)
Here's an example how you could do reliable input for your usecase with comments along the way:
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INVALIDNUMBER -1
#define READERROR -2
int readPositiveNumber(void)
{
char buf[64];
// read a line:
if (!fgets(buf, 64, stdin)) return READERROR;
size_t len = strlen(buf);
// line was empty -> invalid:
if (!len) return INVALIDNUMBER;
// line was not complete (last character isn't newline):
if (buf[len-1] != '\n')
{
// read the rest of the line
do
{
if (!fgets(buf, 64, stdin)) return READERROR;
} while (!buf[strcspn(buf, "\n")]);
// input was invalid
return INVALIDNUMBER;
}
// convert to number:
char *endptr;
long num = strtol(buf, &endptr, 10);
// endptr == buf means no characters could be parsed as a number,
// endptr doesn't point to newline means there were non-numeric characters later:
if (endptr == buf || *endptr != '\n') return INVALIDNUMBER;
// if result is out of range of int or is negative -> invalid:
if (num > INT_MAX || num < 0) return INVALIDNUMBER;
return (int)num;
}
int main(void)
{
fputs("Enter a number between 1 and 200: ", stdout);
int number = readPositiveNumber();
if (number == READERROR) return EXIT_FAILURE;
while (number < 1 || number > 200)
{
fputs("Enter a valid number between 1 and 200: ", stdout);
number = readPositiveNumber();
if (number == READERROR) return EXIT_FAILURE;
}
printf("You entered %d.\n", number);
return EXIT_SUCCESS;
}
Try to understand this function, read the manuals for functions you don't know or understand (google "man strtol" for example will find you a manual page for strtol()).
For reading your yes/no response, use fgets() as well, but of course this function will look different, like check if the input is only 1 character (the second one has to be '\n') and return this one character.
just because it's a bit of fun, here's a possible whole game working robustly:
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define INVALIDINPUT -1
#define READERROR -2
static int readLine(char *buf, size_t bufsize)
{
if (!fgets(buf, bufsize, stdin)) return READERROR;
size_t len = strlen(buf);
if (!len) return INVALIDINPUT;
if (buf[len-1] != '\n')
{
do
{
if (!fgets(buf, bufsize, stdin)) return READERROR;
} while (!buf[strcspn(buf, "\n")]);
return INVALIDINPUT;
}
return 0;
}
static int readPositiveNumber(void)
{
char buf[64];
int rc = readLine(buf, 64);
if (rc < 0) return rc;
char *endptr;
long num = strtol(buf, &endptr, 10);
if (endptr == buf || *endptr != '\n') return INVALIDINPUT;
if (num > INT_MAX || num < 0) return INVALIDINPUT;
return (int)num;
}
static int readYesNo(void)
{
char buf[64];
int rc = readLine(buf, 64);
if (rc < 0) return rc;
if (buf[0] == 'y' || buf[0] == 'Y')
{
if (buf[1] == '\n') return 1;
if ((buf[1] == 'e' || buf[1] == 'E')
&& (buf[2] == 's' || buf[2] == 'S')
&& buf[3] == '\n') return 1;
return INVALIDINPUT;
}
if (buf[0] == 'n' || buf[0] == 'N')
{
if (buf[1] == '\n') return 0;
if ((buf[1] == 'o' || buf[1] == 'O')
&& buf[2] == '\n') return 0;
return INVALIDINPUT;
}
return INVALIDINPUT;
}
int main(void)
{
srand(time(0));
for (;;)
{
int number = rand() % 200 + 1;
int tries = 5;
int found = 0;
while (tries--)
{
int guess = INVALIDINPUT;
while (guess < 1 || guess > 200)
{
fputs("guess [1..200]: ", stdout);
guess = readPositiveNumber();
if (guess == READERROR) return EXIT_FAILURE;
}
if (guess == number)
{
puts("Correct!");
found = 1;
break;
}
else if (guess < number) puts ("Too low!");
else puts("Too high!");
}
if (!found)
{
puts("No luck!");
}
int yn = INVALIDINPUT;
while (yn < 0)
{
fputs("play again (y/n)? ", stdout);
yn = readYesNo();
if (yn == READERROR) return EXIT_FAILURE;
}
if (!yn)
{
puts("Bye!");
return EXIT_SUCCESS;
}
}
}
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.
scanf("%i", ...) reads integers in base 10, not characters or strings.
You need to organize your loops. You have 2 main loops, one that runs while the user wants to keep playing, and another that runs while the a game is on.
You program in a nutshell:
int main()
{
// loop until player has had enough
// pick a number
// game loop :
// get a number from user:
// user entry loop:
// print prompt
// get user entry
// validate
// loop number from user: until 0 <= entry <= 200
// if number is ok
// user has won, exit game loop
// if too low
// say 'low'
// if too high
// say high
// if # of attempts > MAX
// say 'lost' exit game loop
// end game loop
// want to contine?
// user entry loop:
// print prompt
// get user entry
// validate
// loop user entry loop until 0 <= entry <= 200
// end loop
}
You could start your loops within main a bit like this:
int attempts;
char yesno = 0;
int guess;
do // loop until player has had enough
{
// generate a number here
attempts = 0;
while(1) // loop while game is on
{
while (1) // loop until user entered a valid entry
{
// prompt
// get user guess
if (0 <= guess && guess <= 200)
break;
}
if (guessed right)
{
// game over!
break;
}
// tell if high or low.
if (++attempts <= MAX)
{
// game over!
break;
}
}
do // loop until user entered a valid entry.
{
printf("Another game (y/n)?");
yesno = fgetc();
} while(yesno != 'y' && yesno != 'n'); // could make case-insensitive ?
} while (yesno != 'n');
There are probably as many ways to do this as there are numbers between 0 and 200. A good strategy is to start by writing comments in your C file that describe step by step what the program needs to do. Going through them one by one is much easier than having the program only in your head, especially when you are starting to code. It will get easier with time as you get used to juggle the concepts and basic blocks your mind.
The error I got was that the number of game wasn't shown correctly.
For example, I chose to play 2 games. I, then, tried to guess the first number correctly. The solution was shown and then jumped to the next game. However, the second game was shown as Game 3 instead of Game 2.
I tried again. This time, I guessed the letter 1 time incorrectly and 1 time correctly. After the second guess, the game showed the solution and then stopped the game despite me choosing to play 2 games and only 1 game was played.
The order of letters in LetterList file is
d
B
G
w
Q
t
r
Y
u
X
So the first game starts with 'd' and then 'B' and then etc....
The error was shown as if the program itself got rid of even numbers.
I don't know what was wrong with it.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <ctype.h>
#define MAXGUESSES 5
void LetterGuessRules();
void GuessTheLetter(char);
char GetTheGuess();
int CompareLetters(char, char);
int main()
{
FILE *inPtr;
int numGames, i = 0;
char letter;
//display the game rule
LetterGuessRules();
printf("\nHow many games do you want to play? (Max 10) >> ");
scanf("%d", &numGames);
printf("\n\n************************************************\n");
inPtr = fopen("letterList.txt", "r");
for (i = 0; i < numGames; i++)
{
//get a solution letter from file - use fscanf
fscanf(inPtr," %c", &letter);
//change the solution to lowercase
letter = tolower(letter);
//print the solution back onto the screen to test
//Close this when play the game to hide the foreseen solution
printf("\nThe letter is %c\n", letter);
//Number of match
printf("\t\tGame %d\n", i += 1);
//call the GuessTheLetter function and pass it the solution
GuessTheLetter(letter);
}
fclose(inPtr);
return 0;
}
void GuessTheLetter(char letter)
{
int win = 0;
int numGuesses = 0;
char myGuess;
while (numGuesses < MAXGUESSES && win == 0)
{
//get a guess from the user by calling the GetTheGuess function
myGuess = GetTheGuess();
//change the guess to lowercase
myGuess = tolower(myGuess);
//win = call the function to compare the guess with the solution
win = CompareLetters(letter, myGuess);
numGuesses++;//count the number of guesses so far
//use conditions to let the user know if they won or lost the round of the game
if (win == 0)
{
printf("\nOops its wrong.\n");
if (myGuess < letter)
{
printf("Your guessed letter -%c- comes before the solution\n", myGuess);
printf("Please guess again :)\n");
}
else if (myGuess > letter)
{
printf("Your guessed letter -%c- comes after the solution\n", myGuess);
printf("Please guess again :)\n");
}
if (numGuesses == MAXGUESSES && win == 0)
printf("Aw you have lost this game!");
printf("\n");
}
else if (win == 1)
{
printf("\nYou have guessed it right!\n");
printf("Wonderful! You ACE'd this match!\n");
printf("\n");
printf("**** If you play more than 1 game, new match will automatically start ****\n");
printf("\tYou only need to keep guessing for the next letter\n");
printf("------------------------------------------------------------------------------");
printf("\n");
}
}
}
char GetTheGuess()
{
char myGuess;
printf("\t_______________________");
printf("\n\t|What's your guess? >> ");
scanf(" %c", &myGuess);
return myGuess;
}
void LetterGuessRules()
{
printf("\n*** Instruction: ");
printf("\nYou will have 5 attempts to guess the right answer");
printf("\nIf you guess it right, the game will end with your victory.");
printf("\nOtherwise, you will have to guess again.");
printf("\nPlease have fun!");
}
int CompareLetters(char letter, char myGuess)
{
if (letter == myGuess)
{
return 1;
}
else
{
return 0;
}
}
The problem is you increment i twice. First in the for loop, and again here:
//Number of match
printf("\t\tGame %d\n", i += 1);
This is probably because you got Game 0 without it. The simple fix is to start the loop at 1 instead of 0.
Since i means something more than the "loop iterator" I'd call it something more descriptive like gameNum.
/* from 1 to numGames */
int gameNum;
for( gameNum = 1; gameNum <= numGames; gameNum++ ) {
...
}
Note that I check gameNum <= numGames rather than gameNum < numGames since we're now starting from 1.
Also you need to check if the file opened, otherwise it crashes if letterList.txt doesn't exist.
#include <errno.h> /* for errno */
#include <string.h> /* for strerror() */
#include <stdlib.h> /* for exit() */
inPtr = fopen("letterList.txt", "r");
if( inPtr == NULL ) {
fprintf(stderr, "Could not open letterList.txt: %s\n", strerror(errno));
exit(1);
}
Finally, I'd recommend against using #define _CRT_SECURE_NO_WARNINGS while learning C. Those security warnings are important.
I have to create a program that creates a guess the letter game.
Everything is built successfully. However, I run into a problem because the GetTheLetter function and the CompareThe Letters function are combining and printing at the same time. When it prompts the user for a guess, it is also printing out the first if statement of the CompareTheLetters function. What am I doing wrong?
#define _CRT_SECURE_NO_WARNINGS
#define MAXGUESSES 4
#include <stdio.h>
void GuessItRules(); //Game instructions
int PlayOneGame(char letter); //Runs a game
char GetTheLetter(); //Prompts user to guess and returns guess
int IsAWinner(char letter, char userguess); //Returns either a 1 or a 0 depending on if the user guessed correctly
void CompareTheLetters(char letter, char userguess); //Prints message dependent on whether guess comes before, after, or is the letter
int main()
{
//declare additional variables
//declare FILE pointer
FILE* PTR;
int numOfgames; //number of games user wants to play
int i; //iterator
char letter; //letter from file
int win; //variable for the return of the PlayOneGame function
//display instructions
GuessItRules();
//connect to the file HINT: use fopen
PTR = fopen("letters.txt", "r");
//get number of games to play
printf("Let's start playing!\n\nHow many games would you like to play (1-5)?\n\n");
scanf("%d", &numOfgames);
//this for loop will allow the player to play more than one game
//without recompiling
for (i = 0; i < numOfgames; i++)
{
//get a letter from file - use fscanf
fscanf(PTR, "%c", &letter);
//Play one game (Call PlayOneGame function) - remember the function has an int return type
win = PlayOneGame(letter);
//tell the player if they have won or lost (test the variable win)
if (win == 1)
printf("Congrats! You guessed the correct letter!!\n\n");
else if (win == 0)
printf("I'm sorry you did not guess the correct answer :( The letter was %c\n\n", letter);
}
//close file
fclose(PTR);
return 0;
}
//Function definitions
void GuessItRules()
{
printf("Welcome to the Guess the Letter Game!\n-------------------------------------\n\n");
printf("You will have 4 chances to guess the letter per a game\n\n");
}
int PlayOneGame(char letter)
{
int numOfguesses = 0;
int winOrLose = 0; //should be intialized
char userguess; // user guess
//As long as the user has not used up the maximum number
//of guesses and has not guessed correctly
//the game will continue using this while loop
while (numOfguesses < MAXGUESSES && winOrLose == 0)
{
//function call to GetTheletter - returns to userguess variable
userguess = GetTheLetter();
//function call to IsAWinner - returns to winOrLose variable (0 or 1)
winOrLose = IsAWinner(letter, userguess);
//function call to CompareTheLetters - gives the user a message
CompareTheLetters(letter, userguess);
//update counter for number of guesses used
numOfguesses = numOfguesses + 1;
}
return winOrLose; //(0 or 1)
}
char GetTheLetter()
{
char userguess;
printf("Please enter a guess \n\n");
scanf("%c", &userguess);
return (userguess);
}
int IsAWinner(char letter, char userguess)
{
if (userguess == letter)
return (1);
else
return (0);
}
void CompareTheLetters(char letter, char userguess)
{
if (userguess < letter)
{
printf("The letter you are trying to guess comes after %c\n\n", userguess);
}
else if (userguess > letter)
{
printf("The letter you are trying to guess comes before %c\n\n", userguess);
}
else if (userguess == letter)
{
printf("The letter is %c!!!!\n\n", userguess);
printf("Congrats!!! You did it!!\n\n");
}
}
The problem lies in when you ask the user for a letter to guess.
When you use the %c format specifier to scanf, a newline satisfies this format specifier. Since there will have been a newline left in the input buffer from your previous input, this picks up that newline.
Put a space at the beginning of the format string to absorb the newline:
scanf(" %c", &userguess);
Note that other format specifiers such as %d and %f will skip over newlines, unlike %c. So the leading space to skip newlines is needed only for %c.