手把手教你用C语言实现简单的扫雷游戏

目录

1、成品展示

2、扫雷的实现讲解

2.1游戏制作三部曲

2.2从打印菜单选择是否开始或者结束的整体逻辑

2.3、扫雷游戏整体的逻辑实现

2.3.1、棋盘设计(初始化棋盘和打印棋盘)
2.3.2、布置雷
2.3.3、排查雷

3、完结撒花


1、成品展示

扫雷成品


全部代码分享
game.h:

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10;

void InitBoard(char arr[ROWS][COLS], int rows, int cols,char set);

void DisplayBoard(char arr[ROWS][COLS], int row, int col);

void SetMine(char arr[ROWS][COLS], int row, int col);

void FindBoard(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col);

text.c:

#include "game.h"

void meau()
{
	printf("************************************\n");
	printf("******         1.play         ******\n");
	printf("******         0.exit         ******\n");
	printf("************************************\n");
}

void game()
{
	//完成扫雷游戏
	char mine[ROWS][COLS] = { 0 };//数组全部初始化为字符0
	char show[ROWS][COLS] = { 0 };//数组全部初始化为字符*
	InitBoard(mine, ROWS, COLS, '0');//初始化棋盘
	InitBoard(show, ROWS, COLS, '*');

	//布置雷的个数
	//就在9*9棋盘上面布置10个雷
	SetMine(mine, ROW, COL);

	DisplayBoard(show, ROW, COL);//打印棋盘
	//DisplayBoard(mine, ROW, COL);
	FindBoard(show,mine,ROW,COL);//排查雷
}

void test()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		meau();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("已退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
		}
	} while (input);
}

int main()
{
	test();
	return 0;
}

game.c:

#include "game.h"

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

void DisplayBoard(char arr[ROWS][COLS], int row, int col)
{
	printf("------扫雷游戏------\n");
	for (int i = 0; i <= col; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (int i = 1; i <= row; i++)
	{
		printf("%d ", i);
		for (int j = 1; j <= col; j++)
		{
			printf("%c ", arr[i][j]);
		}
		printf("\n");
	}
	printf("------扫雷游戏------\n");
}

void SetMine(char arr[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (arr[x][y] == '0')
		{
			arr[x][y] = '1';
			count--;
		}
	}
}

int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
	//因为雷为字符1,可以根据ASCLL值进行计算
	return mine[x - 1][y - 1] + //这里也可以使用for循环,感兴趣的可以试一下
		mine[x - 1][y] + 
		mine[x - 1][y + 1] +
		mine[x][y - 1] +
		mine[x][y + 1] +
		mine[x + 1][y - 1] +
		mine[x + 1][y] +
		mine[x + 1][y + 1] - 8 * '0';
}

void FindBoard(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
{
	int win = ROW * COL - EASY_COUNT;
	int x = 0;
	int y = 0;
	while (win)
	{
		printf("请输入要排查的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)//检查坐标的有效性
		{
			if (show[x][y] == '*')
			{
				if (mine[x][y] == '1')//判断是不是雷
				{
					printf("很遗憾!你被炸死了\n");
					DisplayBoard(mine, ROW, COL);
					break;
				}
				else//统计坐标周围雷的个数
				{
					int count = GetMineCount(mine, x, y);
					show[x][y] = count + '0';
					DisplayBoard(show, ROW, COL);
					win--;
				}
			}
			else
			{
				printf("该位置已被排查过了,请重新排查\n");
			}
		}
		else
		{
			printf("输入坐标非法,请重新输入\n");
		}
	}
	if (win == 0)
	{
		printf("恭喜你,排雷成功!\n");
		DisplayBoard(mine, ROW, COL);
	}
}

2、扫雷的实现讲解
2.1游戏制作三部曲
对于扫雷游戏的实现我们应在编译器中先创建三个文件(下面以VS2022为例)1.text.c,用来测试整个游戏的逻辑。2.game.h,用来存放函数的声明。3.game.c,用来存放函数的实现。game.h和game.合起来为游戏逻辑的实现。
在这里插入图片描述2.2从打印菜单选择是否开始或者结束的整体逻辑
这里设计的是整个游戏测试的逻辑,除了头文件,都是在taxt.c中完成的。比较简单我们直接上代码
game.h:

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>

taxt.c:

#include "game.h"

void meau()
{
	printf("************************************\n");
	printf("******         1.play         ******\n");
	printf("******         0.exit         ******\n");
	printf("************************************\n");
}

void test()
{
	int input = 0;
	do
	{
		meau();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("开始游戏\n");
			break;
		case 0:
			printf("已退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
		}
	} while (input);
}

int main()
{
	test();
	return 0;
}

代码整理好后我们可以运行,查看代码逻辑是否正确,结果如下
在这里插入图片描述2.3、扫雷游戏整体的逻辑实现
2.3.1、棋盘设计(初始化棋盘和打印棋盘)
我们要实现简单的扫雷游戏,游戏棋盘先设计成99的一个棋盘,成品如图所示:
在这里插入图片描述
我们先对扫雷进行分析,要完成扫雷游戏我们肯定要设置雷(这里说明我们用字符1来表示所设置的雷,字符0表示没有雷),而在游戏过程中要向游戏玩家展示的是如上图所示的一片未知区(如上图,展示棋盘初始化用字符
来表示整个棋盘),设置雷的位置肯定不能展示出来,所以我们需要制定两个棋盘即定义两个二维数组,一个用来存放雷使用,一个用来向玩家展示使用。先假定棋盘大小为99,我们在选择坐标时需要判定其位置是不是雷,如果是雷则游戏结束,如果不是雷我们需要统计这个位置四周8个位置的雷的个数并显示出来,这其实并不难实现,但当我们统计棋盘四边的雷的个数的话,如下图:
在这里插入图片描述
我们如果统计下面那个黄色区域,棋盘的大小为9
9,下面哪一行是统计不了的,为防止这类情况发生,我们可以直接把两个二维数组定义成1111的大小,而在打印的时候只打印第1行到第9行和第1列到第9列即可。如下图:
在这里插入图片描述
所以对于两个二维数组,布置雷的那个棋盘初始化为全字符0,展示的棋盘初始化为全为字符
。代码如下:
game.h:

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>

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

void InitBoard(char arr[ROWS][COLS], int rows, int cols,char set);

void DisplayBoard(char arr[ROWS][COLS], int row, int col);

taxt.c:

#include "game.h"

void meau()
{
	printf("************************************\n");
	printf("******         1.play         ******\n");
	printf("******         0.exit         ******\n");
	printf("************************************\n");
}

void game()
{
	//完成扫雷游戏
	char mine[ROWS][COLS] = { 0 };//数组全部初始化为字符0
	char show[ROWS][COLS] = { 0 };//数组全部初始化为字符*
	InitBoard(mine, ROWS, COLS, '0');//初始化棋盘
	InitBoard(show, ROWS, COLS, '*');

	DisplayBoard(show, ROW, COL);//打印棋盘
	//布置雷的个数

}

void test()
{
	int input = 0;
	do
	{
		meau();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("已退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
		}
	} while (input);
}

int main()
{
	test();
	return 0;
}

game.c:

#include "game.h"

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

void DisplayBoard(char arr[ROWS][COLS], int row, int col)
{
	printf("------扫雷游戏------\n");
	for (int i = 0; i <= col; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (int i = 1; i <= row; i++)
	{
		printf("%d ", i);
		for (int j = 1; j <= col; j++)
		{
			printf("%c ", arr[i][j]);
		}
		printf("\n");
	}
	printf("------扫雷游戏------\n");
}

可以看到在定义时,使用宏定义定义了4个数据,ROW,COL,ROWS,COLS,这是因为以后方便我们调节游戏难度,比如:我们要增大难度打印一个20*20的棋盘,只需在ROW,COL,后该成20即可,如图:
在这里插入图片描述至于对其问题,只需在打印输出的占位符前面加一个符号就行,这里就不在过多赘述。
2.3.2、布置雷
下面我们需要在二维数组mine里面布置雷,我们可以先设定好布置雷的数量,因为没把游戏雷是随机布置的,这不难想到会使用rand生成随机数的函数,生成随机数在存放雷的时候我们要在19行和19列之间进行存放,所以生成的随机数要在1~9之间,在没有存放雷的位置将’0‘改为’1‘,代码见下:
game.h:

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10;

void InitBoard(char arr[ROWS][COLS], int rows, int cols,char set);

void DisplayBoard(char arr[ROWS][COLS], int row, int col);

void SetMine(char arr[ROWS][COLS], int row, int col);

text.c:

#include "game.h"

void meau()
{
	printf("************************************\n");
	printf("******         1.play         ******\n");
	printf("******         0.exit         ******\n");
	printf("************************************\n");
}

void game()
{
	//完成扫雷游戏
	char mine[ROWS][COLS] = { 0 };//数组全部初始化为字符0
	char show[ROWS][COLS] = { 0 };//数组全部初始化为字符*
	InitBoard(mine, ROWS, COLS, '0');//初始化棋盘
	InitBoard(show, ROWS, COLS, '*');

	DisplayBoard(show, ROW, COL);//打印棋盘
	//布置雷的个数
	//就在9*9棋盘上面布置10个雷
	SetMine(mine, ROW, COL);
	//DisplayBoard(mine, ROW, COL);
}

void test()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		meau();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("已退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
		}
	} while (input);
}

int main()
{
	test();
	return 0;
}

game.c:

#include "game.h"

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

void DisplayBoard(char arr[ROWS][COLS], int row, int col)
{
	printf("------扫雷游戏------\n");
	for (int i = 0; i <= col; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (int i = 1; i <= row; i++)
	{
		printf("%d ", i);
		for (int j = 1; j <= col; j++)
		{
			printf("%c ", arr[i][j]);
		}
		printf("\n");
	}
	printf("------扫雷游戏------\n");
}

void SetMine(char arr[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (arr[x][y] == '0')
		{
			arr[x][y] = '1';
			count--;
		}
	}
}

代码敲完我们可以把mine棋盘打印出来进行观察是否正确,如下图:
在这里插入图片描述可以看到每次布置的雷均为10个且位置都是随机的。
2.3.3、排查雷
在分析排查雷的逻辑之前,我们先把text.c,里面的打印棋盘DisBoard函数和布置雷的SetMine函数的顺序交换一下,因为要先布置雷后才要打印棋盘嘛。代码如下:
text.c:

#include "game.h"

void meau()
{
	printf("************************************\n");
	printf("******         1.play         ******\n");
	printf("******         0.exit         ******\n");
	printf("************************************\n");
}

void game()
{
	//完成扫雷游戏
	char mine[ROWS][COLS] = { 0 };//数组全部初始化为字符0
	char show[ROWS][COLS] = { 0 };//数组全部初始化为字符*
	InitBoard(mine, ROWS, COLS, '0');//初始化棋盘
	InitBoard(show, ROWS, COLS, '*');

	//布置雷的个数
	//就在9*9棋盘上面布置10个雷
	SetMine(mine, ROW, COL);

	DisplayBoard(show, ROW, COL);//打印棋盘
	//DisplayBoard(mine, ROW, COL);
}

void test()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		meau();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("已退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
		}
	} while (input);
}

int main()
{
	test();
	return 0;
}

下面我们实现排查雷的逻辑:
在排查雷时,要用到mine和show两个二维数组,所以定义排查雷的函数要把两个二维数组都传参传过去,之后肯定要输入要排查的坐标,因为我们只能在99的棋盘里面查找,所以要先去判断输入坐标的有效性,即排查坐标是否在99的棋盘内,如果排查坐标有效,那么我们就进一步判断该位置是不是雷,如果是则结束游戏,如果不是则需统计四周雷的个数并显示出来,到最后如果把不是雷的位置全部排查出来则游戏获胜,所以我们要创个变量记录空白位置,代码如下:
game.h:

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10;

void InitBoard(char arr[ROWS][COLS], int rows, int cols,char set);

void DisplayBoard(char arr[ROWS][COLS], int row, int col);

void SetMine(char arr[ROWS][COLS], int row, int col);

void FindBoard(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col);

text.c:

#include "game.h"

void meau()
{
	printf("************************************\n");
	printf("******         1.play         ******\n");
	printf("******         0.exit         ******\n");
	printf("************************************\n");
}

void game()
{
	//完成扫雷游戏
	char mine[ROWS][COLS] = { 0 };//数组全部初始化为字符0
	char show[ROWS][COLS] = { 0 };//数组全部初始化为字符*
	InitBoard(mine, ROWS, COLS, '0');//初始化棋盘
	InitBoard(show, ROWS, COLS, '*');

	//布置雷的个数
	//就在9*9棋盘上面布置10个雷
	SetMine(mine, ROW, COL);

	DisplayBoard(show, ROW, COL);//打印棋盘
	//DisplayBoard(mine, ROW, COL);
	FindBoard(show,mine,ROW,COL);//排查雷
}

void test()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		meau();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("已退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
		}
	} while (input);
}

int main()
{
	test();
	return 0;
}

game.c:

#include "game.h"

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

void DisplayBoard(char arr[ROWS][COLS], int row, int col)
{
	printf("------扫雷游戏------\n");
	for (int i = 0; i <= col; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (int i = 1; i <= row; i++)
	{
		printf("%d ", i);
		for (int j = 1; j <= col; j++)
		{
			printf("%c ", arr[i][j]);
		}
		printf("\n");
	}
	printf("------扫雷游戏------\n");
}

void SetMine(char arr[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (arr[x][y] == '0')
		{
			arr[x][y] = '1';
			count--;
		}
	}
}

int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
	//因为雷为字符1,可以根据ASCLL值进行计算
	return mine[x - 1][y - 1] + //这里也可以使用for循环,感兴趣的可以试一下
		mine[x - 1][y] + 
		mine[x - 1][y + 1] +
		mine[x][y - 1] +
		mine[x][y + 1] +
		mine[x + 1][y - 1] +
		mine[x + 1][y] +
		mine[x + 1][y + 1] - 8 * '0';
}

void FindBoard(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
{
	int win = ROW * COL - EASY_COUNT;
	int x = 0;
	int y = 0;
	while (win)
	{
		printf("请输入要排查的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)//检查坐标的有效性
		{
			if (show[x][y] == '*')
			{
				if (mine[x][y] == '1')//判断是不是雷
				{
					printf("很遗憾!你被炸死了\n");
					DisplayBoard(mine, ROW, COL);
					break;
				}
				else//统计坐标周围雷的个数
				{
					int count = GetMineCount(mine, x, y);
					show[x][y] = count + '0';
					DisplayBoard(show, ROW, COL);
					win--;
				}
			}
			else
			{
				printf("该位置已被排查过了,请重新排查\n");
			}
		}
		else
		{
			printf("输入坐标非法,请重新输入\n");
		}
	}
	if (win == 0)
	{
		printf("恭喜你,排雷成功!\n");
		DisplayBoard(mine, ROW, COL);
	}
}

我们如果想检验代码是否正确,是否可以排雷成功,可以把雷的数量改成80,并且在打印的时候将mine的二维数组也打印出来,如图:
在这里插入图片描述
在这里插入图片描述
3、完结撒花
其中其实还有许多细节,比如:重复排查,计算周围雷的数量等我都在代码里面完善了。大家感兴趣的话可以自己上手试一试就知道了,这里就不再一一赘述啦。如果有什么问题或者不懂的地方欢迎下方评论留言,互相指教感谢支持!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值