我正在尝试编写一个数字猜谜游戏

我到了这里,但我还是需要以某种方式使用while循环。 “想要再玩一次(y / n)”和“非法猜测。你的猜测必须在1到200之间。再试一次。你的猜测?” 似乎没有工作。 请帮助我使用while / do-while循环并修复上面的两个问题。 谢谢。

#include  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;inumber) { printf("Too high!"); } else if (guess200); { 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; } 

首先,最重要的是, 打开警告 。 您的代码中存在几个基本错误,这些错误会被编译器警告捕获。 不幸的是,它们默认关闭了。 -Wall打开基本警告。 这不是“全部”警告,因为这是C! -fsanitize=address -Wall -Wshadow -Wwrite-strings -Wextra -Wconversion -std=c99 -pedantic是一组很好的警告。


你可以在循环周围循环,但很快就难以维护。 相反,将游戏放入一个函数并循环。

 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; } 

注意游戏如何只关注游戏。 关于玩另一场比赛没有其他逻辑或问题。 它可以在游戏结束后立即返回。

然后程序的其余部分非常简单。 在无限循环中运行游戏,在完成后突破游戏。

 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; } 

有一个问题,它是scanf 。 它始终是scanfscanf就是这样一个问题,它有一个完整的FAQ 。

scanf("%i")读取单个整数但不读取以下换行符 。 这个换行符和任何其他额外输入都在stdin上挂起。 后来的scanf("%c", &answer); 然后可能会读取该换行符而不是他们的答案。

scanf("%i\n")无法解决问题。 这告诉scanf读取一个整数,然后是换行符,然后查找另一个非空白字符。 scanf很奇怪。

你最好用fgets读取整行并用sscanf解析它。 你可以为进入可变参数的那个写一个小实用函数。

 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); } 

然后像scanf一样使用它。 它保证读取整行,而不是在缓冲区上留下换行符或部分输入。

  int guess; printf("\n\nYour guess? "); line_scanf("%i",&guess); 

这只是一个部分答案,但它可以作为一个起点。 你真的应该有一个可靠的输入function。 你的scanf()不会这样做,即使你修复了试图使用%i获取字符的明显错误,这是整数。 我不会在这里详述,我写了一篇关于此的文件 。 (基本上,你至少会遇到一些难以理解的输入问题,即scanf()只会留下未读的。)

下面是一个示例,说明如何为您的用例做可靠的输入,并带有注释:

 #include  #include  #include  #include  #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; } 

尝试理解这个function,阅读您不了解或不了解的function的手册(例如google“ man strtol ”会找到strtol()的手册页)。

要读取你的是/否响应,也可以使用fgets() ,但当然这个函数看起来会有所不同,比如检查输入是否只有1个字符(第二个必须是'\n' )并返回这个字符。


只是因为它有点有趣,这是一个可能的整个游戏运行稳健:

 #include  #include  #include  #include  #include  #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; } } } 

这个练习是一个练习,让你想到为什么scanf 通常是一个不好的选择,用于混合用户输入! 你可以这样做,但是你必须非常小心地考虑输入缓冲区中剩余的任何字符(即stdin ) – 特别是在进行字符输入时……为什么?

当您输入scanf读取的值时, '\n'将始终保留在输入缓冲区中(除非在您的格式字符串中占用)。 此外,在转换失败时所有字符都将保留在输入缓冲区中。 此外,用户可以做一些愚蠢的事情,如输入"4 is my guess"当提示离开is my guess\n你可以处理。

此外,如果用户通过按ctrl + d (或windoze上的ctrl + z )生成手动EOF取消输入,该怎么办? 您必须考虑每个输入的所有可能性。

您还必须使用正确的格式说明符来读取输入。 你不会用%d%i'y''n' 。 如果要在读取char时读取int使用%d ,请使用%c 。 您还必须考虑到%c 从不跳过前导空格

(你开始明白为什么最好使用fgets然后调用sscanf进行用户输入?)

如何处理保留在输入缓冲区中的字符? 通常,您将使用getchar()读取,直到您读取'\n' (按Enter键生成)或直到遇到EOF 。 您可以通过编写如下的简短函数来轻松自己:

 /* empty characters that remain in stdin */ void fflushstdin () { for (int c = getchar(); c != '\n' && c != EOF; c = getchar()) {} } 

如果在每次输入后调用fflushstdin ,您将始终处理剩余的任何字符。 如果您知道字符遗留在先前输入中但尚未删除,则在输入之前调用它。

不要在代码中使用幻数 (例如1, 5, 200 ),而是在代码的开头定义任何需要的常量,并在代码中使用常量。 为什么? 如果他们改变了,那么你有一个随时可以访问的地方来改变它们,你不必去挑选你的代码来找到它们。 您可以使用#defineenum ,如下所示:

 enum {LOW = 1, TRIES = 5, HIGH = 200 }; 

你的其余问题只是你可以解决的逻辑问题。 结合以上内容,您可以处理(我认为您尝试做的事情)如下:

 #include  #include  #include  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; } 

示例使用/输出

 $ ./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 

注意,上面处理愚蠢的用户输入(如onehundred ),并在故障输出中添加number ,让用户知道他错过了什么。

仔细看看,如果您有其他问题,请告诉我。

scanf(“%i”,…)读取基数10中的整数,而不是字符或字符串。

你需要组织你的循环。 你有2个主循环,一个在用户想要继续游戏时运行,另一个在游戏开启时运行。

简而言之:

 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 } 

您可以在main中启动循环,如下所示:

 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'); 

可能有很多方法可以做到这一点,因为数字介于0和200之间。一个好的策略是首先在C文件中编写注释,逐步描述程序需要做什么。 逐个浏览它们比只在头脑中编写程序要容易得多,尤其是当你开始编写代码时。 随着时间的推移你会变得更容易,因为你习惯了处理概念和基本障碍。