我试图编写一个数字猜谜游戏

时间:2017-06-25 00:45:59

标签: c

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

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

4 个答案:

答案 0 :(得分:3)

首先,最重要的是, 启用警告 。您的代码中存在几个基本错误,这些错误会被编译器警告捕获。不幸的是,它们默认是关闭的。 -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是一个问题,there's a whole FAQ for it

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

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

使用fgets读取整行并用sscanf解析它会好得多。您可以为进入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);
}

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

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

答案 1 :(得分:1)

这只是一个部分答案,但它可以作为一个起点。你真的应该有一个可靠的输入功能。您的scanf()将无法执行,即使您修复了尝试使用%i来获取字符的明显错误,这是针对整数的。我不会在这里详细介绍,wrote a document on this。 (基本上,你至少会遇到scanf()只会留下未读的不可解决的输入问题。)

以下是 为您的用例做出可靠输入的示例:

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

尝试理解此功能,阅读您不了解或不了解的功能的手册(例如,谷歌“ man strtol ”会为您找到strtol()的手册页)。

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

只是因为它有点有趣,这里有一个可能的整个游戏运行得很好:

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

答案 2 :(得分:1)

这个练习是一个练习,在你的脑海中记住为什么scanf 通常是混合用户输入的错误选择!你可以这样做,但你必须非常小心的帐户对于保留在输入缓冲区中的任何字符(即stdin) - ,特别是在进行字符输入时... 为什么?

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

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

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

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

如何处理保留在输入缓冲区中的字符?通常,在阅读getchar()(通过按 Enter 生成)或遇到'\n'之前,您将使用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 <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;
}

示例使用/输出

$ ./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添加到失败输出中,以便让用户知道他错过了什么。

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

答案 3 :(得分:0)

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文件中编写注释,逐步描述程序需要做什么。逐个浏览它们比仅仅掌握程序要容易得多,特别是当你开始编码时。随着时间的推移,你会更容易处理概念和基本障碍。