手把手教你写C语言扫雷【可递归展开版】

手把手教你写C语言扫雷【可递归展开版】,第1张

手把手教你写C语言扫雷【可递归展开版】

编程环境:【Visual Studio 2019】

标题传送门

扫雷怎么玩

编程思路

创建地图即初始化

 打印地图

埋雷

玩家排雷

全部代码(传送门)

 演示效果


扫雷怎么玩

扫雷是一款经典的小游戏。当地图上随机点击一个格子,如果不是则雷,检查以该格子为中心的8个坐标有多少个雷。如果附近8个坐标都不是雷,则展开一片不是雷的区域。当玩家排查完所有地雷,则玩家胜利。如果玩家踩雷,则游戏失败。

编程思路 创建地图即初始化

首先创建两个字符类型的二维数组(mine,show),一个是存放雷的数组,另一个给玩家看的排雷信息数组。如果当玩家输入了处于边缘的坐标,当程序检查附近的雷时导致数组越界,所以两个数组大小应该是地图大小N+2。

假设表示雷为1,非雷为0,给存放雷的数组全部初始化字符0。排雷信息数组全部初始化为星号。

#define ROW 9
#define COL 9

#define ROWS 9+2
#define COLS 9+2

char mine[ROWS][COLS] = { 0 };
//存放排雷信息的数组
char show[ROWS][COLS] = { 0 };

InitBoard(mine, ROWS, COLS,'0');
InitBoard(show, ROWS, COLS, '*');

//初始化
void InitBoard(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 DisplayBoard(char board[ROWS][COLS], int row, int  col)
{
	int i = 0;
	int j = 0;

	for (i = 0;i <= col;i++)
	{
		printf("%d ", i);//行
	}
	printf("n");

	for (i = 1;i <= row;i++)
	{
		printf("%d ", i);//列
		for (j = 1;j <= col;j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("n");
	}
}
埋雷

需要传的是存放雷的数组和地图大小的长和宽。因为需要随机埋雷,需要用到时间戳函数生成随机值。要注意,因为数组大小是9+2×9+2的,而地图则是9×9的,地图边缘不能埋雷,所以在生成随机值时,不能将雷埋在地图外的坐标。当埋的雷满足个数程序结束。

//设置雷个数
#define EASILY_MINE 10

SetMine(mine, ROW, COL);

void SetMine(char mine[ROWS][COLS], int row, int col)
{
    //计数
	int count = EASILY_MINE;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}
玩家排雷

将雷埋好之后,就要开始玩家排雷了,排雷需要用到存放数组和排雷信息数组,让玩家输入排雷坐标。接着判断该坐标的合法性,若坐标非法则让玩家重新输入,若坐标合法,则判断该坐标是不是地雷,如果是雷,game over,不是就以该坐标为中心,计算周围的8个坐标有多少个雷,如果附近8个坐标都不是雷,则向外递归展开一片都不是雷的区域。而main函数内并没有调用过这个展开函数和检查雷函数,它们只是为了支持我们的排雷函数存在的,所以在函数前加一个static让函数失去外部链接属性,只能在这个工程内调用。在头文件也不再需要函数声明了。

//检查雷个数
static int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
	//返回坐标周围的八个坐标
	return mine[x - 1][y - 1] +
		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';
}

//展开函数
static void ExcludeMine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
	int count = get_mine_count(mine, x, y);

	if (count != 0)
	{
		//当周围8个坐标有雷,计算雷个数
		show[x][y] = count + '0';
	}
	else
	{
		//当周围8个坐标没有雷
		show[x][y] = ' ';
		if ('*' == show[x - 1][y - 1])
			ExcludeMine(mine, show, x - 1, y - 1);
		if ('*' == show[x - 1][y])
			ExcludeMine(mine, show, x - 1, y);
		if ('*' == show[x - 1][y + 1])
			ExcludeMine(mine, show, x - 1, y + 1);
		if ('*' == show[x][y - 1])
			ExcludeMine(mine, show, x, y - 1);
		if ('*' == show[x][y + 1])
			ExcludeMine(mine, show, x - 1, y + 1);
		if ('*' == show[x + 1][y - 1])
			ExcludeMine(mine, show, x + 1, y - 1);
		if ('*' == show[x + 1][y])
			ExcludeMine(mine, show, x - 1, y + 1);
		if ('*' == show[x + 1][y + 1])
			ExcludeMine(mine, show, x - 1, y + 1);
	}
}

//玩家排雷
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;

	while (1)
	{
        //外挂(方便测试)
        //DisplayBoard(mine, row, col);
		printf("请输入排雷坐标:");
		scanf("%d %d", &x, &y);
        
		if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] == '*')
		{
            //坐标是雷
			if (mine[x][y] == '1')
			{
				system("cls");
				printf("Peng!!!GAME OVER!n");
				break;
			}
            //不是雷
			else
			{
				
				system("cls");
				ExcludeMine(mine, show, x, y);
				DisplayBoard(show, row, col);
				//DisplayBoard(mine, row, col);

				win = CheckShow(show, row, col);
				win = CheckShow(show, row, col);
				if (win == EASILY_MINE)//当只剩雷的个数,排雷成功	
				{
					system("cls");
					break;
				}
			}
		}
		else
		{
			printf("坐标非法,请重新输入n");
		}
	}

	if (win == EASILY_MINE)
		printf("恭喜你排雷成功!n");
    //查看所有雷的位置
    DisplayBoard(mine, row, col);
	system("pause");
	system("cls");
}

当玩家排雷解决后就要解决如何判定玩家是否胜利,玩家的结束条件有踩雷和排雷成功。踩雷已经解决完毕,接下来就是如何判断玩家的输赢。我的方法是用循环判断还有多少个未排坐标,然后返回一个变量,用变量来和雷的个数比较,如果雷的个数等于未排个数说明排雷成功。当游戏结束后给玩家打印雷的位置,让玩家看看怎么赢或输的。

//判赢
static char CheckShow(char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;

	for (x = 1;x <= row;x++)
	{
		for (y = 1;y <= col;y++)
		{
			if (show[x][y] == '*')
			{
				//计算还有几个未排坐标
				win++;
			}
		}
	}
	return win;
}
全部代码(传送门)
#pragma once
#include
#include
#include
#include

#define ROW 9
#define COL 9

#define ROWS 9+2
#define COLS 9+2
//设置雷个数
#define EASILY_MINE 10


//初始化
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);

//打印
void DisplayBoard(char board[ROWS][COLS], int row, int  col);

//埋雷
void SetMine(char mine[ROWS][COLS], int row, int col);

//排雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);




void InitBoard(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 DisplayBoard(char board[ROWS][COLS], int row, int  col)
{
	int i = 0;
	int j = 0;

	for (i = 0;i <= col;i++)
	{
		printf("%d ", i);//行
	}
	printf("n");

	for (i = 1;i <= row;i++)
	{
		printf("%d ", i);//列
		for (j = 1;j <= col;j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("n");
	}
}

void SetMine(char mine[ROWS][COLS], int row, int col)
{

	int count = EASILY_MINE;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}


//检查雷个数
static int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
	//返回坐标周围的八个坐标
	return mine[x - 1][y - 1] +
		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';
}

static void ExcludeMine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
	int count = get_mine_count(mine, x, y);

	if (count != 0)
	{
		//当周围8个坐标有雷,计算雷个数
		show[x][y] = count + '0';
	}
	else
	{
		//当周围8个坐标没有雷
		show[x][y] = ' ';
		if ('*' == show[x - 1][y - 1])
			ExcludeMine(mine, show, x - 1, y - 1);
		if ('*' == show[x - 1][y])
			ExcludeMine(mine, show, x - 1, y);
		if ('*' == show[x - 1][y + 1])
			ExcludeMine(mine, show, x - 1, y + 1);
		if ('*' == show[x][y - 1])
			ExcludeMine(mine, show, x, y - 1);
		if ('*' == show[x][y + 1])
			ExcludeMine(mine, show, x - 1, y + 1);
		if ('*' == show[x + 1][y - 1])
			ExcludeMine(mine, show, x + 1, y - 1);
		if ('*' == show[x + 1][y])
			ExcludeMine(mine, show, x - 1, y + 1);
		if ('*' == show[x + 1][y + 1])
			ExcludeMine(mine, show, x - 1, y + 1);
	}
}

//判赢
static char CheckShow(char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;

	for (x = 1;x <= row;x++)
	{
		for (y = 1;y <= col;y++)
		{
			if (show[x][y] == '*')
			{
				//计算还有几个未排坐标
				win++;
			}
		}
	}
	return win;
}

//玩家排雷
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;

	while (1)
	{
        //外挂(方便测试)
        //DisplayBoard(mine, row, col);
		printf("请输入排雷坐标:");
		scanf("%d %d", &x, &y);
        
		if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] == '*')
		{
            //坐标是雷
			if (mine[x][y] == '1')
			{
				system("cls");
				printf("Peng!!!GAME OVER!n");
				break;
			}
            //不是雷
			else
			{
				
				system("cls");
				ExcludeMine(mine, show, x, y);
				DisplayBoard(show, row, col);
				//DisplayBoard(mine, row, col);

				win = CheckShow(show, row, col);
				win = CheckShow(show, row, col);
				if (win == EASILY_MINE)//当只剩雷的个数,排雷成功	
				{
					system("cls");
					break;
				}
			}
		}
		else
		{
			printf("坐标非法,请重新输入n");
		}
	}

	if (win == EASILY_MINE)
		printf("恭喜你排雷成功!n");
    //查看所有雷的位置
    DisplayBoard(mine, row, col);
	system("pause");
	system("cls");
}




#include"game.h"

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

void game()
{
	//存放雷信息的数组
	char mine[ROWS][COLS] = { 0 };
	//存放排雷信息的数组
	char show[ROWS][COLS] = { 0 };

	InitBoard(mine, ROWS, COLS,'0');
	InitBoard(show, ROWS, COLS, '*');
	//观察两个数组
	//DisplayBoard(show, ROW, COL);
	//DisplayBoard(mine, ROW, COL);
	//埋雷
	SetMine(mine, ROW, COL);
	//DisplayBoard(mine, ROW, COL);
	system("cls");//清空屏幕
	DisplayBoard(show, ROW, COL);

	//排雷
	FindMine(mine, show, ROW, COL);
}

void test()
{
	int input = 0;
	//时间戳
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		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;
}
 演示效果

因为是测试运行效果,所以用外挂来辅助测试~~

 

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5699072.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存