Comparison of strings for Mastermind game - c

I am writing a program that simulates the game Mastermind, but I am struggling with how to compare guessed pattern to key pattern.
The game conditions are a little bit changed:
patterns consist of letters.
if an element of guessed pattern is equal to element of key pattern, and also index is equal, then print b.
if an element of guessed pattern is equal to element of key pattern, but index is not, then print w.
if an element of guessed pattern is not equal to element of key pattern, print dot.
in feedback about guessed pattern, 'b's come first, 'w's second, '.'s last.
Original key vs guess pattern match code
for (i=0; i<patternlength; i++)
{
for (x=0; x<patternlength; x++)
{
if (guess[i]==key[x] && i==x)
printf("b");
if (guess[i]==key[x] && i!=x)
printf("w");
if (guess[i]!=key[x])
printf(".");
}
}
Revised code
This is using some of the answer provided by Jonathan Leffler. Unfotunately, it isn't working correctly yet; can you help me?
The functions length() and guessnum() are defined already.
#include<stdio.h>
#include<string.h>
int length()
{
int length;
printf("Enter the pattern length: ");
scanf("%d", &length);
return length;
}
int guessnum()
{
int guessnum;
printf("Enter the number of guesses: ");
scanf("%d", &guessnum);
return guessnum;
}
int main(void)
{
int patternlength = length();
char key[patternlength+1];
char keyc[patternlength+1];
int numguess = guessnum();
char guess[patternlength+1];
printf("Input the key pattern with no spaces: ");
scanf("%s", key);
int i,j,count = 1;
int bcount = 0, wcount = 0;
char guessc[patternlength+1];
guessc[0] = '\0';
int ind;
char output[patternlength];
for (ind=0; ind<(patternlength+1); ind++)
output[ind]='\0';
char outputc[patternlength+1];
char guessold[patternlength+1];
for (ind=0; ind<(patternlength+1); ind++)
guessold[ind]='\0';
while (strcmp(key, guess) !=0 && count<=numguess)
{
if(count>1)
strcpy(guessold, guess);
strcpy(keyc, key);
printf("Input a guess pattern with no spaces: ");
scanf("%s", guess);
if (count>1)
printf("%d: %s %s\n", count-1, output, guessold);
strcpy(guessc, guess);
wcount = 0;
bcount = 0;
printf("%d: ", count);
for (i = 0; i < patternlength; i++)
{
if (keyc[i] == guessc[i])
{
putchar('b');
keyc[i] = guessc[i] = '.';
bcount++;
for (ind=0; ind<patternlength; ind++)
output[ind]='b';
}
}
if (bcount != patternlength)
{
for (i = 0; i < patternlength; i++)
{
if (guessc[i] != '.')
{
for (j = 0; j < patternlength; j++)
{
if (guessc[i] == keyc[j])
{
wcount++;
putchar('w');
for (ind=0; ind<patternlength; ind++)
if (output[ind]!='b')
output[ind]='w';
keyc[j] = guessc[i] = '.';
break;
}
}
}
}
for (i = bcount + wcount; i < patternlength; i++)
putchar('.');
for (ind=bcount+wcount; ind<patternlength; ind++)
output[ind]='.';
}
count++;
printf(" %s\n", guess);
strcpy(outputc, output);
}
if (strcmp(key, guess) != 0)
{
printf("You did not guess the pattern!\n");
}
else
{
printf("You guessed the pattern!\n");
}
return 0;
}
output of code above:
Enter the pattern length: 3
Enter the number of guesses: 3
Input the key pattern with no spaces: abc
Input a guess pattern with no spaces: acb
1: bww acb
Input a guess pattern with no spaces: abb
1: bbb acb
2: bb. abb
Input a guess pattern with no spaces: abc
2: bb. abb
3: bbb abc
You guessed the pattern!
required output:
Enter the pattern length: 3
Enter the number of guesses: 3
Input the key pattern with no spaces: abc
Input a guess pattern with no spaces: acb
1: bww acb
Input a guess pattern with no spaces: abb
1: bww acb
2: bb. abb
Input a guess pattern with no spaces: abc
1: bww acb
2: bb. abb
3: bbb abc
You guessed the pattern!
I tried to use one more string, which will store in it feedback of the guess, but when there are several guesses, i think i should use some kind of loop to print feedback of all previous guesses each time new guess is made. but it is difficult to me figure out how should i write this loop with the structure suggested by Jonathan Leffler.
I added my last correction to the code, so I almost reached the desired output. does anyone have an idea what is possible to do here?

I assume that there is a structure (for easy copying of the array contained within it), and that the input validation ensures that the key and the guess are the same length, and that the key and the guess only contain alphabetic characters.
typedef struct pattern
{
char pattern[8];
} pattern;
size_t print_scoring(pattern key, pattern guess)
{
size_t n = strlen(key.pattern);
assert(n == strlen(guess.pattern));
size_t bcount = 0;
for (size_t i = 0; i < n; i++)
{
if (key.pattern[i] == guess.pattern[i])
{
putchar('b');
key.pattern[i] = guess.pattern[i] = '.';
bcount++;
}
}
if (bcount != n)
{
size_t wcount = 0;
for (size_t i = 0; i < n; i++)
{
if (guess.pattern[i] != '.')
{
for (size_t j = 0; j < n; j++)
{
if (guess.pattern[i] == key.pattern[j])
{
wcount++;
putchar('w');
guess.pattern[i] = key.pattern[j] = '.';
break;
}
}
}
}
for (size_t i = bcount + wcount; i < n; i++)
putchar('.');
}
return bcount;
}
The function works on copies of the key and the pattern (the structures are passed by value, not by pointer). It returns the number of correct guesses in the correct position; it assumes the calling code knows how long the pattern is, so the calling code can tell when the pattern is correct. It marks guess and key characters as 'used' by replacing them with a '.'. This is important to prevent a key of "aba" and a guess of "aaa" being incorrectly marked as bbw rather than correctly as bb.. This would be more important in keys/guesses of length 4 or more.
Test Harness
#include <assert.h>
#include <string.h>
#include <stdio.h>
int main(void)
{
enum { NUM_KEYS = 3, NUM_GUESSES = 5 };
pattern k[] = { { "abc" }, { "aba" }, { "aaa" } };
pattern g[] = { { "aaa" }, { "aab" }, { "abc" }, { "cba" }, { "bab" } };
for (int i = 0; i < NUM_KEYS; i++)
{
for (int j = 0; j < NUM_GUESSES; j++)
{
printf("Key: %s; Guess %s; Score: ", k[i].pattern, g[j].pattern);
size_t n = print_scoring(k[i], g[j]);
if (n == 3)
printf(" -- Correct!");
putchar('\n');
}
}
return(0);
}
Test Output
Key: abc; Guess aaa; Score: b..
Key: abc; Guess aab; Score: bw.
Key: abc; Guess abc; Score: bbb -- Correct!
Key: abc; Guess cba; Score: bww
Key: abc; Guess bab; Score: ww.
Key: aba; Guess aaa; Score: bb.
Key: aba; Guess aab; Score: bww
Key: aba; Guess abc; Score: bb.
Key: aba; Guess cba; Score: bb.
Key: aba; Guess bab; Score: ww.
Key: aaa; Guess aaa; Score: bbb -- Correct!
Key: aaa; Guess aab; Score: bb.
Key: aaa; Guess abc; Score: b..
Key: aaa; Guess cba; Score: b..
Key: aaa; Guess bab; Score: b..
From the comments
Why is my code not working? Can you have a look at it please?
The problem is that I cannot go the next step after entering a guess pattern. Maybe I don't see some mistakes in my code.
Instant response:
One of the key points in my answer is that the comparison code is working on copies of the data entered. It is a destructive comparison algorithm, writing dots over the data. Your attempt to merge my code into your program did not retain the separate function working on separate copies of the data which were a crucial part of this answer. The use of a structure was there to make it easy to pass copies of the data around (it's the one time C copies arrays for you automatically). The comparison code should be in a function of its own, not inline in main().
However, we can get the code given to work. There were some transcription errors (marked BUG below), and some other problems (also identified below).
Working version of revised code in question
This is a working version of your program annotated with the crucial changes. Non-crucial changes include spacing around operators and using an indent level of 4 spaces.
#include <string.h>
#include <stdio.h>
static int length(void) { return 3; } // Dummy function
static int guessnum(void) { return 5; } // Dummy function
int main(void)
{
int patternlength = length();
char key[patternlength+1]; // Buffer overflow
char keyc[patternlength+1]; // Copy of key
int numguess = guessnum();
char guess[patternlength+1]; // Buffer overflow
printf("Input the key pattern with no spaces: ");
scanf("%s", key);
int i,j,count = 1;
int bcount = 0, wcount = 0;
char guessc[patternlength+1]; // Buffer overflow
guessc[0] = '\0'; // Initialize!
while (strcmp(key, guess) != 0 && count <= numguess)
{
strcpy(keyc, key); // Copy key too
printf("Input a guess pattern with no spaces: ");
scanf("%s", guess);
strcpy(guessc, guess);
wcount = 0; // Reinitialize
bcount = 0; // Reinitialize
printf("%d: ", count);
for (i = 0; i < patternlength; i++)
{
if (keyc[i] == guessc[i])
{
putchar('b');
keyc[i] = guessc[i] = '.';
bcount++;
}
}
if (bcount != patternlength) // Extraneous semi-colon excised! ;
{
for (i = 0; i < patternlength; i++)
{
if (guessc[i] != '.')
{
//for (j = 0; i < patternlength; j++) BUG
for (j = 0; j < patternlength; j++)
{
//if (guessc[i] == keyc[i]) BUG
if (guessc[i] == keyc[j])
{
wcount++;
putchar('w');
//guessc[i] = keyc[i]; BUG
keyc[j] = guessc[i] = '.';
break;
}
}
}
}
for (i = bcount + wcount; i < patternlength; i++)
putchar('.');
}
count++;
printf(" %s\n", guess);
}
if (strcmp(key, guess) != 0)
{
printf("You did not guess the pattern!\n");
}
else
{
printf("You guessed the pattern!\n");
}
return 0;
}
The compiler told me about the stray semi-colon:
ss2.c: In function ‘main’:
ss2.c:36:37: warning: suggest braces around empty body in an ‘if’ statement [-Wempty-body]
If your compiler didn't tell you about that, you aren't using enough warnings (or you need a better compiler). I routinely use:
gcc -O3 -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
-Wold-style-definition ss2.c -o ss2
The working code passes that without a whimper.
Sample output
Input the key pattern with no spaces: abc
Input a guess pattern with no spaces: aaa
1: b.. aaa
Input a guess pattern with no spaces: bbb
2: b.. bbb
Input a guess pattern with no spaces: ccc
3: b.. ccc
Input a guess pattern with no spaces: cab
4: www cab
Input a guess pattern with no spaces: abc
5: bbb abc
You guessed the pattern!
Final debug-laden code
This is mainly to show the level of printing that I used to see what was going wrong. Using stderr for the diagnostic output meant the diagnostics did not interfere with the buffering of stdout as the output line was built up. That and the use of no indentation on the debug code also meant it was easy to strip the debug code out.
#include <string.h>
#include <stdio.h>
static int length(void) { return 3; }
static int guessnum(void) { return 5; }
int main(void)
{
int patternlength = length();
char key[patternlength+1]; // Buffer overflow
char keyc[patternlength+1]; // Copy of key
int numguess = guessnum();
char guess[patternlength+1]; // Buffer overflow
printf("Input the key pattern with no spaces: ");
scanf("%s", key);
int i,j,count = 1;
int bcount = 0, wcount = 0;
char guessc[patternlength+1]; // Buffer overflow
guessc[0] = '\0'; // Initialize!
while (strcmp(key, guess) != 0 && count <= numguess)
{
strcpy(keyc, key); // Copy key too
printf("Input a guess pattern with no spaces: ");
scanf("%s", guess);
strcpy(guessc, guess);
fprintf(stderr, "B1: (%s) vs (%s)\n", key, guess);
fprintf(stderr, "B2: (%s) vs (%s)\n", keyc, guessc);
wcount = 0; // Reinitialize
bcount = 0; // Reinitialize
printf("%d: ", count);
for (i = 0; i < patternlength; i++)
{
fprintf(stderr, "L1a: %d\n", i);
if (keyc[i] == guessc[i])
{
fprintf(stderr, "L1b: B (%c = %c)\n", keyc[i], guessc[i]);
putchar('b');
keyc[i] = guessc[i] = '.';
bcount++;
}
}
fprintf(stderr, "M1: (%s) vs (%s)\n", keyc, guessc);
if (bcount != patternlength) // Extraneous semi-colon excised! ;
{
fprintf(stderr, "L2a: b = %d (%s) vs (%s)\n", bcount, keyc, guessc);
for (i = 0; i < patternlength; i++)
{
fprintf(stderr, "L2b: %d (%c)\n", i, guessc[i]);
if (guessc[i] != '.')
{
fprintf(stderr, "L2c: %d (%c)\n", i, guessc[i]);
//for (j = 0; i < patternlength; j++) BUG
for (j = 0; j < patternlength; j++)
{
fprintf(stderr, "L2d: %d (%c) vs %d (%c)\n", i, guessc[i], j, keyc[j]);
//if (guessc[i] == keyc[i]) BUG
if (guessc[i] == keyc[j])
{
fprintf(stderr, "L2e: W %d (%c) vs %d (%c)\n", i, guessc[i], j, keyc[j]);
wcount++;
putchar('w');
keyc[j] = guessc[i] = '.';
//guessc[i] = keyc[i]; BUG
break;
}
}
}
}
fprintf(stderr, "L3a: %d + %d vs %d\n", bcount, wcount, patternlength);
for (i = bcount + wcount; i < patternlength; i++)
fprintf(stderr, "L3b: D %d\n", i),
putchar('.');
}
count++;
printf(" %s\n", guess);
}
if (strcmp(key, guess) != 0)
{
printf("You did not guess the pattern!\n");
}
else
{
printf("You guessed the pattern!\n");
}
return 0;
}
Note the trick with the comma operator after the last fprintf() function call.
Keeping a record of previous guesses and marks
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void err_exit(const char *msg, ...);
static void prompt_str(const char *prompt, int bufsiz, char *buffer);
static int prompt_int(const char *prompt);
int main(void)
{
int patternlength = prompt_int("Length of key");
int numguess = prompt_int("Number of guesses");
char key[patternlength+1];
char guesses[numguess][patternlength+1];
char marks[numguess][patternlength+1];
int count = 0;
prompt_str("Input the key pattern with no spaces", patternlength, key);
while (count < numguess)
{
char guess[patternlength+1];
char keyc[patternlength+1];
char mark[patternlength+1];
char *marker = mark;
int wcount = 0;
int bcount = 0;
strcpy(keyc, key);
prompt_str("Input a guess pattern with no spaces", patternlength, guess);
strcpy(guesses[count], guess);
for (int i = 0; i < patternlength; i++)
{
if (keyc[i] == guess[i])
{
*marker++ = 'b';
keyc[i] = guess[i] = '.';
bcount++;
}
}
if (bcount == patternlength)
break;
for (int i = 0; i < patternlength; i++)
{
if (guess[i] == '.')
continue;
for (int j = 0; j < patternlength; j++)
{
if (guess[i] == keyc[j])
{
wcount++;
*marker++ = 'w';
keyc[j] = guess[i] = '.';
break;
}
}
}
for (int i = bcount + wcount; i < patternlength; i++)
*marker++ = '.';
*marker = '\0';
strcpy(marks[count], mark);
count++;
for (int i = 0; i < count; i++)
printf("Guess: %d [%s] marks [%s]\n", i, guesses[i], marks[i]);
}
if (count >= numguess)
printf("You did not guess the pattern (which was [%s])!\n", key);
else
printf("You guessed the pattern!\n");
return 0;
}
static void prompt_str(const char *prompt, int bufsiz, char *buffer)
{
char fmt[8];
int c;
sprintf(fmt, "%%%ds", bufsiz);
printf("%s: ", prompt);
if (scanf(fmt, buffer) != 1)
err_exit("Unexpected input failure\n");
while ((c = getchar()) != EOF && c != '\n')
;
}
static int prompt_int(const char *prompt)
{
int number;
printf("%s: ", prompt);
if (scanf("%d", &number) != 1)
err_exit("Unexpected input failure\n");
if (number <= 0 || number > 9)
err_exit("Number should be in the range 1..9 (not %d)\n", number);
return(number);
}
static void err_exit(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
exit(1);
}
Introduced functions prompt_int() and prompt_str() to get data. The prompt_str() function is reasonably resilient against overflows. There's an error reporting function. The dummy functions are replaced. Here is some sample output. From here on, you're on your own!
Length of key: 4
Number of guesses: 8
Input the key pattern with no spaces: abcd
Input a guess pattern with no spaces: aaaa
Guess: 0 [aaaa] marks [b...]
Input a guess pattern with no spaces: dcba
Guess: 0 [aaaa] marks [b...]
Guess: 1 [dcba] marks [wwww]
Input a guess pattern with no spaces: cdba
Guess: 0 [aaaa] marks [b...]
Guess: 1 [dcba] marks [wwww]
Guess: 2 [cdba] marks [wwww]
Input a guess pattern with no spaces: abcd
You guessed the pattern!

You are basically matching all the elements of key with all elements of guess, which is not what you want.
You need to iterate on guess and differentiate the three cases
Element guessed correctly
Element not guessed correctly but present in the key
Element not guessed correctly and not present in the key
int i,k;
bool found;
for (i=0; i<patternlength; i++)
{
if (key[i] == guess[i])
{
printf("b");
}
else
{
found = false;
for (k=0; k<patternlength && !found; k++)
{
if (key[k] == guess[i])
{
found = true;
printf("w");
}
}
if (!found)
{
printf(".");
}
}
}
Note that in the internal loop, I stop when I find an element with && !found. Otherwise, I'd fall into a problem similar to yours (It will print w for every element that matches my guess present in key)

Related

How to detect duplicate string using strcmp()

#include<stdio.h>
#include<stdio.h>
#include<string.h>
struct stud
{
char nam[20];
int num;
char letter[5];
};
int main()
{
struct stud s[5];
int i, j;
for(i = 0; i < 5; i++){
printf("Enter the name of student #%d: ", i+1);
scanf("%s", s[i].nam);
printf("Enter the number grade of student #%d: ", i+1);
scanf("%d", &s[i].num);
}
for (j = 0; j < i; j++) {
if (strcmp(s[i].nam, s[j].nam) == 0)
printf("Error. Duplicate name detected.");
}
for(i = 0; i < 5; i++){
if(s[i].num >= 90 )
strcpy(s[i].letter, "A");
else if(s[i].num >= 80)
strcpy(s[i].letter, "B");
else if(s[i].num >= 70)
strcpy(s[i].letter, "C");
else if(s[i].num >= 60)
strcpy(s[i].letter, "D");
else
strcpy(s[i].letter, "F");
}
for(i = 0; i < 5; i++)
printf("\n%s has a %s ", s[i].nam, s[i].letter);
return 0;
}
This program has the user enter 5 names and 5 numeric grades, which will then result in the output of their respective letter grades for that student. I'm trying to make it so if the user enters a duplicate name, and message will print saying they can't do that. My attempt in trying to do this is as follows:
for (j = 0; j < i; j++) {
if (strcmp(s[i].nam, s[j].nam) == 0)
printf("Error. Duplicate name detected.");
}
Where I believe that s[j] is the previous string, and compare to see if it equals 0(duplicate) and prints a message. This obviously doesn't work however so I would like to know how to fix this so it can correctly detect duplicate names. Thank you.
Also I have posted this question before but the person that provided an explanation deleted their response before I could provide further questions and ask for clarification. So I am posting this again with an attempt in seeking further aid in what I did wrong in my code.
At the start of the detection loop, i is already 5, so using s[i] is undefined behavior
In your detection loop, i is invariant. you are just comparing a name against the last one [except for the UB, of course].
You need two loops to compare all names against one another.
Also, using 5 everywhere is a "magic number". Better to use a #define (e.g. SMAX)
In the code below, I use cpp conditionals to denote old vs. new code:
#if 0
// old code
#else
// new code
#endif
#if 1
// new code
#endif
Here is the corrected code. It is annotated with the bugs and fixes:
#include <stdio.h>
#include <stdio.h>
#include <string.h>
struct stud {
char nam[20];
int num;
char letter[5];
};
#define SMAX 5 // maximum number of students
int
main()
{
struct stud s[SMAX];
int i, j;
for (i = 0; i < SMAX; i++) {
printf("Enter the name of student #%d: ", i + 1);
scanf("%s", s[i].nam);
printf("Enter the number grade of student #%d: ", i + 1);
scanf("%d", &s[i].num);
}
// NOTE/BUG: i is already SMAX, so using s[i] is UB (undefined behavior)
// NOTE/BUG: i never changes
#if 0
for (j = 0; j < i; j++) {
if (strcmp(s[i].nam, s[j].nam) == 0)
printf("Error. Duplicate name detected.");
}
#else
for (i = 0; i < (SMAX - 1); i++) {
for (j = i + 1; j < SMAX; j++) {
if (strcmp(s[i].nam, s[j].nam) == 0)
printf("Error. Duplicate name detected -- %s\n",s[j].nam);
}
}
#endif
for (i = 0; i < SMAX; i++) {
if (s[i].num >= 90)
strcpy(s[i].letter, "A");
else if (s[i].num >= 80)
strcpy(s[i].letter, "B");
else if (s[i].num >= 70)
strcpy(s[i].letter, "C");
else if (s[i].num >= 60)
strcpy(s[i].letter, "D");
else
strcpy(s[i].letter, "F");
}
// NOTE/BUG: newline should go at the end of the printf to prevent a hanging
// last line
#if 0
for (i = 0; i < SMAX; i++)
printf("\n%s has a %s ", s[i].nam, s[i].letter);
#else
for (i = 0; i < SMAX; i++)
printf("%s has a %s\n", s[i].nam, s[i].letter);
#endif
return 0;
}
UPDATE:
Thanks for the tip! On a side note, how would I make it so while the user is entering the duplicate names, the error message appears and the program ends right there.For example: Enter the name of student 1: dan Enter grade: 87 Enter the name of student 2: dan Enter the grade: 78 Error. No duplicate names allowed. And then the program ends there. –
User234567
Easy enough. I put the duplication detection code into functions.
But, I've added a few more enhancements so this may help you with your learning ;-)
I added reprompting the user if they enter a duplicate.
I hate scanf ;-) I reworked the prompting code by putting it into two functions. It will work better if input is a file. This is useful during testing
I changed the conversion from grade number to grade letter to use a table.
Anyway, here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
struct stud {
char nam[20];
int num;
char letter[5];
};
struct letter {
int num;
const char *letter;
};
#define LET(_num,_let) \
{ .num = _num, .letter = _let }
struct letter letters[] = {
LET(90,"A"),
LET(80,"B"),
LET(70,"C"),
LET(60,"D"),
LET(0,"F"),
LET(0,NULL)
};
#define SMAX 5 // maximum number of students
// chkall -- check entire array for duplicates
int
chkall(const struct stud *s,int smax)
{
int i;
int j;
int dup = 0;
for (i = 0; i < (smax - 1); i++) {
for (j = i + 1; j < smax; j++) {
if (strcmp(s[i].nam, s[j].nam) == 0) {
printf("Error. Duplicate name detected -- %s\n",s[j].nam);
dup += 1;
}
}
}
return dup;
}
// chkone -- check a given entry for duplicate (as they are added)
int
chkone(const struct stud *s,int i)
{
int j;
int dup = 0;
for (j = 0; j < i; j++) {
if (strcmp(s[i].nam, s[j].nam) == 0) {
printf("Error. Duplicate name detected -- %s\n",s[j].nam);
dup += 1;
}
}
return dup;
}
// prompt_string -- prompt user for a string
char *
prompt_string(const char *what,int i,char *buf,size_t siz)
{
static int tty = -1;
// decide if our input is tty or file
if (tty < 0) {
struct winsize ws;
tty = ioctl(0,TIOCGWINSZ,&ws);
tty = (tty >= 0);
}
printf("Enter the %s of student #%d: ", what, i + 1);
fflush(stdout);
char *cp = fgets(buf,siz,stdin);
do {
// handle EOF
if (cp == NULL)
break;
buf[strcspn(buf,"\n")] = 0;
// echo the data if input is _not_ a tty
if (! tty)
printf("%s\n",buf);
} while (0);
return cp;
}
// prompt_number -- prompt user for a number
long long
prompt_number(const char *what,int i)
{
char *cp;
char buf[100];
long long val;
while (1) {
cp = prompt_string(what,i,buf,sizeof(buf));
// handle EOF
if (cp == NULL) {
val = -1;
break;
}
// decode the number
val = strtoll(buf,&cp,10);
if (*cp == 0)
break;
printf("invalid number syntax -- '%s'\n",cp);
}
return val;
}
int
main(void)
{
struct stud s[SMAX];
int i;
for (i = 0; i < SMAX; i++) {
while (1) {
prompt_string("name",i,s[i].nam,sizeof(s[i].nam));
if (! chkone(s,i))
break;
}
s[i].num = prompt_number("number grade",i);
}
// recheck all entries
// this will _never_ report a duplicate because of the chkone above
chkall(s,SMAX);
for (i = 0; i < SMAX; i++) {
for (struct letter *let = letters; let->letter != NULL; ++let) {
if (s[i].num >= let->num) {
strcpy(s[i].letter,let->letter);
break;
}
}
}
for (i = 0; i < SMAX; i++)
printf("%s has a %s\n", s[i].nam, s[i].letter);
return 0;
}

Only main() function executing in C

Starting to learn C. The main function is executing fine, but the program finishes running without ever executing the second function. I feel like I'm making a mistake here in the for loop in main.
int check_key_length(int count);
int main(void)
{
char key[20];
int count = 0;
printf("Enter key: ");
scanf("%s", key);
for(int i = 0; i < strlen(key); i++)
{
if (key[i] != ' ')
count++;
}
printf("Total number of characters in a string: %d", count);
}
int check_key_length(int count)
{
int set_amount = 26;
if (count < set_amount)
printf("Error: Your key is too short! Please input 26 chars\n");
else if (count > set_amount)
printf("Error: Your key is too long! Please input 26 chars\n");
else
string message = get_string("Enter string to encrypt: ");
return 0;
}
You forward declared your function, provided a definition for it, but you need to call the function in your main for your machine to execute it, something like this calls your function as expected
#include <stdio.h>
#include <string.h>
int check_key_length(int count);
int main(void)
{
char key[27];
int count = 0;
int strLength;
do {
printf("Enter key: ");
scanf("%s", key);
strLength = strlen(key);
} while(check_key_length(strLength) != 0);
for(int i = 0; i < strLength; i++)
{
if (key[i] != ' ')
{
count++;
}
}
printf("Total number of characters in a string: %d\n", count);
return 0;
}
int check_key_length(int count)
{
int set_amount = 26;
if (count < set_amount)
{
printf("Error: Your key is too short! Please input 26 chars\n");
return -1;
}
else if (count > set_amount)
{
printf("Error: Your key is too long! Please input 26 chars\n");
return -2;
}
else
{
return 0;
}
}
Note I had to modify the code a bit for it to build without any warning or error, I probably changed the behavior in a way you're not expecting so check my code before pasting it in

Get a problem when make a hangman game ? ( c language)

I'm still really new to the C language and I'm trying to make a hangman game but I keep failing to end the game when I win.
Here is the code:
const int true = 1;
const int false = 0;
char words[][20] = {
"hangman",
"computer",
"programming",
"microsoft",
"visual",
"studio",
"express",
"learning"
};
int isletterinword(char word[], char letter)
{
int i;
for (i = 0; i < strlen(word); i++) {
if (word[i] == letter) {
return true;
}
}
return false;
}
int iswordcomplete(char secretword[], char rights[])
{
int i;
for (i = 0; i < strlen(secretword); i++) {
if (rights[i] == secretword[i] ) {
return true;
}
}
return false;
}
void printhangman(int numofwrongs)
{
// Line 1
printf("\t ______\n");
// Line 2
printf("\t | |\n");
// Line 3
printf("\t | +\n");
// Line 4 - left arm, head and right arm
printf("\t |");
if (numofwrongs > 0) printf(" \\");
if (numofwrongs > 1) printf("O");
if (numofwrongs > 2) printf("/");
printf("\n");
// Line 5 - body
printf("\t |");
if (numofwrongs > 3) printf(" |");
printf("\n");
// Line 6 - left leg and right leg
printf("\t |");
if (numofwrongs > 4) printf(" /");
if (numofwrongs > 5) printf(" \\");
printf("\n");
// Line 7
printf("\t |\n");
// Line 8
printf("\t__|__\n");
}
void printletters(char letters[])
{
int i;
for (i = 0; i < strlen(letters); i++) {
printf("%c ", letters[i]);
}
}
void printscreen(char rights[], char wrongs[], char secretword[])
{
int i;
for (i = 0; i < 25; i++)
printf("\n");
printhangman(strlen(wrongs));
printf("\n");
printf("Correct guesses: ");
printletters(rights);
printf("\n");
printf("Wrong guesses: ");
printletters(wrongs);
printf("\n\n\n");
printf("\t");
for (i = 0; i < strlen(secretword); i++) {
if (isletterinword(rights, secretword[i])) {
printf("%c ", secretword[i]);
}
else {
printf("_ ");
}
}
printf("\n\n");
}
int main()
{
int i;
int secretwordindex;
char rights[20];
char wrongs[7];
char guess;
secretwordindex = 0;
srand(time(0));
secretwordindex = rand() % 8;
for (i = 0; i < 20; i++) {
rights[i] = '\0';
}
for (i = 0; i < 6; i++) {
wrongs[i] = '\0';
}
while (strlen(wrongs) < 6) {
printscreen(rights, wrongs, words[secretwordindex]);
printf("\nPlease enter your guess: ");
scanf(" %c", &guess);
if (isletterinword(words[secretwordindex],guess)) {
rights[strlen(rights)] = guess;
}
else {
wrongs[strlen(wrongs)] = guess;
}
}
printscreen(rights, wrongs, words[secretwordindex]);
if ( iswordcomplete(words[secretwordindex],rights[20])==true && strlen(wrongs) <= 6 ) { // The if condition here might be problematic.
printf("You have won!\n");
}
else {
printf("You have lost!\n");
}
}
Here is the error message:
main.c:197:48: warning: passing argument 2 of ‘iswordcomplete’ makes
pointer from integer without a cast [-Wint-conver sion]
main.c:55:5: note: expected ‘char *’ but argument is of type ‘char’
First things first: The compiler error is caused by the fact that you are passing a single character to your call to iswordcomplete(), rather than an array of characters. So, in the check near the end of your main function, you need to pass rights (unadorned) as the argument, in place of rights[20] (which, incidentally, is an out-of-bounds element of the array). Also, you don't – at that stage – need the second check (counting the number of wrongs – see later). Here's the fix for that part of the code:
// if (iswordcomplete(words[secretwordindex], rights[20]) == true && strlen(wrongs) <= 6) { // The if condition here might be problematic.
if (iswordcomplete(words[secretwordindex], rights)){// && strlen(wrongs) <= 6) { // Needs the whole string as an argument
printf("You have won!\n");
}
Now to address a couple of other issues that will stop your code from working properly ...
(1) Your main while loop won't stop running until you have entered 6 'wrong' letters – even if you do guess the word correctly. So, you need to add an iswordcomplete() check to the while condition (negating it with the !operator†), to keep running the loop only if the word isn't complete. Like this:
while (strlen(wrongs) < 6 && !iswordcomplete(words[secretwordindex], rights)) { // Need to break loop if we win!!
printscreen(rights, wrongs, words[secretwordindex]);
//...
(2) The logic of your iswordcomplete function is flawed, as it will return "true" as soon as it finds any match. Instead, you need two loops, returning false if any of the word's letters is not found in the list of 'rights'. Here's one possible version:
int iswordcomplete(char secretword[], char rights[])
{
int i, j;
for (i = 0; i < strlen(secretword); i++) {
for (j = 0; j < strlen(rights); j++) {
if (secretword[i] == rights[j]) break;
}
if (j >= strlen(rights)) return false; // Didn't find this letter
}
return true;
}
Please feel free for any further clarification and/or explanation.
† If you're not (yet) familiar with this use of the ! operator, then you can explicitly compare the function's return value to the false constant, if you are more comfortable with that, like so:
while (strlen(wrongs) < 6 && iswordcomplete(words[secretwordindex], rights) == false) { // Break loop if we win!

Trying to remove substring from string in C, keep failing

I know this question has been asked many times before but I simply cannot get my head around what I am doing wrong. Everytime I make some progress I get a new error. The code I am using is really basic because I am a newbie and our professor requires the usage of scanf and gets. This is my code so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_SIZE 100
int identify(char[], char[]);
int remove(char[], char[], int);
int scan(choice)
{
while(choice < 0 || choice > 7)
{
printf("Invalid input, choose again\n");
scanf("%d", &choice);
}
return choice;
}
int main()
{
char sentence[MAX_SIZE], word[MAX_SIZE];
int choice, i, j, k, deikths;
printf("Choose one of the following:\n");
printf("1. Give sentence\n");
printf("2. Subtract a word\n");
printf("3. Add a word\n");
printf("4. Count the words\n");
printf("5. Count the sentences\n");
printf("6. Count the characters\n");
printf("7. Is the phrase a palindrome?\n");
printf("0. Exit\n");
scanf("%d", &choice);
if(scan(choice) == 1)
{
printf("Give sentence:\n");
gets(sentence);
gets(sentence);
printf("%s\n", sentence);
}
else(scan(choice) == 2);
{
printf("Give word you want to subtract\n");
gets(word);
printf("%s", word);
deikths = identify(sentence, word);
if(deikths != -1)
{
remove(sentence, word, deikths);
printf("Sentence without word: %s\n", sentence);
}
else
{
printf("Word not found in sentence.\n");
}
}
}
int identify(char sentence[], char word[])
{
int i, j, k;
for(k = 0; word[k] != '\0'; k++);
{
for(i = 0, j = 0; sentence[i] != '\0'; i++)
{
if(sentence[i] == word[j])
{
j++;
}
else
{
j = 0;
}
}
}
if(j == 1)
{
return(i - j);
}
else
{
return -1;
}
}
int remove(char sentence[], char word[], int deikths)
{
int i, k;
for(k = 0; word[k] != '\0'; k++)
{
for(i = deikths; sentence[i] != '\0'; i++)
{
sentence[i] = sentence[i + k + 1];
}
}
}
The error I am getting, is that the remove function has conflicting types. Any help with fixing my code will be greatly appreciated, or even an alternative solution to my problem would bre great.
As established in the comments, the compiler error is generated because remove is already defined in the stdio.h. After changing, the name the code compiles successfully, but still doesn't work as expected.
identify is the function which is meant to find whether a substring exists in a string and return its position. This is very similar to how strstr from the standard library works - I'd suggest having a look at an implementation of that function, to better understand how this is done.
The function you implemented only correctly finds substrings of length 1, at the end of the string. I have highlighted errors in the code below which cause this.
int identify(char sentence[], char word[])
{
int i, j, k;
for(k = 0; word[k] != '\0'; k++); // <- this loops is never actually ran because of the trailing semicolon - this is however a good thing as it is redundant
{
for(i = 0, j = 0; sentence[i] != '\0'; i++)
{
if(sentence[i] == word[j])
{
j++;
}
else
{
j = 0; // <- this makes it so only matches at the end can be found - otherwise, j is just reset back to 0
}
}
}
if(j == 1) // <- this makes it so only matches of length 1 can be found
{
return(i - j); // <- this is only correct if the match is at the end of the sentence
}
else
{
return -1;
}
}
strremove is inefficient due to the nested loops and the range of characters copied needs to be shortened - right now data is access beyond the end of the array.
int strremove(char sentence[], char word[], int deikths)
{
int i, k;
for(k = 0; word[k] != '\0'; k++) // <- this loop is redundant
{
for(i = deikths; sentence[i] != '\0'; i++) // <- you need to add range checking to make sure sentence[i+k+1] doesn't go beyond the end of the string
{
sentence[i] = sentence[i + k + 1];
}
}
}
I will leave the problems in main as an exercise to you - this is an assignment after all.

Find missing lower-case letters that are not in a series of words

As stated in the title I am trying to find all lower-case letters that are not in a series of words. There are no upper-case letters, digits, punctuation, or special symbols.
I need help fixing my code. I am stuck and do not know where to go from here.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void){
int letters[26];
char words[50];
int i = 0, b = 0;
printf("Enter your input : ");
scanf("%s", words);
for(i = 0; i < 26; i++){
letters[i] = 0;
}
while(!feof(stdin)){
for(b = 0; b < strlen(words) - 1; b++){
letters[ words[b] - 'a']++;
scanf("%s", words);
}
}
printf("\nMissing letters : %c ", b + 97);
return 0;
}
My output is giving me some random letter that I do not know where it is coming from.
Here is a working first implementation.
As well as the comments that have already been made, you should use functions wherever possible to separate out the functionality of the program into logical steps. Your main function should then just call the appropriate functions in order to solve the problem. Each function should be something that is self contained and testable.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_INPUT 20 /* Max input to read from user. */
char *readinput(void);
void find_missing_lower_case(char *, int);
int main()
{
char *user_input = readinput();
int len_input = strlen(user_input);
printf("user input: %s\n", user_input);
printf("len input: %d\n", len_input);
find_missing_lower_case(user_input, len_input);
/* Free the memory allocated for 'user_input'. */
free(user_input);
return 0;
}
char *readinput()
{
char a;
char *result = (char *) malloc(MAX_INPUT);
int i;
for(i = 0; i < MAX_INPUT; ++i)
{
scanf("%c", &a);
if( a == '\n')
{
break;
}
*(result + i) = a;
}
*(result + i) = '\0';
return result;
}
void find_missing_lower_case(char *input, int len_input)
{
int a = 97; /* ASCII value of 'a' */
int z = 122; /* ASCII value of 'z' */
int lower_case_chars[26] = {0}; /* Initialise all to value of 0 */
/* Scan through input and if a lower case char is found, set the
* corresponding index of lower_case_chars to 1
*/
for(int i = 0; i < len_input; i++)
{
char c = *(input + i);
if(c >= a && c <= z)
{
lower_case_chars[c - a] = 1;
}
}
/* Iterate through lower_case_chars and print any values that were not set
* to 1 in the above for loop.
*/
printf("Missing lower case characters:\n");
for(int i = 0; i < 26; i++)
{
if(!lower_case_chars[i])
{
printf("%c ", i + a);
}
}
printf("\n");
}
I figured it out and this is the code I used.
int main(void)
{
int array[26];
char w;
int i=0;
for(i=0; i<26; i++) {
array[i]=0; }
printf("Enter your input: ");
scanf("%c", &w);
while(!feof(stdin)) {
array[w-97] = 1;
scanf("%c", &w); }
printf("Missing letters: ");
for(i=0; i<26; i++) {
if(array[i] == 0) {
printf("%c ", i+97); }
}
printf("\n");
return 0;
}

Resources