扫雷实现详解【递归展开+首次必展开+标记雷+取消标记雷】

扫雷

  • 一.扫雷设计思路
  • 二.扫雷代码逐步实现
    • 1.创建游戏菜单
    • 2.初始化棋盘
    • 3.打印棋盘
    • 4.随机布置雷
    • 5.统计周围雷的个数
    • 6.递归展开棋盘
    • 7.标记雷
    • 8.删除雷的标记
    • 9.保证第一次排雷的安全性+棋盘必定展开
    • 10.排查雷
    • 11.判断输赢
  • 三.扫雷总代码
  • 四.截图

一.扫雷设计思路

 1.创建游戏菜单。
 2.初始化棋盘。
 3.打印棋盘。
 4.随机布置雷。
 5.选择:排查雷,标记雷,或取消标记雷。
 7.循环,直到游戏结束。
 8.当然其中的细节处还有很多,带着你一步一步实现。

二.扫雷代码逐步实现

1.创建游戏菜单

void Menu()
{printf("-------------------------------------\n");printf("|              扫雷游戏!           |\n");printf("|        0.exit        1.play       |\n");printf("-------------------------------------\n");
}

当然可以美化一下,发挥你天马行空的想象力!

2.初始化棋盘

  • 扫雷的过程中,布置的雷的信息排查出的周围雷的信息周围雷的个数)都需要存储,所以我们需要⼀定的数据结构来存储这些信息。不妨用9 x 9的棋盘上布置的雷的信息排查出的周围雷的信息(周围雷的个数),我们首先想到的就是创建⼀个9 x 9的数组来存放这些信息。1代表雷,0代表不是雷。

如图:
在这里插入图片描述
由于我们还要统计某个坐标周围雷的个数,所以要访问周围数组元素

  • 假设我们排查(2,5)这个坐标时,我们访问周围的⼀圈8个黄色位置,统计周围雷的个数是1。
  • 假设我们排查(8,6)这个坐标时,我们访问周围的⼀圈8个黄色位置,统计周围雷的个数时,最下面的三个坐标就会越界
  • 为了防止越界,我们在设计的时候,给数组扩大一圈,雷还是布置在中间的9 x 9的坐标上,周围一圈不去布置雷就⾏,这样就解决了越界的问题。所以我们将存放数据的数组创建成11 x 11是比较合适。

如图:
在这里插入图片描述

  • 再继续分析,我们在棋盘上布置了雷,棋盘上雷的信息(1)非雷的信息(0),假设我们排查了某⼀个位置后,这个坐标处不是雷,这个坐标的周围有1个雷,那我们需要将排查出的雷的数量信息记录存储并打印出来,作为排雷的重要参考信息。那这个雷的个数信息存放在哪里呢?如果存放在布置雷的数组中,这样雷的信息和雷的个数信息就可能或产生混淆和打印上的困难。

  • 我们肯定有办法解决,比如:雷和非雷的信息不要使用数字,使用某些字符就行,这样就避免冲突了,但是这样做棋盘上有雷和非雷的信息,还有排查出的雷的个数信息,就比较混杂,不够方便。

  • 我们采用另外⼀种方案,设置两个棋盘,我们专门给⼀个棋盘(对应⼀个数组mine)存放布置好的雷的信息,再给另外⼀个棋盘(对应另外⼀个数组show)存放排查出的雷的信息。这样就互不干扰了,把雷布置到mine数组,在mine数组中排查雷,排查出的数据存放在show数组,并且打印show数组的信息给后期排查参考。

  • 为了保持神秘,show数组开始时初始化为字符 * ,为了保持两个数组的类型⼀致,可以使用同⼀套函数处理,mine数组最开始也初始化为字符’0’,布置雷改成’1’。

如图:
在这里插入图片描述

给出伪代码参考:

#define ROW 9//扫雷的行数
#define COL 9//扫雷的列数
#define ROWS ROW + 2//创建二维数组的行数
#define COLS COL + 2//创建二维数组的列数
void InitBoard(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;}}
}
//创建两个字符二维数组
char mine[ROWS][COLS];//存放该点是不是雷    (0代表不是雷,1代表是雷)
char show[ROWS][COLS];//存放该点周围有多少雷(数字代表周围有多少雷,*代表未知)

3.打印棋盘

代码1:

//定义打印棋盘的函数
void PrintBoard(char board[ROWS][COLS], int row, int col)
{int i = 0;//辅助观察棋盘for (i = 0;i <= row;i++){printf("%d ", i);}printf("\n");//打印棋盘for (i = 1;i <= row;i++){printf("%d ", i);//辅助观察棋盘int j = 0;for (j = 1;j <= col;j++){printf("\033[36m%c \033[0m", board[i][j]);//打印蓝色字符}printf("\n");}printf("--------扫雷--------\n");//辅助观察棋盘
}

为了使棋盘看起来更舒服,我们可以美化一下棋盘。
代码2:

//定义打印棋盘的函数
void PrintBoard(char board[ROWS][COLS], int row, int col)
{printf("\033[34m-----------------扫雷-----------------\033[0m\n");//辅助观察棋盘int i = 0;//辅助观察棋盘printf("0 |");for (i = 1;i <= row;i++){printf(" %-2d", i);if (i < row){printf("|");}}printf("\n");printf("--|");for (i = 1; i <= col; i++){printf("---");if (i < row){printf("|");}}printf("\n");//打印棋盘for (i = 1;i <= row;i++){printf("%-2d|", i);//辅助观察棋盘int j = 0;for (j = 1;j <= col;j++){if (board[i][j] == '1' || board[i][j] == '5'){printf("\033[36m %c \033[0m", board[i][j]);//打印蓝色}else if (board[i][j] == '2' || board[i][j] == '6'){printf("\033[32m %c \033[0m", board[i][j]);//打印绿色}else if (board[i][j] == '3' || board[i][j] == '7'){printf("\033[31m %c \033[0m", board[i][j]);//打印红色}else if (board[i][j] == '4' || board[i][j] == '8'){printf("\033[35m %c \033[0m", board[i][j]);//打印紫色}else if (board[i][j] == '!'){printf("\033[31m %c \033[0m", board[i][j]);//打印红色}else{printf(" %c ", board[i][j]);//打印白色}if (j < col){printf("|");}}//辅助观察棋盘printf("\n");if (i < row){printf("--|");}for (j = 1; j <= col; j++){if (i < row){printf("---");if (j < col){printf("|");}}}printf("\n");}
}

效果如下:
在这里插入图片描述

4.随机布置雷

  • 这里用到了随机数,不知道的小伙伴,可以去我写的猜字小游戏看看
  • 点击即可前往猜字小游戏
//定义布置雷的函数
void SetMine(char mine[ROWS][COLS], int row, int col)
{int count = MINE_NUMBER;//雷的个数while (count){int x = rand() % row + 1;int y = rand() % col + 1;if (mine[x][y] != '1'){mine[x][y] = '1';//如果该坐标不是雷,则布置雷count--;}}
}

5.统计周围雷的个数

代码1:暴力取遍

//统计周围雷的个数的函数1.0
int AroundMineCount(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';
}

代码2:循环取遍

//统计周围雷的个数的函数
int AroundMineCount(char mine[ROWS][COLS], int x, int y)
{int i = 0;int count = 0;for (i = x - 1;i <= x + 1;i++){int j = 0;for (j = y - 1;j <= y + 1;j++){count += mine[i][j] - '0';}}return count;
}

6.递归展开棋盘

  • 已知该坐标不是雷,若周围没有雷,该坐标赋为空,再向外扩展(达到展开一片的效果),否则只显示该坐标周围雷的个数。
//已知该坐标不是雷,若周围没有雷,该坐标赋为空,再向外扩展(达到展开一片的效果),否则只显示该坐标周围雷的个数
void ExpandAround(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{if (x >= 1 && x <= ROW && y >= 1 && y <= COL)//限制在棋盘内展开,防止下标越界{int count = AroundMineCount(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] == '*')//只对位排查的坐标进行展开,防止死循环{ExpandAround(mine, show, i, j);}}}}else{show[x][y] = count + '0';//周围有雷,显示雷数}}
}

7.标记雷

//定义标记雷的函数(用!标记)
void MarkMine(char show[ROWS][COLS], int row, int col)
{int x = 0;int y = 0;while (1){printf("请输入你要标记雷的坐标:>");scanf("%d %d", &x, &y);if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] != '!'){if (show[x][y] == '*'){show[x][y] = '!';system("cls");PrintBoard(show, ROW, COL);break;}else{printf("该坐标已经被排查过,无法标记,请重新输入\n");}}else if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] == '!'){printf("该坐标已经被标记过了,无法二次标记,请重新输入\n");}else{printf("该坐标越界,请重新输入\n");}}
}

8.删除雷的标记

//定义删除雷的标记的函数
void DelectMarkMine(char show[ROWS][COLS], int row, int col)
{int x = 0;int y = 0;while (1){printf("请输入你要删除雷的标记的坐标:>");scanf("%d %d", &x, &y);if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] == '!'){show[x][y] = '*';system("cls");PrintBoard(show, ROW, COL);break;}else if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] != '!'){printf("该坐标未被标记过,无法删除标记,请重新输入\n");}else{printf("该坐标越界,请重新输入\n");}}
}

9.保证第一次排雷的安全性+棋盘必定展开

//定义第一次排雷是安全的,不是雷并且可以达到展开一片的效果,增加了可玩性
void FirstIsSafe(char mine[ROWS][COLS], char show[ROWS][COLS],int x, int y)
{//若该点不是雷且周围无雷,直接展开if (mine[x][y] != '1' && AroundMineCount(mine, x, y) == 0){ExpandAround(mine, show, x, y);}//否则分情况else{//先判断该点是不是雷if (mine[x][y] == '1')//该点为雷,将其移走{mine[x][y] = '0';while (1){int x1 = rand() % ROW + 1;int y1 = rand() % COL + 1;if (mine[x1][y1] != '1' && x1 != x && y1 != y){mine[x1][y1] = '1';break;}}}//以及将雷移走了,在判断周围是否有雷if (AroundMineCount(mine, x, y) != 0)//该点周围有雷,将它们移走{int count = AroundMineCount(mine, x, y);//计算周围雷的数量int i = 0;for (i = x - 1;i <= x + 1; i++){int j = 0;for (j = y - 1; j <= y + 1; j++){mine[i][j] = '0';//将周围的雷变为无雷的状态}}while (count){int x1 = rand() % ROW + 1;int y1 = rand() % COL + 1;if ((x1 != x - 1 && y1 != y - 1)&&(x1 != x - 1 && y1 != y)&&(x1 != x - 1 && y1 != y + 1)&&(x1 != x && y1 != y - 1)&&(x1 != x && y1 != y)&&(x1 != x && y1 != y + 1)&&(x1 != x + 1 && y1 != y - 1)&&(x1 != x + 1 && y1 != y)&&(x1 != x + 1 && y1 != y + 1))//不能将雷移回来{if (mine[x1][y1] != '1'){mine[x1][y1] = '1';//如果该坐标不是雷,则布置雷count--;}}}}ExpandAround(mine, show, x, y);}
}

10.排查雷

  • 代码1:不能实现展开一片的效果
//定义排查雷的函数1.0:不能实现展开一片的效果
int FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{int x = 0;int y = 0;int ret1 = 0;while (1){printf("请输入你要排查的坐标:>");scanf("%d %d", &x, &y);if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] == '*')//判断输入的坐标是否合法且未被排查过{if (mine[x][y] == '1')//该坐标是雷{system("cls");//清空屏幕——头文件windows.hprintf("很遗憾,你被雷炸死了,游戏失败!\n");printf("雷的布局如下图所示(0代表不是雷,1代表是雷)\n");PrintBoard(mine, ROW, COL);ret1 = 1;break;}else//该坐标不是雷{system("cls");//清空屏幕——头文件windows.hint count = AroundMineCount(mine, x, y);show[x][y] = count + '0';//该坐标要存放雷的个数int ret2 = IsWin(show, row, col);if (ret2 == 1){printf("恭喜你!找到了所有的雷,游戏胜利!\n");printf("雷的布局如下图所示(0代表不是雷,1代表是雷)\n");PrintBoard(mine, row, col);}else{PrintBoard(show, ROW, COL);//打印存放展示雷的信息,以便于下一次排雷}break;}}else if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] != '*')//该坐标已经被排查过{printf("该坐标已经被排查过,请重新输入坐标\n");}else//越界{printf("输入的坐标错误,请重新输入\n");}}return ret1;
}
  • 代码2:可以实现展开一片的效果
int FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{int x = 0;int y = 0;int ret1 = 0;while (1){printf("请输入你要排查的坐标:>");scanf("%d %d", &x, &y);if (x >= 1 && x <= row && y >= 1 && y <= col)//判断输入的坐标是否合法且未被排查过{if (show[x][y] == '*' || show[x][y] == '!'){if (show[x][y] == '*'){again:if (mine[x][y] == '1')//该坐标是雷{system("cls");//清空屏幕——头文件windows.hprintf("很遗憾,你被雷炸死了,游戏失败!\n");printf("雷的布局如下图所示(0代表不是雷,1代表是雷)\n");PrintBoard(mine, ROW, COL);ret1 = 1;break;}else//该坐标不是雷{ExpandAround(mine, show, x, y);//直接展开system("cls");//清空屏幕——头文件windows.hint ret2 = IsWin(show, row, col);if (ret2 == 1){printf("恭喜你!找到了所有的雷,游戏胜利!\n");printf("雷的布局如下图所示(0代表不是雷,1代表是雷)\n");PrintBoard(mine, row, col);}else{PrintBoard(show, ROW, COL);//打印存放展示雷的信息,以便于下一次排雷}break;}}else{int select = 0;printf("确定要排查标记过的坐标吗? 0.取消排查 1.坚持排查\n");while (1){printf("请输入(0/1):>");scanf("%d", &select);if (select == 0){return 0;}else if (select == 1){goto again;}else{printf("输入错误,请重新输入\n");}}}}else{printf("该坐标已经被排查过,请重新输入\n");}}else//越界{printf("该坐标越界,请重新输入\n");}}return ret1;
}
  • 代码3:结合FirstIsSafe函数,第一次排查确保该点不是雷以及周围没有雷,达到网页版扫雷的效果,由于是随机排雷,不能保证该点以及周围有无雷,可以将雷,神不知鬼不觉地换掉。
int FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{//第一次排查确保该点不是雷以及周围没有雷,达到网页版扫雷的效果//由于是随机排雷,不能保证该点以及周围有无雷,可以将雷,神不知鬼不觉地换掉static int FindMineCount = 1;if (FindMineCount == 1){int x = 0;int y = 0;while (1){printf("请输入你要排查的坐标:>");scanf("%d %d", &x, &y);if (x >= 1 && x <= row && y >= 1 && y <= col){FirstIsSafe(mine, show, x, y);FindMineCount++;system("cls");//PrintBoard(mine, ROW, COL);PrintBoard(show, ROW, COL);return 0;}else{printf("输入的坐标越界,请重新输入\n");}}}//以后雷的位置就固定下来了else{int x = 0;int y = 0;int ret1 = 0;while (1){printf("请输入你要排查的坐标:>");scanf("%d %d", &x, &y);if (x >= 1 && x <= row && y >= 1 && y <= col)//判断输入的坐标是否合法且未被排查过{if (show[x][y] == '*' || show[x][y] == '!'){if (show[x][y] == '*'){again:if (mine[x][y] == '1')//该坐标是雷{system("cls");//清空屏幕——头文件windows.hprintf("很遗憾,你被雷炸死了,游戏失败!\n");printf("雷的布局如下图所示(0代表不是雷,1代表是雷)\n");PrintBoard(mine, ROW, COL);ret1 = 1;break;}else//该坐标不是雷{ExpandAround(mine, show, x, y);//直接展开system("cls");//清空屏幕——头文件windows.hint ret2 = IsWin(show, row, col);if (ret2 == 1){printf("恭喜你!找到了所有的雷,游戏胜利!\n");printf("雷的布局如下图所示(0代表不是雷,1代表是雷)\n");PrintBoard(mine, row, col);}else{PrintBoard(show, ROW, COL);//打印存放展示雷的信息,以便于下一次排雷}break;}}else{int select = 0;printf("确定要排查标记过的坐标吗? 0.取消排查 1.坚持排查\n");while (1){printf("请输入(0/1):>");scanf("%d", &select);if (select == 0){return 0;}else if (select == 1){goto again;}else{printf("输入错误,请重新输入\n");}}}}else{printf("该坐标已经被排查过,请重新输入\n");}}else//越界{printf("该坐标越界,请重新输入\n");}}FindMineCount++;return ret1;}
}

11.判断输赢

  • 利用剩余的*数目等于雷的数目,返回1,代表游戏胜利,否则返回0,代表游戏继续。
//定义游戏胜利的函数
int IsWin(char show[ROWS][COLS], int row, int col)
{int count = 0;int i = 0;for (i = 1; i <= row; i++){int j = 0;for (j = 1; j <= col; j++){if (show[i][j] == '*')//统计show中*的个数{count++;}}}if (count == MINE_NUMBER)//若剩余的*数目等于雷的数目,返回1,代表游戏胜利{return 1;}return 0;//否则返回0,代表游戏继续
}

三.扫雷总代码

  • game.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<windows.h>#define ROW 9//扫雷的行数
#define COL 9//扫雷的列数#define ROWS ROW + 2//创建二维数组的行数
#define COLS COL + 2//创建二维数组的列数#define MINE_NUMBER 10//设置雷的个数//声明初始化棋盘的函数
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);//声明打印棋盘的函数
void PrintBoard(char board[ROWS][COLS], int row, int col);//声明布置雷的函数
void SetMine(char mine[ROWS][COLS], int row, int col);//声明标记雷的函数
void MarkMine(char show[ROWS][COLS], int row, int col);//声明删除雷的标记的函数
void DelectMarkMine(char show[ROWS][COLS], int row, int col);//声明第一次排雷是安全的,不是雷并且可以达到展开一片的效果,增加了可玩性
void FirstIsSafe(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y);//声明排查雷的函数
int FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);//声明游戏胜利的函数
int IsWin(char show[ROWS][COLS], int row, int col);

game.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"//printf("\033[36m%c \033[0m", board[i][j]);//打印蓝色
//printf("\033[31m%c \033[0m", board[i][j]);//打印红色//定义初始化棋盘的函数
void InitBoard(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 PrintBoard(char board[ROWS][COLS], int row, int col)
//{
//	int i = 0;
//	//辅助观察棋盘
//	for (i = 0;i <= row;i++)
//	{
//		printf("%d ", i);
//	}
//	printf("\n");
//	//打印棋盘
//	for (i = 1;i <= row;i++)
//	{
//		printf("%d ", i);//辅助观察棋盘
//		int j = 0;
//		for (j = 1;j <= col;j++)
//		{
//			printf("\033[36m%c \033[0m", board[i][j]);//打印蓝色
//		}
//		printf("\n");
//	}
//	printf("--------扫雷--------\n");//辅助观察棋盘
//}//定义打印棋盘的函数
void PrintBoard(char board[ROWS][COLS], int row, int col)
{printf("\033[34m-----------------扫雷-----------------\033[0m\n");//辅助观察棋盘int i = 0;//辅助观察棋盘printf("0 |");for (i = 1;i <= row;i++){printf(" %-2d", i);if (i < row){printf("|");}}printf("\n");printf("--|");for (i = 1; i <= col; i++){printf("---");if (i < row){printf("|");}}printf("\n");//打印棋盘for (i = 1;i <= row;i++){printf("%-2d|", i);//辅助观察棋盘int j = 0;for (j = 1;j <= col;j++){if (board[i][j] == '1' || board[i][j] == '5'){printf("\033[36m %c \033[0m", board[i][j]);//打印蓝色}else if (board[i][j] == '2' || board[i][j] == '6'){printf("\033[32m %c \033[0m", board[i][j]);//打印绿色}else if (board[i][j] == '3' || board[i][j] == '7'){printf("\033[31m %c \033[0m", board[i][j]);//打印红色}else if (board[i][j] == '4' || board[i][j] == '8'){printf("\033[35m %c \033[0m", board[i][j]);//打印紫色}else if (board[i][j] == '!'){printf("\033[31m %c \033[0m", board[i][j]);//打印红色}else{printf(" %c ", board[i][j]);//打印白色}if (j < col){printf("|");}}//辅助观察棋盘printf("\n");if (i < row){printf("--|");}for (j = 1; j <= col; j++){if (i < row){printf("---");if (j < col){printf("|");}}}printf("\n");}
}//定义布置雷的函数
void SetMine(char mine[ROWS][COLS], int row, int col)
{int count = MINE_NUMBER;//雷的个数while (count){int x = rand() % row + 1;int y = rand() % col + 1;if (mine[x][y] != '1'){mine[x][y] = '1';//如果该坐标不是雷,则布置雷count--;}}
}//统计周围雷的个数的函数1.0
//int AroundMineCount(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';
//}//统计周围雷的个数的函数2.0
int AroundMineCount(char mine[ROWS][COLS], int x, int y)
{int i = 0;int count = 0;for (i = x - 1;i <= x + 1;i++){int j = 0;for (j = y - 1;j <= y + 1;j++){count += mine[i][j] - '0';}}return count;
}//已知该坐标不是雷,若周围没有雷,该坐标赋为空,再向外扩展(达到展开一片的效果),否则只显示该坐标周围雷的个数
void ExpandAround(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{if (x >= 1 && x <= ROW && y >= 1 && y <= COL)//限制在棋盘内展开,防止下标越界{int count = AroundMineCount(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] == '*')//只对位排查的坐标进行展开,防止死循环{ExpandAround(mine, show, i, j);}}}}else{show[x][y] = count + '0';//周围有雷,显示雷数}}
}//定义标记雷的函数
void MarkMine(char show[ROWS][COLS], int row, int col)
{int x = 0;int y = 0;while (1){printf("请输入你要标记雷的坐标:>");scanf("%d %d", &x, &y);if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] != '!'){if (show[x][y] == '*'){show[x][y] = '!';system("cls");PrintBoard(show, ROW, COL);break;}else{printf("该坐标已经被排查过,无法标记,请重新输入\n");}}else if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] == '!'){printf("该坐标已经被标记过了,无法二次标记,请重新输入\n");}else{printf("该坐标越界,请重新输入\n");}}
}//定义删除雷的标记的函数
void DelectMarkMine(char show[ROWS][COLS], int row, int col)
{int x = 0;int y = 0;while (1){printf("请输入你要删除雷的标记的坐标:>");scanf("%d %d", &x, &y);if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] == '!'){show[x][y] = '*';system("cls");PrintBoard(show, ROW, COL);break;}else if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] != '!'){printf("该坐标未被标记过,无法删除标记,请重新输入\n");}else{printf("该坐标越界,请重新输入\n");}}
}//定义游戏胜利的函数
int IsWin(char show[ROWS][COLS], int row, int col)
{int count = 0;int i = 0;for (i = 1; i <= row; i++){int j = 0;for (j = 1; j <= col; j++){if (show[i][j] == '*')//统计show中*的个数{count++;}}}if (count == MINE_NUMBER)//若剩余的*数目等于雷的数目,返回1,代表游戏胜利{return 1;}return 0;//否则返回0,代表游戏继续
}//定义排查雷的函数1.0:不能实现展开一片的效果
//int FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
//{
//	int x = 0;
//	int y = 0;
//	int ret1 = 0;
//	while (1)
//	{
//		printf("请输入你要排查的坐标:>");
//		scanf("%d %d", &x, &y);
//		if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] == '*')//判断输入的坐标是否合法且未被排查过
//		{
//			if (mine[x][y] == '1')//该坐标是雷
//			{
//				system("cls");//清空屏幕——头文件windows.h
//				printf("很遗憾,你被雷炸死了,游戏失败!\n");
//				printf("雷的布局如下图所示(0代表不是雷,1代表是雷)\n");
//				PrintBoard(mine, ROW, COL);
//				ret1 = 1;
//				break;
//			}
//			else//该坐标不是雷
//			{
//				system("cls");//清空屏幕——头文件windows.h
//				int count = AroundMineCount(mine, x, y);
//				show[x][y] = count + '0';//该坐标要存放雷的个数
//				int ret2 = IsWin(show, row, col);
//				if (ret2 == 1)
//				{
//					printf("恭喜你!找到了所有的雷,游戏胜利!\n");
//					printf("雷的布局如下图所示(0代表不是雷,1代表是雷)\n");
//					PrintBoard(mine, row, col);
//				}
//				else
//				{
//					PrintBoard(show, ROW, COL);//打印存放展示雷的信息,以便于下一次排雷
//				}
//				break;
//			}
//		}
//		else if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] != '*')//该坐标已经被排查过
//		{
//			printf("该坐标已经被排查过,请重新输入坐标\n");
//		}
//		else//越界
//		{
//			printf("输入的坐标错误,请重新输入\n");
//		}
//	}
//	return ret1;
//}//定义排查雷的函数2.0:可以实现展开一片的效果
//int FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
//{
//	int x = 0;
//	int y = 0;
//	int ret1 = 0;
//	while (1)
//	{
//		printf("请输入你要排查的坐标:>");
//		scanf("%d %d", &x, &y);
//		if (x >= 1 && x <= row && y >= 1 && y <= col)//判断输入的坐标是否合法且未被排查过
//		{
//			if (show[x][y] == '*' || show[x][y] == '!')
//			{
//				if (show[x][y] == '*')
//				{
//				again:
//					if (mine[x][y] == '1')//该坐标是雷
//					{
//						system("cls");//清空屏幕——头文件windows.h
//						printf("很遗憾,你被雷炸死了,游戏失败!\n");
//						printf("雷的布局如下图所示(0代表不是雷,1代表是雷)\n");
//						PrintBoard(mine, ROW, COL);
//						ret1 = 1;
//						break;
//					}
//					else//该坐标不是雷
//					{
//						ExpandAround(mine, show, x, y);//直接展开
//						system("cls");//清空屏幕——头文件windows.h
//						int ret2 = IsWin(show, row, col);
//						if (ret2 == 1)
//						{
//							printf("恭喜你!找到了所有的雷,游戏胜利!\n");
//							printf("雷的布局如下图所示(0代表不是雷,1代表是雷)\n");
//							PrintBoard(mine, row, col);
//						}
//						else
//						{
//							PrintBoard(show, ROW, COL);//打印存放展示雷的信息,以便于下一次排雷
//						}
//						break;
//					}
//				}
//				else
//				{
//					int select = 0;
//					printf("确定要排查标记过的坐标吗? 0.取消排查 1.坚持排查\n");
//					while (1)
//					{
//						printf("请输入(0/1):>");
//						scanf("%d", &select);
//						if (select == 0)
//						{
//							return 0;
//						}
//						else if (select == 1)
//						{
//							goto again;
//						}
//						else
//						{
//							printf("输入错误,请重新输入\n");
//						}
//					}
//				}
//			}
//			else
//			{
//				printf("该坐标已经被排查过,请重新输入\n");
//			}
//		}
//		else//越界
//		{
//			printf("该坐标越界,请重新输入\n");
//		}
//	}
//	return ret1;
//}//定义排查雷的函数3.0:可以实现展开一片的效果
int FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{//第一次排查确保该点不是雷以及周围没有雷,达到网页版扫雷的效果//由于是随机排雷,不能保证该点以及周围有无雷,可以将雷,神不知鬼不觉地换掉static int FindMineCount = 1;if (FindMineCount == 1){int x = 0;int y = 0;while (1){printf("请输入你要排查的坐标:>");scanf("%d %d", &x, &y);if (x >= 1 && x <= row && y >= 1 && y <= col){FirstIsSafe(mine, show, x, y);FindMineCount++;system("cls");//PrintBoard(mine, ROW, COL);PrintBoard(show, ROW, COL);return 0;}else{printf("输入的坐标越界,请重新输入\n");}}}//以后雷的位置就固定下来了else{int x = 0;int y = 0;int ret1 = 0;while (1){printf("请输入你要排查的坐标:>");scanf("%d %d", &x, &y);if (x >= 1 && x <= row && y >= 1 && y <= col)//判断输入的坐标是否合法且未被排查过{if (show[x][y] == '*' || show[x][y] == '!'){if (show[x][y] == '*'){again:if (mine[x][y] == '1')//该坐标是雷{system("cls");//清空屏幕——头文件windows.hprintf("很遗憾,你被雷炸死了,游戏失败!\n");printf("雷的布局如下图所示(0代表不是雷,1代表是雷)\n");PrintBoard(mine, ROW, COL);ret1 = 1;break;}else//该坐标不是雷{ExpandAround(mine, show, x, y);//直接展开system("cls");//清空屏幕——头文件windows.hint ret2 = IsWin(show, row, col);if (ret2 == 1){printf("恭喜你!找到了所有的雷,游戏胜利!\n");printf("雷的布局如下图所示(0代表不是雷,1代表是雷)\n");PrintBoard(mine, row, col);}else{PrintBoard(show, ROW, COL);//打印存放展示雷的信息,以便于下一次排雷}break;}}else{int select = 0;printf("确定要排查标记过的坐标吗? 0.取消排查 1.坚持排查\n");while (1){printf("请输入(0/1):>");scanf("%d", &select);if (select == 0){return 0;}else if (select == 1){goto again;}else{printf("输入错误,请重新输入\n");}}}}else{printf("该坐标已经被排查过,请重新输入\n");}}else//越界{printf("该坐标越界,请重新输入\n");}}FindMineCount++;return ret1;}
}//定义第一次排雷是安全的,不是雷并且可以达到展开一片的效果,增加了可玩性
void FirstIsSafe(char mine[ROWS][COLS], char show[ROWS][COLS],int x, int y)
{//若该点不是雷且周围无雷,直接展开if (mine[x][y] != '1' && AroundMineCount(mine, x, y) == 0){ExpandAround(mine, show, x, y);}//否则分情况else{//先判断该点是不是雷if (mine[x][y] == '1')//该点为雷,将其移走{mine[x][y] = '0';while (1){int x1 = rand() % ROW + 1;int y1 = rand() % COL + 1;if (mine[x1][y1] != '1' && x1 != x && y1 != y){mine[x1][y1] = '1';break;}}}//以及将雷移走了,在判断周围是否有雷if (AroundMineCount(mine, x, y) != 0)//该点周围有雷,将它们移走{int count = AroundMineCount(mine, x, y);//计算周围雷的数量int i = 0;for (i = x - 1;i <= x + 1; i++){int j = 0;for (j = y - 1; j <= y + 1; j++){mine[i][j] = '0';//将周围的雷变为无雷的状态}}while (count){int x1 = rand() % ROW + 1;int y1 = rand() % COL + 1;if ((x1 != x - 1 && y1 != y - 1)&&(x1 != x - 1 && y1 != y)&&(x1 != x - 1 && y1 != y + 1)&&(x1 != x && y1 != y - 1)&&(x1 != x && y1 != y)&&(x1 != x && y1 != y + 1)&&(x1 != x + 1 && y1 != y - 1)&&(x1 != x + 1 && y1 != y)&&(x1 != x + 1 && y1 != y + 1))//不能将雷移回来{if (mine[x1][y1] != '1'){mine[x1][y1] = '1';//如果该坐标不是雷,则布置雷count--;}}}}ExpandAround(mine, show, x, y);}
}
  • test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"//菜单
void Menu()
{printf("-------------------------------------\n");printf("|              扫雷游戏!           |\n");printf("|        0.exit        1.play       |\n");printf("-------------------------------------\n");
}
void Game()
{//创建两个二维数组char mine[ROWS][COLS];//存放该点是不是雷    (0代表不是雷,1代表是雷)char show[ROWS][COLS];//存放该点周围有多少雷(数字代表周围有多少雷,*代表未知)//初始化棋盘InitBoard(mine, ROWS, COLS, '0');InitBoard(show, ROWS, COLS, '*');//打印棋盘//PrintBoard(mine, ROW, COL);//PrintBoard(show, ROW, COL);//布置雷SetMine(mine, ROW, COL);//PrintBoard(mine, ROW, COL);PrintBoard(show, ROW, COL);int select = 0;int ret1 = 0;int ret2 = 0;while (ret1 != 1 && ret2 != 1){printf("1.排查雷  2.标记雷  3.删除雷的标记\n");printf("请选择:>");scanf("%d", &select);switch (select){case 1:ret1 = FindMine(mine, show, ROW, COL);ret2 = IsWin(show, ROW, COL);break;case 2:MarkMine(show, ROW, COL);break;case 3:DelectMarkMine(show, ROW, COL);break;default:printf("输入的值错误,请重新输入\n");break;}}
}
int main()
{int input = 0;srand((unsigned int)time(NULL));//设置随机种子do{Menu();printf("请输入(0/1):>");scanf("%d", &input);switch (input){case 1:Game();break;case 0:printf("退出游戏\n");break;default:printf("输入错误,请重新输入\n");break;}} while (input);return 0;
}

四.截图

  • 最后附上其中一张截图。

请添加图片描述

  • 感兴趣的可以去玩一下哈,最后到这文章也就结束了,日后还会更新更多的小游戏,例如贪吃蛇等等,喜欢的话可以三连下下哦,以防找不到了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/319188.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue入门到关门之Vue项目工程化

一、创建Vue项目 1、安装node环境 官网下载&#xff0c;无脑下一步&#xff0c;注意别放c盘就行 Node.js — Run JavaScript Everywhere (nodejs.org) 需要两个命令 npm---->pipnode—>python 装完检查一下&#xff0c;hello world检测&#xff0c;退出crtlc 2、搭建vu…

SwiftUI 5.0(iOS 17.0,macOS 14.0+)新 Inspector 辅助视图之趣味漫谈

概览 在 SwiftUI 开发中,苹果为我们提供了多种辅助视图用来显示额外信息从而极大丰富了应用的表现力,比如:Alert、Sheet、ContextMenu 等等。 从 SwiftUI 5.0(iOS 17+)开始, 又增加了一种全新的辅助视图:Inspector。 在本篇博文中,您将学到如下内容: 概览1. Inspe…

LangChain入门:24.通过Baby AGI实现自动生成和执行任务

随着 ChatGPT 的崭露头角,我们迎来了一种新型的代理——Autonomous Agents(自治代理或自主代理)。 这些代理的设计初衷就是能够独立地执行任务,并持续地追求长期目标。 在 LangChain 的代理、工具和记忆这些组件的支持下,它们能够在无需外部干预的情况下自主运行,这在真…

1-手工sql注入(基础篇)

关于靶场: 本章使用的靶场是pikachu和dvwa&#xff1a;针对于pikachu靶场上的sql注入pikachu和dvwa靶场下载地址&#xff1a;GitHub - digininja/DVWA: Damn Vulnerable Web Application (DVWA)GitHub - zhuifengshaonianhanlu/pikachu: 一个好玩的Web安全-漏洞测试平台 一. sq…

QT中基于TCP的网络通信

QT中基于TCP的网络通信 QTcpServer公共成员函数信号 QTcpSocket公共成员函数信号 通信流程服务器端通信流程代码 客户端通信流程代码 多线程网络通信SendFileClientSendFileServer 使用Qt提供的类进行基于TCP的套接字通信需要用到两个类&#xff1a; QTcpServer&#xff1a;服…

yolo系列目标分类模型训练结果查看

常用指标&#xff1a; 准确率&#xff08;Accuracy&#xff09; Accuracy (TP TN) / (TP TN FP FN) 正确预测的样本数占总样本数的比例。精确率&#xff08;Precision&#xff09; Precision TP / (TP FP) 在所有预测为正的样本中&#xff0c;真正的正样本所占的比例。召…

安装vscode基础配置,es6基础语法,

https://code.visualstudio.com/ es6 定义变量 const声明常量&#xff08;只读变量&#xff09; // 1、声明之后不允许改变 const PI “3.1415926” PI 3 // TypeError: Assignment to constant variable. // 2、一但声明必须初始化&#xff0c;否则会报错 const MY_AGE /…

Word文件后缀

Word文件后缀 .docx文件为Microsoft Word文档后缀名&#xff0c;基于XML文件格式 .dotm为Word启用了宏的模板 .dotx为Word模板 .doc为Word97-2003文档&#xff0c;二进制文件格式 参考链接 Word、Excel 和 PowerPoint 的文件格式参考 Learn Microsoft

专项技能训练五《云计算网络技术与应用》实训7-1:安装mininet

文章目录 mininet安装1. 按6-1教程安装opendaylight控制器。2. 按6-2教程安装RYU控制器。3. 按5-1教程安装openvswitch虚拟交换机并开启服务。4. 将老师所给mininet安装包试用winSCP传送至电脑端。5. 安装net-tools。6. 安装mininet7. 安装完成后&#xff0c;使用命令建立拓扑&…

基于Springboot的旅游管理系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的旅游管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&…

服务网关GateWay原理

文章目录 自动装配核心类GatewayAutoConfigurationDispatcherHandler请求处理阶段apply方法httpHandler#handle方法WebHandler#handle方法DispatchHanlder#handle方法第一步 getHandler获取请求映射第二步 invokeHandler 请求适配第三步 handleResult请求处理总结 上一篇博文我…

【机器学习原理】决策树从原理到实践

基于树的模型是机器学习中非常重要的一类模型&#xff0c;最基础的就是决策树&#xff0c;本篇主要讲述决策树的原理和几类最常见的决策树算法&#xff0c;这也是更复杂的树模型算法的基础。 参考文章&#xff1a; 1.CSDN-基于熵的两个模型(ID3,C4.5)比较详细&#xff0c;有数字…

富文本编辑器 iOS

https://gitee.com/klkxxy/WGEditor-mobile#wgeditor-mobile 采用iOS系统浏览器做的一款富文本编辑器工具。 原理就是使用WKWebView加载一个本地的一个html文件&#xff0c;从而达到编辑器功能的效果&#xff01; 由于浏览器的一些特性等&#xff0c;富文本编辑器手机端很难做…

零基础学习数据库SQL语句之查询表中数据的DQL语句

是用来查询数据库表的记录的语句 在SQL语句中占有90%以上 也是最为复杂的操作 最为繁琐的操作 DQL语句很重要很重要 初始化数据库和表 USE dduo;create table tb_emp(id int unsigned primary key auto_increment comment ID,username varchar(20) not null unique comment…

打工人必备的定时自动化神器

作为打工人&#xff0c;时间管理是一个很重要的观念。面对繁杂的事务&#xff0c;往往会造成某些事情忘记的情况 今天苏音看到一款自动化的任务神器&#xff0c;据说可以实现一键自动定时&#xff0c;并且还支持语音报时和定时任务执行 让我们一起来看看吧。 软件名字——ZT…

【leetcode】数组和相关题目总结

1. 两数之和 直接利用hashmap存储值和对于索引&#xff0c;利用target-nums[i]去哈希表里找对应数值。返回下标。 class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {unordered_map<int, int> mp;vector<int> res;fo…

深入理解 Java 并发:AbstractQueuedSynchronizer 源码分析

序言 在多线程编程中&#xff0c;同步机制是保障线程安全和协调线程之间操作顺序的重要手段。AQS 作为 Java 中同步机制的基础框架&#xff0c;为开发者提供了一个灵活且高效的同步工具。本文将通过对 AQS 源码的分析&#xff0c;解读 AQS 的核心实现原理&#xff0c;并深入探…

web3风格的网页怎么设计?分享几个,找找感觉。

web3风格的网站是指基于区块链技术和去中心化理念的网站设计风格。这种设计风格强调开放性、透明性和用户自治&#xff0c;体现了Web3的核心价值观。 以下是一些常见的Web3风格网站设计元素&#xff1a; 去中心化标志&#xff1a;在网站的设计中使用去中心化的标志&#xff0…

ElasticSearch教程入门到精通——第二部分(基于ELK技术栈elasticsearch 7.x新特性)

ElasticSearch教程入门到精通——第二部分&#xff08;基于ELK技术栈elasticsearch 7.x新特性&#xff09; 1. JavaAPI-环境准备1.1 新建Maven工程——添加依赖1.2 HelloElasticsearch 2. 索引2.1 索引——创建2.2 索引——查询2.3 索引——删除 3. 文档3.1 文档——重构3.2 文…

OpenCV 实现重新映射(53)

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV 实现霍夫圆变换(52) 下一篇 :OpenCV实现仿射变换(54) 目标 在本教程中&#xff0c;您将学习如何&#xff1a; 一个。使用 OpenCV 函数 cv&#xff1a;&#xff1a;remap 实现简…