前言
学了分支和循环语句,我们可以尝试用这些知识来写一个简单的小游戏了!
大家在生活中有没有和朋友玩过猜数字的游戏?
游戏规则: 给定一个猜数字范围1~100(可以改变),一个人出数字,另一个人猜,猜的人每次猜数字,另一个人根据数字大小给出猜大了或者猜小了的反馈,直至猜对,游戏结束。
了解游戏规则后相信大部分人一定与身边的好友玩过,没有玩过也没有关系,今天我们将用C语言写一个这样简单有趣的小游戏,可以与电脑先玩一次!
正文
1. 游戏框架设计
一个游戏必定要有一个游戏框架,这个框架一定是和这个游戏具体的流程相关,大家可以结合我们玩过的单机游戏,如超级玛丽,魂斗罗等等,这些小游戏无一例外地刚开始进入游戏会出现一个菜单,让我们选择玩游戏或退出游戏等,并且一局游戏结束后,菜单还会再次出现让我们选择是否继续游戏,让我们尝试实现这个菜单。
#include <stdio.h>
int main()
{
do
{
printf("*********************\n");
printf("******1.paly*********\n");
printf("******0.exit*********\n");
printf("*********************\n");
printf("请选择:");//以上是菜单的基本结构
} while ();
return 0;
}
大家发现我把菜单放在了do-while循环内了,你们能说说为什么吗?
在之前讲到do-while循环的时候,我也曾用写菜单为例介绍do-while循环,因为一个游戏进去之后就是菜单,第一次的菜单时不需要任何条件就要打印的,这和do-while循环的使用很像,所以这里使用它非常合适!
那下一步就轮到选择了,选择1开始玩游戏,选择0退出游戏(这里让它为0是为了配合计算机中0为假,非0为真的特点),所以我们要创建一个临时变量来接收用户输入的值,再进行判断play或exit,这就用到了我们学过的分支语句,让我们尝试一下。
#include <stdio.h>
int main()
{
int i = 0;//创建临时变量接收用户的选择
do
{
printf("*********************\n");
printf("******1.paly*********\n");
printf("******0.exit*********\n");
printf("*********************\n");
printf("请选择:\n");//以上是菜单的基本结构
scanf("%d",&i);
switch(i)
{
case 1:
print("play\n");
break;
case 0;
printf("游戏结束\n");
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
} while (i);
return 0;
}
上面的代码中我使用了switch语句,if-else亦可。
选择1玩游戏,这里用play暂时代替,稍后我们再在这里写入游戏内容。选择0游戏结束。大家有没有考虑过,如果用户输入的不是1或者0程序该如何处理呢?default子句派上用场了。
到这里游戏框架基本结束了,但是别忘了我们还没有给do-while循环一个合适的循环条件,大家可以看到我在上面的代码中以 i 为条件,i 如果等于1(用户选择了1play),则肯定要再次进入游戏菜单,1为非0为真进入循环。i 如果等于0(用户选择了0exit),那么游戏就要结束了不再进入循环,0为假。这就是刚才将 exit 的选择设置为 0 的妙处。
2.游戏内容的实现
进入菜单选择1play以后,游戏就要开始了,那游戏该怎么开始呢?我们回想,猜数字游戏的过程中首先是一个人出数字,那这个数字就是我们所要围绕的的关键了。这个数字必须是随机的,并且在我们给定的范围1~100内。那我们该怎么产生一个随机数呢?
2.1 随机数生成
2.1.1 rand()函数
C语⾔提供了⼀个函数叫 rand,这个函数是可以生成随机数的,函数我们还没有学,但我们知道函数是C程序的基本单位,C程序是由一个个函数组成的,包括main函数,这里我们简单介绍rand()函数,之后再函数的介绍中会具体学习。rand函数原型如下所示:
int rand(void);
调用rand函数后,rand函数会返回⼀个伪随机数,这个随机数的范围是在0~RAND_MAX之间,RAND_MAX的大小是依赖编译器上实现的,但是大部分编译器上是32767。
rand函数的使用需要包含一个头文件是:stdlib.h
让我们测试一下:
确实得到了五个不同的随机数字,但是当我关闭控制台,再次运行之后我意外地发现生成的五个数字和上次的五个一模一样。
这是怎么回事呢?
rand函数生成的是**伪随机数,**是由固定的算法生成的,所以生成的数字并不是真正的随机数,而真正的随机数并不能预测到它每次的数字是多少,倘若我们使用这样的随机数去猜,当我们玩完这次游戏之后,下一次再次打开这个游戏,猜的数字还是那么几个,这个游戏还有什么意义?那我们该怎么产生真正的随机数呢?
2.1.2 srand()函数
C语言中又提供了⼀个函数叫 srand,用来初始化随机数的生成器的,srand的原型如下:
void srand(unsigned int seed);
rand函数的使用需要包含一个头文件是:stdlib.h
rand函数是对一个叫做种子的基准值进行运算生成随机数,默认的种子是1。
程序中在调用 rand 函数之前先调用 srand 函数,通过 srand 函数的参数 seed 来设置 rand 函数生成随机数的时候的种子,只要种子在变化,每次生成的随机数序列也就变化起来了。那也就是说给 srand 的种子是如果是随机的,rand就能生成随机数,那这时候我们就知道了只要让这个种子随机不就行了呗,那就再调用一次rand函数!这样做对吗?这次调用的rand函数的种子默认还是1,那它产生随机数仍旧是伪随机数,生成一个随机数竟然要调用一个随机数,这很明显不行,那我们还有什么办法去找到一个随机的数呢?
2.1.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函数返回的这个时间差也被叫做:时间戳。
time 函数的时候需要包含头文件:time.h
而我们现在只需要一个随机数字,那么timer参数为NULL即可,让我们测试一下:
我们多测试几次,发现每次生成的随机数的确不一样了,这就达到我们的目的了。
2.2 随机数的范围
上面我们说了调用rand函数生成的这个随机数的范围是在0~RAND_MAX之间,RAND_MAX的大小是依赖编译器上实现的,但是大部分编译器上是32767。
在计算器上我们看到0x7fff==32767,所以使用这个rand函数可以产生0~32767之间的随机数,但是我们想要的范围是1 ~ 100,这该怎么实现呢?
我们可以将rand()%100取余,由于它是一个五位数字,除以100求余后的余数范围是0~99,在这个基础上再加上1,就得到了1 ~ 100的随机数。
rand()%100 //生成0~99的随机数
1+rand()%100 //生成1~100的随机数
101+rand()%100//生成101~200的随机数
我们发现规律:
a+rand()%(b-a+1)//生成a~b的随机数
2.3 游戏内容
学会了生成随机数的方法后,剩余的东西就简单了。我们需要让用户去输入猜值并与答案对比,给出猜大了猜小了猜对了的反馈,有了思路,我们尝试去写一写。
int num = 1 + rand() % 100;//随机数
int guess = 0;//用于接收用户输入的猜值
while(1)
{
printf("请猜数字:<\n");
scanf("%d",&guess);
if(guess > num)
{
printf("猜大了,请重新输入!\n");
}
else if(guess < num)
{
printf("猜小了,请重新输入!\n");
}
else
{
printf("恭喜你!猜对了");
break;
}
}
大家可以看到我设置while的循环条件是1,让它一直循环,再循环体内部给了一个条件break,这种方法适用于当你暂时不清楚循环多少次、循环条件是什么的情况,在循环体内部给出循环继续的条件,放入一个break可以随时跳出循环,防止死循环。
以上就是游戏的逻辑。
3. 猜数字游戏的实现
现在,我们一切准备就绪,只需要把上面的代码拼装在一起,一个猜数字游戏就实现了,当然这很简单。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
srand((unsigned int)time(NULL));
int i = 0;
do
{
printf("*********************\n");
printf("******1.paly*********\n");
printf("******0.exit*********\n");
printf("*********************\n");
printf("请选择:");
scanf("%d", &i);
switch (i)
{
case 1:
{
int num = 1 + rand() % 100;
int i = 0;
while (1)
{
printf("请猜数字(1~100)\n");
scanf("%d", &i);
if (i > num)
{
printf("猜大了,请重新输入\n");
}
else if (i < num)
{
printf("猜小了,请重新输入!\n");
}
else
{
printf("猜对了!");
break;
}
}
}
break;
case 0:
printf("游戏结束\n");
break;
default:
printf("选择错误!请重新选择!\n");
break;
}
} while (i);
return 0;
}
但是,大家有没有发现,其实我们所写的菜单部分,游戏内容部分都是一个独立的整体,每次循环、游戏都会调用这一部分,这和我们即将要学习的函数很像。为了让我们的代码整洁,高效,我们可以把这两部分写成函数,使用的时候调用即可。如下:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void game()
{
int num = 1 + rand() % 100;
int i = 0;
while (1)
{
printf("请猜数字(1~100)\n");
scanf("%d", &i);
if (i > num)
{
printf("猜大了,请重新输入\n");
}
else if (i < num)
{
printf("猜小了,请重新输入!\n");
}
else
{
printf("猜对了!");
break;
}
}
}
void menu()
{
printf("*********************\n");
printf("******1.paly*********\n");
printf("******0.exit*********\n");
printf("*********************\n");
printf("请选择:");
}
int main()
{
srand((unsigned int)time(NULL));
int i = 0;
do
{
menu();
scanf("%d", &i);
switch (i)
{
case 1:
game();
break;
case 0:
printf("游戏结束\n");
break;
default:
printf("选择错误!请重新选择!\n");
break;
}
} while (i);
return 0;
}
对比一看,的确第二种代码更加容易阅读!
总结
猜数字游戏到这里就算介绍完了,其实这个小游戏具体在细节上大家可以根据自己的喜好去做一些修改,比如,如果我想知道我猜了几次猜对的?如何记录?如果我想设置最多猜五次怎么设计呢?等等这些拓展,希望大家可以自己尝试去动手改写!
下篇博客我将为大家介绍一个新的概念:数组。