当前位置: 首页 > news >正文

C语言第二章分支与循环(下)——猜数字游戏

        掌握了前面学习的代码,下来我们来实现一个有趣的代码:猜数字游戏!

游戏要求:1. 电脑自动生成1~100的随机数

                 2. 玩家猜数字,猜数字的过程中,根据猜测数据的大小给出大了或小了的反馈,直到猜对,游戏结束。

一.随机数的生成

        要完成猜数字游戏,根据它的游戏规则,我们应该先实现随机数的生成,那么怎们生成随机数呢?C语言提供了专门的生成随机数的函数,下来我们来看看:

1.rand函数

C语言提供了一个函数叫rand函数,该函数是可以生成随机数的,函数语法如下所示:

int   rand  ( void )

 rand函数会返回一个伪随机数,这个随机数的范围是在0~RAND_MAX之间,这个RAND_MAX的大小是依赖编译器的,不同的编译器该数有可能不同。但是大部分编译器上是32767。rand函数的使用需要包含⼀个头文件:<stdlib.h> ,那我们就测试⼀下rand函数:

#include <stdio.h>
#include <stdlib.h>
int main()
{printf("%d\n", rand());printf("%d\n", rand());printf("%d\n", rand());printf("%d\n", rand());printf("%d\n", rand());return 0;
}

 

        这是第一次经过运行上述代码产生的5个随机数, 貌似看不出来任何猫腻,那么我们再次运行试试:

        上述结果时第二次运行代码产生的随机数,可以发现第一次和第二次产生的随机数是相同的,这样根本不满足游戏的规则,总不能每次游戏产生的随机数都是相同的,这样游戏就没有原本的游戏性,下来我们该怎么办呢? 

         我们可以看到虽然第⼀次运行中产生的5个数字感觉是随机的,但是第二次运行程序生成的随机数和第一次产生的随机数一模一样,这就说明有问题。 如果再深入了解⼀下,我们就不难发现,其实rand函数生成的随机数是伪随机的,伪随机数不是真正的随机数,是通过某种算法生成的随机数。真正的随机数的是无法预测下一个值是多少的。而rand函数是对⼀个叫“种子”的基准值进行运算生成的随机数。之所以前面每次运行程序产生的随机数序列是一样的,那是因为rand函数生成随机数的默认种子是1。如果要生成不同的随机数,就应该让种子变化。

2.srand函数

        C语言提供了srand函数,作用是:初始化随机数的生成器。下来是srand函数的语法形式:

 void  srand (  unsigned   int   seed  );

        程序中在调用rand函数之前先调用srand函数,通过srand函数的参数seed来设置rand函数生成随机数的时候的种子,只要种子在变化,每次生成的随机数序列也就变化起来了。那也就是说给srand的种子是如果是随机的,rand就能生成随机数;在生成随机数的时候又需要一个随机数,这样貌似矛盾了。

3.time函数

        在程序中我们一般是使用程序运行的时间作为种子的,因为时间时刻都在发生变化。只要我们把生成的时间作为srand函数的参数,这样rand生成随机数的种子就是在时刻变化的,这样就可以得到真正的随机数。

         在C语言中有一个函数叫time,就可以获得时间,time函数的语法形式如下:

  time_t   time   (  time_t* timer  );

        time函数会返回当前的日历时间,具体形式就是1970年1月1日0时0分0秒到现在程序运行时间之间的差值,单位是秒。返回的类型是time_t类型的,time_t类型本质上其实就是32位或者64位的整型类型。

        time函数的参数timer如果是非NULL的指针的话,函数会将这个返回的差值放在timer指向的内存中带回去。如果timer是NULL,就只返回这个时间的差值。因为我们只需要time函数返回的变化值,所以就应该把time函数的参数设置为NULL。time函数返回的时间差也被叫做:时间戳。 time函数在用的时候需要包含头文件:<time.h>。

//VS2022 上time_t类型的说明 #ifndef _CRT_NO_TIME_T#ifdef _USE_32BIT_TIME_Ttypedef __time32_t time_t;#elsetypedef __time64_t time_t;#endif#endiftypedef long __time32_t;typedef __int64 __time64_t;

如果只是让time函数返回时间戳,我们就可以这样写:

  time  (  NULL  );                //调用time函数返回时间戳,这里没有接收返回值

这样我们就可以写出生成随机数的代码:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{//使⽤time函数的返回值设置种⼦ //因为srand的参数是unsigned int类型,我们将time函数的返回值强制类型转换 srand((unsigned int)time(NULL));printf("%d\n", rand());printf("%d\n", rand());printf("%d\n", rand());printf("%d\n", rand());printf("%d\n", rand());return 0;
}

 上述代码的解释:先包含了所有要用到的头文件;接下来利用srand函数,改变rand函数生成随机数的种子,且参数时间戳是随时变化的,这样rand函数生成随机数的种子就是随时变化的,得到的随机数就是真正意义上的随机数,而不是伪随机数了。下来用printf函数接收rand函数生成的随机数返回值,生成了5个随机数,方便检验。

srand函数是不需要频繁调用的,一次运行的程序中调用一次就够了。不需要碰到rand函数就使用一次,因为rand函数的随机性依赖于连续的内部状态演进,srand函数是用来启动rand函数生成随机数的演进过程的,只要种子定下来了,后面的rand函数生成的随机数都是真随机数,所以不需要连续调用srand函数。  

上面是两次生成的随机数,可以发现两次生成的随机数不再相同,这样游戏就有了随机性和娱乐性。 

 4.设置随机数的范围

        因为随机数是通过srand函数时刻改变rand种子而生成的真正意义上的随机数,所以我们把生成的随机数通过一些数学变换控制随机数的范围,变化之后的数字依然是真正意义上的随机数。

如果要生成0-99的随机数:

rand() % 100;                //余数的范围是0~99

如果要生成0-100的随机数:

rand()%100+1;                // %100的余数是0~99,0~99的数字+1,范围是1~100

如果要生成100-200的随机数:

100 + rand()%(200-100+1)         //余数的范围是0~100,加100后就是100~200

综上所述要得到a-b范围内的随机数,方法如下:

a  +  rand (  ) % (b - a + 1 )

5.猜数字游戏的代码实践

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void game()        //定义game函数,用于主函数玩游戏时的调用
{int r = rand()%100+1;    //生成1-100的随机数int guess= 0;        //定义变量存放猜测的值while(1){printf("请猜数字>:");    //提示玩家该输入猜测的数字了scanf("%d", &guess);    //读取玩家输入的值if(guess < r)        //判断玩家输入的值和生成的随机值的关系,并打印告知{        printf("猜⼩了\n");}else if(guess > r){printf("猜⼤了\n");}else{printf("恭喜你,猜对了\n");break;}} 
}
void menu()        //定义菜单函数,在屏幕上打印菜单,供玩家选择游戏状态
{printf("***********************\n");printf("****** 1. play ******\n");printf("****** 0. exit ******\n");printf("***********************\n");
}
int main()
{int input = 0;        //定义输入变量,存放玩家输入的菜单数字srand((unsigned int)time(NULL));    //利用srand函数时刻改变rand生成随机数的种子do        //利用do-while循环,上来先打印菜单,并提示玩家输入游戏状态{menu();        //调用菜单函数,打印游戏可供选择的游戏状态printf("请选择:>");scanf("%d", &input);switch(input){case 1:game();        //当选择菜单的数字1时,调用game函数,开始游戏break;case 0:printf("游戏结束\n");    //当选择菜单的数字0时,游戏结束break;default:printf("选择错误,重新选择\n");    //当选择的不是菜单上的数字时,提示玩家错误信息break;}}while(input);    //当输入的input等于0时,循环终止return 0;
}

还可以加上猜数字的次数限制,如果5次猜不出来,就算失败:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void game()
{int r = rand() % 100 + 1;int guess = 0;int count = 5;while (count){printf("\n你还有%d次机会\n", count);printf("请猜数字>:");scanf("%d", &guess);if (guess < r){printf("猜⼩了\n");}else if (guess > r){printf("猜⼤了\n");}else{printf("恭喜你,猜对了\n");break;}count--;}if (count == 0){printf("你失败了,正确值是:%d\n", r);}}
void menu()
{printf("***********************\n");printf("****** 1. play ******\n");printf("****** 0. exit ******\n");printf("***********************\n");
}
int main()
{int input = 0;srand((unsigned int)time(NULL));do{menu();printf("请选择:>");scanf("%d", &input);switch (input){case 1:game();break;case 0:printf("游戏结束\n");break;default:printf("选择错误,重新选择\n");break;}} while (input);return 0;
}
http://www.lryc.cn/news/596251.html

相关文章:

  • MES 管理系统中的仓库管理功能有哪些用途
  • 直接插入排序和冒泡排序
  • MyBatis拦截器插件:实现敏感数据字段加解密
  • 汽车安全 | 汽车安全入门
  • 力扣刷题 -- 101.对称二叉树
  • 贪心算法Day3学习心得
  • LeetCode 刷题【11. 盛最多水的容器】
  • 数据库 × 缓存双写策略深度剖析:一致性如何保障?
  • 《3D printed deformable sensors》论文解读
  • EasyMan 数字人服务全面焕新,交互型AI数字人助推孪生体验全新升级
  • GoLang教程006:循环控制语句
  • 数据结构 之 【排序】(直接选择排序、堆排序、冒泡排序)
  • 自编码器表征学习:重构误差与隐空间拓扑结构的深度解析
  • Dockerfile 详解
  • 鸿蒙卡片开发保姆级教程
  • AI创作系列第22篇:前端缓存与更新机制重构 - 表情包系统的全面升级
  • anchor 智能合约案例6 之 token_lottery
  • 假发行业数字化突围,外贸ERP重构外协管理引擎,助力效率飞跃
  • 34、鸿蒙Harmony Next开发:使用动画-转场动画
  • Jmeter使用 - 2
  • Chrome 开发环境屏蔽 CORS 跨域限制
  • PHICOMM(斐讯)N1盒子 - Armbian25.05(Debian 12)刷入U盘/EMMC
  • SQL 中 JOIN 顺序对性能的影响
  • FastDFS 6.11.0 单机环境搭建与测试(附 Nginx 集成)+ docker构建+k8s启动文件
  • 浏览器地址栏输入URL回车后白屏分析
  • Jenkins接口自动化测试(构建)平台搭建
  • Apache Ignite 中事务的使用方式和机制
  • Excel工具
  • ROS个人笔记
  • Qt Creator集成开发环境使用指南