目录
一、扫雷游戏介绍
二、实现游戏的前期工作
1. 游戏的原理&逻辑
2. 初始化雷区
三、代码实现游戏
1. 实现 test.c 文件
2. 实现 Mine_Sweeper.c 文件
2.1 雷区初始化函数
2.2 埋地雷函数
2.3 展示雷区函数
2.4 排查地雷函数
2.4.1 排查地雷子函数
2.4.2 标记地雷子函数
2.4.3 取消标记子函数
3. 完整代码
四、游戏展示
一、扫雷游戏介绍
《扫雷》是一款大众类的益智小游戏,于1992年发行。游戏目标是在最短的时间内根据点击格子出现的数字找出所有非地雷的格子,同时避免踩到地雷,踩到一个地雷全盘皆输。
玩家需要在雷区中,将所有地雷一一排查出来,同时扫雷游戏提供了插旗标记地雷和取消插旗标记地雷的功能。
本篇博客将会与大家一起学习,如何运用我们所学的C语言知识,来实现这一经典而富有娱乐性的小游戏,冲🐛🐛🐛!!!
※ ( 本篇博客实现的是 9×9 的扫雷游戏,在雷区放置 10 个地雷 )
※ ( 本篇博客采用多文件的方式来实现扫雷游戏 )
test.c - - - - 测试游戏的逻辑
MineSweeper.c - - - - 游戏代码的实现
MineSweeper.h - - - - 游戏代码的声明 ( 函数声明,符号定义 )
二、实现游戏的前期工作
1. 游戏的原理&逻辑
让我们把目光聚焦到上面的GIF图,首先映入眼帘的一定是 9×9 的雷区,这 81 个被遮盖的格子,等待被我们翻开。
当我们随机的点击其中的格子时,会出现以下三种情况:
① 当翻开的格子是地雷时,玩家被炸“死”,游戏结束;
② 当翻开的格子不是地雷时,该格子会显示周围的 8 个格子存在的地雷的个数;
③ 当翻开的格子不是地雷,且周围 8 个格子不存在地雷时,雷区会一下子翻开一片区域;
如此反复,直到玩家把雷区所有的地雷都排查出来,排雷成功,玩家获胜,游戏结束。
游戏的原理和逻辑捋的差不多了,一个新的问题也随之被抛出。我们如何实现这样一个可以将每个格子都一一翻转的雷区呢?
首先我们会将 9×9 的雷区与二维数组联想起来,能不能用二维数组实现那?一个二维数组能实现像游戏中那样每个格子都能翻转的雷区吗?细想好像一个二维数组实现起来比较困难。
一个二维数组不行,那两个呢?
答案是肯定的,本篇博客我们将使用两个二维数组来实现游戏里的雷区。
一个二维数组来设置雷区,即存放地雷,另外一个二维数组展示给玩家看,即实现翻转格子功能,达到与游戏相同的效果。
2. 初始化雷区
| 原理&逻辑 |
首先我们要先创建两个二维数组,一个用来设置地雷,另一个用来展示给玩家看。
对于设置地雷的二维数组,我们规定:用字符 ‘0’ 和 ‘1’ 来填埋地雷,字符 ‘0’ 代表无地雷,相对应的,字符 ‘1’ 代表有地雷。
对于展示于玩家的二维数组,我们规定:在格子未被翻转前,用字符 ‘*’ 来对其进行遮盖,而格子翻转以后,该格子则显示周围的 8 个格子存在的地雷的个数。
讲到这里,又引出了一个新的问题。二维数组设置成几行几列合适?
有的小兄弟可能会说,这篇博客开头不是说要实现 9×9 的扫雷游戏吗,那就设置成 9 行 9 列!
但 9×9 的二维数组真的万无一失吗?这就是我们接下来要和各位一起探讨的问题。
我们上面规定了,若翻转的格子不是地雷,则显示该格子周围 8 个格子存在地雷的个数,这一操作对雷区中间区域的格子可能不会有问题,但对雷区边界的格子执行这一操作可能会导致数组的越界访问,有图有真相,请看下面的图例。
| 图例 |
由图例可以得出结论,实现 9×9 的扫雷游戏,创建一个 9 行 9 列的二维数组并不合适。
既然对 9 行 9 列的二维数组的边界元素进行操作时,会导致数组越界访问,那我们干脆就直接将二维数组扩大一圈,将那些会导致越界访问的范围包括在数组内,从源头上解决问题,这是一个非常巧妙的办法!
| 图例 |
弄明白了两个二维数组创建的逻辑后,接下来就是用代码一步一步的实现我们的游戏了。
三、代码实现游戏
1. 实现 test.c 文件
当玩家进入游戏后,程序会向玩家展示一个菜单,供玩家选择程序 ( 1.玩游戏 0.退出游戏 ),玩家做出选择后,可进入相应的程序。当一把游戏结束后,玩家不过瘾,还能继续选择玩游戏,直到玩家不想玩时,可选择退出。
① 对于 “玩家做出选择后,可进入相应的程序” 这句话,我们会情不自禁的联想 switch 语句。
② 对于 “当一把游戏结束后,玩家不过瘾,还能继续选择玩游戏,直到玩家不想玩时,可选择退出” 这句话,我们联想到了 do while 语句,玩家进入程序就先展示菜单,待玩家做出选择后再判断是继续程序,还是结束程序。
| test.c 代码 |
#include"Mine_Sweeper.h"
void MENU()
{
printf("******************************\n");
printf("********* 1.PLAY *********\n");
printf("********* 0.EXIT *********\n");
printf("******************************\n");
}
void Game()
{
char mine[ROWS][COLS] = { 0 }; // mine 数组用于埋地雷
char show[ROWS][COLS] = { 0 }; // show 数组用于展示于玩家
InitBoard(mine, ROWS, COLS, '0');
SetMine(mine, ROW, COL);
InitBoard(show, ROWS, COLS, '*');
DisplayBoard(show, ROW, COL);
CheckMine(mine, show, ROW, COL);
}
int main()
{
srand((unsigned int)time(NULL)); //这一语句是为了埋地雷,后文会讲解
int input = 0;
do
{
MENU();
printf("\n请选择程序>>");
scanf("%d", &input);
system("cls"); //这一语句是为了优化游戏界面
switch (input)
{
case 1:
Game();
break;
case 0:
printf("\n<退出游戏>\n\n");
break;
default:
printf("\n<选择错误,请重新选择>\n\n");
break;
}
} while (input);
return 0;
}
眼神比较犀利的小兄弟可能会发现,上面的代码段中 Game 函数里存在 “ROW,COL,ROWS,COLS”,那这些代表什么意义呢?再仔细观察,我们可以发现 test.c 文件只包含了一个头文件 "Mine_Sweeper.h",而 “ROWS,COLS,ROW,COL” 就是在头文件中用 #define 定义的常量名。( ROW 代表常量 9,COL 代表常量 9,ROWS 代表 ROW + 2,即常量 11,COLS 代表 COL + 2,即常量 11 )
| MineSweeper.h 代码 |
#pragma once
#define ROW 9 //二维数组显示行数
#define COL 9 //二维数组显示列数
#define ROWS ROW+2 //二维数组实际行数
#define COLS COL+2 //二维数组实际列数
#define MINE 10 //地雷个数
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set); //初始化二维数组的函数声明
void SetMine(char board[ROWS][COLS], int row, int col); //埋地雷的函数声明
void DisplayBoard(char board[ROWS][COLS], int row, int col); //展示雷区的函数声明
void CheckMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col); //玩家排查地雷的函数声明
2. 实现 Mine_Sweeper.c 文件
2.1 雷区初始化函数
| 代码 |
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
本篇博客在讲解初始化雷区的原理和逻辑处已经规定了,用 ‘0’ 和 ‘1’ 对二维数组 mine 进行初始化,用 ‘*’ 对二维数组 show 进行初始化。
在调用此函数的同时将 数组名 数组行数 数组列数 初始化字符 作为函数参数进行传参。该函数对目标数组进行遍历,且将每个元素赋值,从而达到雷区初始化的效果。
2.2 埋地雷函数
| 代码 |
void SetMine(char board[ROWS][COLS], int row, int col)
{
int count = MINE; // count 记录地雷的个数
while (count) //循环地雷数量次,则埋下地雷数量个地雷
{
int x = rand() % row + 1; //获取随机数,且使随机数的范围为 1~9,作为行坐标
int y = rand() % col + 1; //获取随机数,且使随机数的范围为 1~9,作为列坐标
//判断坐标是否已经被埋了地雷
if (board[x][y] == '0') //坐标元素为字符 '0' 则表示未被埋地雷
{
board[x][y] = '1'; //埋地雷
count--; //每循环一次,则埋下一个地雷,地雷数量减1
}
}
}
该代码需要获取随机数,以作为埋地雷的 行坐标 和 列坐标,获取随机数则需要调用库函数 rand 函数,注意,在调用 rand 函数前还要调用库函数 srand 函数,而程序中获取随机数只需要调用一次 srand 函数即可,所以我们把 srand 函数的调用置于 test.c 文件中的 主函数 的开头位置,我们使用时间戳作为 srand 函数的参数。
※ srand 函数和 rand 函数都需要程序包含头文件 <stdlib.h>
※ time 函数则需要程序包含 <time.h>
2.3 展示雷区函数
| 代码 |
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("\n----MineSweeper----\n"); //作用是使游戏界面更美观
for (j = 0; j <= col; j++) //打印每一列的坐标数字,便于玩家辨别
{
printf("%d ", j);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i); //打印每一行的坐标数字,便于玩家辨别
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("----MineSweeper----\n\n"); //作用是使游戏界面更美观
}
在向玩家展示雷区时,可以将雷区的行坐标与列坐标预先打印出来,给予玩家更好的游戏体验。
该函数通过对二维数组的遍历打印,向玩家展示雷区,值得注意的是,本篇博客实现的是 9×9 的扫雷游戏,也就是说玩家看到的是 9 行 9 列的雷区,所以我们使用代码遍历二维数组时,要注意行和列都应该从 1 开始打印 ( i = 1 j = 1 ),以打印到 ROW 和 COL 为截至条件 ( i <= ROW j <= COL )。
※ ( 该函数中 i <= row j <= col,并不是书写错误,row 和 col 是函数的形参名,分别接收调用函数时传来的 ROW 和 COL )
| 效果展示 |
2.4 排查地雷函数
做完以上准备工作后,从这里开始才是真正意义上的开始扫雷。
当玩家选择玩游戏时,程序会再次向玩家展示一个菜单,供玩家选择程序 ( 1.排查地雷 2.标记地雷 3.取消标记 ),我们分别用三个函数去实现这三个功能。
※ ( 该函数由多个子函数嵌套调用而成 )
| 代码 |
void menu()
{
printf("********************************\n");
printf("********* 1.CHECK *********\n");
printf("********* 2.MARK *********\n");
printf("********* 3.UNMARK *********\n");
printf("********************************\n");
}
void CheckMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int ret = 0; // ret 用来判断玩家是否踩到地雷
int win = 0; // win 用来判断玩家是否获胜
int input = 0;
do
{
menu(); //菜单函数
printf("\n请选择程序>>");
scanf("%d", &input);
switch (input)
{
case 1:
if (win < row * col - MINE) //讲解点
{
ret = Check(mine, show, row, col, &win); //要将 win 的地址一同作为参数进行传参
if (ret == 1) // 玩家踩到地雷,返回上一层函数
{
return;
}
}
if (win == row * col - MINE) //讲解点
{
system("cls");
printf("<恭喜你,排雷成功>\n");
DisplayBoard(mine, row, col); //向玩家展示雷区的地雷布局
return;
}
break;
case 2:
Mark(show, row, col); //标记地雷是展示给玩家看的,所以将 show 数组作为参数进行传参
break;
case 3:
UnMark(show, row, col); //取消标记地雷也是展示给玩家看的,所以也是将 show 数组作为参数进行传参
break;
default:
printf("\n<选择错误,请重新选择>\n\n");
break;
}
system("cls"); //作用是清楚屏幕上的内容,使游戏界面更美观
DisplayBoard(show, row, col);
} while (input);
}
| 讲解点 |
2.4.1 排查地雷子函数
| 代码 |
int Check(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int* win)
{
int x = 0;
int y = 0;
again:
printf("\n请输入要排查的坐标>>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col) //判断玩家输入的坐标是否合法
{
if (show[x][y] == '*' || show[x][y] == '!') //判断坐标是否被排查过,若 show 数组在该坐标的元素为 ‘*’ 则表示该坐标未被排查,若 show 数组在该坐标的元素为 ‘!’ 则表示该坐标仅被标记,但未被排查
{
if (mine[x][y] == '1') // mine 数组在该坐标的元素为 ‘1’ 则表示该坐标为地雷
{
system("cls"); //为了美化游戏界面,清除屏幕上的所有内容
printf("\n<很遗憾,你踩到雷了,游戏结束>\n");
DisplayBoard(mine, ROW, COL); //向玩家展示雷区的地雷布局,让玩家输的明明白白
return 1; //返回1,表示玩家踩到地雷了
}
else //玩家输入的坐标不是地雷
{
Unfold(mine, show, x, y, win); //展开一篇雷区函数,讲解点
DisplayBoard(show, row, col); //将数据反馈给玩家,显示该坐标周围的8个格子存在几个地雷
}
}
else //该坐标已被排查过
{
printf("\n<该坐标已排查,无需重复排查,请重新输入>\n");
goto again;
}
}
else //玩家输入坐标非法
{
printf("\n<坐标非法,请重新输入>\n");
goto again; //利用 goto 语句将程序的进程跳转至第5行的 again 处开始执行,让玩家重新输入一个合法的坐标
}
return 0; //若 int 函数正常运行结束,注意不要忘记 return 0;否则默认返回1,若返回1,程序将误以为玩家踩到地雷了
}
| 讲解点 |
在扫雷游戏中,当我们点击的方格不是地雷,且周围一片区域都没有地雷时,会直接展开一片雷区,具体效果如下图
如何用代码实现这一功能呢?
还是按照惯例,先弄明白其中的原理和逻辑。
| 原理&逻辑 |
扫雷游戏中,当玩家翻转一个方格时,若该方格不是地雷则会显示该方格周围 8 个方格存在的地雷个数。如果该方格周围 8 个坐标都不存在地雷时会将这 9 个方格都展开,以此类推直到遇到一个方格的周围 8 个方格存在地雷时停止展开,两种情况如下图所
为实现这一功能,则需要我们遍历玩家输入的坐标的周围 8 个坐标,统计该坐标周围所存在的地雷个数。
| 代码 |
int count_mine(char board[ROWS][COLS], int x, int y)
{
return board[x - 1][y]
+ board[x - 1][y + 1]
+ board[x][y + 1]
+ board[x + 1][y + 1]
+ board[x + 1][y]
+ board[x + 1][y - 1]
+ board[x][y - 1]
+ board[x - 1][y - 1]
- 8 * '0'; //注意最后的 - 8 * '0' 不要忘记!!
}
展开一片雷区,是一个重复的过程,这片区域没有地雷,则继续展开下一片区域,直至遇到一片区域周围存在地雷时,停止展开。
每次展开都要判断是否遇到地雷,每次的判断都是类似的,所以这一功能可以用我们所学的函数递归实现。
接下来我们继续用图例的形式捋清展开一片雷区的逻辑。
| 图例 |
| 代码 |
void Unfold(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int* win)
{
if (x<1 || x>ROW || y<1 || y>COL) //当坐标非法时,停止递归,返回上一层函数
{
return;
}
if (show[x][y] != '*') //当 show 数组在该坐标上的元素不为 '*' 时,表示该坐标已被排查过,则停止递归,返回上一层函数
{
return;
}
int count = count_mine(mine, x, y); //调用 count_mine 函数计算坐标周围的8个坐标有几个地雷
if (count > 0) //当该坐标周围的地雷数量大于0时,不递归,不展开一片雷区
{
(*win)++; //每当玩家输入的坐标不是地雷时,win++
show[x][y] = count + '0'; //由于 show 数组为 char 类型,所以 count加上 '0' 使该坐标周围的地雷个数变为字符 '地雷个数',这样才能与 show 数组元素类型对应起来
return;
}
else if (count == 0) //当该坐标周围的地雷数量等于0时,递归函数,对周围的8个坐标依次调用 Unfold 函数
{
(*win)++; //每当玩家输入的坐标不是地雷时,win++
show[x][y] = ' '; //将不是地雷的坐标赋值为'空格',给玩家营造出该区域没有地雷的视觉效果
Unfold(mine, show, x - 1, y, win);
Unfold(mine, show, x - 1, y + 1, win);
Unfold(mine, show, x, y + 1, win);
Unfold(mine, show, x + 1, y + 1, win);
Unfold(mine, show, x + 1, y, win);
Unfold(mine, show, x + 1, y - 1, win);
Unfold(mine, show, x, y - 1, win);
Unfold(mine, show, x - 1, y - 1, win);
}
}
2.4.2 标记地雷子函数
在扫雷游戏中,如果我们已经推断出一个坐标必定是地雷时,我们可以通过插旗子的方式去标记地雷,如下图。
接下来我们就要学习如何用代码实现这一标记地雷的功能。
| 代码 |
void Mark(char board[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("\n请输入要标记的坐标>>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col) //判断玩家输入坐标是否合法
{
if (board[x][y] == '!') //若坐标元素为 '!' 则表示该坐标已被标记过
{
printf("\n<该坐标已标记,无需重复标记,请重新输入>\n");
}
else //该坐标未被标记
{
board[x][y] = '!'; //将 '!' 赋值给该坐标元素
DisplayBoard(board, row, col); //将标记地雷后的雷区展示于玩家
break;
}
}
else
{
printf("\n<坐标非法,请重新输入>\n\n");
}
}
}
2.4.3 取消标记子函数
如果玩家因判断失误而错误的标记了地雷,则此时我们还需要实现一个取消标记的功能,这样才不至于让玩家只能标记地雷而不能取消标记。
| 代码 |
void UnMark(char board[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("\n请输入要取消标记的坐标>>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col) //判断玩家输入坐标是否合法
{
if (board[x][y] != '!') //该坐标未被标记
{
int i = 0;
int j = 0;
int flag = 1; // flag 用于判断雷区内是否有标记,若雷区内没有标记则 flag = 1
for (i = 1; i <= row; i++) //对 show 数组遍历
{
for (j = 1; j <= col; j++)
{
if (board[i][j] == '!') //雷区内存在标记
{
flag = 0; //雷区内存在标记,则将 flag 赋值为0
goto out; //利用 goto 语句将程序的进程跳转至第27行的 out 处开始执行
}
}
}
out:
if (flag) // flag == 1,则表示雷区内不存在标记;flag == 0,则表示雷区内存在标记
{
printf("\n<雷区尚未做标记,无需取消>\n\n");
return;
}
else //雷区内存在标记,但玩家输入的坐标未被标记
{
printf("\n<该坐标未标记,无需取消,请重新输入>\n");
}
}
else //该坐标已被标记
{
board[x][y] = '*'; //用字符 '*' 来覆盖 '!',从而达到取消标记的效果
DisplayBoard(board, row, col); //将取消标记后的雷区展示于玩家
break;
}
}
else
{
printf("\n<坐标非法,请重新输入>\n\n");
}
}
}
3. 完整代码
| 代码 |
| 头文件 >> Mine_Sweeper.h |
#pragma once
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define MINE 10
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
void SetMine(char board[ROWS][COLS], int row, int col);
void DisplayBoard(char board[ROWS][COLS], int row, int col);
void CheckMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
| 源文件 >> Mine_Sweeper.c |
#include"Mine_Sweeper.h"
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
void SetMine(char board[ROWS][COLS], int row, int col)
{
int count = MINE;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("\n----MineSweeper----\n");
for (j = 0; j <= col; j++)
{
printf("%d ", j);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("----MineSweeper----\n\n");
}
int count_mine(char board[ROWS][COLS], int x, int y)
{
return board[x - 1][y] + board[x - 1][y + 1] + board[x][y + 1] + board[x + 1][y + 1] + board[x + 1][y] + board[x + 1][y - 1] + board[x][y - 1] + board[x - 1][y - 1] - 8 * '0';
}
void menu()
{
printf("********************************\n");
printf("********* 1.CHECK *********\n");
printf("********* 2.MARK *********\n");
printf("********* 3.UNMARK *********\n");
printf("********************************\n");
}
void Unfold(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int* win)
{
if (x<1 || x>ROW || y<1 || y>COL)
{
return;
}
if (show[x][y] != '*')
{
return;
}
int count = count_mine(mine, x, y);
if (count > 0)
{
(*win)++;
show[x][y] = count + '0';
return;
}
else if (count == 0)
{
(*win)++;
show[x][y] = ' ';
Unfold(mine, show, x - 1, y, win);
Unfold(mine, show, x - 1, y + 1, win);
Unfold(mine, show, x, y + 1, win);
Unfold(mine, show, x + 1, y + 1, win);
Unfold(mine, show, x + 1, y, win);
Unfold(mine, show, x + 1, y - 1, win);
Unfold(mine, show, x, y - 1, win);
Unfold(mine, show, x - 1, y - 1, win);
}
}
int Check(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int* win)
{
int x = 0;
int y = 0;
again:
printf("\n请输入要排查的坐标>>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (show[x][y] == '*' || show[x][y] == '!')
{
if (mine[x][y] == '1')
{
system("cls");
printf("\n<很遗憾,你踩到雷了,游戏结束>\n");
DisplayBoard(mine, ROW, COL);
return 1;
}
else
{
Unfold(mine, show, x, y, win);
DisplayBoard(show, row, col);
}
}
else
{
printf("\n<该坐标已排查,无需重复排查,请重新输入>\n");
goto again;
}
}
else
{
printf("\n<坐标非法,请重新输入>\n");
goto again;
}
return 0;
}
void Mark(char board[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("\n请输入要标记的坐标>>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (board[x][y] == '!')
{
printf("\n<该坐标已标记,无需重复标记,请重新输入>\n");
}
else
{
board[x][y] = '!';
DisplayBoard(board, row, col);
break;
}
}
else
{
printf("\n<坐标非法,请重新输入>\n\n");
}
}
}
void UnMark(char board[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("\n请输入要取消标记的坐标>>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (board[x][y] != '!')
{
int i = 0;
int j = 0;
int flag = 1;
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++)
{
if (board[i][j] == '!')
{
flag = 0;
goto out;
}
}
}
out:
if (flag)
{
printf("\n<雷区尚未做标记,无需取消>\n\n");
return;
}
else
{
printf("\n<该坐标未标记,无需取消,请重新输入>\n");
}
}
else
{
board[x][y] = '*';
DisplayBoard(board, row, col);
break;
}
}
else
{
printf("\n<坐标非法,请重新输入>\n\n");
}
}
}
void CheckMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int ret = 0;
int win = 0;
int input = 0;
do
{
menu();
printf("\n请选择程序>>");
scanf("%d", &input);
switch (input)
{
case 1:
if (win < row * col - MINE)
{
ret = Check(mine, show, row, col, &win);
if (ret == 1)
{
return;
}
}
if (win == row * col - MINE)
{
system("cls");
printf("<恭喜你,排雷成功>\n");
DisplayBoard(mine, row, col);
return;
}
break;
case 2:
Mark(show, row, col);
break;
case 3:
UnMark(show, row, col);
break;
default:
printf("\n<选择错误,请重新选择>\n\n");
break;
}
system("cls");
DisplayBoard(show, row, col);
} while (input);
}
| 源文件 >> test.c |
#include"Mine_Sweeper.h"
void MENU()
{
printf("******************************\n");
printf("********* 1.PLAY *********\n");
printf("********* 0.EXIT *********\n");
printf("******************************\n");
}
void Game()
{
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
InitBoard(mine, ROWS, COLS, '0');
SetMine(mine, ROW, COL);
InitBoard(show, ROWS, COLS, '*');
DisplayBoard(show, ROW, COL);
CheckMine(mine, show, ROW, COL);
}
int main()
{
srand((unsigned int)time(NULL));
int input = 0;
do
{
MENU();
printf("\n请选择程序>>");
scanf("%d", &input);
system("cls");
switch (input)
{
case 1:
Game();
break;
case 0:
printf("\n<退出游戏>\n\n");
break;
default:
printf("\n<选择错误,请重新选择>\n\n");
break;
}
} while (input);
return 0;
}
四、游戏展示
扫雷游戏展示
扫雷游戏展示
到这里本篇博客就接近尾声了,希望看完本篇博客对你有所帮助,期待下次与你相遇。
< 你的关注,点赞,评论,收藏都是对我创作最大的鼓励 > 文章来源:https://www.toymoban.com/news/detail-607418.html
( 若本篇博客存在错误,望指出,感谢! ) 文章来源地址https://www.toymoban.com/news/detail-607418.html
到了这里,关于扫雷游戏【敢看完就敢教会你】------- C语言的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!