C语言实现扫雷(展开、标记、撤销、判断胜利等,附源码)

一.游戏思路

1.创建菜单界面函数
2.存放雷的信息
3.进行雷的初始化棋盘,并打印展示的扫雷棋盘
4.布置雷的信息
5.输入要排查的雷的坐标。
6.检查出的坐标是不是雷,布置雷存放的是字符(1) 没有放置的是字符(0)
7.输入坐标的时候一共有3种情况:很遗憾你被炸死了!、该位置已经查看过了!、坐标非法,请重新输入
8.是否进行雷的标记,以及是否进行雷的撤销
9.判断是否标记的雷全部与雷区重合,若是则胜利

二.实现步骤

1.构建菜单

void menu()
{
	printf("-----------------------------\n");
	printf("|          1.扫雷           |\n");
	printf("|          0.退出           |\n");
	printf("-----------------------------\n");
}

2.定义扫雷区

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

1.利用宏定义出行、列
2.打印出ROWxCOL的棋盘,但是初始化出ROWSxCOLS的棋盘,便于判断靠边的行和列周围8个格子是否有雷
3.利于改变棋盘大小
在这里插入图片描述
支持多行多列

3.棋盘初始化

char mine[ROWS][COLS];  //定义雷数组
	char show[ROWS][COLS];  //定义查看数组
	//初始化数组
	Initboard(mine,ROWS,COLS,'0');  //将雷区初始化为0
	Initboard(show, ROWS, COLS, '*');//将查看区初始化为*
void Initboard(char board[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++)
		{
			board[i][j] = set;
		}
	}
}
  1. 定义两个二维数组
    2.一个用于存放雷,一个用于扫雷
    3.根据查看数组的坐标植入到扫雷数组,二者联系起来

4.棋盘打印

测试时可以打印雷数组
这里只打印查看数组
注意,只打印ROWxCOL大小的棋盘!!

//Display(mine, ROW, COL);//打印出来看看
	Display(show, ROW, COL);//打印出来看看
void Display(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	for (j = 1; j <= col; j++)
	{
		if (j < 10)
		{
			printf("|(%d)", j);
		}
		else
		{
			printf("| %d", j);
		}

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

效果图
在这里插入图片描述

5.放雷

#define EASY_COUNT 10

1.定义雷的数量(简单)
2.可通过多设置分支供选择以达到多个难度的效果
3.为判断胜利提供判断条件

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

1.利用随机数,将雷区数组中为字符‘0’的格子变为字符‘1’的雷
2. 使用随机数记得设置一次时间戳

6.找雷

void Find_Mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int c = 0;
	while (c != EASY_COUNT)  //循环结束条件
	{
		printf("请输入要查看的坐标:> ");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] != '*')
			{
				printf("该位置已经查看过了!\n");
				continue;
			}
			if (mine[x][y] == '1')
			{
				printf("很遗憾你被炸死了!!\n");
				Display(mine, ROW, COL);//炸死后打印出雷区棋盘让玩家看一看雷分布
				break;
			}
			else
			{
				None_mine(mine, show, x, y);//展开无雷区
				Display(show, row, col);   //打印展开后的数组
				Define_mine(mine, show, ROW, COL);//标记雷
				system("cls");//清屏
				Display(show, row, col); //清屏后打印带标记的雷的数组
				c = Iswin(mine, show, row, col);//判断胜利
			}
		}
		else
			printf("坐标非法,请重新输入!\n");
	}
	printf("恭喜你获得胜利!!\n");
}

1.一般我们输入坐标都是从(1,1)开始,所以要判断输入坐标是否合法
if (x >= 1 && x <= row && y >= 1 && y <= col)
2.判断循环结束条件,也就是胜利标志
while (c != EASY_COUNT)
这里c时Iswin函数的返回值,详细见后面的判断胜利条件
3.如果输入坐标合法并且不是雷,判断周围雷的个数并确定是否需要展开
None_mine(mine, show, x, y);函数实现

7.判断选定坐标周围雷的个数、展开雷


int get_mine(char mine[ROWS][COLS], int x, int y)
{
	int i = 0;
	int count = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		int j = 0;
		for (j = y - 1; j <= y + 1; j++)
		{
			if (mine[i][j] == '1')
			{
				count++;//将周围9个格子判断一遍,有雷就+1
			}
		}
	}
	return count; //重要!!!,如果此处不写,会有问题,具体可以自行尝试
}
void None_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
	if (x >= 1 && x <= ROW && y >= 1 && y <= COL)//判断坐标合法性
	{
		int count = get_mine(mine, x, y);
		if (count != 0)//说明有雷
		{
			show[x][y] = count + '0';//数量+‘0’可以得到‘数量’字符,参见网上ASCII码表
		}
		else if (show[x][y] != ' ')//说明周围没有雷,并且如果周围的格子!=‘ ’,执行
		{
			show[x][y] = ' ';  //将无雷的格子转化为‘ ’;
			int i = 0;
			for (i = x - 1; i <= x + 1; i++)
			{
				int j = 0;
				for (j = y - 1; j <= y + 1; j++)
				{
					None_mine(mine, show, i, j);//递归,将要展开的格子周围8个格子在进行判断,直到展开的格子周围有雷为止
				}
			}
		}
		else
		{
			return;
		}
	}
}

在这里插入图片描述

8.标记雷,撤销雷

1.如果有确定的雷的坐标,输入进行标记
2.如果标记错误,在下一次标记中可以撤销

void Define_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row,int col)
{
	int x = 0;
	int y = 0;
	char ch = 0;
	while (1)
	{
		printf("是否有确定的,需要标记的雷?(y/n/撤销雷:d):-> ");
		while (ch = getchar() != '\n');
		{
			;
		}
		ch = getchar();
		switch(ch)
		{
		case 'y':
			while (1)
			{
				printf("请输入要标记的坐标:-> ");
				scanf("%d%d", &x, &y);
				{
					if (x >= 1 && x <= row && y >= 1 && y <= col)
					{
						if (show[x][y] == '*')
						{
							show[x][y] = '!';//还有一层
							printf("是否还有需要标记的坐标?(y/n ");
							while (ch = getchar() != '\n');
							{
								;
							}
							ch = getchar();
							switch (ch)
							{
							case 'y':
									while (1)
									{
										printf("请输入要标记的坐标:-> ");
										scanf("%d%d", &x, &y);
										{
											if (x >= 1 && x <= row && y >= 1 && y <= col)
											{
												if (show[x][y] == '*')
												{
													show[x][y] = '!';
													goto end;
												}
												else
												{
													printf("该坐标已被判断,请重试!!\n");
												}
											}
											else
											{
												printf("坐标非法,请重新输入!\n");
											}
										}
									}
								break;
							case 'n':
								goto end;
								break;
							default:
								printf("输入错误,请重新输入!\n");
								break;
							}
						}
						else
						{
							printf("该坐标已被判断,请重试!!\n");
						}
					}
					else
					{
						printf("坐标非法,请重新输入!\n");
					}
				}
			}
			break;
		case 'n':
			goto end;
			break;
		case 'd':
			printf("是否有确定的,需要撤销的雷?(y/n):-> ");
			while (ch = getchar() != '\n');
			{
				;
			}
			ch = getchar();
			switch (ch)
			{
			case 'y':
				while (1)
				{
					printf("请输入要撤销的坐标:-> ");
					scanf("%d%d", &x, &y);
					{
						if (x >= 1 && x <= row && y >= 1 && y <= col)
						{
							if (show[x][y] == '!')
							{
								show[x][y] = '*';
								printf("是否有确定的,还需要撤销的雷?(y/n):-> ");
								while (ch = getchar() != '\n');
								{
									;
								}
								ch = getchar();
								switch (ch)
								{
								case 'y':
									while (1)
									{
										printf("请输入要撤销的坐标:-> ");
										scanf("%d%d", &x, &y);
										{
											if (x >= 1 && x <= row && y >= 1 && y <= col)
											{
												if (show[x][y] == '!')
												{
													show[x][y] = '*';
												}
												else
												{
													printf("该坐标已被判断,请重试!!\n");
												}
											}
											else
											{
												printf("坐标非法,请重新输入!\n");
											}
										}
									}
									break;
								case 'n':
									goto end;
									break;
								default:
									printf("输入错误,请重新输入!\n");
									break;
								}

							}
							else
							{
								printf("该坐标已被判断,请重试!!\n");
							}
						}
						else
						{
							printf("坐标非法,请重新输入!\n");
						}
					}
				}
				break;
			case 'n':
				goto end;
				break;
			default:
				printf("输入错误,请重新输入!\n");
				break;
			}
		default:
			printf("输入错误,请重新输入!\n");
			break;
		}
	}
end:
	return;
}

1.这里是用switch嵌套while,由于二者都含有break的缘故,想要跳出循环我用了一个不好的代码-goto
2.整体我的代码一点都不简洁,而且嵌套两次,只能标记/撤销两次,也许这里用循环会更简单,更简洁一点,读者可以自行尝试
3.这里将‘星号’替换为‘!’以标记雷,或许可以换成你满意的字符!
4.撤销雷无非不是把‘!’在替换为(‘星号’)
5.标记或撤销还是要判断坐标合法性,这点很重要
6.或者你可以把标记雷,撤销雷写成多个函数嵌套使用,这样会更美观!
看图!
被标记的雷(3,3)
在这里插入图片描述
被撤销的雷(3,3)
在这里插入图片描述

9.判断获胜条件

int Iswin(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int i = 0;
	int win = 0;
	for (i = 1; i <= row; i++)
	{
		int j = 0;
		for (j = 1; j <= col; j++)
		{
			if (show[i][j] == '!' && mine[i][j] == '1')
				win++;
		}
	}
	return win;
}

1.当show数组中‘!’字符和mine数组中‘1’字符相等并且坐标一致,win+1
当win等于EASY_COUNT时,跳出前面的循环,取得胜利
while (c != EASY_COUNT)
2.如果标记的雷不符win则不会+1,此时可能有误判的雷,就需要撤销
3.如果show数组全部标记为‘!’,那同样会胜利,这也是一个小bug,对‘!’字符的限制可以解决这个问题,请读者自行尝试,嘻嘻
在这里插入图片描述
所有‘!’与雷相符,获得胜利!

三.源代码

1.test.c

#define  _CRT_SECURE_NO_WARNINGS
#include "game.h"

void menu()
{
	printf("-----------------------------\n");
	printf("|          1.扫雷           |\n");
	printf("|          0.退出           |\n");
	printf("-----------------------------\n");
}
void game()
{
	char mine[ROWS][COLS];  //定义雷数组
	char show[ROWS][COLS];  //定义查看数组
	//初始化数组
	Initboard(mine,ROWS,COLS,'0');  //将雷区初始化为0
	Initboard(show, ROWS, COLS, '*');//将查看区初始化为*
	//Display(mine, ROW, COL);//打印出来看看
	Display(show, ROW, COL);//打印出来看看
	//放雷
	Set_Mine(mine, ROW, COL);//放雷
	//Display(mine, ROW, COL);
	Find_Mine(mine, show, ROW, COL);
	//Display(show, ROW, COL);
}

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

}


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

2.game.h

#define  _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<windows.h>


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

void Initboard(char board[ROWS][COLS], int rows, int cols,char set);
void Display(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);

void None_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y);
void Define_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
int Iswin(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
int get_mine(char mine[ROWS][COLS], int x, int y);

3.game.c

#define  _CRT_SECURE_NO_WARNINGS
#include "game.h"

void Initboard(char board[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++)
		{
			board[i][j] = set;
		}
	}
}
void Display(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	for (j = 1; j <= col; j++)
	{
		if (j < 10)
		{
			printf("|(%d)", j);
		}
		else
		{
			printf("| %d", j);
		}

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

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

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

void None_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
	if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
	{
		int count = get_mine(mine, x, y);
		if (count != 0)
		{
			show[x][y] = count + '0';
		}
		else if (show[x][y] != ' ')
		{
			show[x][y] = ' ';
			int i = 0;
			for (i = x - 1; i <= x + 1; i++)
			{
				int j = 0;
				for (j = y - 1; j <= y + 1; j++)
				{
					None_mine(mine, show, i, j);
				}
			}
		}
		else
		{
			return;
		}
	}
}

void Find_Mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int c = 0;
	while (c != EASY_COUNT)  //循环结束条件
	{
		printf("请输入要查看的坐标:> ");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] != '*')
			{
				printf("该位置已经查看过了!\n");
				continue;
			}
			if (mine[x][y] == '1')
			{
				printf("很遗憾你被炸死了!!\n");
				Display(mine, ROW, COL);
				break;
			}
			else
			{
				None_mine(mine, show, x, y);//展开无雷区
				Display(show, row, col);   //打印展开后的数组
				Define_mine(mine, show, ROW, COL);//标记雷
				system("cls");//清屏
				Display(show, row, col); //清屏后打印带标记的雷的数组
				c = Iswin(mine, show, row, col);//判断胜利
			}
		}
		else
			printf("坐标非法,请重新输入!\n");
	}
	printf("恭喜你获得胜利!!\n");
}

void Define_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row,int col)
{
	int x = 0;
	int y = 0;
	char ch = 0;
	while (1)
	{
		printf("是否有确定的,需要标记的雷?(y/n/撤销雷:d):-> ");
		while (ch = getchar() != '\n');
		{
			;
		}
		ch = getchar();
		switch(ch)
		{
		case 'y':
			while (1)
			{
				printf("请输入要标记的坐标:-> ");
				scanf("%d%d", &x, &y);
				{
					if (x >= 1 && x <= row && y >= 1 && y <= col)
					{
						if (show[x][y] == '*')
						{
							show[x][y] = '!';//还有一层
							printf("是否还有需要标记的坐标?(y/n) ");
							while (ch = getchar() != '\n');
							{
								;
							}
							ch = getchar();
							switch (ch)
							{
							case 'y':
									while (1)
									{
										printf("请输入要标记的坐标:-> ");
										scanf("%d%d", &x, &y);
										{
											if (x >= 1 && x <= row && y >= 1 && y <= col)
											{
												if (show[x][y] == '*')
												{
													show[x][y] = '!';
													goto end;
												}
												else
												{
													printf("该坐标已被判断,请重试!!\n");
												}
											}
											else
											{
												printf("坐标非法,请重新输入!\n");
											}
										}
									}
								break;
							case 'n':
								goto end;
								break;
							default:
								printf("输入错误,请重新输入!\n");
								break;
							}
						}
						else
						{
							printf("该坐标已被判断,请重试!!\n");
						}
					}
					else
					{
						printf("坐标非法,请重新输入!\n");
					}
				}
			}
			break;
		case 'n':
			goto end;
			break;
		case 'd':
			printf("是否有确定的,需要撤销的雷?(y/n):-> ");
			while (ch = getchar() != '\n');
			{
				;
			}
			ch = getchar();
			switch (ch)
			{
			case 'y':
				while (1)
				{
					printf("请输入要撤销的坐标:-> ");
					scanf("%d%d", &x, &y);
					{
						if (x >= 1 && x <= row && y >= 1 && y <= col)
						{
							if (show[x][y] == '!')
							{
								show[x][y] = '*';
								printf("是否有确定的,还需要撤销的雷?(y/n):-> ");
								while (ch = getchar() != '\n');
								{
									;
								}
								ch = getchar();
								switch (ch)
								{
								case 'y':
									while (1)
									{
										printf("请输入要撤销的坐标:-> ");
										scanf("%d%d", &x, &y);
										{
											if (x >= 1 && x <= row && y >= 1 && y <= col)
											{
												if (show[x][y] == '!')
												{
													show[x][y] = '*';
												}
												else
												{
													printf("该坐标已被判断,请重试!!\n");
												}
											}
											else
											{
												printf("坐标非法,请重新输入!\n");
											}
										}
									}
									break;
								case 'n':
									goto end;
									break;
								default:
									printf("输入错误,请重新输入!\n");
									break;
								}

							}
							else
							{
								printf("该坐标已被判断,请重试!!\n");
							}
						}
						else
						{
							printf("坐标非法,请重新输入!\n");
						}
					}
				}
				break;
			case 'n':
				goto end;
				break;
			default:
				printf("输入错误,请重新输入!\n");
				break;
			}
		default:
			printf("输入错误,请重新输入!\n");
			break;
		}
	}
end:
	return;
}

int Iswin(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int i = 0;
	int win = 0;
	for (i = 1; i <= row; i++)
	{
		int j = 0;
		for (j = 1; j <= col; j++)
		{
			if (show[i][j] == '!' && mine[i][j] == '1')
				win++;
		}
	}
	return win;
}

好了,本篇到此结束了
如果还有什么不懂的,需要改进的,欢迎留言!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值