構造体 (構造体に含まれる配列を簡単にコピーするため) があり、入力検証によって、キーと推測が同じ長さであり、キーと推測にアルファベット文字のみが含まれていることが保証されると仮定します。
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;
}
この関数はキーとパターンのコピーで機能します (構造体はポインターではなく値で渡されます)。正しい位置にある正しい推測の数を返します。呼び出し元のコードがパターンの長さを知っていると想定しているため、呼び出し元のコードはパターンがいつ正しいかを知ることができます。推測文字とキー文字を「.」に置き換えることで「使用済み」としてマークします。これは、 のキーが正しく ではなく誤って とマークされるのを防ぐために重要"aba"
です。これは、長さが 4 以上のキー/推測ではより重要になります。"aaa"
bbw
bb.
テストハーネス
#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);
}
テスト出力
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..
コメントから
コードが機能しないのはなぜですか? よろしければご覧いただけますか?問題は、推測パターンを入力した後、次のステップに進むことができないことです。私のコードにいくつかの間違いが見られないかもしれません。
即時応答:
私の回答の重要なポイントの 1 つは、入力されたデータのコピーに対して比較コードが機能しているということです。これは破壊的な比較アルゴリズムであり、データにドットを書き込みます。私のコードをあなたのプログラムにマージしようとしても、この回答の重要な部分であるデータの個別のコピーで動作する個別の機能が保持されませんでした。構造体を使用すると、データのコピーを簡単に渡すことができます (C が配列を自動的にコピーするのは 1 回だけです)。比較コードは、インラインではなく、独自の関数内にある必要がありますmain()
。
ただし、指定されたコードを機能させることはできます。いくつかの転記エラー (以下で BUG とマークされています) と、その他の問題 (以下で特定) がいくつかありました。
問題の改訂コードの作業バージョン
これは、重要な変更の注釈が付けられたプログラムの作業バージョンです。重要ではない変更には、演算子の前後のスペースと 4 つのスペースのインデント レベルの使用が含まれます。
#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;
}
コンパイラは、迷子のセミコロンについて教えてくれました:
ss2.c: In function ‘main’:
ss2.c:36:37: warning: suggest braces around empty body in an ‘if’ statement [-Wempty-body]
コンパイラがそれについて教えてくれなかった場合は、十分な警告を使用していません (または、より優れたコンパイラが必要です)。私は日常的に使用します:
gcc -O3 -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
-Wold-style-definition ss2.c -o ss2
作業コードは、気まぐれでそれを渡します。
サンプル出力
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!
最終的なデバッグを含んだコード
これは主に、何が問題なのかを確認するために使用した印刷のレベルを示すためのものです. 診断出力に使用すると、出力ラインが構築されたときにstderr
、診断がバッファリングに干渉しませんでした。stdout
また、デバッグ コードにインデントを使用しないことで、デバッグ コードを簡単に取り除くことができました。
#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;
}
fprintf()
最後の関数呼び出しの後のコンマ演算子のトリックに注意してください。
以前の推測とマークの記録を保持する
#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);
}
prompt_int()
関数を導入prompt_str()
し、データを取得します。このprompt_str()
関数は、オーバーフローに対して適度に回復力があります。エラー報告機能があります。ダミー関数が置き換えられます。出力例を次に示します。ここから先はあなた次第!
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!