一.项目需求
实现一个控制台版猜数字游戏,具备以下功能:
1.电脑随机生成一个1 ~ 100之间的整数
2.提供主菜单,支持选择“开始游戏”或“退出游戏”
3.处理玩家非法输入(如字母、符号、非法数字),防止程序卡死
4.每次猜测后给出“猜大了”“猜小了”的提示
5.玩家猜测正确后,游戏回到主菜单界面
二.分步实现代码与思路讲解
那我们正式开始梳理我们的设计思路,首先搭建一个主函数框架
//主函数框架intmain(){return0;}随后,我们要打印一个简单的主菜单,要让用户“有得选”,这里我们可以选择封装为一个函数,命名为menu,这个函数不需要返回值和参数,只负责打印主菜单
当然printf函数需要头文件<stdio.h>(咱们后续就默认已经有了这个头文件了)
#include<stdio.h>//打印主菜单voidmenu(){printf("---------------------\n");printf("------猜数字游戏------\n");printf("-------1. play-------\n");printf("-------0. exit-------\n");printf("---------------------\n");}主菜单有了,用户也就可以开始进行选择了,这里我们可以用printf函数先告诉用户可以选择数字了,随后用scanf函数接收用户输入的值
在用户输入完成后,我们需要用到条件语句去对用户输入的值进行判断,输入1的话,进入游戏(这里我们先拟定一个game函数,后面进行实现);输入0的话,退出游戏;如果输入的是其他值,可以告诉用户输入的值是非法的
这里我选择的是switch语句,当然如果使用if语句也是可以的,这里就不过多做展开了
intmain(){intinput=0;menu();printf("请输入您要选择的数字:");scanf("%d",&input);//判断用户输入switch(input){case1:game();break;case0:printf("退出游戏\n");break;default:printf("输入非法,请输入有效数字!\n");break;}return0;}这个时候我们可能在想,如果每一次都让break直接跳出的话,那相当于程序的运行是“一次性”的,也就是说这个游戏只能运行一次,那我们的初心“输入0退出游戏”就显得有些多余了,有没有什么办法能让用户如果不输入0,就一直运行下去呢?
使用循环(迭代)去解决问题是相对比较好的,我在这里选择的是do-while循环,因为我们希望程序上来不要管别的,先打印一次主菜单,也就是先运行一次游戏,后续再进行逻辑判断:用户要玩还是退出,do-while循环明显是更合适的
问题在于循环条件是什么呢?根据上述代码,我们发现输入0为“退出”这个设计刚好让while的判断条件为假,而其他任何数字都会被判断为真,所以while的循环条件同样可以使用input进行判断
intmain(){intinput=0;//循环执行逻辑do{menu();printf("请输入您要选择的数字:");scanf("%d",&input);switch(input){case1:game();break;case0:printf("退出游戏\n");break;default:printf("输入非法,请输入有效数字!\n");break;}}while(input);return0;}当然,这里我们也要考虑到用户可能输入的是字母和符号,也就是字符,那这里我们可以对scanf的返回值进行判断,如果返回值不是正常的1,就用getchar函数清空缓冲区,随后continue回到循环的起始位置
intmain(){intinput=0;do{menu();printf("请输入您要选择的数字:");///合法性判断if(scanf("%d",&input)!=1){while(getchar()!='\n');printf("输入非法,请输入有效数字!\n");continue;}switch(input){case1:game();break;case0:printf("退出游戏\n");break;default:printf("输入非法,请输入有效数字!\n");break;}}while(input);return0;}主函数的框架我们基本已经完成了,接下来就是对game函数的处理
game函数也同样不需要任何返回值和参数,我们只需要关心函数内部的逻辑是如何实现的
但在那之前,我们首先要做一件事情:生成随机值
这里我们可以使用rand函数来进行随机值的生成,rand函数是可以返回一个随机值的
但rand函数生成的随机值是“伪随机”的,因为它依靠种子(Seed)去生成随机值,这是它的“基准”。且Seed默认都是1,而且rand是无参函数,不能传参,那我们怎么改变这个Seed呢?
这里需要用到srand函数,对Seed进行初始化,但进行传参的时候,我们同样需要一个随机值,那有没有什么值是一直在变化的呢?
对,时间戳!
我们可以调用time函数,对其传一个空指针,就可以对srand的参数进行随机生成了,因为时间戳是一直在变化的
rand和srand函数的调用需要用到头文件<stdlib.h>,time函数的调用需要头文件<time.h>
但需要注意time函数的返回值是time_t类型的,而srand的参数要求是无符号整型,所以需要强制类型转换
(感兴趣的也可以去网上查,这里用的网址是www.cplusplus.com)
这里有一个小问题,我们项目的要求是随机值要在1 ~ 100之间,而rand生成的随机数字范围是多大?
是0 ~ RAND_MAX,而RAND_MAX的值通常是32767
那怎么做到把这个范围变成1 ~ 100?
取模是个不错的选择
也就是让这个数字模上100,那么这个随机值的余数就是0 ~ 99,只需要再加上1,范围就变成了1 ~ 100,符合项目要求,beautiful!
之后,我们就可以在函数内部使用printf和scanf,提醒用户可以输入自己猜测的数字了,并用scanf函数接收用户输入的值
我们可以定义一个guess变量,存储用户猜测的数字,把rand的返回值存储到num变量里,然后使用if条件语句对guess和num进行大小比较判断,而我们想实现让“一直猜”的效果,就可以用while死循环来实现,猜对后break跳出即可
这里我的循环条件使用了Bool类型,使用别的当然也是可以的(布尔类型需要头文件<stdbool.h>)
逻辑如下:
#include<stdlib.h>#include<time.h>#include<stdbool.h>voidgame(){srand((unsignedint)time(NULL));intguess=0;intnum=rand()%100+1;while(true){printf("请输入您要猜测的数字:");if(scanf("%d",&guess)!=1){while(getchar()!='\n');printf("输入非法,请输入有效数字!\n");continue;}if(guess>num){printf("猜大了\n");}elseif(guess<num){printf("猜小了\n");}else{printf("恭喜你,猜对了!\n");break;}}}但这里也需要注意,time(NULL)的精度是秒,如果在同一秒内多次开始游戏,生成的随机数会完全一样,所以如果玩的人比较多的话,把srand放在main函数里是更好的
至此,我们的简易项目就圆满完成了
三.功能拓展
那我们这个项目能不能拓展一些功能呢?或者进行一些优化呢?
相信大家都有自己的想法,这里我就以“增加猜测次数限制功能”举例
我们怎么实现猜测次数限制呢?
首先game函数里的while循环条件要动吗?其实可以改,也可以不改
因为我们的代码逻辑是“一直猜,猜对了就break跳出循环”,如果不改的话,无非就是增加一个次数变量,在循环内部的开头判断一次,如果条件为真break跳出即可,这和改动循环条件为次数变量,归0时再判断本质上是一样的,我们这里就不进行改动了
由于次数限制可能是需要频繁被修改的,我们可以直接宏定义一个GUESS_TIMES,次数用这个宏定义去定义就可以了
当然,如果想在用户游戏失败后,告诉用户正确答案,也是可行的
这里我就直接展示代码了,逻辑如下:
#defineGUESS_TIMES5voidgame(){srand((unsignedint)time(NULL));intleft=GUESS_TIMES;intguess=0;intnum=rand()%100+1;while(true){if(left==0){printf("很遗憾,%d次机会用完,您失败了!\n",GUESS_TIMES);printf("正确答案为:%d\n",num);break;}printf("请输入您要猜测的数字(您还有%d次机会):",left);if(scanf("%d",&guess)!=1){while(getchar()!='\n');printf("输入非法,请输入有效数字!\n");continue;}if(guess>num){printf("猜大了\n");left--;}elseif(guess<num){printf("猜小了\n");left--;}else{printf("恭喜你,猜对了!\n");break;}}}至此,我们的代码优化也结束了
接下来是完整的.c代码:
#define_CRT_SECURE_NO_WARNINGS#defineGUESS_TIMES5#include<stdio.h>#include<stdlib.h>#include<time.h>#include<stdbool.h>voidgame(){srand((unsignedint)time(NULL));intleft=GUESS_TIMES;intguess=0;intnum=rand()%100+1;while(true){if(left==0){printf("很遗憾,%d次机会用完,您失败了!\n",GUESS_TIMES);printf("正确答案为:%d\n",num);break;}printf("请输入您要猜测的数字(您还有%d次机会):",left);if(scanf("%d",&guess)!=1){while(getchar()!='\n');printf("输入非法,请输入有效数字!\n");continue;}if(guess>num){printf("猜大了\n");left--;}elseif(guess<num){printf("猜小了\n");left--;}else{printf("恭喜你,猜对了!\n");break;}}}voidmenu(){printf("---------------------\n");printf("------猜数字游戏------\n");printf("-------1. play-------\n");printf("-------0. exit-------\n");printf("---------------------\n");}intmain(){intinput=0;do{menu();printf("请输入您要选择的数字:");if(scanf("%d",&input)!=1){while(getchar()!='\n');printf("输入非法,请输入有效数字!\n");continue;}switch(input){case1:game();break;case0:printf("退出游戏\n");break;default:printf("输入非法,请输入有效数字!\n");break;}}while(input);return0;}代码虽然简单,但是也还是挺有趣的~