扫雷游戏基础版

目录

一:游戏要实现的目标

二:对多文件使用的基础介绍

三:对游戏的分析设计

3.1游戏思路分析

3.2需要思考的问题

四:代码的实践

4.1打印游戏菜单

4.2游戏模块之创建基本变量

4.3打印棋盘,并完成数组的初始化

4.4布雷

4.5排雷

五:基础版扫雷可扩展

六:完整代码


一:游戏要实现的目标

使用控制台实现经典的扫雷游戏
游戏可以通过菜单实现继续玩或者退出游戏
扫雷的棋盘是9*9的格⼦
默认随机布置10个雷
可以排查雷
     ◦ 如果位置不是雷,就显示周围有几个雷
     ◦ 如果位置是雷,就炸死游戏结束
     ◦ 把除10个雷之外的所有雷都找出来,排雷成功,游戏结束

二:对多文件使用的基础介绍

  在这次游戏代码的实现中,我们将建立多个文件,在main文件是我们游戏的主体和框架,game.c放置的是游戏实现的代码,game.h放的是我们自定义函数的声明。

  其中我们要在主函数中引用我们自己定义的头文件格式:#include "game.h"。

  先简单介绍一下多文件的好处:

  1.有利于多人协作完成,在完成复杂代码中,多文件可以让多个程序员一起编程,每个人负责一个小部分

  2.有利于寻找错误,方便数值的更改,让代码更有可读性(这句话在本次代码中有初步体现)


三:对游戏的分析设计

3.1游戏思路分析

  1.对于最基础的扫雷格式,是9*9形式的,而要打印出9*9的表格,就需要用到二维数组的打印。

  2.我们需要随机设置10个雷,这就需要用到生成随机数的函数rand,一想到rand就要想到在猜数字用戏中对rand种子数设置所用到的函数srand

  3.还要在玩家确定一个地方后,如果这个地方不是雷,就要显示这个地方周围区域有几个雷

  4.打印一个建议游戏菜单

3.2需要思考的问题

  1.对于分析1和3来说,我们需要用二维数组打印一个9*9的表格,确定表格一个位置后,如果不是雷,还要数清周围区域(即周围八个格子)雷的数目,那就可能存在图片问题:

对于上面图片处于第八行的数字来说,它周围八个数字会超出数组的最大值,也就是说在我们编程时,如果玩家选择的是边框上的数字,再计算周围有几个雷时会出现数据溢出的情况,那怎么解决呢?难道我们对边框外的每一个溢出数据都要一一说明吗?不如换一个思路,让数组扩大,让数0据都在数组可存储的范围。例如下图:

最好的解决办法就是让数组扩大一圈,这样就可以排查9*9棋盘中所有坐标周围雷的数目。当让这个范围也不要扩的太大(比如扩大到100*100,绝对能装下),一是没这个必要,二是过大,会占多余的内存,也不易查找9*9棋盘内的数据。

2.对于分析3,我们还要再想一个问题

  我们放置雷时,在最基础的版本,从上面两张图片也可以看出我们用0表示无雷,用1表示有雷,那如果我们选择了一个地方,它显示出1,它表示是的究竟是有雷的1还是这个地方无雷,但周围有一个雷呢?表述会出现歧义。所以我们不妨建立两个相同的11*11表格,一个负责放置雷的信息,另一个放置排雷的数目的信息,刚开始打印表格时,肯定不能让玩家直接看见那个地方有雷那个地方没有,所以需要将信息隐藏起来,不妨用打印*

  如图,为设计预想

3.我们这个代码的长度在200+,所以如果一次行把所有的功能全打印在main函数中,太过冗长,不利于他人阅读,也不利于自己纠错,改进,所以我们要用多文件,自定义函数分多模块来完成。


四:代码的实践

  了解游戏大概要实现的功能,分析完潜在的问题后,我们可以开始正示敲代码了。

4.1打印游戏菜单

再简单的游戏,也应该有一个游戏界面让玩家看到,并且让玩家拥有是否进入游戏的选择权。这是就需要在正式开始游戏部分的代码前,先打印一份游戏菜单。

上述代码就可以打印一份简易菜单。

注:对于代码较多的工程来说,最好写一小段代码就运行一下,判断代码没有出错,不然写完工程代码无法运行,代码太长又不好寻找。

4.2游戏模块之创建基本变量

确定好代码无误后就可以在case1中自定义一个game的函数,开始代码书写。

在图片中可以看出,我们要用*打印表格时,*是字符,而这两个要打印的表格所用到的函数程序是一样的,如果我们用数字1和数字0表示有无雷,数组类型就不一样,自定义函数时就要用两组,无法提现函数的复用性,也比较麻烦,所以在此处我们就可以用字符1和字符0(即;'1' '0')

4.3打印棋盘,并完成数组的初始化

我们创建完数组后,首先要对两个数组初始化,让其中所有的地址均为‘0‘或*,我们可以自定义一个函数InitMine来完成。

从第一张代码图看出,我们在初始化时只能初始化一个字符,而两个数组要初始化两个不同的字符,那怎么办呢?----再建立一个变量,用来接受字符

那我们初始化之后,想看看自己有没有初始化成功呢?正好来到游戏模块的另一步,打印棋盘。

在这里我们要注意在打印棋盘时,我们给玩家展示的依然是9*9棋盘,我们为防止数据溢出而多加的那一圈不再打印出来,也就是说,我们打印只打印1~9即可。

此时当我们运行时会有一个错误显示printf未定义,原因是我们在main.c文件中加了printf的头文件,而在game.c中没有加printf的头文件,这是由于两个文件都引用了game.h这个头文件,不妨把printf的头文件也放进去,这样即使以后工程有很多源文件,一些函数的头文件敲一次就可以了。

通过打印结果我们可以看出,目前我们的逻辑还没有错。那么打印完框后,我们发现,如果我们直接让玩家输入坐标,坐标的行列数目并不是一目了然的,这样不利于玩家游戏体验,所以我们不妨打印出行列的数目,让坐标更加清晰。

打印完列之后,发现又出现问题了,原本对齐的行因为多了一列,对不起了,那怎么办呢?我们可以让行也加一个,让i=1变成i=0

这样一个坐标清晰的9*9棋盘就打印出来了,当然我们为了让玩家知道游戏开始了,可以加一个小细节

更正上述代码中,j再循环中的赋值是1,而不是0

到此处,我们的棋盘就打印好并初始化了,下一环节就是:布雷

4.4布雷

  在简单版的扫雷中,一般是10个雷,我们就用10个雷举例,各位友友想设计几个雷,也可以根据个人想法。

  扫雷游戏的关键就是排除不同位置的雷,而且没局游戏位置都不能一样,这就要请我们猜字游戏用到的老朋友rand函数了,而对应rand函数的种子,又要请出srand了,在这两个函数的辅助下,我们就可以保证每次游戏雷的数目随机了。

在以上代码中,我们就成功的随机布置好了雷,在这过程中有以下两点要注意:

1.不要忘记rand,time函数的头文件

2.在x=rand()%row+1时,拿row=9举例,此时取模的范围是0~8,加一之后就变成了1~9.如果模10,范围在0~9就太大了。

在布雷完成后,就差最后一步排雷,就大功告成了!

4.5排雷

  在排雷这一步中,我们首先要弄明白,最后展现给玩家的9*9棋盘,打印的是show数组的,所以我们排雷是,首先要在mine数组统计所输入的值再不是雷的情况下,周围有几个雷,再把有几个雷这个信息传送给show数组。

这样,基础的扫雷就可以运行了。

在这其中我们用来一个比较陌生的知识,字符与数字的转换:

‘0’-‘0’=0     就是字符对应的ASCLL码值相减,所以想要得到字符四就可以用‘0’加上4

‘1’-‘0’=1

‘4’-‘0’=4

这时,我想测一下游戏成功是,会不会有输出,我们玩完一场游戏,有点太浪费时间,这是不妨把10个雷改成80,但是这是又有一个问题,有很多地方有10,一个个该很麻烦,也不方便以后数值的改变,不妨设一个变量,把雷的数量存在变量中。

以上就是实现代码的全过程了

五:基础版扫雷可扩展

是否可以选择游戏难度
  ◦ 简单 9*9 棋盘,10个雷
  ◦ 中等 16*16棋盘,40个雷
  ◦ 困难 30*16棋盘,99个雷
如果排查位置不是雷,周围也没有雷,可以展开周围的一片
是否可以标记雷
是否可以加上排雷的时间显示

六:完整代码

game.h

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<stdlib.h>
#include <math.h>



#define ROW 9
#define COL 9

#define ROWS ROW+2
#define COLS COL+2

#define num 80
//初始化棋盘
void InitMine(char arr[ROWS][COLS], int rows, int cols, char set);
//打印棋盘
void PrintMine(char arr[ROWS][COLS], int row, int col);
//虽然打印的区域变成了9*9,但是数组的大小还是不变11*11
//布雷
void PutMine(char arr[ROWS][COLS], int row, int col);
//排雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

​​​​​​​game.c

#include "game.h";

void InitMine(char arr[ROWS][COLS], int rows, int cols,char set)
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			arr[i][j] = set;      
		}
	}
}

void PrintMine(char arr[ROWS][COLS], int row, int col)
{
	int i = 0;
	{
		printf("-----扫雷-----\n");
		for (i = 0; i <= row; i++)//打印行
		{
			printf("%d", i);
		}
		printf("\n");
		for (i = 1; i <= row; i++)
		{
			printf("%d",i);//打印列
			int j = 0;
			
			for (j = 1; j <= col; j++)
			{
				printf("%c", arr[i][j]);
			}
			printf("\n");

		}

	}
}
void PutMine(char arr[ROWS][COLS], int row, int col)
{
	//让横纵坐标都是1~9之间
	
	int count = num;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		//避免重复布雷
		if (arr[x][y] == '0')//避免两次随机数模玩以后,值相同
		{
			arr[x][y] = '1';
			count--;
		}
	}//循环产生10个雷,所以根据条件循环次数可能会大于10
}
int GetMine(char mine[ROWS][COLS],int x, int y) 
{
	//把字符转换成数字
	return (mine[x - 1][y - 1] + mine[x - 1][y + 1] + mine[x + 1][y - 1] + mine[x + 1][y - 1] +
		mine[x - 1][y + 1] + mine[x - 1][y + 1] + mine[x + 1][y + 1] + mine[x + 1][y + 1]-8*'0');
}

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0, y = 0;
	int win =0;
	//一共有81格,除去10个雷,最多循环71次
	while (win < row * col - num)
	{
		printf("请输入你要排查的坐标-->> ");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			
			if (mine[x][y] == 1)
			{
				printf("很遗憾,游戏失败!\n");
				PrintMine(mine, ROW, COL);
				break;
			}
			else
			{
				int count = GetMine(mine, x, y);
				show[x][y] = count + '0';//把数字转为字符的形式
				PrintMine(show, ROW, COL);
				win++;//没成功一次,就加1,最多71次(以9为例)

			}

		}
		else
			printf("您输入的值不在范围内,请重新输入!\n");
		
	}
	if (win == row * col - num)
	{
		printf("恭喜你成功通关\n");
		PrintMine(show, ROW, COL);

	}

}

test.c

#include "game.h";
void game()//该函数是确保游戏运行的,也不需要返回值
{
	char mine[ROWS][COLS] = { 0 };//用于布置雷
	char show[ROWS][COLS] = { 0 };//用于排雷
	//如果我们直接写11,在以后换棋盘规格后,所有的11都要更改,过于麻烦
	//所以不妨用变量表示,以后只改变量就可以改变所有的数值
	 InitMine(mine, ROWS, COLS,'0');//'0'
	 InitMine(show, ROWS, COLS,'*');//'*'
	 //PrintMine(mine, ROW, COL);//在游戏时,排雷界面是不打印的
	                            //此时只是看一下我们代码对不对
	 PrintMine(show, ROW, COL);
	 PutMine(mine, ROW, COL);//布雷只在mine中布置就行
	 PrintMine(mine, ROW, COL);//查看一下雷的分布
	 FindMine(mine, show, ROW, COL);
}
void menu()
{
	printf("******************\n");
	printf("***** 1.play *****\n");
	printf("***** 0.exit *****\n");
	printf("******************\n");

}
int main()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		menu();//不需要返回值,只要打印出菜单就行
		printf("请输入 -> ");
		scanf("%d", &input);
		switch (input)//根据input的值选择不同的结果
		{
		case 1 :
			game();
			break;
		case 0 :
			printf("退出游戏\n");
			break;
		default :
			printf("输入错误 请重新输入\n");
			break;
		}

	} while (input);//用do while循环上来就会先执行一次,让玩家选择
		return 0;
}


好啦!简易版的扫雷就完成了,友友们,有问题老规矩哦!

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玖剹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值