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