【C语言】猜数字游戏的实现与解析(可能是本年度最详细的代码实现与解析)

  前言:

  在写这一篇博客之前,我一直在想要用什么样的方式给大家展现出来,是先把整个工程完成再去讲每段代码?还是按照代码的实现过程去一步一步推进?或许两者随便哪个都行,但是我觉得,如果想要把一个知识点讲透了,让读者能够跟着自己的文章一起去思考,并且能够独立完成这个项目,那么,我需要做的就不止这点了。

【C语言】猜数字游戏的实现与解析(可能是本年度最详细的代码实现与解析)_第1张图片


目录

                              前言:

                     项目功能的逐步实现:

                  一:开始前的准备,需求分析

                  二:项目模块化

                  三,函数的实现

              1,main函数的设计

              代码解析:

     1)引用头文件的作用:

     2)使用do while循环的逻辑:

     3)switch 语句选择在本程序中的原理:

      函数测试:

              2,封装函数功能实现

       1)menu函数封装:

       2)game函数封装

              代码解析:

         1)生成随机数实现原理及方法:

2)设置所生成随机数范围的方法:

3)设置次数的逻辑:

  代码测试:

                  四,项目的总体预览:

            test.c

            game.c

           game.h

                  五,作者的话:


 

项目功能的逐步实现:

  一:开始前的准备,需求分析

  作为一个1~100之间的猜数字游戏,需求分析如下:

  1. 游戏开始菜单
  2. 进入游戏后,需要随机生成一个1~100之间的随机数
  3. 当我们在猜数字时,要根据与被猜数的距离给出相应的提示
  4. 设置猜的次数,超过次数则游戏结束
  5. 游戏结束时需要能够继续让用户选择是否继续游戏

二:项目模块化

  在项目中,我本人比较建议大家尽量进行函数模块化,这样可以使代码逻辑更加清晰,不论是自己还是别人都能够很好地去阅读代码。

  例如:本项目我会分成三个模块:

  1. test.c ————用于存放main函数
  2. game.c————用于存放自己封装的函数
  3. game.h————头文件,用于存放整个项目的声明

【C语言】猜数字游戏的实现与解析(可能是本年度最详细的代码实现与解析)_第2张图片

三,函数的实现

  1,main函数的设计

  在写函数的功能之前,我们应当先想清楚程序的运行逻辑,故在写main函数时,若是需要什么功能,可先写个函数名以及他的功能,等主函数完成后,再去一一实现,这样有利于逻辑思考的连续性。

  根据需求分析得:该游戏需要提前打印菜单,并且用户可以选择开始或退出游戏,与此同时,该游戏在结束时需要能够让客户自行选择是否继续游戏。代码如下(选择用截图是因为编译器对关键字有颜色区分,可以看得清楚一点):【C语言】猜数字游戏的实现与解析(可能是本年度最详细的代码实现与解析)_第3张图片

  代码解析:
     1)引用头文件的作用:

       在代码第十行(左边行号)处有一段代码:#include “game.h” ,这个是包含我们自己所创建的头文件的作用 ,这样一来我们就可以把所有的函数引用,库函数头文件包含等等,通通写进game.h中,而我们只需要在主函数前包含这一个文件即可,如此可以使得代码看起来更简洁干净。

      (  如果是包含库函数的头文件就是写成:#include <头文件名>,若是包含我们自己创建的头文件,就写成:#include “头文件名”)

     2)使用do while循环的逻辑:

        之所以使用do while循环,主要是因为无论用户是否开始游戏,函数都会先打印菜单。

     3)switch 语句选择在本程序中的原理:

       在主函数中,我设定了局部变量 input,并将其作为do while循环的判断条件。

       在do while循环的判断条件中,非0为真,0为假。所以1为真,我们可以在case 1 下存放游戏函数,当用户输入1时,便会开始游戏,并且在游戏结束时break跳到do while判定,因为1为真,故继续执行循环,打印菜单...以此类推

       而0为假,当用户输入0时,程序跳到case 0 语句再执行break跳出循环来到do while判定,因为0为假,故循环结束,退出游戏

        而且为了防止用户乱输入,我们还应当加上 default 以纠正。 (default:根据用户输入的值,执⾏相应的 case 分⽀。如果找不到对应的值,就执⾏ default 分⽀)

  函数测试:

    main函数的逻辑完善后,我们还应当检验其是否能够以正确的方式输出我们想要的值,该代码输出如下:

【C语言】猜数字游戏的实现与解析(可能是本年度最详细的代码实现与解析)_第4张图片

  可以看到,输出正常,证明逻辑没有出错。

 2,封装函数功能实现
    1)menu函数封装:

  根据需求分析得,当用户执行程序时,需要菜单来选择自己是开始游戏或者退出游戏,所以我们需要封装一个 menu 函数用于打印菜单。而封装的 menu 函数放入game.c的源文件中(game.c也需要包含头文件game.h)。如下图:

【C语言】猜数字游戏的实现与解析(可能是本年度最详细的代码实现与解析)_第5张图片
  menu函数:这是我们自己封装的一个函数,主要作用就是打印菜单,所以无需传参,并且无需返回值,所以函数形式为:void menu();。
 


  当我们完成函数的封装后,需要将函数的声明写入头文件中,如下:【C语言】猜数字游戏的实现与解析(可能是本年度最详细的代码实现与解析)_第6张图片

2)game函数封装

  根据需求分析,可以得出game()函数应当具备以下功能:

   1,随机生成一个1~100之间的随机数

   2,当我们在猜数字时,要根据与被猜数的距离给出相应的提示

   3,设置猜的次数,超过次数则游戏结束

函数如下:【C语言】猜数字游戏的实现与解析(可能是本年度最详细的代码实现与解析)_第7张图片

  该函数无需返回任何值,故返回类型设置成void,并且无需传参。同样的,与menu函数相同,需要将函数引用放置在头文件game.h中,如下:【C语言】猜数字游戏的实现与解析(可能是本年度最详细的代码实现与解析)_第8张图片

 
代码解析:
  1)生成随机数实现原理及方法:

   在讲生成随机数之前,我需要先给大家铺垫三个函数:

  1,rand函数:C语⾔提供了⼀个函数叫 rand,这函数是可以⽣成随机数的。rand函数的使用需要包含头文件:stdlib.h。

  但是当我们多次输出rand函数时,发现每次输出的随机数是一样的,如下:【C语言】猜数字游戏的实现与解析(可能是本年度最详细的代码实现与解析)_第9张图片

  不管运行多少次,所输出的随机值都与上一次结果相同,说明这是有问题的。

  如果再深⼊了解⼀下,我们就不难发现,其实rand函数⽣成的随机数是伪随机的,伪随机数不是真正的随机数,是通过某种算法⽣成的随机数。真正的随机数的是⽆法预测下⼀个值是多少的。⽽rand函数是对⼀个叫“种⼦”的基准值进⾏运算⽣成的随机数。

  之所以前⾯每次运⾏程序产⽣的随机数序列是⼀样的,那是因为rand函数⽣成随机数的默认种⼦是1。如果要⽣成不同的随机数,就要让种⼦是变化的。

   2,srand函数:C语⾔中⼜提供了⼀个函数叫 srand,⽤来初始化随机数的⽣成器的。

//原型为:
void srand(unsigned int send);

   程序中在调⽤ rand 函数之前先调⽤ srand 函数,通过 srand 函数的参数seed来设置rand函数⽣成随机数的时候的种⼦,只要种⼦在变化,每次⽣成的随机数序列也就变化起来了。那也就是说给srand的种⼦是如果是随机的,rand就能⽣成随机数;在⽣成随机数的时候⼜需要⼀个随机数,这就⽭盾了。

   注意:srand函数每个工程只需要调用一次即可!

   3,time函数:在C语⾔中有⼀个函数叫 time ,time函数的使用需要包含头文件:time.h。

//原型为:
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(NULL);//调用time返回时间戳,这里没有返回接收值

  由上铺垫可得:我们如果用rand需要生成一个随机数,就需要调用srand函数来初始化rand函数的种子,而为了使种子随时在变化,我们就将时间戳设置为rand函数的种子,因为时间一直在变化,所以种子就一直在变化,同样的,随机数也一直在变化。

  代码形式如下:

srand((unsigned int)time(NULL));

   注意:srand函数一个工程只需要调用一次,故该段代码应当放在main函数中调用!

2)设置所生成随机数范围的方法:

  在我们设置好随机数后,需要再设置随机数的生成范围,在该项目中,我们需要生成1~100的随机数,方法及操作逻辑如下:

  假设随机生成一个数n,我们只需将 n%100,那么n%100的数字便会变成0~99,我们只需操作为:n%100 + 1;此时生成的随机数的范围便是1~100;

   推而广之,如果要生成a~b的随机数,方法如下:【C语言】猜数字游戏的实现与解析(可能是本年度最详细的代码实现与解析)_第10张图片

 
3)设置次数的逻辑:

  在game()函数中,我将猜数字设置成while循环,并且将局部变量times设为判定条件。每次进入循环(猜数字)结束后,times--,故当times=0时,while判定为假,结束循环,即可实现。

  代码测试:

【C语言】猜数字游戏的实现与解析(可能是本年度最详细的代码实现与解析)_第11张图片

【C语言】猜数字游戏的实现与解析(可能是本年度最详细的代码实现与解析)_第12张图片

 可以看到,代码的所以功能都在我的设想当中,故没有问题。当然,整个程序还可以更加好,提示也可以更加细致,如根据不同的距离显示不同的提示等等,但是由于时间原因,我只是写了一个主体出来,如果大家有兴趣的话,可以接着将其优化。

四,项目的总体预览:

test.c
#define _CRT_SECURE_NO_WARNINGS 1 

#include "game.h"

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();//菜单
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();//游戏封装
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("非法输入,请重新输入\n");
			break;
		}

	} while (input);
	return 0;
}

game.c
#define _CRT_SECURE_NO_WARNINGS 1 

#include "game.h"


void menu()
{
	printf("************************\n");
	printf("************************\n");
	printf("******* 1:> play *******\n");
	printf("******* 2:> exit *******\n");
	printf("************************\n");
	printf("************************\n");
	return;
}

void game()
{
	int count = rand() % 100 + 1;
	int find = 0;
	int times = 8;//猜数字次数
	while (times)
	{
		printf("\n");
		printf("你还有%d次机会 ",times);
		printf("请输入您要猜的数字:> ");
		scanf("%d", &find);
		if (find < count)
		{
			printf("您猜小了,再猜大一点的数字\n");
		}
		else if (find > count)
		{
			printf("您猜大了,再猜小一点的数字\n");
		}
		else if (find == count)
		{
			printf("恭喜您,猜对了!\n");
			break;
		}
		times--;
	}
	if (times == 0)
		printf("游戏失败,正确数字为%d\n",count);

	return;
}

game.h
#pragma once

#include 

#include 

#include 

void menu();

void game();

五,作者的话:

 整篇文章,我对于一些比较简单的内容讲的比较细致,比如switch语句,设置次数等等,主要是想帮助一些不懂的朋友理解这个代码这样写的原因,如果您觉得啰嗦了,可以在评论区留言,以便我下次改进。

 目前我在考虑写一个有关扫雷实现的博客,但是扫雷我还有几个功能一直没能实现,等我彻底搞明白之后就会和大家分享!

  谢谢大家的观看,再见!

你可能感兴趣的:(c语言)