扫雷的过程中,布置的雷和排查出的雷的信息都需要存储,所以我们需一定的数据结构来存储这些信息。
因为我们需要在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'
,否者会出现以下状况
因为这加起来的和是'?'
的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);
}
}
}
}
这里我们需要注意两个易错点:
1.这里必须判断一下当前位置是否赋值,否则将在两个相邻坐标间来回进入此函数,造成死循环;
2.当第一次进入SearchAround()
函数时我们知道x
和y
的取值都只是在1~9之间。那么不断递归后x
和y
将越过此范围,造成错误。
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);
}
}
如果有哪儿出现错误欢迎指正,私聊我。