当前位置: 首页 > news >正文

扫雷——C语言实现

扫雷

文章目录

  • 扫雷
    • 实现代码
    • 什么是扫雷
    • 基本功能实现
      • 显示选择菜单
      • 定义几个二维数组?
      • 确定数组大小
      • 初始化数组
      • 布置地雷
      • 打印展示数组
      • 排查地雷
        • 记录指定区域周围地雷的个数
        • 判断排雷成功
        • 排查地雷实现代码
      • 基本功能的实现代码和效果展示
    • 拓展功能
      • 简化游戏界面
      • 改变字体颜色
      • 实现爆炸展开效果
        • 实现效果
      • 插旗功能
        • 插旗
        • 取消所插旗子
        • 调整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 10void 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 + 2void 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 10void 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 10void 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;
}

相关文章:

扫雷——C语言实现

扫雷 文章目录 扫雷实现代码什么是扫雷基本功能实现显示选择菜单定义几个二维数组&#xff1f;确定数组大小初始化数组布置地雷打印展示数组排查地雷记录指定区域周围地雷的个数判断排雷成功排查地雷实现代码 基本功能的实现代码和效果展示 拓展功能简化游戏界面改变字体颜色实…...

CSS基础学习--6 CSS Text(文本)

一、文本颜色 color:red; 颜色属性被用来设置文字的颜色。 颜色是通过CSS最经常的指定&#xff1a; 十六进制值 - 如: &#xff03;FF0000一个RGB值 - 如: RGB(255,0,0)颜色的名称 - 如: red body {color:red;} h1 {color:#00ff00;} h2 {color:rgb(255,0,0);} 二、文本的…...

高精度电压源的应用场合有哪些

高精度电压源是一种能够提供恒定、稳定电压输出的设备&#xff0c;被广泛应用于各种领域。高精度电压源是现代电力、通信、控制等领域中重要的测试仪器之一&#xff0c;其主要功能是提供稳定可靠的直流或交流电源&#xff0c;并具有高精度和高分辨率的特点。在实际应用中&#…...

Android约束布局

一、嵌套布局效率可能很低。 在 Android 开发中,我们常常需要使用嵌套布局来实现某些较复杂的界面效果。但是嵌套层级太深会带来一些问题,主要包括: 视图层级过深,导致内存占用过高和性能下降。Android 需要为每个 View 对象分配内存,嵌套层级过深会创建很多 View 对象,占用较…...

selenium基础语法

文章目录 selenium基础语法1.定位页面元素2.元素的操作1) 模拟键盘输入(send_keys)2) 点击操作(click)3) 清除去对象输入的文本内容(clear)4) 获取文本(gettext) 3. 等待4. 信息打印5. 窗口6. 导航7. 弹窗8. 鼠标和弹窗9.选择框10.文件上传11.屏幕截图 selenium基础语法 1.定位…...

运行后端SpringBoot项目

目录 一、注册微信开发者账号 1. 注册开发者账号 2. 获取appid和密钥 二、开通腾讯云TRTC服务 1. TRTC业务介绍 2. 为什么不使用阿里云的实时音视频服务&#xff0c;偏要选用腾讯云TRTC服务&#xff1f; 3. 开通TRTC服务 4. 领取TRTC的AppID和密钥 三、导入 emos-api …...

#如何对待工作中的失误?# 如何对待工作与生活中的失误——一些不成熟的忠告

关于如何对待工作与生活中的失误的忠告 1.在面对失误而带来的指责和沮丧时&#xff0c;应该如何做&#xff1f;1.1 正确认识失误1.2 处理失误后情绪与问题的途径1.2.1 接受现实&#xff0c;不要否认错误1.2.2 不要过度臆想1.2.3 安排调整情绪的时间1.2.4 向他人寻求帮助 2.发生…...

Shell脚本文本三剑客之awk编辑器

目录 一、awk简介 二、awk工作原理 三、awk命令格式 四、awk命令的使用 1.print操作按行输出文本 2.print操作按字段截取输出文本 3.使用BEGIN和END指定操作 4.使用管道符号&#xff0c;双引号调用shell命令 5.使用操作getline 6.使用操作OFS 7.配合数组使用 一、…...

Focal Loss介绍

目录 前言一. Focal Loss二. 总结 前言 在目标检测算法中&#xff0c;我们会经常遇到Focal Loss这个东西&#xff0c;今天我们就来简单的分下下这个损失。 一. Focal Loss 在深度学习训练的时候&#xff0c;在遇到目标类别不平衡时&#xff0c;如果直接计算损失函数&#xff0…...

【数据结构与算法】04 哈希表 / 散列表 (哈希函数、哈希冲突、链地址法、开放地址法、SHA256)

一种很好用&#xff0c;很高效&#xff0c;又一学就会的数据结构&#xff0c;你确定不看看&#xff1f; 一、哈希表 Hash Table1.1 核心概念1.2 哈希函数 Hash Function1.3 哈希冲突 Hash Collision1.4 哈希冲突解决1.41 方法概述1.42 链地址法 Separate Chaining1.43 开放寻址…...

每日一道面试题之介绍一下Java的序列化和反序列化!

什么是序列化&#xff1f; 序列化是将对象转换为容易传输的格式的过程&#xff0c;它是一种用来处理对象流的机制&#xff0c;将对象的内容流化&#xff0c;从而使流化后的对象传输于网络之间&#xff0c;以便它们可以在网络上传输或在磁盘上存储。反序列化是将序列化后的数据…...

Netty实战(十一)

预置的ChannelHandler和编解码器&#xff08;一&#xff09;HTTP和SSL/TLS的添加和使用 一、SSL和TLS添加二、基于Netty的HTTP程序2.1 HTTP解码器、编码器和编解码器2.2 聚合HTTP消息2.3 HTTP压缩 一、SSL和TLS添加 作为一个通讯框架&#xff0c;通讯数据的安全性也是不可或缺的…...

Qos服务质量、心跳机制、保留消息,遗嘱信息,用户密码认证

这里写目录标题 Qos服务质量使用ESP8266接收QoS1的MQTT消息保留消息&#xff08;retainFlag&#xff09;心跳机制遗嘱信息 Qos服务质量 若想实现QoS>0&#xff0c;订阅端连接服务端时cleanSession需要设置为false&#xff0c;订阅端订阅主题时QoS>0&#xff0c;发布端发…...

MATLAB 之 线性方程组求解

这里写目录标题 一、线性方程组求解1. 线性方程组的直接解法1.1 利用左除运算符的直接解法1.2 利用矩阵的分解求解线性方程组 2. 线性方程组的迭代解法2.1 Jacobi 迭代法2.2 Gauss-Serdel 迭代法 3. 求线性方程的通解 一、线性方程组求解 在 MATLAB 中&#xff0c;关于线性方程…...

华为OD机试真题 Java 实现【字符串序列判定】【2022Q4 100分】,附详细解题思路

一、题目描述 输入两个字符串a和b,都只包含英文小写字母。a长度<=100,b长度<=500,000。 判定a是否是b的有效子串。 判定规则: a中的每个字符在b中都能找到(可以不连续),且a在b中字符的前后顺序与a中顺序要保持一致。 (例如,a=”qwt”是b=”qwerty”的一个子…...

taro使用小记 —— 持续更新

目录 1、在 taro 中使用 axios2、在 taro 中添加全局组件自动引入和方法自动引入3、在 taro 中使用 pinia 1、在 taro 中使用 axios taro 3.6 版本已经支持了网络请求库。 需安装插件 tarojs/plugin-http 使用和注意事项说明&#xff1a; https://www.npmjs.com/package/taroj…...

【LeetCode】110. 平衡二叉树

110. 平衡二叉树&#xff08;简单&#xff09; 思路 对二叉树做先序遍历&#xff0c;从底至顶返回子树最大高度&#xff0c;若判定某子树不是平衡树则“剪枝”直接向上返回。 递归返回值&#xff1a; 当节点 root 左、右子树的高度差 > 1&#xff1a;返回 -1&#xff0c;代…...

SQL视图、存储过程、触发器

一、视图 &#xff08;一&#xff09;介绍 视图(view&#xff09;是一种虚拟存在的表。视图中的数据并不在数据库中实际存在&#xff0c;行和列数据来自定义视图的查询中使用的表&#xff0c;并且是在使用视图时动态生成的。 通俗的讲&#xff0c;视图只保存了查询的SQL逻辑&…...

DNS隧道穿透

介绍&#xff1a; DNS隧道&#xff0c;是隧道技术中的一种。当我们的HTTP、HTTPS这样的上层协议、正反向端口转发都失败的时候&#xff0c;可以尝试使用DNS隧道。DNS隧道很难防范&#xff0c;因为平时的业务也好&#xff0c;使用也罢&#xff0c;难免会用到DNS协议进行解析&am…...

1.2 Scala变量与数据类型

一、变量声明 &#xff08;一&#xff09;简单说明 Scala中变量的声明使用关键字val和var。val类似Java中的final变量&#xff0c;也就是常量&#xff0c;一旦初始化将不可修改&#xff1b;var类似Java中的非final变量&#xff0c;可以被多次赋值&#xff0c;多次修改。 val - …...

深入探讨软件测试的质量度量指标

本文的目的是介绍项目中使用到主要质量指标&#xff0c;这些质量指标可以分为以下三类&#xff1a; 质量保证过程指标生产事故管理指标度量质量文化指标 质量保证过程指标 质量保证指标可以通过测试覆盖率来度量功能和非功能测试的覆盖率&#xff0c;同时也可以根据测试发现…...

6.12作业

1、pinia和vuex的区别 1.pinia没有mutations&#xff0c;只有state,getters,actions 2.pinia分模块不需要modules (之前vuex分模块需要modules) 3.pinia体积更小(性能更好) 4.pinia可以直接修改state数据 2、Vue2和vue3的响应式原理分别是什么&#x…...

RabbitMQ集群部署之镜像模式

RabbitMQ集群的普通模式中&#xff0c;一旦创建队列的主机宕机&#xff0c;队列就会不可用。不具备高可用能力。如果要解决这个问题&#xff0c;必须使用官方提供的镜像集群方案。 官方文档地址&#xff1a;https://www.rabbitmq.com/ha.html 1.镜像模式的特征 默认情况下&a…...

【算法】Remove Zero Sum Consecutive Nodes from Linked List 从链表中删去总和值为零的连续节点

文章目录 Remove Zero Sum Consecutive Nodes from Linked List 从链表中删去总和值为零的连续节点问题描述&#xff1a;分析代码 Remove Zero Sum Consecutive Nodes from Linked List 从链表中删去总和值为零的连续节点 问题描述&#xff1a; 给你一个链表的头节点 head&am…...

音悦台项目测试报告

文章目录 项目背景项目功能测试计划与设计功能测试自动化测试 测试结果功能测试结果UI自动化测试结果 项目背景 现如今人们的生活压力大&#xff0c;容易使人疲惫&#xff0c;为了使得人们在闲暇之余可以听音乐放松&#xff0c;为此设计出一款轻量的听音乐网站&#xff0c;快速…...

数据库存储过程和函数

MySQL存储过程和存储函数 MySQL中提供存储过程&#xff08;procedure&#xff09;与存储函数&#xff08;function&#xff09;机制&#xff0c;我们先将其统称为存储程序&#xff0c;一般的SQL语句需要先编译然后执行&#xff0c;存储程序是一组为了完成特定功能的SQL语句集&…...

Spring依赖注入有哪些?各有什么优缺点?

文章目录 前言概述一、属性注入1.1 实例1.2 优点1.3 缺点 二、Setter注入2.1 实例2.2 优点2.3 缺点 三、 构造方法注入3.1 实例3.2 优点3.3 缺点 四、扩展 前言 IoC和DI是Spring中重要的两个概念&#xff0c;其中IoC指的是控制反转&#xff0c;DI(依赖注入)指的是IoC的具体实现…...

java八股文-并发篇

并发篇 1. 线程状态 要求 掌握 Java 线程六种状态掌握 Java 线程状态转换能理解五种状态与六种状态两种说法的区别 六种状态及转换 分别是 新建 当一个线程对象被创建&#xff0c;但还未调用 start 方法时处于新建状态此时未与操作系统底层线程关联 可运行 调用了 start …...

Elasticsearch8.6.0安装

Elasticsearch 8.5.0 安装 Elasticsearch 简介Elasticsearch 8.6.0 安装创建网络拉取镜像运行镜像设置密码修改kibana配置绑定ES代码绑定&#xff1a;手动绑定&#xff1a; 配置ik分词器扩展词词典停用词词典 Elasticsearch 简介 Elasticsearch&#xff08;ES&#xff09; 是一…...

Vue - 第五天 动态组件 插槽 自定义指令

动态组件& 插槽& 自定义指令 一、动态组件1.什么是动态组件2.如何实现动态组件渲染3.使用 keep-alive 保持状态4. keep-alive 对应的生命周期函数5. keep-alive 的 include 属性6.动态展示左右组件7.例子 二、插槽1.什么是插槽2.体验插槽的基础用法2.1 没有预留插槽的内…...

制作一个网站多少钱/邯郸百度推广公司

docker使用教程相关系列 目录 目录 Docker构建镜像的方法主要有两种 构建tomcat基础镜像 方案一&#xff1a; 一&#xff1a;下载安装包 二&#xff1a;编写dockerfile文件 三&#xff1a;build 生成本地镜像 方案二&#xff1a; 一&#xff1a;准备centos镜像 二&…...

wordpress图片上传不显示/大数据精准营销的策略

摘要&#xff1a;微信搜索【三桥君】 使用软件&#xff1a;Axure RP 9软件 说明&#xff1a;实现方式不唯一&#xff0c;这里给出三桥君制作的一种方式。 一、问题 本篇文章三桥君主要解决关于轮播图交互效果的设置&#xff0c;以泉师新闻轮播图为例。 该轮播图的效果主要有几…...

wordpress 页面查询/网络服务提供商

题目描述 小蓝正在学习一门神奇的语言&#xff0c;这门语言中的单词都是由小写英文字母组 成&#xff0c;有些单词很长&#xff0c;远远超过正常英文单词的长度。小蓝学了很长时间也记不住一些单词&#xff0c;他准备不再完全记忆这些单词&#xff0c;而是根据单词中哪个字母出…...

南京网站公安备案/天津seo推广

从JDk1.2版本开始&#xff0c;把对象的引用分为四种级别&#xff0c;从而使程序能更加灵活的控制对象的生命周期。这四种级别由高到低依次为&#xff1a;强引用、软引用、弱引用、虚引用。1、强引用(StrongReference)如果一个对象具有强引用&#xff0c;那就类似于必不可少的生…...

网站建设准备工作/网站优化关键词公司

原 Oracle 10g OCP 042 题库 1-30 题 共168题https://blog.csdn.net/tianlesoftware/article/details/5767606版权声明&#xff1a; https://blog.csdn.net/tianlesoftware/article/details/5767606 声明&#xff1a;对于答案的相关的说明&#xff0c;是个人对Oracle的理解。 1…...

营销网站建设步骤/友缘在线官网

1.方法一&#xff1a;创建分割日志文件的脚本&#xff0c;添加定时任务 脚本存放路径&#xff1a;/usr/local/nginx/sbin/cut_nginx_logs.sh,按天分割具体内容&#xff1a; [rootroot sbin]# pwd /usr/local/nginx/sbin [rootroot sbin]# ll -rwxr-xr-x 1 root root 865 …...