一:游戏要求
1.电脑自动生成1~100随机数字
2.玩家猜数字,在猜数字过程中,根据猜数字的大小,根据猜数据的大小,给出大了还是小了的反馈,直到猜对游戏
二:随机数的生成
要完成猜数字游戏,最重要的就是要生成随机数,那怎么才能生成随机数呢?
2.1 rand函数
基于上述问题,就要介绍一个C语言函数了-----rand,这个函数是可以生成随机数的,函数原型如下:
int rand(void);//void 指的是这个函数不含参数
rand函数会返回一个伪随机数(为什么成为伪,请看后续讲解),这个随机数的范围是0~RAND_MAX,这个RAND_MAX的大小是依赖编译器实现的,但是大部分编译器是32767.
rand函数的使用要包含一个头文件:stdlib.h
这里我先演示一下rand函数使用:随机生成五个数字
int main()
{
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
return 0;
}
看到这也许会疑问,为什么我会贴两张一模一样的运行结果呢?
其实着是我用上面代码,运行两次的结果,也就是说rand产生的值是随机的,但是是相对随机的
这时就要求我们就深入了解rand的机制,明白它为什么会产生随机值,随机值为什么又是相对的?
rand函数由上述代码可以看出生成的随机数是伪随机数,而伪随机不是真正的随机数,是通过一种算法生成的随即数,真正的随机数是无法预测下一个值是多少的。而rand函数是对一个叫**"种子"**的基准值进行运算生成的随机数。
之所以前面每次程序产生的随机数序列是一样的,那是因为rand函数生产的随机数的默认种子是1。如果要生成不同的随机数,就要让**"种子**"发生变化。
2.2 srand
正巧是是,C语言中有一个函数叫srand,用来初始化随机数的生成器(初始化种子基准值),srand的原型如下:
void srand(unsigned int seed);
程序在调用rand函数之前先调用srand函数,通过srand函数的参数seed来设置rand函数生成随机数的时候的种子,只要种子发生变化,每次生成的随机数的序列就会发生变化。
也就是说,给srand的种子如果是随机的,rand就能生成随机数;在生成随机数的时候又需要一个随机数,这似乎像套娃一样,没有尽头了
2.3 time
srand的种子是随机的,其中一层意思就是,它的种子是一直在变化的,才能保证rand的随机数列在变化。这是,我们可以想一下,什么是一直变化,且在短时间内是不会重复的?
此时,各位不妨看一下你们手机或电脑的时间,是不是每一秒都在变化?
所以,在程序中我们一般使用程序运行的时间作为种子的,因为时间时刻在变化。
time_t time (time_t* time);
time函数会返回当前的日历时间,其实返回的是1970年1月1日0时0分0秒到现在程序运行时间的插值,单位是秒。返回的类型是time_t类型的,time _t类型的本质上其实就是32位或64位的整数类型。
time函数的参数是timer,如果是非NULL的指针的话,函数也会将这个返回的差值放在timer指向的内存中带回去
如果timet是NULL,就只返回这个时间的差值。time函数返回的这个时间差也被叫做:时间戳。
time函数需要包含头文件:time.h
如果只是让time函数返回时间戳,我们可以这样写:
time(NULL);
int main()
{
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一次性产生大量的随机数,构成一个随机数列,所以srand不用频繁使用,一次运行的程序中调用一次就可以了
2.4随机数的范围
如果我们要生成0~99之间随机数,方法如下:
rand()%100//余数是0~99
如果要生成1~100之间的随机数,方法如下:
rand()%100 + 1 //%100的余数是0~99,,0~99+1就是1~100
如果要生成100~200之间的数字
100 + rand() % (200-100+1) //%101生成0~100之间的数字,+100就是100~200
由此可得出:生成a~b的随机数,方法如下:
a + rand()%(b-a+1)
三:游戏设计
3.1菜单
首先,我们应该打印一个游戏菜单,让玩家选择是否进入游戏,是退出游戏还是继续游戏?
其次就是,这个菜单时玩家已进入程序就会立即出现的,在这之前玩家还无需输入什么,菜单出现后,可以根据菜单的提示,做出相应的选择,所以这里就要用do while循环
printf("*******************\n");
printf("***** 0. exit *****\n");//0退出
printf("***** 1. play *****\n");//1游戏
printf("*******************\n");
为了让主函数更加简洁,易懂,让菜单可以重复利用,我们可以将菜单封装到一个函数中
3.2核心代码实现
核心代码的操作与菜单类似,封装到一个函数中
而在核心代码之前,应该有让玩家选择输入1还是0,输入1,才会进行游戏
有一个问题,srand生成的种子是放在game函数中,还是main函数中呢?
1.从调用次数来说,放在game函数中,如果玩家一次性玩多次,会被调用多次,而放在main函数中,玩家调用一次程序,在退出游戏之前,srand就只调用一次。从次数来说,放在main函数中好。
2.考虑极端情况,放在game中,如果玩家第一次玩游戏是一次性就猜中了,然后有选择继续玩,这是game就被调用第二次,srand就会根据当前时间,再次生成随机数,但是一次性猜中的情况下,两局游戏距离时间可能是十分短,所以srand产生的种子可能相同,那么随机数就是伪随机了。而放在main函数中,即使两次时间距离短暂,但是既然没有退出程序,那下次再猜数字就是从生成好的随机数列中继续向下选取数字,在一列随机数中,两个随机数不会相同。
综上,放在main函数中。
四:代码
void menu()
{
printf("*******************\n");
printf("***** 0. exit *****\n");//0退出
printf("***** 1. play *****\n");//1游戏
printf("*******************\n");
}
void game()
{
int num = rand() % 100 + 1;
int guess = 0;
while (1)
{
printf("请输入您猜的数字:");
scanf("%d", &guess);
if (guess < num)
{
printf("您猜小了\n");
}
else if (guess > num)
{
printf("您猜大了\n");
}
else
{
printf("恭喜您,猜对了\n");
break;
}
}
}
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("退出游戏");
break;
default:
printf("输入错误,请重新输入");
}
} while (input);
}
五:游戏进阶
以上是猜数字游戏的基础版,再次之上还可以加各种限制要求,例如猜一定的次数就不可以再猜了......