(C语言)扫雷——防止第一次踩雷+展开

本文详细介绍了扫雷游戏的实现过程,从创建源文件、定义主函数开始,逐步阐述game函数的设计思路,包括初始化、打印、设置地雷和排查地雷等核心功能的实现。通过函数定义和源码分析,解释了如何防止第一步踩雷,以及如何展开安全区域,最终完成游戏的逻辑。此外,还展示了游戏的实际游玩效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

电影《地雷区》

1.新建源文件

在这里插入图片描述

test.c中存放主函数;
game.h中存放使用的函数的声明;
game.c中存放使用的函数的定义;

2.建立主函数

需求:

1.玩家选择是否进行游戏;
2.要有反复游玩的功能;

主函数代码如下:

int main()
{
	int input;
	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\n");
			break;
		}

	} while (input);
	return 0;
}

生成的游戏菜单
在这里插入图片描述

玩家输入1,进入game函数,进行游戏;
玩家输入0,结束游戏;
玩家输入其他字符,提示输入错误,重新输入;

3.game函数设计思路

思路:定义界面->初始化->打印->埋雷->找雷

1.游戏界面

扫雷游戏的界面是一个方盘
在这里插入图片描述
我们很容易联想到建立一个二维数组来作为游戏界面,其中需要存放地雷的位置信息,同时玩家面对的二维数组则是一片未知区域,只有点下某一点位时才出现周围雷区的信息。于是我们建立两个二维数组,mine数组存放地雷信息,show数组则是面对玩家实际操作。
首先利用宏定义行数与列数

#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
char  mine[ROWS][COLS],show[ROWS][COLS];

这里不得不提到一点:因为需要统计坐标周围8个位置雷的个数,假如要统计边界坐标周围雷的个数,那么就会造成数组越界,如果单独讨论就十分繁琐。如果我们定义了9* 9的边界,也就要定义11*11的数组元素,最外边一圈元素我们不要打印出来,如下图
在这里插入图片描述
地雷存放在橙色区域中,以及玩家面对的游戏界面也为橙色区域。行的范围1~ROW,列的范围1 ~ COL。
—————————————————————————

2.初始化 Init

既然数组已经定义好了,我们就要初始化这两个二维数组。
我们规定地雷用字符'1'表示,安全区域用字符'0'表示,而玩家的show数组则全部初始化为字符'*'
初始化函数声明:

void Init(char board[ROWS][COLS], int rows, int cols, char sign);

—————————————————————————

3.打印函数 Displayboard

验证是否初始化成功;
以及每次玩家点击某个坐标后,我们都要将结果打印出来。
打印函数声明:

void Displayboard(char board[ROWS][COLS], int row, int col)

—————————————————————————

4.设置雷的位置 SetMine

首先利用宏定义雷的数量

#define bomb 10

然后利用函数rand随机在上述橙色区域中布置地雷。
设置地雷函数声明:

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

—————————————————————————

5.排查雷 FindMine

需求:
1.防止第一步踩雷
2.如果踩到雷,提示玩家踩中地雷,该轮游戏结束。
3.如果没有踩雷,则在坐标中标记周围地雷数,如果标记为0,那么展开。
4.找出所有地雷,提示玩家排雷成功,该轮游戏结束。
排查雷函数声明:

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

4.函数定义 源码分析

初始化,打印和设置地雷函数都比较简单,直接上源码。扫雷的核心在于排查地雷。

4.1 初始化函数 Init

void Init(char board[ROWS][COLS], int rows, int cols, char sign)
{
	int i, j;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = sign;
		}
	}
}

4.2 打印函数 Displayboard

void Displayboard(char board[ROWS][COLS], int row, int col)
{
	int i, j;
	printf("------------------------------------------\n");
	for (i = 0; i <= row; 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");
	}
	printf("------------------------------------------\n");
}

4.3 设置地雷函数 SetMine

void SetMine(char board[ROWS][COLS], int row, int col)
{
	int i=bomb;
	while(i)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (board[x][y] != '1')
		{
			board[x][y] = '1';
			i--;
		}
	}
}

4.4 排查地雷函数FindMine

排查雷一定是一个循环的过程,直到出结果才能终止循环。我们可以声明变量win为安全地区的数量,当win==ROW*COL-bomb时,则排完所有地雷。
而且要求输入的坐标必须在上述的橙色区域内,否则要求玩家重新输入。
那我们就有了一个基本的框架:

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x, y;
	int win=0;//win为排雷数
	while (1)
	{
		printf("输入坐标:>");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			//进入排雷.......
		}
		else
		{
			printf("越界 重新输入\n");
		}
	}
}
4.4.1 防止第一步踩雷

好的,现在我们假设输入的坐标正确,终于能玩游戏了,但是有些时候很倒霉,一上来就踩中了地雷,那我们如何避免呢?
这里就要先设计一个函数,避免你的霉运——Prevent_jinx
将你第一步脚下的地雷挪到没雷的位置去。

//防止第一步就踩中地雷
void Prevent_jinx(char mine[ROWS][COLS],int x,int y)
{
	int m, n;
	while (1)
	{
		m = rand() % ROW + 1;
		n = rand() % COL + 1;
		if (mine[m][n] != '1')
		{
			mine[m][n] = '1';
			mine[x][y] = '0';
			break;
		}
	}
}

那我们怎么触发这个函数呢?
一定是在第一步的时候——即此刻排查出的安全区的数量win==0,而且你选择的坐标正好下面埋着一颗雷——mine[x][y] == '1'

于是我们的框架得到了更新:
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x, y;
	int win = 0;//win为安全数 如果win=总数-地雷数 则成功
	
	while (1)
	{
		printf("输入坐标:>");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (win == 0 && mine[x][y] == '1')//如果一上来就踩雷
			{
				Prevent_jinx(mine, x, y);
			}
			//正式进入排雷.......
		}
		else
		{
			printf("越界 重新输入\n");
		}
	}
}

趟过了第一步便正式进入了排雷,下面我们将判断之后的每一步是否踩中了地雷。
假设踩中了地雷,那么我们就需要提示玩家踩中地雷,而且需要打印实际地雷位置信息,好让玩家‘死’个明白,然后退出概该轮游戏。

于是我们的框架得到了更新:
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x, y;
	int win = 0;//win为安全地区数 如果win=总数-地雷数 则成功
	
	while (1)
	{
		printf("输入坐标:>");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (win == 0 && mine[x][y] == '1')//如果一上来就踩雷
			{
				Prevent_jinx(mine, x, y);
			}
			if (mine[x][y] == '1' ) //踩中雷
			{
				printf("踩到雷啦!\n");
				Displayboard(mine, ROW, COL);
				break;
			}
			else                       //未踩中雷
			{
				.....
			}
		}
		else
		{
			printf("越界 重新输入\n");
		}
	}
}
4.4.2 展开

如果没有踩中地雷,那么我们需要在show[x][y] 处标记周围八个方向的地雷个数,如果个数为0则需要展开

展开:如果(x,y)处为0,则周围8个点依次向外找雷,如果周围找到了地雷则标记个数,否则继续向外找雷。
实现定义找周围的雷数函数——Getbombcount

int Getbombcount(char mine[ROWS][COLS], int x, int y)
{
	int i, j,count=0;
	for (i = x - 1; i <= x + 1; i++)
	{
		for (j = y - 1; j <= y + 1; j++)
		{
			if (mine[i][j] == '1')
				count++;
		}
	}
	return count;
}

我们定义递归函数OpenMine来执行**展开**操作:

void OpenMine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
	if (x >= 1 && x <= ROW && y >= 1 && y <= COL && mine[x][y]!='1')  //x,y在合法范围内,而且不是雷区
	{
		int count = Getbombcount(mine, x, y);
		if (count == 0)                              //如果count=0,则需要继续扩展找雷
		{
			show[x][y] = '0';
			int i, j;
			//依次遍历周围八个点
			for (i = x - 1; i <= x + 1; i++)
			{
				for (j = y - 1; j <= y + 1; j++)
				{
					if (show[i][j] != '*') continue;   //如果已被标记过则不用进入递归,避免重复查找
					OpenMine(mine, show, i, j);
				}
			}
		}
		else                                         //count不为0 停止扩展找雷
		{
			show[x][y] = count + '0';
		}
	}
	else return;
}

这样的依次展开就可以排查掉许多安全区域

现在我们需要一个函数Countshow来统计以下安全区域的数量

int Countshow(char board[ROWS][COLS], int row, int col, char sign)
{
	int count = 0,i,j;
	for (i = 1; i <= row; i++)
	{
		for (j = 1; j <= col; j++)
		{
			if (board[i][j] == sign)
				count++;
		}
	}
	return count;
}

于是我们的FindMine函数就完全写出来了:

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x, y;
	int win = 0;//win为排雷数 如果win=总数-地雷数 则成功
	
	while (1)
	{
		printf("输入坐标:>");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (win == 0 && mine[x][y] == '1')//如果一上来就踩雷
			{
				Prevent_jinx(mine, x, y);
			}
			if (mine[x][y] == '1' )
			{
				printf("踩到雷啦!\n");
				Displayboard(mine, ROW, COL);
				break;
			}
			else                                     //未踩中雷
			{
				OpenMine(mine, show, x, y);
				win = ROW*COL-Countshow(show, ROW, COL, '*');
				if (Countshow(show, ROW, COL, '*')== bomb)
				{
					Displayboard(show, ROW, COL);
					printf("排雷成功\n");
					break;
				}
				Displayboard(show, ROW, COL);
			}
		}
		else
		{
			printf("越界 重新输入\n");
		}
	}
}

5.函数声明 game.h 源码

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//定义宏
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define bomb 3


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

//打印游戏界面
void Displayboard(char board[ROWS][COLS], int row, int col);

//布置地雷
void SetMine(char board[ROWS][COLS], int row, int col);

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

6.main函数 test.h 源码

#include "game.h"
void menu()
{
	printf("*******************\n");
	printf("***0.exit 1.play***\n");
	printf("*******************\n");

}
void game()
{
	//定义存放地雷的数组 和 玩家游玩的数组
	char  mine[ROWS][COLS],show[ROWS][COLS];
	//初始化数组
	Init(mine,ROWS,COLS,'0');
	Init(show, ROWS, COLS, '*');
	//打印
	//Displayboard(mine,ROW,COL);
	//Displayboard(show, ROW, COL);
	//布置地雷
	SetMine(mine,ROW,COL);
	Displayboard(mine, ROW, COL);
	Displayboard(show, ROW, COL);
	//排查雷
	FindMine(mine,show,ROW,COL);
}

int main()
{
	int input;
	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\n");
			break;
		}

	} while (input);
	return 0;
}

7 函数定义 game.c 源码

#include "game.h"

//初始化
void Init(char board[ROWS][COLS], int rows, int cols, char sign)
{
	int i, j;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = sign;
		}
	}
}

//打印游戏界面
void Displayboard(char board[ROWS][COLS], int row, int col)
{
	int i, j;
	printf("------------------------------------------\n");
	for (i = 0; i <= row; 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");
	}
	printf("------------------------------------------\n");
}


//布置地雷
void SetMine(char board[ROWS][COLS], int row, int col)
{
	int i=bomb;
	while(i)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (board[x][y] != '1')
		{
			board[x][y] = '1';
			i--;
		}
	}
}
//获取坐标周围地雷数目
int Getbombcount(char mine[ROWS][COLS], int x, int y)
{
	int i, j,count=0;
	for (i = x - 1; i <= x + 1; i++)
	{
		for (j = y - 1; j <= y + 1; j++)
		{
			if (mine[i][j] == '1')
				count++;
		}
	}
	return count;
}
//防止第一步就踩中地雷
void Prevent_jinx(char mine[ROWS][COLS],int x,int y)
{
	int m, n;
	while (1)
	{
		m = rand() % ROW + 1;
		n = rand() % COL + 1;
		if (mine[m][n] != '1')
		{
			mine[m][n] = '1';
			mine[x][y] = '0';
			break;
		}
	}
}
void OpenMine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
	if (x >= 1 && x <= ROW && y >= 1 && y <= COL && mine[x][y]!='1')  //x,y在合法范围内,而且不是雷区
	{
		int count = Getbombcount(mine, x, y);
		if (count == 0)                              //如果count=0,则需要继续扩展
		{
			show[x][y] = '0';
			int i, j;
			for (i = x - 1; i <= x + 1; i++)
			{
				for (j = y - 1; j <= y + 1; j++)
				{
					if (show[i][j] != '*') continue;   //如果已被排雷过则不用递归
					OpenMine(mine, show, i, j);
				}
			}
		}
		else                                         //count不为0 停止扩展
		{
			show[x][y] = count + '0';
		}
	}
	else return;
}

int Countshow(char board[ROWS][COLS], int row, int col, char sign)
{
	int count = 0,i,j;
	for (i = 1; i <= row; i++)
	{
		for (j = 1; j <= col; j++)
		{
			if (board[i][j] == sign)
				count++;
		}
	}
	return count;
}

//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x, y;
	int win = 0;//win为排雷数 如果win=总数-地雷数 则成功
	
	while (1)
	{
		printf("输入坐标:>");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (win == 0 && mine[x][y] == '1')//如果一上来就踩雷
			{
				Prevent_jinx(mine, x, y);
			}
			if (mine[x][y] == '1' )
			{
				printf("踩到雷啦!\n");
				Displayboard(mine, ROW, COL);
				break;
			}
			else                                     //未踩中雷
			{
				OpenMine(mine, show, x, y);
				win = ROW*COL-Countshow(show, ROW, COL, '*');
				if (Countshow(show, ROW, COL, '*')== bomb)
				{
					Displayboard(show, ROW, COL);
					printf("排雷成功\n");
					break;
				}
				Displayboard(show, ROW, COL);
			}
		}
		else
		{
			printf("越界 重新输入\n");
		}
	}
}

8.实际游玩效果

第一把 :设置bomb为2
在这里插入图片描述
在这里插入图片描述
第二把:设置bomb为4
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值