C语言--分支循环实践:猜数字游戏
在这次的实践项目中,需要编写一个可以在控制台运行的猜数字的游戏。
1. 游戏要求
- 电脑自动生成1~100的随机数
- 玩家输入要猜的数字,猜数字的过程中根据猜的数字的大小给出猜大了猜小了的反馈,直到猜对游戏结束。
2. 游戏设计思路
2.1 游戏界面
首先设置一个菜单让玩家可以自行选择是否进入游戏,对于菜单部分只需要使用基本的printf函数根据自己的想法打印出菜单即可。
void menu()
{
printf("***********************\n");
printf("****** 1. play ******\n");
printf("****** 0. exit ******\n");
printf("***********************\n");
}
2.2 随机数的生成
要想完成猜数字的游戏首先要生成随机数,所以以下随机数的生成部分是本次实践的重点部分。
2.2.1 rand
C语言提供了一个函数叫做rand
,这个函数是可以生成随机数的,函数原型如下所示:
int rand (void);
rand函数会返回一个伪随机数,这个随机数的范围是在0 ~ RAND_MAX
之间,这个RAND_MAX的大小是依赖编译器上实现的,但是大部分编译器上是32767。
rand函数的使用需要包含一个头文件:stdlib.h
下面是在VS中调用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个数字是相对随机的,但是下一次运行程序生成的结果和上一次是一摸一样的这就说明是有一定问题的。
这是因为其rand函数生成的随机数是伪随机的,伪随机数不是真正的随机数,是通过某种算法生成的随机数。真正的随机数是无法预测下一个值是多少的。而rand函数就是对一个叫做 **“种子”**的基准值进行运算生成的随机数。
之所以前面每次运行程序产生的随机数序列是一样的,那是因为rand函数生成的随机数默认种子是1 。所以要生成不同的随机数,就要让种子是变化的。
2.2.2 srand
C语言中又提供了一个函数srand,用来初始化随机数的生成器的,srand函数的原型如下:
void srand (unsigned int seed);
程序中调用rand函数之前先调用srand函数的参数seed来设置rand函数生成随机数的时候的种子,只要种子在变化,每次生成的随机数序列也就变化起来了。
那也就是说给srand的种子是如果是随机的,rand就能生成随机数;在生成随机数的时候又需要一个随机数,这就矛盾了。
2.2.3 time
在程序中我们的一般使用程序运行的时间作为种子,因为时间是时刻发生变化的。
在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函数返回的这个时间差也被叫做:[时间戳](时间戳(Unix timestamp)转换工具 - 在线工具)。
time函数的时候需要包含头文件:time.h
//VS2022 上time_t类型的说明
#ifndef _CRT_NO_TIME_T
#ifdef _USE_32BIT_TIME_T
typedef __time32_t time_t;
#else
typedef __time64_t time_t;
#endif
#endif
typedef 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函数是不需要频繁调用的,一次运行的程序中调用一次就够了。
5.2.2.5 设置随机数的范围
如果要生成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);
//余数的范围是0~100,加100之后就是100~200
如果要生成a~b之间的随机数
a + rand() % (b - a + 1);
5.2.3 游戏过程中的实现
游戏中的核心是利用分支循环语句实现每次玩家输进去的值都和利用rand函数获取的随机数进行比较,再对三种情况分别在对应的分支语句中输出想要展示给玩家的信息。
同时还可以在游戏开始之前,询问玩家想要猜数字的次数,可以增加游戏难度和趣味性,并在每猜一次再把剩下的次数返回给玩家。
5.3 代码部分
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void game()
{
int r = rand() % 100 + 1; //生成0~100的随机数
int guess = 0; //用于存储玩家猜测的数字
int count = 0; //规定猜数字的次数
printf("请选择你猜数字的次数:"); //玩家自定义猜数字的字数,并在每次游戏开始的时候都有一次机会选择
scanf("%d", &count);
do
{
printf("请猜数字:");
scanf("%d", &guess);
if (guess < r)
{
printf("猜小了\n");
count--;
printf("你还有%d次机会\n", count);
}
else if (guess > r)
{
printf("猜大了\n");
count--;
printf("你还有%d次机会\n", count);
}
else
{
printf("恭喜你猜对了\n");
break;
}
} while (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();
scanf("%d", &input);
switch (input) //对玩家选择的不同模式进行分支处理
{
case 1:
game();
break;
case 0:
printf("游戏结束,请重新选择");
break;
default:
printf("选择错误请重新选择");
break;
}
} while (input);
return 0;
}