Interactive, restrictive, dynamic user input - c

I provided a link to a PDF of the assignment instructions.
[TL;DR]
Ask two questions: Are you afraid of the dark? Do you exercise?
Input: ‘Y’ for yes, ‘N’ for no.
If input to second question is 'Y', One additional question: Minutes per day exercised?
Input: Integer > 0. However, if less than 10 per day, unqualified.
Output: Tells the user whether they can to enter ninja training or not.
What I'm having difficulties with:
Dynamically Allocating a Multidimensional String Array
I have only been coding for about a week, and I understand it's probably overkill for this assignment. With that being said, I got an idea while doing this assignment, and while I find it rather challenging to articulate my idea with words, here's an image that I feel captures what I am "visualizing."
Visual posted on https://www.eskimo.com/~scs/cclass/int/sx9b.html by Steve Summit
In this particular assignment, I think it's a waste of memory to keep the user's answer's. Nevertheless, what if I want to write a program that ask the user for a series of inputs, and at the end, correlate them, or make spurious correlations for teh lulz?
Spurious Correlations http://www.tylervigen.com/spurious-correlations by Tyler Vigen
A more practical reason, however, an MBTI Personality Type test? I don't know exactly all the possibilities, but they seem exciting.
That's what I want to achieve with dynamically allocating a multidimensional string array
Will update later
Original post follows...
The code works but some inputs are still allowed, namely any characters entered if the the first element is either Y or N.
Here's the code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
char reference[][2] = { "Y", "N" };
char (*ptr_reference)[2] = reference;
int reference_minutes[1] = { 10 };
int *ptr_reference_minutes = reference_minutes;
char **user_input;
user_input = (char**)malloc(3 * sizeof(char*));
int i;
for (i = 0; i < 3; i++) {
user_input[i] = (char*)malloc(4 * sizeof(char));
}
if (!user_input) {
printf("Could not allocate memory!/n");
exit(1);
}
i = 0;
while (i == 0) {
printf("\nAre you afraid of the dark? Choose either [Y/N], and press enter when finished: \n");
fgets(user_input[i], 4, stdin);
user_input[i] = realloc(user_input[i], sizeof(char));
if (strncmp(user_input[i], *ptr_reference, 1) == 0) {
printf("\nPatience, Young Grasshoper! You are not ready to become a ninja.");
i = 3;
break;
} else if (strncmp(user_input[i], *(ptr_reference + 1), 1) == 0) {
i++;
break;
} else {
printf("\nPlease enter Y for yes or N for no.\n\n");
continue;
}
}
while (i == 1) {
printf("\nDo you exercise? Input [Y/N], and press enter when finished: \n");
fgets(user_input[i], 4, stdin);
if (strncmp(user_input[i], *ptr_reference, 1) == 0) {
i++;
break;
} else if (strncmp(user_input[i], *(ptr_reference + 1), 1) == 0) {
printf("\nDo you even lift, Bro?");
i = 3;
break;
} else {
printf("\nPlease enter Y for yes or N for no.\n\n");
continue;
}
}
while (i == 2) {
int sscanf_result, answer;
printf("\nHow many minutes a day do you exercise? Type an integer greater than 9 and press enter when finished.\n");
fgets(user_input[i], 4, stdin);
sscanf_result = sscanf(user_input[i], "%d", &answer);
if ((sscanf_result == 0) | (sscanf_result == EOF)) {
/* either a non-integer entered or an end-of-line */
printf ("\nYou have to enter an integer!\n");
i = 2;
continue;
} else if (answer < *ptr_reference_minutes) {
printf("\nCome on! You kids are soft! You lack discipline!\n");
i = 3;
break;
} else {
printf("\nYou are a good fit for ninja training.\n");
for (i = 0; i < 3; i++) {
free(user_input[i]);
}
free(user_input);
user_input = NULL;
break;
}
}
return 0;
}

Ok, I could have a look at your program. The problem is not really in allocation but is indeed in string management. In C a string is a null terminated char array (please copy this 100 times...).
When you read a line with fgets, you get the new line character (\n) in your buffer, so if user types Y Enter you get {'Y', '\n', '\0', undeterminated_char }. The following realloc is then plain wrong:
it is likely to be a noop: the compiler shall only give you a buffer at least as large as your requirement. As 4 > 1, it can (and my implementation did) give the original buffer unchanged
you are not allowed to use anything past what you have required, and in particular, you shall not assume that there is a null!
So if you insist in doing a string comparison, you should only ensure that the second char is null: user_input[i][2] = '\0';
But IMHO what is required here is just:
if (user_input[i][0] == 'Y') {
...
That is not all. You try to do a great job in input processing, but just forgot one detail: if a line is longer than the declared size, fgets fills its buffer and leaves the remaining part of the line available for next read.
What follows is only my advice:
You have tried to use everything you know (and probably things you do not fully master...) into a single and simple program. Don't. Keep each program as simple as possible (Keep It Stupid Simple is a general good practice...), and trust your learner to give you other assignments for other patterns. So here you should:
read a line until you find the \n (it might require several fgets)
test the first character of the buffer to be 'Y' or 'N'
test the second one to '\0'
As it is a common requirement in real world, and only after your program works, you could considere:
ignoring initial blank characters
accept lower case as upper case
accept any character after first one (in order to accept Yes and No)
Last advice if nice coding matters for you: once your program works correctly, you should considere posting it in Code Review to get interesting comments on it.

#Serge Ballesta #Serga Ballesta Thank you kindly for your feedback. It was extremly helpful. After following your advice, I was able to write a better program, although, I'm not sure if I was successful in "Keep It Simple, Stupid."
I still can't figure out how to restrict characters after 'Y' or 'N', i.e., Nnb or Nq is accepted as N and goes through to the second question.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main (void) {
char *user_input = NULL;
user_input = (char*)malloc(3*sizeof(char));
int sscanf_result, answer;
int i;
while (!*user_input)
{
printf("\nAre you afraid of the dark? \n\nInput Specifications: Please type 'Y' to indicate yes, or 'N' to indicate no, and then press the 'Enter' or 'return' key when finished.\n\n");
i = 0;
fgets(user_input, sizeof(user_input), stdin);
/* trim off last character */
user_input[strlen(user_input)-1] = '\0';
if (user_input[i] == 'Y')
{
printf("\nFear of the dark? Fear those who lurk in the dark, my friend. Ninjas! You may not train as a ninja!");
free(user_input);
for (i = 0; i < 3; i++)
{
user_input[i] = '\0';
}
exit(0);
}
else if (user_input[i] == 'N')
{
free(user_input);
for (i = 0; i < 3; i++)
{
user_input[i] = '\0';
}
printf("\nDo you exercise?\n\nInput Specifications: Please type 'Y' to indicate yes, or 'N' to indicate no, and then press the 'Enter' or 'return' key when finished.\n\n");
i = 0;
fgets(user_input, sizeof(user_input), stdin);
user_input[strlen(user_input)-1] = '\0';
if (user_input[i] == 'N')
{
printf("\nDo you even lift, Bro?\n");
free(user_input);
for (i = 0; i < 3; i++)
{
user_input[i] = '\0';
}
exit(0);
}
else if (user_input[i] == 'Y')
{
free(user_input);
for (i = 0; i < 3; i++)
{
user_input[i] = '\0';
}
printf("\nHow many minutes a day do you exercise?\n\nInput Specifications: Please type a positive integer and press the 'Enter' or 'return' key when finished.\n\n");
i = 0;
fgets(user_input, sizeof(user_input), stdin);
user_input[strlen(user_input)-1] = '\0';
sscanf_result = sscanf(user_input, "%d", &answer);
if ((sscanf_result == 0) | (sscanf_result == EOF))
{
/* either a non-integer entered or an end-of-line */
printf ("\nPlease type a positive integer and then press the 'Enter' or 'return' key when finished.\n");
free(user_input);
for (i = 0; i < 3; i++)
{
user_input[i] = '\0';
}
continue;
}
if (answer < 10)
{
printf("\nCome on! You kids are soft! You lack discipline!\n");
free(user_input);
for (i = 0; i < 3; i++)
{
user_input[i] = '\0';
}
exit(0);
}
else
{
printf("\nYou may begin training as a ninja!\n");
free(user_input);
for (i = 0; i < 3; i++)
{
user_input[i] = '\0';
}
exit(0);
}
}
else
{
printf("\nInput Error: Please carefully read the input specifications that are provided after each question prompt and then try again.\n");
free(user_input);
for (i = 0; i < 3; i++)
{
user_input[i] = '\0';
}
continue;
}
}
else
{
printf("\nInput Error: Please carefully read the input specifications that are provided after each question prompt and then try again.\n");
free(user_input);
for (i = 0; i < 3; i++)
{
user_input[i] = '\0';
}
continue;
}
}
return 0;
}

Related

I'm trying to make a program in C in which you can play hangman but as soon as it prints the option to guess the letter the program terminates

The program isn't printing after giving me the first chance to guess.
#include <stdio.h>
#include <string.h>
int main() {
char menu;
int c = 0, flag = 0, life = 8;
printf("\nWelcome to Hangman!!!");
printf("\nThis is a game of hangman.");
printf("Player 1 enters a random word and the other has to guess it.");
printf("You get 8 lives in total i.e. you can have a maximum of 8 wrong guesses.");
printf("\n");
printf("Press n for new game\n");
printf("Press q to quit\n");
printf("\n");
scanf("%c", &menu);
int i = 0, j = 0;
char w[20], ch;
if (menu == 'q') {
printf("Exiting...");
printf("Thanks for playing");
}
else if (menu == 'n') {
printf("Player 1 enters a word\n");
scanf("%s", w);
int len = strlen(w);
for (int i = 0; i < len; i++) {
toupper(w[i]);
}
printf("\e[1;1H\e[2J");
char arr[len - 1];
for (int i = 0; i < len - 1; i++) {
arr[i] = '_';
printf("%c", arr[i]);
}
printf("\n");
while (life != 0) {
for (int i = 0; i < len - 1; i++) {
if (arr[i] == '_') {
flag = 1;
break;
}
else {
flag = 0;
}
}
if (flag == 0) {
printf("You Won!!\n");
printf("You Guessed The Word: %s", w);
break;
}
else {
char ans;
printf("Enter a letter between A-Z");
scanf("%c", ans);
toupper(ans);
for (int j = 0; j < len; j++) {
if (ans == w[j]) {
arr[j] = ans;
c++;
}
}
if (c == 0) {
life--;
}
c = 0;
for (int j = 0; j < len; j++) {
printf("%c", arr[j]);
}
printf("\n Lives Remaining= %d \n", life);
}
}
if (life == 0) {
printf("\n You Lost!!! \n");
printf("The Word Was: %s", w);
}
}
else {
printf("Invalid Character");
}
}
Output:
Welcome to Hangman!!!
This is a game of hangman.Player 1 enters a random word and the other has to >guess it.You get 8 lives in total i.e. you can have a maximum of 8 wrong >guesses.
Press n for new game
Press q to quit
n
Player 1 enters a word
Hello
Enter a letter between A-Z
PS C:\Users\arora\Desktop\Programs\C>
There are quite a few problems with your program. Here are the major ones:
You want to use use space prefix in the format string for scanf(" %c", ...) to ensure previous newlines are ignored.
scanf("%c", ans); should be &ans. It causes scanf() to fail rendering the remain of the program non-interactive. Without input from the user the core game logic doesn't work.
Here are some of the other issues:
#include <ctype.h>.
(not fixed) Consider changing the menu logic so 'q' quits, and any other letter starts a game.
Game prompt contains long lines that are hard to read for the player(s).
You use a printf() per line which makes it hard to read. Use a single call and multi-line strings as input.
Try to branch your code less by making use of early return. It makes it easier to read.
Check the return value of scanf(). If it fails then whatever variable it read doesn't have a well defined value.
Ensure that scanf() read no more than 19 bytes into a 20 byte array w. It takes a little macro magic to generate the 19 so I didn't make this change but it's a good idea to #define constants for magic values like the 20.
arr is not \0 terminated (len-1). Most c programmers expect a string so it's not worth the confusion to save 1 byte.
Use a function or macro for the ANSI escape to clear the screen.
Eliminate unused variables i, j.
Reduce scope of variables (declare variables close to where you use them).
The calculation of the flag variable is cumbersome.
(not fixed) The prompt "Enter a letter between A-Z" is somewhat ambiguous. Suggest "... between A and Z".
It's generally a good idea to leave user input as you read. If you care about the repeated toupper() you can create a copy of the user input with letters in upper case, and create another variable to hold the upper case version of the player's guess. This avoid you saying things like you entered the word "BOB" when the actual input was "bob".
You attempt to use toupper() to convert each letter to upper case but don't assign the result to anything so it does not do anything constructive.
Consider some functions to document what each your code does. I added some comments for now.
(mostly not fixed) Consider using better variable names (c, w, arr, flag).
(not fixed) Should you reject a word with your magic '_' value? In general should you validate that the word is reasonable (a-z, len > 0, len < 20)?
(not fixed) Consider, in arr, just storing if a letter was correctly guess (boolean). When evaluating the state show the letter from w if it is already guessed otherwise the _.
(not fixed) If you guess a correct letter again, it's considered a good guess. Should it?
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#define clear() printf("\e[1;1H\e[2J")
int main() {
printf(
"Welcome to Hangman!!!\n"
"\n"
"This is a game of hangman.\n"
"Player 1 enters a random word and the other has to guess it.\n"
"You get 8 lives in total i.e. you can have a maximum of 8 wrong guesses.\n"
"\n"
"Press n for new game\n"
"Press q to quit\n"
);
char menu;
if(scanf(" %c",&menu) != 1) {
printf("scanf failed\n");
return 1;
}
switch(menu) {
case 'q':
printf(
"Exiting..."
"Thanks for playing\n"
);
return 0;
case 'n':
break;
default:
printf("Invalid Character");
return 1;
}
printf("Player 1 enters a word\n");
char w[20];
if(scanf("%19s", w) != 1) {
printf("scanf failed\n");
return 1;
}
clear();
char arr[20];
int len=strlen(w);
for(int i=0;i<len;i++) {
arr[i]='_';
}
arr[len] = '\0';
int life=8;
for(;;) {
printf("%d Lives Remaining\n", life);
// read a guess from player
for(int i = 0; i < len; i++) {
printf("%c", arr[i]);
}
printf(" Enter a letter between A-Z ");
char guess;
if(scanf(" %c", &guess) != 1) {
printf("scanf failed\n");
return 1;
}
// determine if any of the letters are in the secret word
int c = 0;
for(int i=0; i<len; i++) {
if(toupper(guess) == toupper(w[i])) {
arr[i]=guess;
c = 1;
}
}
if(c==0) {
life--;
}
// game over?
int flag = 0;
for(int i = 0; i<len; i++) {
if(arr[i]=='_') {
flag=1;
break;
}
}
if(flag==0) {
printf("You Won!!\n");
printf("You Guessed The Word: %s\n",w);
break;
}
if(life==0) {
printf("\n You Lost!!!\n");
printf("The Word Was: %s\n", w);
break;
}
}
}

Hello i got an assignment in c programing and i dont really understand the c/malloc function i think

Hello i got an assignment in c programing and i dont really understand the c/malloc function i think,
they told us that we need to do the free function after using this function, but every time i do free it breaks the program
The assignment is :
collect an input string.
every upper case letter to lower
every lower case letter to upper
if there is number do series of numbers from '9' until the input number but with out it (for '6' do '9','8','7'. (with out 6))
if there is other stuff don't add it in to the out put.
input example : A$q6#G4
output example : aQ987g98765
it is not allowed to change the input string.
in the input allowed to be every thing.
the output sting needs to be exactly in the array size
(if 123 = the size of will be input[2])
photo of the error
the error : wntdll.pdb contains the debug information required to find the source for the module ntdll.dll
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
/* Function declarations */
char Ex1FNumbers(char);
char Ex1FLetters(char);
/* ------------------------------- */
//
int main()
{
system("cls"); //delete when send
int select = 0, i, all_Ex_in_loop = 0;
printf("Run menu once or cyclically?\n(Once - enter 0, cyclically - enter other number) ");
if (scanf_s("%d", &all_Ex_in_loop) == 1)
do
{
for (i = 1; i <= 3; i++)
printf("Ex%d--->%d\n", i, i);
printf("EXIT-->0\n");
do {
select = 0;
printf("please select 0-3 : ");
scanf_s("%d", &select);
} while ((select < 0) || (select > 3));
switch (select)
{
case 1: //Ex1
{
int size, i, n, counter = 0;
char inPut[] = "";
char outPut[] = "";
char* Ptr_inPut_address, * Ptr_outPut_address, num;
printf("Please enter a string :\n");
scanf("%s", inPut);
size = strlen(inPut);
Ptr_outPut_address = &outPut;
Ptr_inPut_address = (char*) calloc(size+1 , sizeof(char));
Ptr_outPut_address = (char*) calloc(0, sizeof(char));
if (!Ptr_inPut_address || !Ptr_outPut_address)
{
break;
}
for (i = 0; i < size; i++)
{
Ptr_outPut_address = (char*)realloc(Ptr_outPut_address, counter);
if (inPut[i] >= 'a' && inPut[i] <= 'z' || inPut[i] >= 'A' && inPut[i] <= 'Z')
{
if (inPut[i] >= 'a' && inPut[i] <= 'z')
{
outPut[counter++] = inPut[i] - 32;
}
else
{
outPut[counter++] = inPut[i] + 32;
}
}
else if (inPut[i] <= '9' && inPut[i] >= '0')
{
n = '9' - inPut[i];
Ptr_outPut_address = (char*)realloc(Ptr_outPut_address, counter + n);
for (n; n > 0; n--) // "o" of 8 and not "n" because 8 is the max num for this "for".
{
outPut[counter++] = inPut[i] + n;
}
}
}
Ptr_outPut_address = (char*)realloc(Ptr_outPut_address, counter);
outPut[counter] = '\0';
Ptr_outPut_address = &outPut;
printf("%s\n", Ptr_outPut_address);
if (Ptr_outPut_address != NULL)
{
free(Ptr_outPut_address);
}
if (Ptr_inPut_address != NULL)
{
free(Ptr_inPut_address);
}
} break;
case 2: //Ex2
{
}break;
case 3: //Ex3
{
}break;
}
} while (all_Ex_in_loop && select);
system("pause");//delete when send
main();//delete when send
//return 0; // return when send
}
char inPut[] = "";
char outPut[] = "";
This declares two arrays that contain exactly one char, initializing them to '\0'. That's what the above means in C. This does not mean that these two arrays will have infinite size and can store any string. That's not how this works. But then, immediately afterwards:
scanf("%s", inPut);
This is guaranteed to overflow the array, since it is capable of holding only one char. Any string this reads will have at least two chars: the single read character, followed by '\0'. This results in memory corruption and undefined behavior.
There are several other bugs in the shown code. One more example:
Ptr_outPut_address = &outPut;
This has the effect of setting this variable to the starting address of a char array that was declared earlier.
Ptr_outPut_address = (char*)realloc(Ptr_outPut_address, counter);
You can only realloc something that was malloced, realloced, or calloced. No exceptions. You cannot realloc anything else. The char array was not malloced, realloced, or calloced. C does not work this way.
Several other problems exists in the shown code. Looks like this entire program was written all at once, before an attempt was made to test everything. This approach is very unlikely to succeed, and will likely produce many different kinds of bugs, such as the one that I've described. This makes it difficult to analyze and fix everything, since you're not looking for just one bug, but an unknown number of bugs. Plus it is likely that there will be an eventual realization that some or most of what was written need to be rewritten from scratch since the shown approach turned out to be fundamentally wrong.
Which is what you should probably do: start from scratch, write only a few lines of code, before testing them, and making sure that they work correctly before proceeding to write more code. If you attempt to fix just the problems that I explained it's likely that this will just create other problems, additionally, there are other problems as well, I just didn't mention them. The entire approach that was used here needs to be changed, fundamentally.

How to loop through a users input using getchar() and check to see if the characters are an alphabet or a digit

Im totally new to programming, I picked up a C manual to learn on my own. I dont want to use an array as Im trying to practice with getchar(). I want to be able to output an error message if the user enters anything other than a digit or an alphabet. I am also trying to practice the C library function isalpha() and isdigit(). This is what I wrote so far, but my output is not quite right.
Input 1: "hello"
Expected output : "valid output"
Input 2: "hello5"
Expected output : "valid output"
Input 3: "hello!"
Expected output : "invalid output"
But my program returns "valid input" for all the three inputs above
Please help a newbie try to learn. I greatly appreciate it.
#include <stdio.h>
#include <ctype.h>
int main ()
{
char ch;
int len;
int valid;
printf("Enter a word: ");
for(length = 0; (ch = getchar()) != '\n'; len++)
{
if(isalpha(ch) || isdigit(ch))
{
valid = 1;
}
else
{
valid = 0;
}
}
printf("Input length: %d\n", len);
if (valid == 1)
{
printf("Valid\n");
}
if(valid == 0)
{
printf("\n");
}
return 0;
}
You were almost there. Few pitfalls:
1st there is a typo for your variable name “length” instead of “len”.
2nd As Mitchel0022 stated, your program will always display “valid” providing the last character entered is valid because you reassign a new value for the variable ‘valid’ on each iteration. but you don’t have to use a ‘break statement since you need the loop to continue in order to get your length, so stick with your flag.
Now your program should run fine. Copy and paste the code below:
#include <stdio.h>
#include <ctype.h>
int main ()
{
char ch;
int len;
//flag is set true
int valid = 1;
printf("Enter a word: \n");
for(len = 0; (ch = getchar()) != '\n'; len++)
{
//change flag if character is not number nor letter
if(!isalpha(ch) && !isdigit(ch))
{
valid = 0;
}
}
printf("Input length: %d\n", len);
if (valid == 1)
{
printf("Valid\n");
}
else
{
printf("Invalid\n");
}
return 0;
}
The error here is when checking every character, by toggling valid to 0 and 1 really you are only looking at the last character. What you want to do is for every character as soon as you find one character that is not a digit/alpha character then exit
you can replace your if/else with something like this
vlaid = 1; //always assume its valid
for(int length = 0; (ch = getchar()) != '\n'; length++)
{
if(!isalnum(ch)) //if not a valid character
{
valid = 0; // string is not valid anymore
break; // we can exit the loop
}
}

I'm trying to code a number guessing game

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.

Run-Time Check Failure #2 - s (C language on Microsoft Visual C++ 2015 (community version))

I have faced this problem : Run-Time Check Failure #2 - S in visual studio 15 . This happen before where I also trying to use array in C language. According to my textbook, what happen to the below code is
char myname[20];
printf("Type your name:");
scanf("%19s", myname);
printf("\n\n%s,Welcome to the class\n", myname);
According to the textbook, if I input my name as for example : Tony Stark, the problem will only scan the Tony and ignore all the thing after the blank space. However, when I try it, it appear Run-Time Check Failure #2.
Also in below code
#include<stdio.h>
int main(void)
{
char name[30];
int seat[30] = { 0 };
int i, seatt, j;
char decision[1];
do
{
printf("Hi, what is your name? ");
scanf("%s", name);
printf("Welcome %s!\n\n", name);
printf("*********************\n");
printf("CINEMA 1 SEATING PLAN\n");
printf("*********************");
for ( i = 0; i < 30; i++)
{
if (i % 5 == 0)
{
printf("\n\n");
}
if (seat[i] == 0)
{
printf("%3d", i);
}
else
{
printf("%3s", "**");
}
}
printf("\n\n*********************");
do
{
printf("\n\nWhich seat do you want? ");
scanf("%d", &seatt);
if (seat[seatt]!=0)
{
printf("Sorry, seat is taken!\n");
for ( j = 0; j < 30; j++)
{
if (seat[j] == 0)
{
printf("Suggest to take seat: %d", j);
break;
}
}
}
} while (seat[seatt] != 0);
++seat[seatt];
printf("\n\nWould you like to make next booking (Y or N)? ");
scanf("%s", decision);
printf("\n\n");
if (decision[0] == 'Y' || decision[0] == 'y')
{
system("cls");
}
} while (decision[0] == 'Y' || decision[0] == 'y');
printf("See you again!\n");
return 0;
}
Everything is ok, until when until the last part where it ask me where to book the next ticket, if I keyin another other than Y, it also appear the same problem.
You are not very careful with respect to stack overflows. In the second code, you use:
char decision[1];
scanf("%s", decision);
The scanf will append a trailing \0 termination character already interfering with some other data on the stack even if you really only input a single character. More disaster is at hand when the user input is longer. Scan with a "%c" format in this case.
What you should do is you should scan in character instead of String
Do:
char decision[1];
scanf("%c", decision);

Resources