c语言实现扫雷

扫雷

  • 游戏运行效果
  • 数据结构的分析
  • 实现过及注意事项
    • 文件结构
    • 主函数
    • 棋盘初始化函数
    • 棋盘打印函数
    • 布置雷函数
    • 排查雷及判断输赢函数
      • 返回周围雷数量函数
      • 雷区展开函数
      • 未排查坐标数量函数
  • 源代码

游戏运行效果

c语言实现扫雷_第1张图片
初始界面
c语言实现扫雷_第2张图片
排雷界面
c语言实现扫雷_第3张图片

数据结构的分析

扫雷的过程中,布置的雷和排查出的雷的信息都需要存储,所以我们需一定的数据结构来存储这些信息。
因为我们需要在9*9的棋盘上布置雷的信息和排查雷,我们首先想到的就创建⼀个的数组来存放信息。

为了防止越界,我们在设计的时候,给数组扩大一圈,雷还是布置在中间的的坐标上,周围⼀圈不去布置雷就⾏,这样就解决了越界的问题。所以我们将存放数据的数组创建成11*11是比较合适。

为了避免混杂,这里我们采用这样的方案,我们专门给⼀个棋盘(对应⼀个数组mine)存放布置好的雷的信息,再给另外⼀个棋盘(对应另外⼀个数组show)存放排查出的雷的信息。这样就互不⼲扰了,把雷布置到mine数组,在mine数组中排查雷,排查出的数据存放在show数组,并且印show数组的信息给后期排查参考。
同时为了保持神秘,show数组开始时初始化为字符 ‘*’,为了保持两个数组的类型⼀致,可以使⽤同⼀套函数处理,mine数组最开始也初始化为字符’0’,布置雷改成’1’

1.char mine[11][11] = {0};//⽤来存放布置好的雷的信息
2.char show[11][11] = {0};//⽤来存放排查出的雷的个数信息

实现过及注意事项

文件结构

1.test.c //⽂件中写游戏的测试逻辑 
2.game.c //⽂件中写游戏中函数的实现等
3.game.h //⽂件中写游戏需要的数据类型和函数声明等

主函数

#include "game.h"
int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		manu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出\n");
			break;
		default:
			printf("选择错误,请重新选择:>\n");
			break;
		}
	} while (input);
	return 0;
}

菜单函数

void manu()
{
	printf("********************\n");
	printf("***   _1.play_   ***\n");
	printf("***   _0.exit_   ***\n");
	printf("********************\n");
}

具体的游戏执行流程

void game()
{
	char mine[ROWS][COLS]; // 存放雷信息的棋盘
	char show[ROWS][COLS]; //展示给玩家的棋盘
	//初始化棋盘  
	//mine棋盘全部初始化为'0'    show棋盘全部初始化为'*'
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');
	//打印棋盘
	//DisplayBoard(mine, ROWS, COLS);
	DisplayBoard(show, ROWS, COLS);
	//布置雷
	SetMine(mine, ROWS, COLS);
	//DisplayBoard(mine, ROWS, COLS);
	//排查雷
	FindMine(mine, show, ROW, COL);
}

棋盘初始化函数

我们通过InitBoard()函数对char mine[ROWS][COLS]char show[ROWS][COLS]进行初始化,前者放'0'后者放'*'
我们用两个for循环来遍历数组

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

棋盘打印函数

void DisplayBoard(char Board[ROWS][COLS], int rows, int cols)
{
	int i = 0;
	printf("\n");
	for (i = 0; i <= rows - 2; i++)
		printf("%d ", i);
	printf("\n");
	for (i = 1; i < rows-1; i++)
	{
		int j = 0;
		printf("%d ", i);
		for (j = 1; j < cols-1; j++)
		{
			printf("%c ", Board[i][j]);
		}
		printf("\n");
	}
}

布置雷函数

我们用rand()来生成随机数,用srand((unsigned int)time(NULL))确保了游戏的随机性。

void SetMine(char Board[ROWS][COLS], int rows, int cols)
{
	int n = EASY_COUNT;//雷的数量
	while (n)
	{
		int x = rand() % 9 + 1;
		int y = rand() % 9 + 1;
		if (Board[x][y] != '1')
		{
			Board[x][y] = '1';
			n--;
		}
	}
}

排查雷及判断输赢函数

void FindMine(char MineBoard[ROWS][COLS], char ShowBoard[ROWS][COLS], int row, int col)
{
	int count = row * col;//未知坐标的数量
	while (count > EASY_COUNT)
	{
		int x, y;
		printf("请输入你想排查的坐标:>");
		scanf("%d%d", &x, &y);
		if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
		{
			if (MineBoard[x][y] == '1')
			{
				printf("很遗憾你踩到雷了,游戏结束\n");
				DisplayBoard(MineBoard, ROWS, COLS);
				break;
			}
			else
			{
				//统计周围雷的数量
				SearchAround(MineBoard, ShowBoard, x, y);
				DisplayBoard(ShowBoard, ROWS, COLS);
				count = JudgeCount(ShowBoard, row, col);
			}
		}
		else
		{
			printf("坐标非法,请重新输入!\n");
		}
	}
	if (count == EASY_COUNT)
	{
		printf("恭喜你扫雷成功:>\n");
		DisplayBoard(MineBoard, ROWS, COLS);
	}
}

返回周围雷数量函数

这里就体现了我们将数组初始化为'0',雷为'1'的好处。通过周围八格的和再减去8*'0'就是周围雷的数量了。

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

需要注意的是:这里我们必须减去8*'0',否者会出现以下状况
c语言实现扫雷_第4张图片
因为这加起来的和是'?'的ASCII表中的值,减去8*'0'才是周围雷的数量

雷区展开函数

//如果排查位置没有雷,且周围位置也没有雷,返回周围一片
void SearchAround(char MineBoard[ROWS][COLS], char ShowBoard[ROWS][COLS], int x, int y)
{
	if (ShowBoard[x][y] == '*')//判断当前位置是否已经赋值
	{
		int arounds = GetMineCount(MineBoard, x, y);
		if (arounds != 0)
		{
			ShowBoard[x][y] = arounds + '0';
		}
		else
		{
			if ((x >= 1 && x <= ROW) && (y >= 1 && y <= COL))
			{
				ShowBoard[x][y] = arounds + ' ';
				SearchAround(MineBoard, ShowBoard, x - 1, y - 1);
				SearchAround(MineBoard, ShowBoard, x - 1, y);
				SearchAround(MineBoard, ShowBoard, x - 1, y + 1);
				SearchAround(MineBoard, ShowBoard, x, y - 1);
				SearchAround(MineBoard, ShowBoard, x, y + 1);
				SearchAround(MineBoard, ShowBoard, x + 1, y - 1);
				SearchAround(MineBoard, ShowBoard, x + 1, y);
				SearchAround(MineBoard, ShowBoard, x + 1, y + 1);
			}
		}
	}
}

c语言实现扫雷_第5张图片
这里我们需要注意两个易错点
1.这里必须判断一下当前位置是否赋值,否则将在两个相邻坐标间来回进入此函数,造成死循环;
2.当第一次进入SearchAround()函数时我们知道xy的取值都只是在1~9之间。那么不断递归后xy将越过此范围,造成错误。

未排查坐标数量函数

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

源代码

头文件game.h

#define  _CRT_SECURE_NO_WARNINGS 1
#pragma once

#include
#include
#include
#include

#define EASY_COUNT 10

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

//初始化棋盘
void InitBoard(char Board[ROWS][COLS], int rows, int cols, char set);
//打印棋盘
void DisplayBoard(char Board[ROWS][COLS], int rows, int cols);
//布置雷
void SetMine(char Board[ROWS][COLS], int rows, int cols);
//排查雷
void FindMine(char MineBoard[ROWS][COLS], char ShowBoard[ROWS][COLS], int row, int col);

测试文件test.c

#include "game.h"

void game()
{
	char mine[ROWS][COLS]; // 存放雷信息的棋盘
	char show[ROWS][COLS]; //展示给玩家的棋盘
	//初始化棋盘  
	//mine棋盘全部初始化为'0'    show棋盘全部初始化为'*'
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');
	//打印棋盘
	//DisplayBoard(mine, ROWS, COLS);
	DisplayBoard(show, ROWS, COLS);
	//布置雷
	SetMine(mine, ROWS, COLS);
	//DisplayBoard(mine, ROWS, COLS);
	//排查雷
	FindMine(mine, show, ROW, COL);
}

void manu()
{
	printf("********************\n");
	printf("***   _1.play_   ***\n");
	printf("***   _0.exit_   ***\n");
	printf("********************\n");
}

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		manu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出\n");
			break;
		default:
			printf("选择错误,请重新选择:>\n");
			break;
		}
	} while (input);
	return 0;
}

函数实现文件game.c

#include "game.h"

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

//打印棋盘
void DisplayBoard(char Board[ROWS][COLS], int rows, int cols)
{
	int i = 0;
	printf("\n");
	for (i = 0; i <= rows - 2; i++)
		printf("%d ", i);
	printf("\n");
	for (i = 1; i < rows-1; i++)
	{
		int j = 0;
		printf("%d ", i);
		for (j = 1; j < cols-1; j++)
		{
			printf("%c ", Board[i][j]);
		}
		printf("\n");
	}
}

//布置雷
void SetMine(char Board[ROWS][COLS], int rows, int cols)
{
	int n = EASY_COUNT;//雷的数量
	while (n)
	{
		int x = rand() % 9 + 1;
		int y = rand() % 9 + 1;
		if (Board[x][y] != '1')
		{
			Board[x][y] = '1';
			n--;
		}
	}
}

//返回周围雷的数量
int GetMineCount(char Board[ROWS][COLS],int x,int y)
{
	return (Board[x - 1][y - 1] + Board[x - 1][y] + Board[x - 1][y + 1] +
		Board[x][y - 1] + Board[x][y + 1] +
		Board[x + 1][y - 1] + Board[x + 1][y] + Board[x + 1][y + 1] - 8 * '0');
}

//如果排查位置没有雷,且周围位置也没有雷,返回周围一片
void SearchAround(char MineBoard[ROWS][COLS], char ShowBoard[ROWS][COLS], int x, int y)
{
	if (ShowBoard[x][y] == '*')//判断当前位置是否已经赋值
	{
		int arounds = GetMineCount(MineBoard, x, y);
		if (arounds != 0)
		{
			ShowBoard[x][y] = arounds + '0';
		}
		else
		{
			if ((x >= 1 && x <= ROW) && (y >= 1 && y <= COL))
			{
				ShowBoard[x][y] = arounds + ' ';
				SearchAround(MineBoard, ShowBoard, x - 1, y - 1);
				SearchAround(MineBoard, ShowBoard, x - 1, y);
				SearchAround(MineBoard, ShowBoard, x - 1, y + 1);
				SearchAround(MineBoard, ShowBoard, x, y - 1);
				SearchAround(MineBoard, ShowBoard, x, y + 1);
				SearchAround(MineBoard, ShowBoard, x + 1, y - 1);
				SearchAround(MineBoard, ShowBoard, x + 1, y);
				SearchAround(MineBoard, ShowBoard, x + 1, y + 1);
			}
		}
	}
}

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

//排查雷
void FindMine(char MineBoard[ROWS][COLS], char ShowBoard[ROWS][COLS], int row, int col)
{
	int count = row * col;//未知坐标的数量
	while (count > EASY_COUNT)
	{
		int x, y;
		printf("请输入你想排查的坐标:>");
		scanf("%d%d", &x, &y);
		if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
		{
			if (MineBoard[x][y] == '1')
			{
				printf("很遗憾你踩到雷了,游戏结束\n");
				DisplayBoard(MineBoard, ROWS, COLS);
				break;
			}
			else
			{
				//统计周围雷的数量
				SearchAround(MineBoard, ShowBoard, x, y);
				DisplayBoard(ShowBoard, ROWS, COLS);
				count = JudgeCount(ShowBoard, row, col);
			}
		}
		else
		{
			printf("坐标非法,请重新输入!\n");
		}
	}
	if (count == EASY_COUNT)
	{
		printf("恭喜你扫雷成功:>\n");
		DisplayBoard(MineBoard, ROWS, COLS);
	}
}

如果有哪儿出现错误欢迎指正,私聊我。

你可能感兴趣的:(c语言学习,c语言,开发语言)