目录

C语言扫雷游戏代码以及基本原理教学一看就会

C语言扫雷游戏代码以及基本原理教学(一看就会)

目录


1.什么是扫雷游戏以及扫雷游戏怎么玩

(1)什么是《扫雷》

《扫雷》是一款大众类的益智小游戏,于1992年发行。游戏目标是在最短的时间内根据点击格子出现的数字找出所有非雷格子,同时避免踩雷,踩到一个雷即全盘皆输。

(2)《扫雷》背景故事

扫雷在科技历史上也扮演了相似的角色。这个基于数字的逻辑谜题最早来自20世纪六七十年代,当时Jerimac Ratliff推出的名为“Cube”的游戏已经非常受人欢迎。几十年后的1992年,扫雷游戏被加入window系统,这并不是为了展示Windows是游戏操作系统专家,而是为了训练用户的鼠标左右键操作能力,让这些动作变得非常自然,并培养鼠标移动的速度和准确性。

(3)怎么玩《扫雷》

  1. 在格子的数字里,看显示的数字是多少,相邻就有多少个雷。

  2. 数字1,相对简单,8个格子只有一个是雷。

  3. 数字2,8个格子有二个是雷。

  4. 以此类推,基本上3,4,5,6的数字都是这样的意思。

  5. 当遇到不能确定的情况,我们需要从另一边推进,尽量翻出更多的数字来推理。

2.扫雷游戏代码实现的基本思路

(1) 基本思路(创建几个文件及其设计意义)

test.c文件      扫雷游戏的测试逻辑

game.h文件    测试游戏函数的函数声明

game.c文件     游戏函数的实现

test.c: 放主函数及其代码的基本框架,以及扫雷的基本逻辑。

game.h: 放扫雷游戏函数的函数声明,以及一些头文件声明,方便在test.c和game.c中使用。

game.c:实现游戏函数的内容。

(2)扫雷游戏的基本框架

游戏思路有了接下来就要设计游戏框架,进而一步一步的实现游戏,一步步封装游戏。扫雷需要有 存放雷的数组 ,也需要有查找时, 周围一圈雷的个数,这又需要存放一个数组 。为什们不存放在一个数组内呢,是因为方便观察呀,还有需要注意的一个点在查找雷时,要找周围一圈雷的个数, 如果再找最边里的雷查找周围的雷可能会发生数组越界,所以要创建一个大一圈的数组 。创建完两个数组,再进行数组的初始化,为了方便数组的使用,然后肯定需要打印两个棋盘了,然后在一个棋盘上布置雷,然后在排查雷将信息放到另一个数组上,这就是扫雷游戏的基本框架。

总结

  1. 创建两个数组,一个存放布置好的雷(mine数组),一个存放排查雷的信息(show数组)
  2. 初始化数组:将mine数组初始化为"0",布置雷的时候改为"1"(就是雷是1,非雷是0)                        show数组初始化" * " ,排查雷后改为具体数字.
  3. 打印两个棋盘,方便观察
  4. 然后在mine数组中随机布置雷
  5. 再排查雷,将排查雷的信息记录在show数组上
  6. 判断游戏结果(踩雷或者排雷完成)

画图解释为什们要建立大一圈的数组:

https://i-blog.csdnimg.cn/blog_migrate/487adea783b84b56d8cdeeb5fc68e219.png https://i-blog.csdnimg.cn/blog_migrate/b223b65d01e9fcebfef2a830cf800e54.png https://i-blog.csdnimg.cn/blog_migrate/9d9723d05165ea9d1c9dd4059a640cee.png

《扫雷》这些雷和数字都要存放到99的二维数组里,为了避免越界访问需要创建一个大一圈的数组1111,这样在查找雷时,就不会越界访问。

有了框架,就跟着框架写代码就非常好写了

3.《扫雷》代码实现

(1)游戏的外部封装

对于《扫雷》游戏不可能玩一把就不玩了,所以应写循环,玩完一把在玩下一把,不想玩就退出,对于这个游戏至少应该进行一次,所以用do-while()循环。为了让玩家看的明白怎么我玩应该封装一个菜单函数,1—进入游戏,0—退出游戏。为什们这么设置也是有用意的,在循环中0—表示假,就可以直接退出游戏,而1–表示真就可以进入游戏,我们需要选择,可以用switch语句进行选择。下面就是代码实现。可以在游戏函数简单写一个输出函数printf简单模拟游戏函数,然后在简单运行一下,看现在已写代码的正确性。

//扫雷游戏的外部封装
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void menu()
{
	printf("***************************\n");
	printf("******1---进入游戏---******\n");
	printf("******0---退出游戏---******\n");
	printf("***************************\n");
}
void game()
{
	printf("游戏开始\n");
}
int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请输入n->");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出程序\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
	return 0;
}

(2)创建数组

创建两个数组 一个存放布置好的雷(mine数组),一个存放排查雷的信息(show数组) .

一个放雷,不需要给玩家看,一个放排雷的信息,给玩家看

//创建两个数组
void game()
{
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };
}

(3)初始化数组

mine数组初始化为" 0 " ,布置雷的时候改为"1"                                                              show数组初始化为" * " ,排查雷后改为具体数字

在函数传参,传参传(数组,行,列,以及初始化的字符)

//初始化mine和show数组。
void init_board(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

(4)打印棋盘

两个数组初始化完成,就可以打印了,再通过循环打印出来,

需要注意初始化是1111的数组,而打印棋盘是给玩家看的,打印99就可以了。

在打印过程中需要打印行号和列号,这个东西需要找到规律后也会发现很简单,具体操作请看代码。每个代码后都有注释。

https://i-blog.csdnimg.cn/blog_migrate/2c47d927d4fe6a3fe52ef47b5e214940.png

//打印棋盘
void print_board(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	//需要在每一行每一列上加上坐标好让玩家好观察
	//先打印每一列的坐标,因为还有1列行坐标,行坐标上是0
	for (j = 0; j <= col; j++)
	{
		printf("%d ", j);
	}
	printf("\n");//打印玩列坐标后换行

	//因为传的是11*11的数组,但是是打印9*9的数组
	//从第二个元素开始打印对应到数组里就是也就是从1开始打印到9.
	for (i = 1; i <= row; i++)
	{
		//打印每一行的坐标
		printf("%d ", i);
		//列也是从第二个元素开始打印
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);//打印每个数据
		}
		printf("\n");//每行数据换行
	}
}

(5)随机布置雷

因为为了让每次的扫雷游戏都不一样,雷的布置应该随机布置,而随机布置就需要rand(),srand()函数,具体这些函数怎么使用就不多赘述了,请去cplusplus网站上查看 ),

布置完雷之后可以打印一下看一下布置的雷,下图就是随机布置的雷每次布置的都不一样。

https://i-blog.csdnimg.cn/blog_migrate/514ab03f007e5bfab584ac4f406bf9c0.png https://i-blog.csdnimg.cn/blog_migrate/4f31278c2d3b3ecd704455bdc6bd7bad.png

//布置雷
void set_mine(char mine[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	//在game.h中定义EASY_COUNT是雷的个数
	int count = EASY_COUNT;
	//布置10个雷知道count=0就退出循环
	while (count)
	{
		x = rand() % row + 1;
		y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';//布置成功就是字符'1'
			count--;
		}
	}
	
}

(6)排查雷

排查雷需要在mine数组中找到周围有几个雷,然后将数据储存到show数组上打印出来,所以在排查雷函数需要传两个数组,和行数列数,每行代码都有注释请仔细阅读代码。

//查找输入坐标周围的雷的个数
int get_mine_count(char mine[ROWS][COLS],int x,int y)
{
	//用字符的好处可以直接计算
	return (mine[x - 1][y] +
		mine[x - 1][y + 1] +
		mine[x][y + 1] +
		mine[x + 1][y + 1] +
		mine[x + 1][y] +
		mine[x + 1][y - 1] +
		mine[x][y - 1] +
		mine[x + 1][y - 1] - 8 * '0');
}
//排查雷
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	//win表示除去雷,后无雷的个数
	int win = 0;
	//如果将除去雷的无雷的坐标全部找见,说明排雷成功
	while (win < row * col - EASY_COUNT)
	{
		printf("请输入排查雷的坐标->");
		scanf("%d%d", &x, &y);
		//查看坐标是否合法
		if (x >= 1 && x <= row && y >= 1 & y <= col)
		{
			//看show数组是否被排查过了,如果排查过就重新输入
			if (show[x][y] == '*')
			{
				//判断是否是雷
				if (mine[x][y] == '1')
				{
					printf("很遗憾,你被炸死了\n");
					//游戏失败,打印最后失败的扫雷图。
					print_board(show, ROW, COL);
					break;
				}
				//没有踩到雷,需要计算周围的雷
				else
				{
					//封装一个函数找到周围雷的个数
					int count = get_mine_count(mine, x, y);
					//count为整型,转换为字符'0',就加上'0',参考ASCⅡ表
					show[x][y] = count + '0';
					//查找完然后打印show数组,看清楚周围有几个雷
					print_board(show, ROW, COL);
					//win是无雷的个数,也是出循环的条件
					win++;
				}
			}
			else
			{
				printf("该坐标已被排查过了,请重新输入\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}
	//判断雷是否都排完了
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
		//查看最终排雷结果
		print_board(show, ROW, COL);
	}
}

(7)测试游戏代码是否成功

因为是9*9的二维数组,共有81个位置,有10个位置有雷,如果傻乎乎的一个个排,非常浪费时间, 因为在game.h中宏定义了雷的个数#define EASY_COUNT 10(雷的个数),直接将雷的个数变为80,就只有一个雷在第6步排查雷时,当win=1时就排查雷成功了,并且在游戏函数时打印出mine数组中雷的排放,根据雷的位置,排查雷,等到游戏进行时,在注释掉,并把雷的个数改回去

https://i-blog.csdnimg.cn/blog_migrate/7a5217cf1ffc4b1ebadec4249bc912d0.png

(8)函数封装完成,组装函数

<1>test.c文件      扫雷游戏的测试逻辑(也是游戏代码的基本框架)

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
	printf("***************************\n");
	printf("******1---进入游戏---******\n");
	printf("******0---退出游戏---******\n");
	printf("***************************\n");
}
void game()
{
	//创建两个数组
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };
	//初始化数组
	init_board(mine, ROWS, COLS,'0');
	init_board(show, ROWS, COLS,'*');
	//打印数组,这些先不用打印等最后才需要打印
	//print_board(mine, ROW, COL);
	//print_board(show, ROW, COL);
	//布置雷
	set_mine(mine, ROW, COL);
	print_board(show, ROW, COL);
	//print_board(mine, ROW, COL);
	find_mine(mine, show, ROW, COL);

}
int main()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		menu();
		printf("请输入n->");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出程序\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
	return 0;
}

<2>game.h文件    测试游戏函数的函数定义以及头文件

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define ROW 9    //行
#define COL 9     //列
#define ROWS ROW+2  //扩展行
#define COLS COL+2   //扩展列
#define EASY_COUNT 10  //简单版本《扫雷》雷的个数
//初始化函数
void init_board(char board[ROWS][COLS], int rows, int cols, char set);
//打印函数
void print_board(char board[ROWS][COLS], int row, int col);
//布置雷
void set_mine(char mine[ROWS][COLS], int row, int col);
//排查雷
void find_mine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col);

<3>game.c文件     游戏函数的声明

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//初始化mine和show数组。
void init_board(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}
//打印数组
void print_board(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	//需要在每一行每一列上加上坐标好让玩家好观察
	//先打印每一列的坐标,因为还有1列行坐标,行坐标上是0
	for (j = 0; j <= col; j++)
	{
		printf("%d ", j);
	}
	printf("\n");//打印玩列坐标后换行

	//因为传的是11*11的数组,但是是打印9*9的数组
	//从第二个元素开始打印对应到数组里就是也就是从1开始打印到9.
	for (i = 1; i <= row; i++)
	{
		//打印每一行的坐标
		printf("%d ", i);
		//列也是从第二个元素开始打印
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);//打印每个数据
		}
		printf("\n");//每行数据换行
	}
}
void set_mine(char mine[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	//在game.h中定义EASY_COUNT是雷的个数
	int count = EASY_COUNT;
	//布置10个雷知道count=0就退出循环
	while (count)
	{
		x = rand() % row + 1;
		y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';//布置成功就是字符'1'
			count--;
		}
	}
	
}
//查找雷
int get_mine_count(char mine[ROWS][COLS],int x,int y)
{
	//用字符的好处可以直接计算
	return (mine[x - 1][y] +
		mine[x - 1][y + 1] +
		mine[x][y + 1] +
		mine[x + 1][y + 1] +
		mine[x + 1][y] +
		mine[x + 1][y - 1] +
		mine[x][y - 1] +
		mine[x + 1][y - 1] - 8 * '0');
}
//排查雷
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	//win表示除去雷,后无雷的个数
	int win = 0;
	//如果将除去雷的无雷的坐标全部找见,说明排雷成功
	while (win < row * col - EASY_COUNT)
	{
		printf("请输入排查雷的坐标->");
		scanf("%d%d", &x, &y);
		//查看坐标是否合法
		if (x >= 1 && x <= row && y >= 1 & y <= col)
		{
			//看show数组是否被排查过了,如果排查过就重新输入
			if (show[x][y] == '*')
			{
				//判断是否是雷
				if (mine[x][y] == '1')
				{
					printf("很遗憾,你被炸死了\n");
					//游戏失败,打印最后失败的扫雷图。
					print_board(show, ROW, COL);
					break;
				}
				//没有踩到雷,需要计算周围的雷
				else
				{
					//封装一个函数找到周围雷的个数
					int count = get_mine_count(mine, x, y);
					//count为整型,转换为字符'0',就加上'0',参考ASCⅡ表
					show[x][y] = count + '0';
					//查找完然后打印show数组,看清楚周围有几个雷
					print_board(show, ROW, COL);
					//win是无雷的个数,也是出循环的条件
					win++;
				}
			}
			else
			{
				printf("该坐标已被排查过了,请重新输入\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}
	//判断雷是否都排完了
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
		//查看最终排雷结果
		print_board(show, ROW, COL);
	}
}

4.总结

这个扫雷代码其实就是把简单的知识组合在一起,其实非常简单,难得是逻辑,不好想。但多看看,自己多调试。让自己理解的更深刻。以上就是《扫雷》的全部代码,源码,只要通过自己的努力自己写出来,肯定成就感满满,而且再让别人玩你的《扫雷》,你的感觉肯定会不一样的。让我们一起加油!!!!