扫雷
文章目录
- 扫雷
- 实现代码
- 什么是扫雷
- 基本功能实现
- 显示选择菜单
- 定义几个二维数组?
- 确定数组大小
- 初始化数组
- 布置地雷
- 打印展示数组
- 排查地雷
- 记录指定区域周围地雷的个数
- 判断排雷成功
- 排查地雷实现代码
- 基本功能的实现代码和效果展示
- 拓展功能
- 简化游戏界面
- 改变字体颜色
- 实现爆炸展开效果
- 实现效果
- 插旗功能
- 插旗
- 取消所插旗子
- 调整MineFind()函数
- 实现效果
- 拓展功能实现代码
实现代码
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<Windows.h>
#include<stdbool.h>
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
#define MINENUMBER 10
void BoardInit(char board[][COLS], int row, int col, char set);
void BoardDisplay(char board[][COLS], int row, int col);
void MineSet(char board[][COLS], int row, int col);
int MineNumber(char board[][COLS], int x, int y);
void MineFind(char mine[][COLS], char show[][COLS], int row, int col);
bool MineFinish(char board[][COLS], int row, int col);
void Explode(char mine[][COLS], char show[][COLS], int x, int y);
void Flag_In(char show[][COLS]);
void Flag_Out(char show[][COLS]);
void meau()
{
printf("****************\n");
printf("*****1.Play*****\n");
printf("*****0.Exit*****\n");
printf("****************\n");
}
void game()
{
//生成时间戳
srand((unsigned int)time(NULL));
//定义存放地雷的数组,和展示结果的数组
char mineBoard[ROWS][COLS];
char showBoard[ROWS][COLS];
//对两个数组进行初始化
BoardInit(mineBoard, ROWS, COLS, '0');
BoardInit(showBoard, ROWS, COLS, '*');
//设置地雷
MineSet(mineBoard, ROW, COL);
//BoardDisplay(mineBoard, ROW, COL);
BoardDisplay(showBoard, ROW, COL);
//排查地雷
MineFind(mineBoard, showBoard, ROW, COL);
}
void BoardInit(char board[][COLS], int row, int col, char set)
{
for (int i = 0; i < row; i++)
for (int j = 0; j < col; j++)
board[i][j] = set;
}
void BoardDisplay(char board[][COLS], int row, int col)
{
for (int i = 0; i <= col; i++)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 4 | 16);
printf("%d ", i);
}
printf("\n");
for (int i = 1; i <= row; i++)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 4 | 16);
printf("%d ", i);
for (int j = 1; j <= col; j++)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7 | 16);
if(board[i][j] == '$')
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 6 | 16);
printf("%c ", board[i][j]);
}
printf("\n");
}
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7 | 16);
}
void MineSet(char board[][COLS], int row, int col)
{
int count = 0;
while (count < MINENUMBER)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
count++;
}
}
}
void MineFind(char mine[][COLS], char show[][COLS], int row, int col)
{
int x, y;
char ch;
while (1)
{
printf("请输入您要排查的坐标(用空格分隔):");
scanf_s("%d %d", &x, &y);
system("cls");
if (show[x][y] == '*' && x > 0 && x <= row && y > 0 && y <= col)
{
if (mine[x][y] == '1')
{
printf("很遗憾,排雷失败\n");
BoardDisplay(mine, row, col);
return;
}
else
{
Explode(mine, show, x, y);
if (MineFinish(show, row,col))
{
printf("恭喜你,排雷成功\n");
BoardDisplay(mine, row, col);
return;
}
BoardDisplay(show, row, col);
getchar();
printf("是否需要插旗(Y/N):\n");
if((ch = getchar()) == 'Y')
Flag_In(show);
getchar();
printf("是否需要取消所插的旗子(Y/N):\n");
if ((ch = getchar()) == 'Y')
Flag_Out(show);
}
}
else if (show[x][y] != '*' && x > 0 && x <= row && y > 0 && y <= col)
{
printf("该位置已经被检查,请重新输入:\n");
BoardDisplay(show, row, col);
}
else
{
printf("坐标非法,请重新输入:\n");
BoardDisplay(show, row, col);
}
}
}
int MineNumber(char board[][COLS], int x, int y)
{
return (board[x][y - 1] + board[x][y + 1]
+ board[x - 1][y] + board[x - 1][y - 1] + board[x - 1][y + 1]
+ board[x + 1][y] + board[x + 1][y - 1] + board[x + 1][y + 1]
- 8 * '0');
}
bool MineFinish(char board[][COLS], int row, int col)
{
int count = 0;
for(int i = 1; i <= row; i++)
for (int j = 1; j <= col; j++)
{
if (board[i][j] != '*')
{
count++;
if (count == row * col - MINENUMBER)
return true;
}
}
return false;
}
void Explode(char mine[][COLS], char show[][COLS], int x, int y)
{
if (show[x][y] == ' ' || x < 1 || y < 1 || x > ROW || y > COL)
return;
if (MineNumber(mine, x, y) == 0)
show[x][y] = ' ';
else
{
show[x][y] = MineNumber(mine, x, y) + '0';
return;
}
Explode(mine, show, x, y - 1);
Explode(mine, show, x, y + 1);
Explode(mine, show, x + 1, y + 1);
Explode(mine, show, x + 1, y);
Explode(mine, show, x + 1, y - 1);
Explode(mine, show, x - 1, y + 1);
Explode(mine, show, x - 1, y - 1);
Explode(mine, show, x - 1, y);
}
void Flag_In(char show[][COLS])
{
int row = 0, col = 0;
printf("您想在哪个位置插入旗子:\n");
printf("注:用空格分隔,输入0 0结束插旗\n");
while(1)
{
scanf_s("%d %d", &row, &col);
if (row == 0 && col == 0)
break;
if (row < 1 || row > ROW || col < 1 || col > COL)
{
printf("坐标非法,重新输入:");
continue;
}
else if (show[row][col] != '*')
printf("给位置不能插旗\n");
else
{
system("cls");
show[row][col] = '$';
BoardDisplay(show, ROW, COL);
}
}
}
void Flag_Out(char show[][COLS])
{
int row, col;
printf("您想取消哪个位置的旗子:\n");
printf("注:用空格分隔,输入0 0结束\n");
while (1)
{
scanf_s("%d %d", &row, &col);
if (row == 0 && col == 0)
break;
if (row < 1 || row > ROW || col < 1 || col > COL)
{
printf("坐标非法,重新输入:");
continue;
}
else if (show[row][col] != '$')
printf("该位置不是旗子\n");
else
{
system("cls");
show[row][col] = '*';
BoardDisplay(show, ROW, COL);
}
}
}
int main()
{
system("color 17");
int input;
meau();
printf("请输入您的选择:");
while (1)
{
scanf_s("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏,感谢游玩\n");
break;
default :
printf("输入错误,请重新输入:");
break;
}
meau();
printf("是否重新游玩:");
}
return 0;
}
什么是扫雷
-
要用代码来实现扫雷这个小游戏,我们就需要先了解扫雷这个游戏的规则和有关操作。
-
我们先来看一段扫雷游戏的动画:
-
游戏目标:在N*N的区域中找到M个随机布置的地雷
-
若点击的区域不是雷,那么就会显示一个数字,这个数字代表着这块区域周围雷的个数
-
若点击的区域是雷,那么就表示排雷失败,游戏结束
基本功能实现
显示选择菜单
-
游戏的最开始,我们需要提醒用户如何进行游戏以及如何退出游戏
-
我们可以用以下代码实现
#include<stdio.h> void meau() { printf("****************\n"); printf("*****1.Play*****\n"); printf("*****0.Exit*****\n"); printf("****************\n"); } int main() { int input; meau(); printf("请输入您的选择:"); while (1) { scanf_s("%d", &input); switch (input) { case 1: game(); //实现扫雷的函数 break; case 0: printf("退出游戏,感谢游玩\n"); break; default : printf("输入错误,请重新输入:"); break; } meau(); printf("是否重新游玩:"); } return 0; }
定义几个二维数组?
-
假设游戏区域为大小为N*N的正方形区域
-
可以明确的是,我们需要用一个二维数组来存放这N*N个位置的信息,那我们还需不需要定义其他数组呢?
-
根据上面的动画我们可以看到,游玩游戏时,系统展示给我们的是一个空白的未知区域,而没有将这片区域存放的数据(即地雷的位置)展示给我们,因此我们还需要定义一个二维数组用来对用户展示其排查的结果。
确定数组大小
-
,二维数组的大小为多少合适呢?可能有小伙伴认为,定义一个长宽都为N的二维数组不就行了吗?其实这不是最优解,我们先将这个问题放一边,先来看看我们是如何记录一块区域周围雷的个数的:
-
假设我们要排查的坐标是(X,Y),那么它周围的区域就是这八个坐标:(X,Y-1), (X,Y+1), (X+1,Y), (X+1,Y+1), (X+1,Y-1), (X-1,Y), (X-1,Y+1), (X-1,Y-1),如图所示
-
我们要查看这八个坐标区域是否是雷,并记录
-
-
那么问题来了,如果我们排查的是如下图画线区域的坐标呢?假设我们定义的是长宽都为N的二维数组,那么当我们排查(0,0),(0,1),(1,0)……这些边界坐标时就会出现数组越界的情况,因此我们就有必要适当的扩大。
-
我们可以将二维数组的长和宽各增加两行/列(但有效区域仍然是大小为N*N的区域),这样当我们排查原本是边界坐标时就不会出现数组越界的情况了。
-
为了保证操作的一致性,我们也将展示数组的大小定义为(N+2)*(N+2)
/* ROW,COL即真实的区域大小 ROWS,COLS即扩容后的大小 */ #define ROW 9 #define COL 9 #define ROWS ROW + 2 #define COLS COL + 2 void game() { //定义存放地雷的数组,和展示结果的数组 //此时,[1,ROW]为横纵坐标的有效取值 char mineBoard[ROWS][COLS]; char showBoard[ROWS][COLS]; }
初始化数组
-
我们规定,两个数组都是字符数组
-
用字符‘1’代表地雷,字符‘0’代表非地雷(具体原因会在后面讲到)
-
将展示数组全部初始化为‘*’,代表未知
/* row,col即传入数组的行数和列数,这里row = ROWS,col = COLS 因为要将扩容的区域初始化为没有地雷的状态 set即要初始化的字符 */ void BoardInit(char board[][COLS], int row, int col, char set) { for (int i = 0; i < row; i++) for (int j = 0; j < col; j++) board[i][j] = set; }
布置地雷
-
假设我们要随机布置MINENUMBER个地雷
-
既然涉及到了“随机”这一概念,那么显然,我们就要用到随机数这一概念
-
我们可以利用循环,在循环里随机生成地雷的坐标,只要这个坐标没被布置过地雷(即这个坐标代表的字符不是‘1’),那么就在这个坐标布置地雷
/* 这里row = ROW,col = COL,因为地雷不会被布置在扩容区域 扩容区域只是为了方便记录边界区域周围的地雷个数 */ void MineSet(char board[][COLS], int row, int col) { int count = 0; while (count < MINENUMBER) { int x = rand() % row + 1; int y = rand() % col + 1; if (board[x][y] == '0') { board[x][y] = '1'; count++; } } }
打印展示数组
-
布置完地雷后,我们就需要打印出展示数组,方便用户进行扫雷
-
为了方便用户确定要排查区域的位置,我们可以顺便打印出展示数组的行数和列数,如图:
/* 这里row = ROW,col = COL,扩容区域不需要排查 扩容区域只是为了方便记录边界区域周围的地雷个数 */ void BoardDisplay(char board[][COLS], int row, int col) { for (int i = 0; i <= col; i++) printf("%d ", i); printf("\n"); for (int i = 1; i <= row; i++) { printf("%d ", i); for (int j = 1; j <= col; j++) printf("%c ", board[i][j]); printf("\n"); } }
排查地雷
- 接下来就需要用户排查地雷了
- 我们利用循环提醒用户输入要排查的坐标,有以下五种情况:
- 如果排查的坐标是雷,那么直接退出game()函数,表示排雷失败(如果排雷失败,那么就要向用户展示存放雷的数组,让用户明白到底错在了哪)
- 如果排查的坐标已经被检查过,那么提醒用户,重新输入
- 如果排查的坐标非法,那么提醒用户,重新输入
- 如果排查的坐标不是雷,并且还未排雷完毕,那么显示该区域周围有几个雷,继续循环
- 如果排查的坐标不是雷,并且所有雷已经排查完毕,那么退出game()函数,表示排雷成功
- 每排查完一次,都要重新打印一次展示数组
记录指定区域周围地雷的个数
-
上面我们讲到,要确定一个指定坐标(X,Y)周围地雷的个数,只需要查看(X,Y-1), (X,Y+1), (X+1,Y), (X+1,Y+1), (X+1,Y-1), (X-1,Y), (X-1,Y+1), (X-1,Y-1)这八个坐标的值
-
前面我们已经假设字符‘1’代表地雷,字符‘0’代表非地雷,由于字符‘0’的ASCII的值为48,字符‘1’的ASCII值为49(比字符‘0’大一),那么我们统计(X,Y)周围地雷的个数就只需要将周围八个坐标储存的值相加,然后减去8个字符‘0’即可
-
这也是将字符‘1’设置为地雷,字符‘0’设置为非地雷的巧妙之处,可以在计算地雷个数的时候少去很多操作
/* 这里row = ROWS,col = COLS 这样可以确保排查任意区域操作的一致性(数组不会越界) */ int MineNumber(char board[][COLS], int x, int y) { return (board[x][y - 1] + board[x][y + 1] + board[x - 1][y] + board[x - 1][y - 1] + board[x - 1][y + 1] + board[x + 1][y] + board[x + 1][y - 1] + board[x + 1][y + 1] - 8 * '0'); }
判断排雷成功
-
如果被排查完的区域的总个数等于整块区域的个数减去地雷个数,那就说明排雷成功
/* 传入的是展示数组showBoard 这里row = ROW,col = COL,扩容区域不需要排查 扩容区域只是为了方便记录边界区域周围的地雷个数 */ //如果排雷成功,就返回真,否则返回假 bool MineFinish(char board[][COLS], int row, int col) { int count = 0; //count用来记录已经排查的区域的个数 for(int i = 1; i <= row; i++) for (int j = 1; j <= col; j++) { if (board[i][j] != '*') { count++; if (count == row * col - MINENUMBER) //MINENUMBER就是地雷总个数 return true; } } return false; }
排查地雷实现代码
void MineFind(char mine[][COLS], char show[][COLS], int row, int col)
{
int x, y;
while (1)
{
printf("请输入您要排查的坐标(用空格分隔):");
scanf_s("%d %d", &x, &y);
//如果排查的区域未被检查并且坐标合法
if (show[x][y] == '*' && x > 0 && x <= row && y > 0 && y <= col)
{
//如果踩到了雷
if (mine[x][y] == '1')
{
printf("很遗憾,排雷失败\n");
BoardDisplay(mine, row, col);
return;
}
//否则继续排雷
else
{
show[x][y] = MineNumber(mine, x, y) + '0';
//
if (MineFinish(show, row,col))
{
printf("恭喜你,排雷成功\n");
BoardDisplay(mine, row, col);
return;
}
BoardDisplay(show, row, col);
}
}
//如果排查的区域合法但已经被检查
else if (show[x][y] != '*' && x > 0 && x <= row && y > 0 && y <= col)
{
printf("该位置已经被检查,请重新输入:\n");
BoardDisplay(show, row, col);
}
//如果排查的区域不合法
else
{
printf("坐标非法,请重新输入:\n");
BoardDisplay(show, row, col);
}
}
}
基本功能的实现代码和效果展示
-
我们先来看看实现效果:
-
实现代码:
#include<stdio.h> #include<stdlib.h> #include<time.h> #include<stdbool.h> #define ROW 9 #define COL 9 #define ROWS ROW + 2 #define COLS COL + 2 #define MINENUMBER 10 void BoardInit(char board[][COLS], int row, int col, char set); void BoardDisplay(char board[][COLS], int row, int col); void MineSet(char board[][COLS], int row, int col); int MineNumber(char board[][COLS], int x, int y); void MineFind(char mine[][COLS], char show[][COLS], int row, int col); bool MineFinish(char board[][COLS], int row, int col); void meau() { printf("****************\n"); printf("*****1.Play*****\n"); printf("*****0.Exit*****\n"); printf("****************\n"); } void game() { //生成时间戳 srand((unsigned int)time(NULL)); //定义存放地雷的数组,和展示结果的数组 char mineBoard[ROWS][COLS]; char showBoard[ROWS][COLS]; //对两个数组进行初始化 BoardInit(mineBoard, ROWS, COLS, '0'); BoardInit(showBoard, ROWS, COLS, '*'); //设置地雷 MineSet(mineBoard, ROW, COL); //打印展示数组 BoardDisplay(showBoard, ROW, COL); //排查地雷 MineFind(mineBoard, showBoard, ROW, COL); } void BoardInit(char board[][COLS], int row, int col, char set) { for (int i = 0; i < row; i++) for (int j = 0; j < col; j++) board[i][j] = set; } void BoardDisplay(char board[][COLS], int row, int col) { for (int i = 0; i <= col; i++) printf("%d ", i); printf("\n"); for (int i = 1; i <= row; i++) { printf("%d ", i); for (int j = 1; j <= col; j++) printf("%c ", board[i][j]); printf("\n"); } } void MineSet(char board[][COLS], int row, int col) { int count = 0; while (count < MINENUMBER) { int x = rand() % row + 1; int y = rand() % col + 1; if (board[x][y] == '0') { board[x][y] = '1'; count++; } } } void MineFind(char mine[][COLS], char show[][COLS], int row, int col) { int x, y; while (1) { printf("请输入您要排查的坐标(用空格分隔):"); scanf_s("%d %d", &x, &y); if (show[x][y] == '*' && x > 0 && x <= row && y > 0 && y <= col) { if (mine[x][y] == '1') { printf("很遗憾,排雷失败\n"); BoardDisplay(mine, row, col); return; } else { show[x][y] = MineNumber(mine, x, y) + '0'; if (MineFinish(show, row,col)) { printf("恭喜你,排雷成功\n"); BoardDisplay(mine, row, col); return; } BoardDisplay(show, row, col); } } else if (show[x][y] != '*' && x > 0 && x <= row && y > 0 && y <= col) { printf("该位置已经被检查,请重新输入:\n"); BoardDisplay(show, row, col); } else { printf("坐标非法,请重新输入:\n"); BoardDisplay(show, row, col); } } } int MineNumber(char board[][COLS], int x, int y) { return (board[x][y - 1] + board[x][y + 1] + board[x - 1][y] + board[x - 1][y - 1] + board[x - 1][y + 1] + board[x + 1][y] + board[x + 1][y - 1] + board[x + 1][y + 1] - 8 * '0'); } bool MineFinish(char board[][COLS], int row, int col) { int count = 0; for(int i = 1; i <= row; i++) for (int j = 1; j <= col; j++) { if (board[i][j] != '*') { count++; if (count == row * col - MINENUMBER) return true; } } return false; } int main() { int input; meau(); //展示选择菜单 printf("请输入您的选择:"); while (1) { scanf_s("%d", &input); switch (input) { case 1: game(); break; case 0: printf("退出游戏,感谢游玩\n"); break; default : printf("输入错误,请重新输入:"); break; } meau(); printf("是否重新游玩:"); } return 0; }
拓展功能
简化游戏界面
-
根据上面的展示我们可以看到,我们每排查一次,展示数组就要打印一次,这样就会使得游戏界面太过复杂
-
我们可以使用system(“cls”)函数来清除已经不需要的打印结果
-
注:要包含头文件<Windows.h>
-
修改后的MineFind()函数:
void MineFind(char mine[][COLS], char show[][COLS], int row, int col) { int x, y; while (1) { printf("请输入您要排查的坐标(用空格分隔):"); scanf_s("%d %d", &x, &y); //就是多了这样一条语句 system("cls"); if (show[x][y] == '*' && x > 0 && x <= row && y > 0 && y <= col) { if (mine[x][y] == '1') { printf("很遗憾,排雷失败\n"); BoardDisplay(mine, row, col); return; } else { show[x][y] = MineNumber(mine, x, y) + '0'; if (MineFinish(show, row,col)) { printf("恭喜你,排雷成功\n"); BoardDisplay(mine, row, col); return; } BoardDisplay(show, row, col); } } else if (show[x][y] != '*' && x > 0 && x <= row && y > 0 && y <= col) { printf("该位置已经被检查,请重新输入:\n"); BoardDisplay(show, row, col); } else { printf("坐标非法,请重新输入:\n"); BoardDisplay(show, row, col); } } }
-
实现效果:
改变字体颜色
-
为了将坐标数字和代表地雷个数的数字作出区分,我们可以改变这两种数字的颜色
-
如果对如何改变字体颜色和背景色还不是太了解,可以先看看C语言——修改控制台背景色和字体颜色这篇博客
-
直接上代码(只需要调整打印数组的函数BoardDisplay且在主函数最开始加上system(“color”)函数即可)
int main() { system("color 17"); ………………; }
void BoardDisplay(char board[][COLS], int row, int col) { for (int i = 0; i <= col; i++) { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 4 | 16); printf("%d ", i); } printf("\n"); for (int i = 1; i <= row; i++) { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 4 | 16); printf("%d ", i); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7 | 16); for (int j = 1; j <= col; j++) printf("%c ", board[i][j]); printf("\n"); } }
-
看看效果:
实现爆炸展开效果
-
我们看到,网页版的扫雷中,会出现点击一个区域就自动展开一片区域的情况,如图:
-
要实现这一功能,那我们首先就需要知道什么条件下才会出现爆炸展开的情况。
-
展开条件:如果排查的坐标(X,Y)周围地雷的总个数是0(既没有地雷),那么就自动排查它周围的八个区域(即坐标(X,Y-1), (X,Y+1), (X+1,Y), (X+1,Y+1), (X+1,Y-1), (X-1,Y), (X-1,Y+1), (X-1,Y-1)),对这八个坐标执行同样的操作,不断递归,直达每个区域的周围都至少有一个地雷(找到爆炸展开的边界)
-
实现这个功能要注意以下几点:
- 注意递归时数组不要越界
- 注意递归时排查重复递归的情况
- 如果排查区域周围地雷的个数为0,那么为了界面的简洁,就不再将0打印出,改为打印空格
-
Explode()的实现代码:
//x,y即用户输入的排查坐标 void Explode(char mine[][COLS], char show[][COLS], int x, int y) { //如果该位置已经配排查(重复递归)或坐标无效,那么退出这个函数(不是结束递归) if (show[x][y] == ' ' || x < 1 || y < 1 || x > ROW || y > COL) return; //如果该位置周围的地雷个数为0,那么符合递归条件,继续排查 if (MineNumber(mine, x, y) == 0) show[x][y] = ' '; //如果坐标有效但周围的地雷个数不为零,那么退出这个函数(不是结束递归) else { show[x][y] = MineNumber(mine, x, y) + '0'; return; } //自动排查(X,Y)周围的8个坐标 Explode(mine, show, x, y - 1); Explode(mine, show, x, y + 1); Explode(mine, show, x + 1, y + 1); Explode(mine, show, x + 1, y); Explode(mine, show, x + 1, y - 1); Explode(mine, show, x - 1, y + 1); Explode(mine, show, x - 1, y - 1); Explode(mine, show, x - 1, y); }
-
要对MineFind()函数做出相应的修改
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7sF9UEGD-1686494176819)(C:/Users/HUASHUO/Desktop/扫雷.5.gif)]void MineFind(char mine[][COLS], char show[][COLS], int row, int col) { int x, y; while (1) { printf("请输入您要排查的坐标(用空格分隔):"); scanf_s("%d %d", &x, &y); system("cls"); if (show[x][y] == '*' && x > 0 && x <= row && y > 0 && y <= col) { if (mine[x][y] == '1') { printf("很遗憾,排雷失败\n"); BoardDisplay(mine, row, col); return; } else { Explode(mine, show, x, y); if (MineFinish(show, row,col)) { printf("恭喜你,排雷成功\n"); BoardDisplay(mine, row, col); return; } BoardDisplay(show, row, col); } } else if (show[x][y] != '*' && x > 0 && x <= row && y > 0 && y <= col) { printf("该位置已经被检查,请重新输入:\n"); BoardDisplay(show, row, col); } else { printf("坐标非法,请重新输入:\n"); BoardDisplay(show, row, col); } } }
实现效果
插旗功能
- 我们假设旗子的标志是字符:‘$’
- 注:用户如果在一个区域插上旗子,就说明用户认为这个区域是雷,即插旗是为了方便用户排雷
- 实现逻辑并不复杂,至少需要注意细节的处理
插旗
void Flag_In(char show[][COLS])
{
int row = 0, col = 0;
printf("您想在哪个位置插入旗子:\n");
printf("注:用空格分隔,输入0 0结束插旗\n");
while(1)
{
scanf_s("%d %d", &row, &col);
if (row == 0 && col == 0)
break;
if (row < 1 || row > ROW || col < 1 || col > COL)
{
printf("坐标非法,重新输入:");
continue;
}
else if (show[row][col] != '*')
printf("该位置不能插旗\n");
else
{
system("cls");
count++; //旗子数目加一
show[row][col] = '$';
BoardDisplay(show, ROW, COL);
}
}
while (getchar() != '\n'); //清空缓冲区
}
取消所插旗子
void Flag_Out(char show[][COLS])
{
int row, col;
printf("您想取消哪个位置的旗子:\n");
printf("注:用空格分隔,输入0 0结束\n");
while (1)
{
scanf_s("%d %d", &row, &col);
if (row == 0 && col == 0)
break;
if (row < 1 || row > ROW || col < 1 || col > COL)
{
printf("坐标非法,重新输入:");
continue;
}
else if (show[row][col] != '$')
printf("该位置不是旗子\n");
else
{
system("cls");
show[row][col] = '*';
BoardDisplay(show, ROW, COL);
}
}
}
调整MineFind()函数
void MineFind(char mine[][COLS], char show[][COLS], int row, int col)
{
int x, y;
char ch;
while (1)
{
printf("请输入您要排查的坐标(用空格分隔):");
scanf_s("%d %d", &x, &y);
system("cls");
if (show[x][y] == '*' && x > 0 && x <= row && y > 0 && y <= col)
{
if (mine[x][y] == '1')
{
printf("很遗憾,排雷失败\n");
BoardDisplay(mine, row, col);
return;
}
else
{
Explode(mine, show, x, y);
if (MineFinish(show, row,col))
{
printf("恭喜你,排雷成功\n");
BoardDisplay(mine, row, col);
return;
}
BoardDisplay(show, row, col);
getchar();
printf("是否需要插旗(Y/N):\n");
if((ch = getchar()) == 'Y')
Flag_In(show);
getchar();
printf("是否需要取消所插的旗子(Y/N):\n");
if ((ch = getchar()) == 'Y')
Flag_Out(show);
}
}
else if (show[x][y] != '*' && x > 0 && x <= row && y > 0 && y <= col)
{
printf("该位置已经被检查,请重新输入:\n");
BoardDisplay(show, row, col);
}
else
{
printf("坐标非法,请重新输入:\n");
BoardDisplay(show, row, col);
}
}
}
实现效果
拓展功能实现代码
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<Windows.h>
#include<stdbool.h>
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
#define MINENUMBER 10
void BoardInit(char board[][COLS], int row, int col, char set);
void BoardDisplay(char board[][COLS], int row, int col);
void MineSet(char board[][COLS], int row, int col);
int MineNumber(char board[][COLS], int x, int y);
void MineFind(char mine[][COLS], char show[][COLS], int row, int col);
bool MineFinish(char board[][COLS], int row, int col);
void Explode(char mine[][COLS], char show[][COLS], int x, int y);
void Flag_In(char show[][COLS]);
void Flag_Out(char show[][COLS]);
void meau()
{
printf("****************\n");
printf("*****1.Play*****\n");
printf("*****0.Exit*****\n");
printf("****************\n");
}
void game()
{
//生成时间戳
srand((unsigned int)time(NULL));
//定义存放地雷的数组,和展示结果的数组
char mineBoard[ROWS][COLS];
char showBoard[ROWS][COLS];
//对两个数组进行初始化
BoardInit(mineBoard, ROWS, COLS, '0');
BoardInit(showBoard, ROWS, COLS, '*');
//设置地雷
MineSet(mineBoard, ROW, COL);
//BoardDisplay(mineBoard, ROW, COL);
BoardDisplay(showBoard, ROW, COL);
//排查地雷
MineFind(mineBoard, showBoard, ROW, COL);
}
void BoardInit(char board[][COLS], int row, int col, char set)
{
for (int i = 0; i < row; i++)
for (int j = 0; j < col; j++)
board[i][j] = set;
}
void BoardDisplay(char board[][COLS], int row, int col)
{
for (int i = 0; i <= col; i++)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 4 | 16);
printf("%d ", i);
}
printf("\n");
for (int i = 1; i <= row; i++)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 4 | 16);
printf("%d ", i);
for (int j = 1; j <= col; j++)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7 | 16);
if(board[i][j] == '$')
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 6 | 16);
printf("%c ", board[i][j]);
}
printf("\n");
}
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7 | 16);
}
void MineSet(char board[][COLS], int row, int col)
{
int count = 0;
while (count < MINENUMBER)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
count++;
}
}
}
void MineFind(char mine[][COLS], char show[][COLS], int row, int col)
{
int x, y;
char ch;
while (1)
{
printf("请输入您要排查的坐标(用空格分隔):");
scanf_s("%d %d", &x, &y);
system("cls");
if (show[x][y] == '*' && x > 0 && x <= row && y > 0 && y <= col)
{
if (mine[x][y] == '1')
{
printf("很遗憾,排雷失败\n");
BoardDisplay(mine, row, col);
return;
}
else
{
Explode(mine, show, x, y);
if (MineFinish(show, row,col))
{
printf("恭喜你,排雷成功\n");
BoardDisplay(mine, row, col);
return;
}
BoardDisplay(show, row, col);
getchar();
printf("是否需要插旗(Y/N):\n");
if((ch = getchar()) == 'Y')
Flag_In(show);
getchar();
printf("是否需要取消所插的旗子(Y/N):\n");
if ((ch = getchar()) == 'Y')
Flag_Out(show);
}
}
else if (show[x][y] != '*' && x > 0 && x <= row && y > 0 && y <= col)
{
printf("该位置已经被检查,请重新输入:\n");
BoardDisplay(show, row, col);
}
else
{
printf("坐标非法,请重新输入:\n");
BoardDisplay(show, row, col);
}
}
}
int MineNumber(char board[][COLS], int x, int y)
{
return (board[x][y - 1] + board[x][y + 1]
+ board[x - 1][y] + board[x - 1][y - 1] + board[x - 1][y + 1]
+ board[x + 1][y] + board[x + 1][y - 1] + board[x + 1][y + 1]
- 8 * '0');
}
bool MineFinish(char board[][COLS], int row, int col)
{
int count = 0;
for(int i = 1; i <= row; i++)
for (int j = 1; j <= col; j++)
{
if (board[i][j] != '*')
{
count++;
if (count == row * col - MINENUMBER)
return true;
}
}
return false;
}
void Explode(char mine[][COLS], char show[][COLS], int x, int y)
{
if (show[x][y] == ' ' || x < 1 || y < 1 || x > ROW || y > COL)
return;
if (MineNumber(mine, x, y) == 0)
show[x][y] = ' ';
else
{
show[x][y] = MineNumber(mine, x, y) + '0';
return;
}
Explode(mine, show, x, y - 1);
Explode(mine, show, x, y + 1);
Explode(mine, show, x + 1, y + 1);
Explode(mine, show, x + 1, y);
Explode(mine, show, x + 1, y - 1);
Explode(mine, show, x - 1, y + 1);
Explode(mine, show, x - 1, y - 1);
Explode(mine, show, x - 1, y);
}
void Flag_In(char show[][COLS])
{
int row = 0, col = 0;
printf("您想在哪个位置插入旗子:\n");
printf("注:用空格分隔,输入0 0结束插旗\n");
while(1)
{
scanf_s("%d %d", &row, &col);
if (row == 0 && col == 0)
break;
if (row < 1 || row > ROW || col < 1 || col > COL)
{
printf("坐标非法,重新输入:");
continue;
}
else if (show[row][col] != '*')
printf("给位置不能插旗\n");
else
{
system("cls");
show[row][col] = '$';
BoardDisplay(show, ROW, COL);
}
}
}
void Flag_Out(char show[][COLS])
{
int row, col;
printf("您想取消哪个位置的旗子:\n");
printf("注:用空格分隔,输入0 0结束\n");
while (1)
{
scanf_s("%d %d", &row, &col);
if (row == 0 && col == 0)
break;
if (row < 1 || row > ROW || col < 1 || col > COL)
{
printf("坐标非法,重新输入:");
continue;
}
else if (show[row][col] != '$')
printf("该位置不是旗子\n");
else
{
system("cls");
show[row][col] = '*';
BoardDisplay(show, ROW, COL);
}
}
}
int main()
{
system("color 17");
int input;
meau();
printf("请输入您的选择:");
while (1)
{
scanf_s("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏,感谢游玩\n");
break;
default :
printf("输入错误,请重新输入:");
break;
}
meau();
printf("是否重新游玩:");
}
return 0;
}