扫雷
一.扫雷设计思路
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.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:可以实现展开一片的效果
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:结合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.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");
}
}
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.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");
}
}
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;
}
四.截图
- 最后附上其中一张截图。
- 感兴趣的可以去玩一下哈,最后到这文章也就结束了,日后还会更新更多的小游戏,例如贪吃蛇等等,喜欢的话可以三连下下哦,以防找不到了。