C语言-猜数字小游戏

先确定要实现的目标需求,写一个猜数字游戏。

1.生成一个1 - 100之间的随机数。

2.输入数字进行猜数字。

  • a.猜对了,提示“恭喜猜中了”,游戏结束。

  • b.猜错了,提示数字猜的大了,还是猜的小了,继续猜,直到猜对为止。

3.游戏结束后可以进行下一轮继续猜,除非选择退出游戏。

首先在main()函数中写出大致的框架:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int main()
{
int input = 0;
do
{
menu(); // 打印游戏菜单
printf("请选择:>", input);
scanf("%d", &input);
switch (input)
{
case 1:
printf("猜数字\n");
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("ERROR,请重新输入\n");
break;
}

} while (input);
}

此时用do……while循环,首先“do”进行代码的运行,当游戏结束后,input为”1“,则还可以进行下一轮游戏。

1
2
3
4
5
6
7
8
// 定义菜单
void menu()
{
printf("*********************\n");
printf("******* 1.play ******\n");
printf("******* 0.exit ******\n");
printf("*********************\n");
}

此时可以测试,进入循环中的switch时,当输入数字“1”的时候,打印“猜数字”,当输入数字不为“1”或“0”时,则重新选择数字,当输入数字为“0”的时候,则打印“退出游戏”。

image-20240125121048196

当主体框架搭建好,接下来就是构建游戏本体函数”game()“,首先分析游戏实现步骤:

  1. 生成随机数

  2. 猜数字,然后提示

则第一步生成随机数,使用”stdlib.h”库文件里的”rand()”函数,打印生成的随机数结果进行输出,测试。

1
2
3
4
5
6
// 定义游戏主体
void game()
{
int num = rand();
printf("%d\n", num);
}

当你进行测试的时候,你会发现,当关闭再打开程序所生成的随机数竟然是一模一样的,那这样固定序列的游戏就丧失了猜的乐趣,所以通过查阅手册发现:

The rand function returns a pseudorandom integer in the range 0 to RAND_MAX. Use the srand function to seed the pseudorandom-number generator before calling rand.

rand函数返回一个范围从0到rand_MAX的伪随机整数。在调用rand之前,请使用srand函数为伪随机数生成器设定种子。

由”rand”函数的定义可知,在调用”rand”函数之前,需要调用”srand”函数设置一个随机数生成器种子,当种子一样的时候,生产的随机数序列也是一样的,那么有没有一种方法可以使种子数也跟着随机呢?

有,答案是时间戳,通过函数获取当前时间然后转换为时间戳再将值传递给随机数种子,这样就是真正随机生成的数了。

时间戳

Unix 时间戳是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒。

image-20240125131253905

C语言获取时间戳代码:

1
2
3
4
5
6
7
8
#include <sys/time.h>

// ...
struct timeval tv;
gettimeofday(&tv, NULL);
// 秒: tv.tv_sec
// 毫秒: tv.tv_sec * 1000LL + tv.tv_usec / 1000

则游戏主体代码修改为:

1
2
3
4
5
6
7
// 定义游戏主体
void game()
{
srand((unsigned int)time(NULL));
int num = rand(); // rand()生成随机数返回0~32757之间的一个数字
printf("%d\n", num);
}

”void_cdecl srand(unsigned int_Seed)“所以需要将时间戳函数”time(NULL)“强制转化为unsigned int类型,输出结果测试:

image-20240125133519347

会发现,两次测试的结果特别相近,同时输入较快时,还是可能会出现相同的随机数,由此分析,其实每次打开只需要进行一次随机数种子设置,并不一定要在游戏调用时进行设置,可以将随机数种子设置放在循环中,由此可以达到真正的随机。

还有一个问题,我们猜数字游戏目标是生成一个1 - 100之间的随机数,使用rand函数生成的随机数在0-32767之间,那么怎么生成1-100的随机数呢?很简单,让rand()生成随机数进行模100操作,返回的值范围在0-99,此时再加1返回的就是1-100的随机数。

1
int num = rand() % 100 + 1;

打印结果进行测试:

image-20240125134522544

然后就是游戏的实现方法,也比较简单,就是进行”if“语句判断,判断输入的数字比随机生成的数字大还是小。

如果比随机生成的数字大,则提示”猜大了“,反之则”猜小了“,如果都不属于,那就是想等,打印”猜中了“然后跳出while循环。

此时此刻,input的值是1,do while循环的判断条件为真,则继续进行下一轮do while循环,跳到menu函数进行选择,可以选择继续游戏,也可以选择退出游戏。

至此,游戏实现完毕,快来玩一下吧!

image-20240125135725715

完整代码,已上传到GitHub:https://github.com/ivanished/C-learning

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <time.h>
#include <stdlib.h>

//先确定要实现的目标需求,写一个猜数字游戏。
//
//1.生成一个1 - 100之间的随机数。
//
//2.输入数字进行猜数字。
//
//- a.猜对了,提示“恭喜猜中了”,游戏结束。
//
//- b.猜错了,提示数字猜的大了,还是猜的小了,继续猜,直到猜对为止。
//
//3.游戏结束后可以进行下一轮继续猜,除非选择退出游戏。

// 定义Menu
void menu()
{
printf("*********************\n");
printf("******* 1.play ******\n");
printf("******* 0.exit ******\n");
printf("*********************\n");
}

// 定义游戏主体
void game()
{
int num = rand() % 100 + 1; // rand()生成随机数进行模100返回的值范围在0-99,此时再加1返回的就是1-100
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("请选择:>", input);
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("ERROR,请重新输入\n");
break;
}

} while (input);
}