目录
文章目录
前言
一、建立文件
二、实现扫雷游戏的步骤
1.扫雷游戏的呈现
2.明确任务
3.代码实现
1.建立棋盘:
2. 放置雷
3.排雷,显示雷数
4.判断是否扫雷成功
四、完整代码
总结
在写完三子棋游戏代码后,我们又来学习扫雷游戏的代码。
依旧是建立三个文件。扫雷.h放各种函数声明,宏定义的。test.c文件放的是扫雷游戏的测试逻辑,呈现给玩家看的。扫雷.c放的是实现游戏的函数和各种程序。
这是我在浏览器网页上找的一个在线可玩的扫雷游戏。我们今天就是来做一个简陋的扫雷。
从这张图上,我们可以看出来,我们首先要做的就是建立棋盘。我们且叫它棋盘吧。
然后,我们随便点一个区域,会出现这种情况。
从中,我们可以看出来,我们要实现的东西,还有随机把雷放在棋盘中,然后判断我们所选的地方有没有雷,四周有没有雷,然后要显示周围雷数。最后,我们还要判断是否排雷成功。
1.建立棋盘
2.放雷
3.排雷,显示雷数(这边还会用到递归的知识)
4.判断是否成功扫雷
首先,我们要设置两个数组,一个数组用来存放放置好的雷的信息,另一个数组用来存放排查处理的雷的信息,设置两个数组就可以避免干扰。
其次,我们要判断周围八个坐标有没有雷,要显示周围雷的个数。那我们是不是要考虑一下边缘部分呢?边缘部分要判断的话,就要越界访问了,不就出错了吗?
像这样,我们建立一个多设两行,多设两列,然后在里面不放东西,就好了,也省去了判断是否在边缘位置,给我们省下了麻烦。
还是先在扫雷.h中先宏定义,行列数,实际行列数,放雷个数。
#define Row 9
#define Col 9
#define Rows Row + 2
#define Cols Col + 2
#define minecount 10
在game.c中,我们初定义二维数组,建立棋盘:
void Init_board(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 line_board(int row, int col)
{
int i = 0;
for (i = 0; i <= row; i++)
{
printf("----");
}
printf("\n");
}
void Print_board(char board[ROWS][COLS], int row, int col)
{
int n = 0;
for (n = 1; n <= col; n++)
{
printf(" %-3d", n);
}
printf("\n");
line_board(row, col);
int i = 0;
for (i = 1; i <= row; i++)
{
printf("%-3d|", i);
int j = 0;
for (j = 1; j <= col; j++)
{
printf(" %c |", board[i][j]);
}
printf("\n");
line_board(row, col);
}
我们要避免在一个格子里,放两个雷。
void set_mine(char mine[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int count = minecount;
while (count)
{
//利用rand函数随机生成在棋盘上的雷
x = rand() % row + 1;
y = rand() % col + 1;
//if语句避免重复放雷
//1:有雷;0:无雷
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
①获取输入坐标周围雷的数量
static int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
//这就是为什么我们把真实棋盘初始化为0,有雷设1的原因了。这样子程序更简单
return mine[x - 1][y] +
mine[x - 1][y - 1] +
mine[x][y - 1] +
mine[x + 1][y - 1] +
mine[x + 1][y] +
mine[x + 1][y + 1] +
mine[x][y + 1] +
mine[x - 1][y + 1] - 8 * '0';
}
② 递归展开一片区域
要满足以下三个条件,可以递归去看周围的8个坐标:
1.该坐标不是雷
2.该坐标没有被排查过
3.该坐标周围没雷
void Unfold(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* win)
{
if (x >= 1 && x <= row && y >= 1 && y <= col)//在棋盘内
{
if (show[x][y] == ' ' || show[x][y] != '*')//该坐标已被排查过,或不是雷
return;
else if (get_mine_count(mine, x, y) != 0)//该坐标周围没有雷
{
show[x][y] = get_mine_count(mine, x, y) + '0';
(*win)++;
return;
}
else
{
show[x][y] = ' ';
(*win)++;
Unfold(mine, show, row, col, x - 1, y, win);
Unfold(mine, show, row, col, x + 1, y, win);
Unfold(mine, show, col, col, x, y - 1, win);
Unfold(mine, show, row, col, x, y + 1, win);
}
}
return;
}
void Find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win < (row * col) - minecount)
//row*col的结果是一整个可见棋盘的大小个数。minecount指的是埋雷个数。
//当win等于(row*col)-minecount的值的时候,全部没有雷的区域全部排完。
{
printf("请输入排查雷的坐标:>\n");
scanf_s("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)//在棋盘内
{
if (mine[x][y] == '1')
{
printf("很遗憾,您被炸死了\n");
//被炸死后打印埋雷的棋盘
Print_board(mine, ROW, COL);
break;
}
if (show[x][y] != '*')
{
printf("该坐标已经被排查过了,重新输入\n");
continue;
}
else
{
Unfold(mine, show, row, col, x, y, &win);
Print_board(show, ROW, COL);
}
}
else
{
printf("坐标输入错误 请重新输入\n");
}
if (win == (row * col) - minecount)
{
printf("恭喜你,扫雷成功!\n");
}
}
}
#include
#include
#include
#define ROW 9//显示行
#define COL 9//显示列
#define ROWS ROW+2//实际行
#define COLS COL+2//实际列
#define minecount 10//放雷个数
//初始化棋盘
void Init_board(char board[ROWS][COLS], int rows, int cols, char set);
//打印棋盘
void Print_board(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 Unfold(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* win);
扫雷.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "扫雷.h"
//初始化棋盘
void Init_board(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 line_board(int row, int col)
{
int i = 0;
for (i = 0; i <= row; i++)
{
printf("----");
}
printf("\n");
}
//打印棋盘
void Print_board(char board[ROWS][COLS], int row, int col)
{
int n = 0;
for (n = 1; n <= col; n++)
{
printf(" %-3d", n);
}
printf("\n");
line_board(row, col);
int i = 0;
for (i = 1; i <= row; i++)
{
printf("%-3d|", i);
int j = 0;
for (j = 1; j <= col; j++)
{
printf(" %c |", board[i][j]);
}
printf("\n");
line_board(row, col);
}
}
//放置雷
void set_mine(char mine[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int count = minecount;
while (count)
{
//利用rand函数随机生成在棋盘上的雷
x = rand() % row + 1;
y = rand() % col + 1;
//if语句避免重复放雷
//1:有雷;0:无雷
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
//获取输入坐标周围雷的数量
static int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
//这就是为什么我们把真实棋盘初始化为0,有雷设1的原因了。这样子程序更简单
return mine[x - 1][y] +
mine[x - 1][y - 1] +
mine[x][y - 1] +
mine[x + 1][y - 1] +
mine[x + 1][y] +
mine[x + 1][y + 1] +
mine[x][y + 1] +
mine[x - 1][y + 1] - 8 * '0';
}
//递归展开一片区域
void Unfold(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* win)
{
if (x >= 1 && x <= row && y >= 1 && y <= col)//在棋盘内
{
if (show[x][y] == ' ' || show[x][y] != '*')
return;
else if (get_mine_count(mine, x, y) != 0)
{
show[x][y] = get_mine_count(mine, x, y) + '0';
(*win)++;
return;
}
else
{
show[x][y] = ' ';
(*win)++;
Unfold(mine, show, row, col, x - 1, y, win);
Unfold(mine, show, row, col, x + 1, y, win);
Unfold(mine, show, col, col, x, y - 1, win);
Unfold(mine, show, row, col, x, y + 1, win);
}
}
return;
}
//查找雷
void Find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win < (row * col) - minecount)
//row*col的结果是一整个可见棋盘的大小个数。minecount指的是埋雷个数。
//当win等于(row*col)-minecount的值的时候,全部没有雷的区域全部排完。
{
printf("请输入排查雷的坐标:>\n");
scanf_s("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("很遗憾,您被炸死了\n");
//被炸死后打印埋雷的棋盘
Print_board(mine, ROW, COL);
break;
}
if (show[x][y] != '*')
{
printf("该坐标已经被排查过了,重新输入\n");
continue;
}
else
{
Unfold(mine, show, row, col, x, y, &win);
Print_board(show, ROW, COL);
}
}
else
{
printf("坐标输入错误 请重新输入\n");
}
if (win == (row * col) - minecount)
{
printf("恭喜你,扫雷成功!\n");
}
}
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "扫雷.h"
//测试扫雷逻辑
void menu()
{
printf("请玩家输入:(1:开始游戏;0:结束游戏\n");
printf("*********** 1 .play ************\n");
printf("*********** 0 .exit ************\n");
}
//扫雷游戏的实现
void game()
{
//创建存放棋盘变量
char mine[ROWS][COLS] = { 0 };//真正的棋盘
char show[ROWS][COLS] = { 0 };//显示给玩家看的棋盘
//初始化棋盘
//无雷为0,有雷为1.方便后面计算周围雷的个数
Init_board(mine, ROWS, COLS, '0');//初始化真正棋盘
Init_board(show, ROWS, COLS, '*');//初始化玩家看的棋盘
//打印棋盘
Print_board(show, ROW, COL);
//存放雷
set_mine(mine, ROW, COL);
//Print_board(mine, ROW, COL);//我们测试的时候可以让他们把真正的棋盘打印出来,就可以心知肚明地做测试了。
//排查雷
Find_mine(mine, show, ROW, COL);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:>\n");
scanf_s("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误 请重新输入\n");
break;
}
} while (input);
return 0;
}