一、创建菜单函数

基础起手式do while循环没什么好说的

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

int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请输入选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("游戏开始!\n");
			game();//进入游戏
			break;
		case 0://输入0 do while 条件判定 0 结束循环
			printf("退出游戏!\n");
			break;
		default:
			printf("选择错误!\n");
			break;
		}
	} while (input);
	return 0;
}

二、扫雷游戏

流程

1.创建两个数组,一个用于布雷,一个用于显示

2.初始化两个数组

3.布雷

4. 开局开辟一块安全区

5.显示棋盘

6.开始扫雷游戏

void game()
{
	//创建两个数组,一个用于布雷,一个用于显示
	char mine[ROWS][COLS];
	char show[ROWS][COLS];
	//初始化
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');
	//布雷
	SetMine(mine, ROW, COL);
	DisplayBoard(mine, ROW, COL);
	//开局开辟一块安全区
	FindMineStart(mine, show, ROW, COL);
	//显示棋盘
	DisplayBoard(show, ROW, COL);
	//开始扫雷
	FindMine(mine, show, ROW, COL);
}

1.创建两个数组并初始化

创建两个ROW+2COL+2列数组用于表示ROW*COL(ROW行COL列)的棋盘

写一个初始化函数,通过两次调用,对两个数组进行初始化

规定:

布雷数组:

0 表示无雷,1 表示有雷

显示数组:

*表示未排查的雷区,排查过的雷区用数字标记附近雷的数量,附近无雷则用 空格表示安全区

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;
		}
	}
}

2.打印棋盘函数

从数组下标[1]开始,打印ROWCOL列棋盘

void DisplayBoard(char arr[ROWS][COLS], int row, int col)
{
	for (int i = 0; i <= 2*row+1; i++)
	{
		printf("-");
	}
	printf("\n");//"--------------"
	for (int i = 0; i <= row; 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");
	}
	for (int i = 0; i <= 2 * row + 1; i++)
	{
		printf("-");
	}
	printf("\n");//"--------------"
}

3.布雷函数

设定雷的数量为COUNT

需要用到随机值,rand函数(stdlib.h)、time函数(time.h)

创建随机坐标x,y,范围在1~9,第一次循环后要判断坐标是否已经布雷

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

在main函数中加上srand((unsigned int)time(NULL));

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请输入选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("游戏开始!\n");
			game();
			break;
		case 0://输入0 do while 条件判定 0 结束循环
			printf("退出游戏!\n");
			break;
		default:
			printf("选择错误!\n");
			break;
		}
	} while (input);
	return 0;
}

4.扫雷函数主体

用x,y表示坐标

1.提示输入坐标

2.scanf函数输入坐标

3.判断坐标是否在棋盘范围内

4.判断是否踩雷,踩雷炸死游戏结束,未踩雷用扫雷工具显示周围雷数量(需要创建函数来实现)

5.扫完所有的雷,while循环结束

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	while (1)
	{
		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
				{
					//扫雷工具
          FindMineTool(mine, show, ROW, COL, x, y);
          //显示棋盘
          DisplayBoard(show, ROW, COL);
				}
			}
			else
			{
				printf("重复坐标\n");
			}
		}
		else
		{
			printf("非法坐标\n");
		}
	}
}

5.扫雷工具函数

显示周边雷的数量

GetMineCount函数表示

统计(x,y)周围的8个坐标的雷分布情况

arr[x][y]得到'1'的是字符型,可以通过'1' - '0'转化为整形1

int GetMineCount(char arr[ROWS][COLS], int x, int y)
{
	return arr[x][y - 1] + arr[x][y + 1] +
		arr[x - 1][y - 1] + arr[x - 1][y] + 
		arr[x - 1][y + 1] + arr[x + 1][y - 1] + 
		arr[x + 1][y] + arr[x + 1][y + 1] - 8 * '0';

}

递归探索

我们玩扫雷游戏时候,并不是单纯一个一个坐标判断的,这样很浪费时间,实际上当坐标附近都没雷的时候,我们可以把这个坐标和四周的8个坐标判断为安全区,并以安全区为原点向外继续扫雷,直到附近有雷为止。可以用递归方法实现这个功能。

*表示未排查的雷区,排查过的雷区用数字标记附近雷的数量,附近无雷则用 空格表示安全区。

void FindMineTool(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y)
{
	if (x >= 1 && x <= row && y >= 1 && y <= col)
	{
		if (show[x][y] == ' ')//当坐标是被标记过的安全区
		{
			return;//返回,结束
		}
		else if (GetMineCount(mine, x, y) != 0)//当坐标附件有雷时
		{
			show[x][y] = GetMineCount(mine, x, y) + '0';//显示雷的数量
			return;
		}
		show[x][y] = ' ';//附件无雷为安全区,用空格表示
		FindMineTool(mine, show, ROW, COL, x - 1, y - 1);
		FindMineTool(mine, show, ROW, COL, x - 1, y);
		FindMineTool(mine, show, ROW, COL, x - 1, y + 1);
		FindMineTool(mine, show, ROW, COL, x, y - 1);
		FindMineTool(mine, show, ROW, COL, x, y + 1);
		FindMineTool(mine, show, ROW, COL, x + 1, y - 1);
		FindMineTool(mine, show, ROW, COL, x + 1, y);
		FindMineTool(mine, show, ROW, COL, x + 1, y + 1);
	}
}

6.通关条件

扫雷通关条件就是扫完所有坐标,标记所有雷

棋盘最后剩余的*坐标都是雷,也就是*的数量等于设定的雷的数量COUNT

int FindMineWin(char show[ROWS][COLS], int row, int col)
{
	int count = 0;
	for (int i = 1; i <= row; i++)
	{
		for (int j = 1; j <= col; j++)
		{
			if (show[i][j] == '*')
				count++;
		}
	}
	return count;
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	while (FindMineWin(show, ROW, COL) > COUNT)
	{
		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
				{
					// 扫雷工具:
					// 显示坐标周围的雷的数量
					// 如果数量为0,就以坐标周围的8个点为原点
					// 扩散扫雷,直到附近有雷为止
					//
					FindMineTool(mine, show, ROW, COL, x, y);
					DisplayBoard(show, ROW, COL);
				}
			}
			else
			{
				printf("重复坐标\n");
			}
		}
		else
		{
			printf("非法坐标\n");
		}
	}
	if (FindMineWin(show, ROW, COL) == COUNT)
	{
		printf("游戏通关\n");
		DisplayBoard(mine, ROW, COL);
	}
}


7.开局开辟一块安全区

每次开局我们输的前几个坐标都有直接被雷炸死的风险,开局被炸死非常影响游戏,我们需要一个功能可以实现,游戏开局就自动显示一块安全区,FindMineStart函数

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

三、完整代码

头文件 game.h

#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ROW 9
#define COL 9

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

#define 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 FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
void FindMineStart(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

主函数 main.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"

void menu()
{
	printf("**************************\n");
	printf("*********扫    雷*********\n");
	printf("*****1.play    0.exit*****\n");
	printf("**************************\n");

}

void game()
{
	//创建两个数组,一个用于布雷,一个用于显示
	char mine[ROWS][COLS];
	char show[ROWS][COLS];
	//初始化
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');
	//布雷
	SetMine(mine, ROW, COL);
	DisplayBoard(mine, ROW, COL);
	//开局开辟一块安全区
	FindMineStart(mine, show, ROW, COL);
	//显示棋盘
	DisplayBoard(show, ROW, COL);
	//开始扫雷
	FindMine(mine, show, ROW, COL);
}

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请输入选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("游戏开始!\n");
			game();
			break;
		case 0://输入0 do while 条件判定 0 结束循环
			printf("退出游戏!\n");
			break;
		default:
			printf("选择错误!\n");
			break;
		}
	} while (input);
	return 0;
}

游戏功能函数 game.c

#define _CRT_SECURE_NO_WARNINGS 1

#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)
{
	for (int i = 0; i <= 2*row+1; i++)
	{
		printf("-");
	}
	printf("\n");//"--------------"
	for (int i = 0; i <= row; 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");
	}
	for (int i = 0; i <= 2 * row + 1; i++)
	{
		printf("-");
	}
	printf("\n");//"--------------"
}

void SetMine(char arr[ROWS][COLS], int row, int col)
{
	int count = 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 arr[ROWS][COLS], int x, int y)
{
	return arr[x][y - 1] + arr[x][y + 1] +
		arr[x - 1][y - 1] + arr[x - 1][y] + 
		arr[x - 1][y + 1] + arr[x + 1][y - 1] + 
		arr[x + 1][y] + arr[x + 1][y + 1] - 8 * '0';

}

void FindMineTool(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y)
{
	if (x >= 1 && x <= row && y >= 1 && y <= col)
	{
		if (show[x][y] == ' ')//当坐标是被标记过的安全区
		{
			return;//返回,结束
		}
		else if (GetMineCount(mine, x, y) != 0)//当坐标附件有雷时
		{
			show[x][y] = GetMineCount(mine, x, y) + '0';//显示雷的数量
			return;
		}
		show[x][y] = ' ';//附件无雷为安全区,用空格表示
		FindMineTool(mine, show, ROW, COL, x - 1, y - 1);
		FindMineTool(mine, show, ROW, COL, x - 1, y);
		FindMineTool(mine, show, ROW, COL, x - 1, y + 1);
		FindMineTool(mine, show, ROW, COL, x, y - 1);
		FindMineTool(mine, show, ROW, COL, x, y + 1);
		FindMineTool(mine, show, ROW, COL, x + 1, y - 1);
		FindMineTool(mine, show, ROW, COL, x + 1, y);
		FindMineTool(mine, show, ROW, COL, x + 1, y + 1);
	}
}

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

int FindMineWin(char show[ROWS][COLS], int row, int col)
{
	int count = 0;
	for (int i = 1; i <= row; i++)
	{
		for (int j = 1; j <= col; j++)
		{
			if (show[i][j] == '*')
				count++;
		}
	}
	return count;
}

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	while (FindMineWin(show, ROW, COL) > COUNT)
	{
		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
				{
					// 扫雷工具:
					// 显示坐标周围的雷的数量
					// 如果数量为0,就以坐标周围的8个点为原点
					// 扩散扫雷,直到附近有雷为止
					//
					FindMineTool(mine, show, ROW, COL, x, y);
					DisplayBoard(show, ROW, COL);
				}
			}
			else
			{
				printf("重复坐标\n");
			}
		}
		else
		{
			printf("非法坐标\n");
		}
	}
	if (FindMineWin(show, ROW, COL) == COUNT)
	{
		printf("游戏通关\n");
		DisplayBoard(mine, ROW, COL);
	}
}