目录
🌲了解扫雷游戏的作用原理并梳理思路
🌲扫雷游戏前期部分完善
🌷文件的创建
🌷创建菜单,完善主函数
🌳代码呈现:
🌲扫雷游戏主题内容
🌴第一步初始化棋盘
🍃第二步打印棋盘
🍂第三步设置雷的位置
🍁第四步排查雷的位置
🌿代码呈现:
🍀扫雷步骤完善
🌱标记雷的位置
🍀连续拓展功能
🌸大家好,今天要给大家带来的博客主题是——扫雷游戏。相信大家对于扫雷游戏一定都不陌生,但是对于C语言的新手们对其可以说是跃跃欲试但又无从下手,那么我们今天就带领大家亲自实现一下扫雷游戏吧!(本篇博客对于逻辑思维等方面的要求相比与之前的简单游戏设计略高,请大家耐心观看)
🌲了解扫雷游戏的作用原理并梳理思路
🌸老规矩我们还是先借鉴一下大佬们写好的扫雷的模板,来观察一下,我们想要设计的扫雷究竟是一个什么样的游戏。(明确游戏细节)
🌸就像我们在上图中看到的那样,最基础版本的扫雷游戏由 9 * 9 规格大小的方格为棋盘,在 9 * 9 大小的棋盘中随机布置着数量确定的雷。我们点击任意一个方块,如果是雷,扫雷失败游戏结束。如果不是雷则显示周围雷的数量便于玩家进一步判断雷的位置。
🌸对于游戏棋盘的打印我们可能并不陌生,之前我们在博客中说到过的三子棋游戏的实现就建立了一个棋盘。这个能不能和三子棋的棋盘一样?也是用一个二维数组吗?
🌸没错,对于扫雷游戏棋盘的打印也确实和我们想的一样需要采用一个二维数组。那么接下来我们就来进一步对于游戏设计思路进行进一步构建。
🌸先抛开期盼打印不说我们想怎样判断周围雷的个数呢?我们可能会想:先将棋盘上的所有元素全部初始化为0,然后将布置的雷的位置改变为1,然后对周围8个坐标进行一次求和得到中间位置的坐标类的个数。道理是没错,但是你有没有发现,假如四周只有一个雷的话,你计算求得的1到底是原先的雷还是你统计的雷的个数呢?而这给怎样区分呢?
🌸既然此路不通,那么我们就先来换一条路继续摸索前进,对于刚开始还没有进行扫雷的时候棋盘上的数据都是隐藏起来的,都会展示成一个特殊符号就比如方块,星号之类的。那么我们是不是应该使用一个字符型数组去设计并初始化我们的棋盘呢?既然我们上面想到的在布置雷的棋盘中进行统计雷的个数无法实现的话,我们是不是可以设计两个棋盘,一个棋盘布置雷,另一个棋盘统计并打印雷的情况呢?这确实是一个很好的思路。我们不妨先来试试看。
🌸但是接着来想,我们的游戏棋盘按照道理来说应该是 9 * 9 规格的,但是只设计一个 9 * 9 的二维数组的话我们统计雷的个数的时候应该怎么办呢?到达边界的时候会产生数组越界的吧?难道每一次到达边界都要进行数组越界判断吗?这未免也太过麻烦了吧?(统计雷的个数的时候是对周围八个方块进行判断,但是数组边界处下方或上左右方不属于数组的内容,如果强行进行访问的话会产生数组越界。)
🌸针对我们上面的问题,我们就需要按照一定的规格扩大我们扫雷游戏设计的棋盘了。想象一下,假如我们将 9 * 9 的游戏棋盘初始化为 11 * 11 的规格的时候那么只进行 9 * 9 规格的访问的时候是不是就不需要考虑数组越界的问题了呢?
🌸经过上面的思考之后,我们已经深度了解了扫雷游戏设计的精髓,那么接下来我们就可以正式着手于我们扫雷游戏的实现了。
🌲扫雷游戏前期部分完善
🌷文件的创建
🌸我们还是先创建三个文件,一个test.c文件,一个game.c文件,一个game.h文件。进行程序化的分装处理。
🌷创建菜单,完善主函数
🌸之后我们要做的第一步操作大致和三子棋的第一步操作一样,我们要先打印出一个游戏选择的菜单,可以供玩家进行多方面的选择,
🌸同时完善主函数中的游戏规则与游戏设置相关的说明操作,并完善主函数至可以重复进行选择。
🌳代码呈现:
//test.c
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
void test()
{
int input;
do
{
meun();
scanf("%d", &input);
switch (input)
{
case 1:
printf("开始游戏\n");
break;
case 2:
introduction();
break;
case 3:
gameset();
break;
case 0:
printf("退出游戏。");
break;
default:
printf("选择错误,请重新选择:");
break;
}
} while (input);
}
int main()
{
test();
return 0;
}
//game.h
#pragma once
#include<stdio.h>
//打印游戏菜单
void meun();
//打印游戏说明
void introduction();
//打印游戏设置说明
void gameset();
//game.c
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
void meun()
{
printf("**************************************\n");
printf("*** ***\n");
printf("*** 1.开始游戏 2.游戏规则 ***\n");
printf("*** ***\n");
printf("*** 3.游戏设置 0.退出游戏 ***\n");
printf("*** ***\n");
printf("**************************************\n");
}
void introduction()
{
printf("**************************************\n");
printf("*** 扫雷游戏规则: ***\n");
printf("*** 玩家需要在尽量短的时间内 ***\n");
printf("*** 找出隐藏起来的雷。 ***\n");
printf("*** 如果四周有雷,就会打印雷 ***\n");
printf("*** 的个数,以供玩家参考并通 ***\n");
printf("*** 关游戏。 ***\n");
printf("**************************************\n");
}
void gameset()
{
printf("**************************************\n");
printf("*** 扫雷游戏设置介绍: ***\n");
printf("*** 玩家需要亲自前往game.h头 ***\n");
printf("*** 文件中更改我们的宏定义, ***\n");
printf("*** 进而更改至自己想要的难度 ***\n");
printf("**************************************\n");
}
🌸经过我们上面的处理操作那么接下来的任务就是我们正式的扫雷游戏的设计了。
🌲扫雷游戏主题内容
🌸我们可以通过上面的执行效果可以看出当我们选择1.开始游戏的时候,只是简简单单的打印一个开始游戏是绝对不行的,所以我们应该将开始游戏的打印更换为一个相对独立的 game 函数,以便于我们想要开始游戏的时候可以进一步执行关于游戏开始的相关操作。
🌴第一步初始化棋盘
🌸之后我们就可以开始对我们最开始的思考进行实现了。首先我们先来创建一个 11 * 11 的字符型数组。为了使我们的程序具有更高的利用性,即可以实现不同规格的扫雷游戏。那么我们这里就需要在 game.h 中设置一个宏定义,使得我们想要更改游戏大小的时候只需要在宏定义中修改即可。
🌸上图中 COLS 和 ROWS 为我们规格大小为 11 * 11 的数组的行数和列数。COL 和 ROW 是我们之后将要在屏幕打印出来的游戏的界面规格大小。
🌸等我们创建好两个数组的时候我们要做的第一步就是我们数组内容的初始化。重新将思路拉回我们对于两个不同棋盘的思考,首先我们 set 数组的作用是在棋盘上设置我们的雷,而 show 数组的作用是在棋盘上展示我们已经周围雷的信息。我们依旧按照最开始的思路将 set 棋盘初始化为全0,之后在一个坐标上布置一个雷那么就改变相应位置上的 0 改为 1 。之后我们的 show 棋盘初始化为全 * ,便于我们后面输入一个坐标就显示一个坐标上的信息。
🌸但是我们会发现我们两个数组中初始化的内容并不完全相同,我们的预期结果是将 set 数组中的数据初始化为字符0,将 show 中的数据初始化为字符 * 。难道得写两次初始化函数吗?其实并不需要,仔细思考之后就会发现,假如我们将我们想要初始化的字符也作为一个参数传递给我们的初始化函数的话,那么我们完全可以将初始化函数的赋值步骤更改为 board [ i ] [ j ] = init(传入函数的字符),如下:
🌸经过这样一个简单的步骤之后我们就已经将我们的两个数组中的内容全部初始化完毕了,那么接下来为了验证我们的操作达到了预期的效果,我们可以在写一个打印函数,用来打印我们初始化完成的两个数组。
🍃第二步打印棋盘
🌸这一步我们就开始打印我们的棋盘,你可能会想打印棋盘有什么难的?不就是一个循环嵌套之后就搞定了的事吗?千万不要轻敌,还记得我们的数组是扩大过之后了的吗?那我们在屏幕上要打印出的棋盘是扩大之后的呢?还是原本扩大之前的呢?答案当然是扩大之前的。所以我们在建立这个函数的同时,在函数传参过程中需要特别进行注意:要传入的行数和列数是我们上面定义的 COL 和 ROW 并不是 COLS 和 ROWS 。在搞清楚这一点之后我们就可以开始着手我们的棋盘的打印了。
🌸但是需要特别注意的是:我们棋盘的打印不应该从0开始的了而是,从1开始到我们的 ROW 和 COL 结束这是因为我们第一行是为了放置数组越界进行的预处理,如果依旧从0开始进行操作的话,我们之前的努力都会前功尽弃了。
🌸当我们设计好打印函数之后的打印效果是这样的,但是我们要想让我们一下子找到相应的坐标可能有些困难,一个游戏要想要拥有一定的市场那么就一定要学会为玩家所考虑。那么我们需要在第一行和第一列的位置上加上一排提示行和列的数字提示以便于玩家可以更好的进行判断,想要在那个坐标的位置上进行排雷操作。并且,为了使我们的条例更加清晰,我们还可以适当加上分割符使得我们的游戏界面更加整洁。那么我们修改之后的打印函数的打印效果如下:
🌸但是实际上我们只需要打印出我们的 show 数组就可以了,因为我们 set 数组中包含着我们雷的位置,假如一下子就把答案公布出去,那多没有意思是吧?那么我们接着进行下一步操作。
🍂第三步设置雷的位置
🌸同样的道理我们创建一个 Setbomb 函数在 game.h 中声明,再到 game.c中实现。
🌸想要生成雷那么就得先生成一个随机的坐标(两个随机数),之后改写 set 中所对应的坐标上的元素。所以这一步的核心就在于——随机生成两个数字,确定雷的个数。就像我们所说的,我们到现在还不知道雷的个数呀?别忘了,雷是我们自己设置的。我们可以自己定义雷的个数呀!
🌸和我们定义函数和列数相同,对于我们雷的个数也可以在 game.h 头文件中定义一个宏,用来作为我们雷的个数,如果我们产生了一个坐标符合条件就进行改写并使 count - - 。直到我们的 count 的值为0时说明我们的雷已经布置完毕。这就是我们设置雷函数的关键思想。
🌸那么我们布置雷的函数也已经完成了。那么我们就可以开始我们下一步骤排查雷的操作了。
🍁第四步排查雷的位置
🌸这是我们扫雷游戏工程的最后一步操作,我们需要对上一步我们设置好的雷进行修改与排查。即我们输入一定的坐标可以打印出我们想要的信息情况。(如果是雷,那么游戏结束,如果不是雷的话,那么就打印周围雷的状况便于下一次继续进行排雷操作)
🌸同样的我们先创建一个 Findbomb 函数进行大致的判断。首先在想象中我们这个函数的功能是这样的,我们想要输入一个坐标,之后系统会自动对该处是否为雷进行判断。那么当我们正好选择雷时可以直接跳出循环,结束游戏,但是如果不是呢?我们就需要以这个坐标为中心进行排查周围八个坐标,找出雷的个数,并将雷的个数打印在当前的坐标位置上。那么应该怎么将周围的情况打印出来并显示在当前所选择的坐标位置上呢?
🌸这个时候我们就需要将我们的注意力重新转向我们最初设置的两个数组上了。我们设置雷的数组中刚开始我们将我们所有的坐标初始化为字符0,将布置好的雷设置为了字符1。拿我们上面的图片进行举例:在红色方括号中的最中心处应该统计得到数字为2。统计得到的数字我们可以对周围的八个坐标依次进行排查得到,但是应该怎么将数字2转换成字符2呢?
🌸仔细思考的小伙伴们可以发现,在我们的ASCII码表中字符0和数字0之间的差值为48,字符1和数字1之间的差值也是48.因此我们就可以利用这个规律给我们统计后的数字加上48,得到的也就是我们转换成字符之后的ASCII码值。这样我们就可以直接用于赋值了。接下来我们就将我们的想象付出实践。
🌸最后一步操作就是对坐标进行合法性判断并设置循环的次数。同样的,我们可以再次设置一个变量 win 我们可以将 win 初始为0。当我们对一个坐标进行改写完毕之后就对 win 进行 ++ 操作,最后直到 win 等于我们的总格数减去雷的数量的时候就跳出循环。游戏排雷成功的判断也是同样的道理:在游戏的末尾对 win 进行判断,当 win 等于总格数减去雷的数量的时候就说明我们已经将全部的雷都排除完毕了。那么我们也就取得了游戏胜利。游戏运行如下:
🌸为了测试的简便性我们将雷的个数更新为80,并在设置好雷之后打印出雷的位置,以便我们使用。
🌿代码呈现:
//test.c
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
void game()
{
char set[COLS][ROWS] = { 0 };
char show[COLS][ROWS] = { 0 };
//初始化棋盘
Initboard(set, COLS, ROWS,'0');
Initboard(show, COLS, ROWS,'*');
//打印棋盘
Printboard(show, COL, ROW);
//设置炸弹
Setbomb(set, COL, ROW);
//排查炸弹
Findbomb(set, show, COL, ROW);
}
void test()
{
int input;
do
{
meun();
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 2:
introduction();
break;
case 3:
gameset();
break;
case 0:
printf("退出游戏。");
break;
default:
printf("选择错误,请重新选择:");
break;
}
} while (input);
}
int main()
{
srand((unsigned int)time(NULL));
test();
return 0;
}
//game.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<Windows.h>
#define COL 9 //实际游戏的列数
#define ROW 9 //实际游戏的行数
#define COLS COL+2 //进行操作的游戏列数
#define ROWS ROW+2 //进行操作的游戏行数
#define COUNT 10 //雷的个数
//打印游戏菜单
void meun();
//打印游戏说明
void introduction();
//打印游戏设置说明
void gameset();
//初始化棋盘
void Initboard(char board[COLS][ROWS],int cols,int rows,char set);
//打印棋盘
void Printboard(char board[COLS][ROWS], int col, int row);
//设置炸弹
void Setbomb(char board[COLS][ROWS],int col,int row);
//排查炸弹
void Findbomb(char set[COLS][ROWS], char show[COLS][ROWS], int col, int row);
//game.c
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
void meun()
{
printf("**************************************\n");
printf("*** ***\n");
printf("*** 1.开始游戏 2.游戏规则 ***\n");
printf("*** ***\n");
printf("*** 3.游戏设置 0.退出游戏 ***\n");
printf("*** ***\n");
printf("**************************************\n");
}
void introduction()
{
printf("**************************************\n");
printf("*** 扫雷游戏规则: ***\n");
printf("*** 玩家需要在尽量短的时间内 ***\n");
printf("*** 找出隐藏起来的雷。 ***\n");
printf("*** 如果四周有雷,就会打印雷 ***\n");
printf("*** 的个数,以供玩家参考并通 ***\n");
printf("*** 关游戏。 ***\n");
printf("**************************************\n");
}
void gameset()
{
printf("**************************************\n");
printf("*** 扫雷游戏设置介绍: ***\n");
printf("*** 玩家需要亲自前往game.h头 ***\n");
printf("*** 文件中更改我们的宏定义, ***\n");
printf("*** 进而更改至自己想要的难度 ***\n");
printf("**************************************\n");
}
void Initboard(char board[COLS][ROWS], int cols, int rows,char set)
{
int i = 0;
int j = 0;
for (i = 0; i < cols; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
void Printboard(char board[COLS][ROWS], int col, int row)
{
int i = 0;
int j = 0;
printf("------扫雷游戏------\n");
for (i = 0; i <= COL; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= col; i++)
{
printf("%d ", i);
for (j = 1; j <= row; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("------扫雷游戏------\n");
}
void Setbomb(char board[COLS][ROWS], int col, int row)
{
int count = COUNT;
int i = 0;
while (count)
{
int x = rand() % col + 1;
int y = rand() % row + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
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 Findbomb(char set[COLS][ROWS], char show[COLS][ROWS], int col, int row)
{
int x = 0;
int y = 0;
int win = 0;
while (win < (row * col - COUNT))
{
printf("请输入想要检查的坐标:");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (show[x][y] != '*')
{
printf("该坐标已被排查。");
continue;
}
if (set[x][y] == '1')
{
system("cls");
printf("很遗憾,你被炸死了\n");
Printboard(set, COL, ROW);
break;
}
else
{
system("cls");
int n= get_mine_count(set, x, y);
show[x][y] = n + 48;
Printboard(show, COL, ROW);
win++;
}
}
}
if (win == (row * col - COUNT))
{
printf("恭喜你,排雷成功。\n");
Printboard(set, COL, ROW);
}
}
🌸经过我们上面步骤中的操作,我们的扫雷游戏也就初步完成了,怎么样是不是很简单?但是别着急,上面的内容只是我们维持我们扫雷游戏的正常进行但是要是想让我们的扫雷游戏做的和我们真实所见到的一样,那么还需要一些完善步骤。
🍀扫雷步骤完善
🌱标记雷的位置
🌸在我们最常见到的扫雷游戏中,我们单击鼠标右键是可以标记雷的位置的,当我们标记好了雷的位置之后屏幕上的雷的数量就会相应的减少。
🌸对于这个操作我们可以在我们输入坐标之前进行一个选择判断,同样可以利用一个 switch 语句进行控制,假如输入的值为1的话就进行标记,输入值为2的话就取消标记,输入的值为3的话就不进行任何操作,继续排雷。当然我们还应该进行标记的合法判断,假如,想要标记的是已经排查过的坐标那么就会显示标记失败,请重新选择。如果坐标非法同样如此。
🌸而对于我们标记完一个坐标之后需要是屏幕上提示的雷的数量相应的减少,那么我们就需要考虑怎样才可以即可以在打印棋盘处可以使用一个变量,也可以在进行标记处也可以改变这个变量,那么我们肯定会想到创建一个全局变量,当我们的游戏结束的时候,就将我们定义的全局变量进行还原,这样就不会影响我们下一局游戏的计数效果了。
🌸那么经过上面的步骤之后我们的标记效果也就做完了,接下来我们还需要制作一个更重要的功能。
🍀连续拓展功能
🌸如果已经尝试过我们上面代码的朋友们相信大家已经发现了,上面的代码在运行的时候只是当前一个坐标的变化不会向我们真正玩的游戏那样有一连串的大面积展开,那么我们接下来就来实现这个操作。
🌸首先我们需要重新把我们的注意力转移到对于扫雷游戏的分析上。
🌸就像我们上图中显示的那样,当我们点开一个位置的时候如果四周都没有雷,也就是说该处存储的值应该为字符0。那么我们就会向四周进行拓展,直到遇见可以显示数字的坐标为止。
🌸上述操作很显然是一个递归操作。但是需要注意的是不要对已经判断过的坐标进行重复判断,否则的话就会出现死递归的情况。
🌸针对我们上面的操作我们可以仿照我们上面的 Findbomb 函数进行改写,我们可以新建一个函数为 spread 同样将我们的两个数组以及行和列数作为参数传递给 spread 函数,但是我们需要增加两个新的参数那就是我们的 x 和 y 坐标。
🌸就像是我们上图中所展示的代码一样。当我们进行计数时一同进行判断,如果 count 值为0的话就进行拓展操作。并将我们此处坐标的元素改写为空格。并对四周的坐标进行以此判断。注意:if ( show [ i ] [ j ] == ' * ' ) 这一步操作的价值在于不重复进行递归操作,进而放止死递归的发生。
🌸有了上面的经验相信我们的代码完善起来肯定很简单。那么下面我们就来展示一下我们代码的完善的结果吧!
🌸标记之后雷的数量减少,撤销标记之后雷的数量恢复,当选择不进行更改的时候可以输入坐标进行排雷操作,如果周围没有雷可以出现一大片空白。是不是感觉我们的扫雷游戏一下子就高级了许多?那么接下来就向大家展示我们的最终成果。
//test.c
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
void game()
{
char set[COLS][ROWS] = { 0 };
char show[COLS][ROWS] = { 0 };
//初始化棋盘
Initboard(set, COLS, ROWS, '0');
Initboard(show, COLS, ROWS, '*');
//打印棋盘
Printboard(show, COL, ROW);
//设置炸弹
Setbomb(set, COL, ROW);
//排查炸弹
Findbomb(set, show, COL, ROW);
}
void test()
{
int input;
do
{
meun();
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 2:
introduction();
break;
case 3:
gameset();
break;
case 0:
printf("退出游戏。");
break;
default:
printf("选择错误,请重新选择:");
break;
}
} while (input);
}
int main()
{
srand((unsigned int)time(NULL));
test();
return 0;
}
//game.c
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
void meun()
{
printf("**************************************\n");
printf("*** ***\n");
printf("*** 1.开始游戏 2.游戏规则 ***\n");
printf("*** ***\n");
printf("*** 3.游戏设置 0.退出游戏 ***\n");
printf("*** ***\n");
printf("**************************************\n");
}
void introduction()
{
printf("**************************************\n");
printf("*** 扫雷游戏规则: ***\n");
printf("*** 玩家需要在尽量短的时间内 ***\n");
printf("*** 找出隐藏起来的雷。 ***\n");
printf("*** 如果四周有雷,就会打印雷 ***\n");
printf("*** 的个数,以供玩家参考并通 ***\n");
printf("*** 关游戏。 ***\n");
printf("**************************************\n");
}
void gameset()
{
printf("**************************************\n");
printf("*** 扫雷游戏设置介绍: ***\n");
printf("*** 玩家需要亲自前往game.h头 ***\n");
printf("*** 文件中更改我们的宏定义, ***\n");
printf("*** 进而更改至自己想要的难度 ***\n");
printf("**************************************\n");
}
void Initboard(char board[COLS][ROWS], int cols, int rows, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < cols; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
//已经标记的雷的个数
int mark = 0;
void Printboard(char board[COLS][ROWS], int col, int row)
{
int i = 0;
int j = 0;
printf("------扫雷游戏------\n");
for (i = 0; i <= COL; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= col; i++)
{
printf("%d ", i);
for (j = 1; j <= row; j++)
{
printf("%c ", board[i][j]);
}
if (i == 1)
{
printf(" 剩余雷的个数:");
}
if (i == 3)
{
printf(" %d", COUNT - mark);
}
printf("\n");
}
printf("------扫雷游戏------\n");
}
void Setbomb(char board[COLS][ROWS], int col, int row)
{
int count = COUNT;
int i = 0;
while (count)
{
int x = rand() % col + 1;
int y = rand() % row + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
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 spread(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)
{
int count = get_mine_count(mine, x, y);
if (count == 0)
{
show[x][y] = ' ';
int i = 0;
for (i = x - 1; i <= x + 1; i++)
{
int j = 0;
for (j = y - 1; j <= y + 1; j++)
{
if (show[i][j] == '*')
{
spread(mine, show, row, col, i, j);
}
}
}
}
else
{
show[x][y] = count + '0';
}
}
}
void Findbomb(char set[COLS][ROWS], char show[COLS][ROWS], int col, int row)
{
int x = 0;
int y = 0;
int win = 0;
while (win < (row * col - COUNT))
{
int choice = 0;
printf("请问你要对标记进行更改吗?(1:标记炸弹的位置 2:撤销标记 3:不进行更改)\n");
scanf("%d", &choice);
switch (choice)
{
case 1:
printf("请输入你想标记的坐标:");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (show[x][y] == '*')
{
show[x][y] = '@';
mark++;
Printboard(show, COL, ROW);
}
else
{
printf("标记失败,不可对已标记或已查验的坐标进行标记。\n");
printf("\n");
printf("\n");
}
}
else
{
printf("非法坐标,请重新操作。\n");
printf("\n");
printf("\n");
}
break;
case 2:
printf("请输入你想撤销标记的坐标:");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (show[x][y] == '@')
{
show[x][y] = '*';
mark--;
Printboard(show, COL, ROW);
}
else
{
printf("撤销失败,该坐标未被标记\n");
printf("\n");
printf("\n");
}
}
else
{
printf("非法坐标,请重新操作。\n");
printf("\n");
printf("\n");
}
break;
case 3:
printf("请输入你认为安全的坐标:");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (show[x][y] != '*')
{
printf("该坐标已被排查或标记。\n");
printf("\n");
printf("\n");
continue;
}
if (set[x][y] == '1')
{
system("cls");
printf("很遗憾,你被炸死了\n");
Printboard(set, COL, ROW);
goto now;
}
else
{
spread(set, show, row, col, x, y);
Printboard(show, COL, ROW);
printf("\n");
printf("\n");
win++;
}
}
else
{
printf("非法坐标,请重新输入。\n");
printf("\n");
printf("\n");
}
}
}
now:
if (win == (row * col - COUNT))
{
printf("恭喜你,排雷成功。\n");
mark = COUNT;
Printboard(set, COL, ROW);
}
mark = 0;
}
//game.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define COL 9 //实际游戏的列数
#define ROW 9 //实际游戏的行数
#define COLS COL+2 //进行操作的游戏列数
#define ROWS ROW+2 //进行操作的游戏行数
#define COUNT 10 //雷的个数
//打印游戏菜单
void meun();
//打印游戏说明
void introduction();
//打印游戏设置说明
void gameset();
//初始化棋盘
void Initboard(char board[COLS][ROWS], int cols, int rows, char set);
//打印棋盘
void Printboard(char board[COLS][ROWS], int col, int row);
//设置炸弹
void Setbomb(char board[COLS][ROWS], int col, int row);
//排查炸弹
void Findbomb(char set[COLS][ROWS], char show[COLS][ROWS], int col, int row);
🌸以上就是我们本次博客的全部内容了,感谢大家的观看,祝大家天天开心。