当前位置: 首页 > 知识库问答 >
问题:

如何为我的类修复 C 中的此随机数生成代码?

柳刚豪
2023-03-14

我是C编程的初学者,如果我能得到一些关于如何设置程序重新启动的提示,我将不胜感激?我目前正在构建一个猜谜游戏,其中用户有4次尝试猜测随机提供的秘密号码。我几乎已经完成了代码,但它不能正常运行,并且它返回的某些内容不正确。请帮忙。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
  int a, Thenumber, GuessNumber, NumberofTries = 4, theAnswer;
  time_t ti;
  printf("Welcome to the game. \nI am going to choose any number between 10 and 30");
  printf("\nYou must try to guess the number. If you guess incorrect, I will tell you if you were too high or too low");
  printf("\nYou will have 4 attempts to guess the number.\n"); 

  srand((unsigned)time(&ti));
  Thenumber = rand() % 1 + 3 + 1;//10+20+1;4

  for (NumberofTries = 0; NumberofTries < 4; NumberofTries++) {
    printf("\n \nPlease enter a number:");
    scanf("%d", &GuessNumber);
    if (GuessNumber > 30) {
      printf("That is illegal. Your number can only be between 10 and 30. Please enter a number between those numbers:/n");

    }
    else if (GuessNumber > Thenumber) {
      printf("I'm sorry, that is too high. \n");
    }
  }

  if (GuessNumber < Thenumber) {
    printf("I'm sorry, that is too low. \n");
  }

  else if (GuessNumber == Thenumber) {
    printf("You are correct. Do you want to continue playing? y-yes n-no:  \n");
    return 0;
  }

  printf("\n \nI'm sorry, you have no more attempts.\n \nDo you want to play again? y-yes n-no: \n");
  scanf("%d", &theAnswer);

  if ((theAnswer = 'n')) {
    printf("Goodbye, thank you for playing. Please play again soon.");
  }
  return 0;
}

共有3个答案

卫博学
2023-03-14

您的输入程序非常脆弱。如果您这样做,会发生什么情况:

Please enter a number: twenty-one

使用scanf()会发生匹配失败,从标准输入中提取的字符将停止, 将留在

相反,所有用户输入都应该使用面向行的输入函数来完成,例如fgets()或POSIXgetline()。有了足够大的输入缓冲区(字符数组),fgets()将一次消耗整行(最多为以nul结尾的字符的缓冲区-1的大小)。如果初始lineptr参数设置为NULL并且n=0参见man 3 getline,getline()将动态分配足够的缓冲区来保存整行

然后,使用sscanf()解析缓冲区中的数值,而不是使用 scanf() 尝试读取用户输入。与所有用户输入函数和数字转换函数一样,您必须验证返回值,以了解输入或转换是否成功。这不是可选的。否则,您将邀请“未定义行为”。

为了提示"…继续玩?",您将需要嵌套循环。处理游戏的简单方法是:

  while (choice == 'y') {
    /* generate random in range */
    for (tries = 0; tries < NTRIES; ) {
      /* handle your guesses */
      tries += 1;
    }
    /* prompt for continue playing? & read choice */
  }

(注意:(;;;)的中没有增量。) 循环定义。如果输入有效,则只增加尝试次数。)

当您在C中使用具有自动存储持续时间的变量(在程序堆栈上创建的普通变量)时,任何在初始化之前访问其值的尝试(即当其值不确定时)都会导致未定义行为)。现在您可以使用ti,因为您不会在以下位置访问其值:

  srand((unsigned)time(&ti));

但是使用它的地址——但我怀疑这种区别已经完全消失了。新程序员的经验法则——初始化所有值以避免UB的可能性。对于srand()播种随机数生成器可以简单地使用:

  srand (time (NULL));

您对Thenumber的初始化不会导致10-30之间的值。相反,您需要:

  Thenumber = rand() % (30 - 10 + 1);

接下来,不要在代码中分散魔术数字。如果您需要一个常量,#定义它或使用全局枚举来做同样的事情。例如:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define NTRIES      4       /* if you need a constant, #define one (or more) */
#define MINNUM     10
#define MAXNUM     30
#define MAXC     1024

那么你你的号码一代就变成了:

  Thenumber = rand() % (MAXNUM - MINNUM + 1);

这样,如果尝试的次数改变了,或者你想改变游戏的范围,你就不必通过每个循环限制和条件语句来进行改变。您只需更改在代码顶部方便定义的常量。

当您向用户呈现输出时,如果您的输出中不涉及转换,则无需使用可变参数printf()函数。如果您正在输出文本并想要尾随'\n',您可以简单地使用put(),或者如果您需要行尾控制(例如在提出问题并且您不想要'\n'输出时),您可以简单地使用put()。无论如何,对于任何一个输出,您不需要对输出函数进行一次以上的调用,例如。

  printf ("Welcome to the game.\n\n"                    /* a single printf will do */
          "I am going to choose any number between %d and %d\n\n"
          "You must try to guess the number. If you guess incorrect, I will tell you\n"
          "if you were too high or too low. You will have %d attempts to guess the "
          "number.\n", MINNUM, MAXNUM, NTRIES);

接下来,您需要一种系统化的方法来检查用户是否:

  1. 已经猜到了正确的数字,(这可能是第一个条件检查,因为如果他猜到了,回合就结束了)
  2. 如果用户的猜测超出范围,
  3. 如果猜测小于数字,或者最后
  4. 如果猜测大于数字。

您可以编写类似于以下内容的逻辑:

      if (guess == number) {                            /* winner! */
        puts ("\n  Correct!");
        goto again;
      }
      else if (guess < MINNUM || MAXNUM < guess) {      /* guess out-of-range */
        printf ("\n  guess out of range, allowed %d - %d\n", MINNUM, MAXNUM);
        continue;
      }
      else if (guess < number) {                        /* guess < number */
        fputs ("\n  I'm sorry, that was too low.\n", stdout);
      }
      else {                                            /* guess > number */
        fputs ("\n  I'm sorry, that was too high.\n", stdout);
      }

(同样,在任何极限中都没有幻数)

很明显,当你进入循环控制部分,要求用户再次播放时,你被卡住了。你将有你的外环控制选择继续和内环是一个完整的游戏。内部和外部之间的唯一混合将是一个可选选择,即是否在上面的条件中添加另一个elseif(tries==NTRIES),并在内部循环中处理未尝试的情况,或者是否允许在中对(tries=0;tries)进行正常循环控制

总而言之,你可以这样做:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define NTRIES      4       /* if you need a constant, #define one (or more) */
#define MINNUM     10
#define MAXNUM     30
#define MAXC     1024

int main() {
  
  char buf[MAXC] = "y";                                 /* array to hold user-input */
  int guess, number, tries;                             /* 3 int variables */
  
  printf ("Welcome to the game.\n\n"                    /* a single printf will do */
          "I am going to choose any number between %d and %d\n\n"
          "You must try to guess the number. If you guess incorrect, I will tell you\n"
          "if you were too high or too low. You will have %d attempts to guess the "
          "number.\n", MINNUM, MAXNUM, NTRIES);

  srand (time(NULL));                                   /* Using NULL is fine */
  
  while (*buf == 'y') {                                 /* test choice */
    number = MINNUM + rand() % (MAXNUM - MINNUM + 1);   /* rand() 10 - 30 (inclusive) */
  
    for (tries = 0; tries < NTRIES; ) {                 /* do not increment */
      fputs ("\nenter a number: ", stdout);             /* no conversion, fputs() */
        
      if (fgets (buf, MAXC, stdin) == NULL) {           /* validate every input */
        fputs ("(user canceled input)\n", stdout);
        return 0;
      }
        
      if (sscanf (buf, "%d", &guess) != 1) {            /* validate every conversion */
        fputs ("error: invalid integer.\n", stderr);
        continue;
      }
      
      if (guess == number) {                            /* winner! */
        puts ("\n  Correct!");
        goto again;
      }
      else if (guess < MINNUM || MAXNUM < guess) {      /* guess out-of-range */
        printf ("\n  guess out of range, allowed %d - %d\n", MINNUM, MAXNUM);
        continue;
      }
      else if (guess < number) {                        /* guess < number */
        fputs ("\n  I'm sorry, that was too low.\n", stdout);
      }
      else {                                            /* guess > number */
        fputs ("\n  I'm sorry, that was too high.\n", stdout);
      }
      
      tries += 1;       /* increment only if not continued due to error in input */
    }
    
    printf ("\n\n%d tries exhaused, the number was '%d'.\n", NTRIES, number);
    
  again:;
    
    fputs ("\n\nplay again (y/n)? ", stdout);
    if (fgets (buf, MAXC, stdin) == NULL) {
      fputs ("(user canceled input)\n", stdout);
      return 0;
    }
  }
  
  /* no conversion involved and appended '\n' wanted -- use puts() */
  puts ("\nGoodbye, thank you for playing. Please play again soon.");
}

(注意:上面的设计选择使用一个简单的goto在成功猜测时跳过out-of-tries代码,留下forloop来控制out-of-tries逻辑,而不是添加另一个 -完全由您决定。用两种方式编写并比较差异)

最后,虽然不是错误,但C通常避免使用camelCaseMixedCase变量和函数名,而只使用小写,同时保留大写名称以用于宏和常量。这是一个风格问题,所以这完全取决于你,但如果不遵循它,在某些圈子里可能会导致错误的第一印象。

示例使用/输出

您的输入必须处理用户可以输入的任何内容(或猫踩键盘):

$ ./bin/guessnumber
Welcome to the game.

I am going to choose any number between 10 and 30

You must try to guess the number. If you guess incorrect, I will tell you
if you were too high or too low. You will have 4 attempts to guess the number.

enter a number: twenty-one
error: invalid integer.

enter a number: 3

  guess out of range, allowed 10 - 30

enter a number: 33

  guess out of range, allowed 10 - 30

enter a number: 20 okay, okay, okay!!!

  I'm sorry, that was too high.

enter a number: 15

  Correct!


play again (y/n)? y
...

再次选择播放必须生成一个新的数字,重置所有值:

enter a number: 20

  I'm sorry, that was too low.

enter a number: 25

  I'm sorry, that was too low.

enter a number: 28

  I'm sorry, that was too high.

enter a number: 27

  I'm sorry, that was too high.


4 tries exhaused, the number was '26'.


play again (y/n)? n

Goodbye, thank you for playing. Please play again soon.

当你编写一个输入例程时 -- 去尝试打破它。输入尽可能多的有趣值,看看它是否正确处理它。如果没有,请修复它并重试。用户输入必须是防弹的...因为用户会用你的代码做最愚蠢的事情。确保它可以处理它。

还要注意,如果对fgets()的测试失败,则会显示"(用户取消输入)"。这是因为用户可以使用Ctrl d(或Windows上的Ctrl z)生成手动EOF。在这种情况下,这不是错误,用户只是选择取消输入——因此应该在此时使用优雅的程序退出来处理。

有很多东西需要消化。仔细看看,如果你还有其他问题,请告诉我。学习C不是一场比赛,而是一场旅程——放慢速度,理解你在每行中使用的每个字符,然后享受这段旅程。

汪鸿波
2023-03-14

我不知道这是否能解决你的问题,但这里有几点:

srand(time(&ti));

您不必将time()强制转换为unsigned,因为1970年1月1日之后的时间不能为负值。

我不知道你的代码会发生什么,没有编译器来测试它,但是你应该始终初始化你的变量,因为你正在调用未定义的行为。

另一种方法是:

srand(time(null));

这将使用您机器上的当前时间初始化随机生成器。

Thenumber = rand() %  1+3+1;//10+20+1;

< code>%对< code> 有优先权,因此< code>rant()%1总是返回< code>0

梁德馨
2023-03-14

检查“猜测数字”的 if-else 语句应位于 for 循环内,而不是位于其后。

当你在 main() 方法中使用 return 0; 时,你的程序将终止,所以你不想这样做。

尝试更改:

if ((theAnswer = 'n'))

至:

if ('n' == theAnswer)

因为你想比较,而不是分配。双括号不会导致逻辑错误,但会增加混乱并降低代码的易读性。

 类似资料:
  • 本文向大家介绍C# 生成随机数的代码,包括了C# 生成随机数的代码的使用技巧和注意事项,需要的朋友参考一下 以上就是本文的全部内容了,希望大家能够喜欢,能够对大家学习C#有所帮助。

  • 本文向大家介绍C++编写生成不重复的随机数代码,包括了C++编写生成不重复的随机数代码的使用技巧和注意事项,需要的朋友参考一下 C++编写生成不重复的随机数代码 再来一例: 方法三:来个复杂点的 以上3种方法均可实现生成不重复的随机数,具体的效率如何,小伙伴们自己测试下吧。

  • 我想用骰子做一个游戏,我需要在其中加入随机数(以模拟骰子的侧面。我知道如何在1到6之间进行)。使用 不能很好地工作,因为当我运行程序几次时,我得到的输出是: 所以我想要一个每次都会生成不同随机数的命令,而不是连续5次生成相同的随机数。是否有命令可以执行此操作?

  • 本文向大家介绍C++生成不重复的随机整数,包括了C++生成不重复的随机整数的使用技巧和注意事项,需要的朋友参考一下 C++生成不重复的随机数,供大家参考,具体内容如下 给定正整数的范围[n,m],生成k个不重复的随机数字。 IDE是vs013。 运行结果: 这个程序可以用于班级内部按照学号进行随机抽签。 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持呐喊教程。

  • 本文向大家介绍C语言/C++如何生成随机数,包括了C语言/C++如何生成随机数的使用技巧和注意事项,需要的朋友参考一下 本文分享了C语言/C++如何生成随机数的具体实现方法,供大家参考,具体内容如下 C语言/C++怎样产生随机数:这里要用到的是rand()函数, srand()函数,C语言/C++里没有自带的random(int number)函数。 (1) 如果你只要产生随机数而不需要设定范围的

  • 我想使用c生成0-2^64范围内的非常大的随机数。我使用了rand()函数,但它没有生成非常大的数字。任何一个都可以帮助吗?