扫雷
创作不易,还请观众老爷耐心看完儿!
目录
1.扫雷框架
2.初始化棋盘
这是用户看到的棋盘,9*9大小,需要自己去排查雷
为了程序员更好地去控制扫雷,我们需要两个数组
- 程序员看的雷区数组 -- 里面存放着雷的位置 -- 11*11大小
- 用户看的展示数组 -- 全是*号,需要用户去排查 -- 11*11大小
使这两个数组一样大的原因:是为了使我们后面设计的接口函数更加兼容,即使展示的数组本质是11*11大小,但是我们打印的时候打印9*9就可以了
为了可以提高代码可维护性和兼容性,我们将数组的行数和列数,使用宏来替换
-
-
#define ROWS 11
-
#define COLS 11
-
-
-
#define ROW 9
-
#define COL 9
-
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set)
-
{
-
for (
int i =
0; i < rows; ++i)
-
{
-
for (
int j =
0; j < cols; ++j)
-
{
-
board[i][j] = set;
-
}
-
}
-
}
我们将如果初始化的内容以字符的形式作为参数传进去,上述代码就可以很好地解决了兼容性,这一个函数可以解决雷区棋盘和展示用户的棋盘的初始化。
3.打印棋盘
打印棋盘大家可以充分发挥自己的艺术细胞,根据自己的喜好去设置棋盘的打印格式。下面博主提供两种。
格式1:
-
void print_mineline(int row)//打印分割线
-
{
-
for (
int i =
0; i < row; ++i)
-
{
-
if (i == row /
2)
-
{
-
printf(
"扫雷");
-
}
-
printf(
"==");
-
}
-
printf(
"\n");
-
}
-
-
void DisplayBoard(char board[ROWS][COLS], int row, int col)
-
{
-
print_mineline(row);
//先打印一行分割线
-
//打印列号
-
for (
int j =
0; j <= col; ++j)
-
{
-
if (
0 == j)
//将列对齐
-
{
-
printf(
" ");
-
continue;
-
}
-
printf(
"%d ", j);
-
}
-
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");
-
}
-
print_mineline(row);
//最后再打印一行分割线
-
printf(
"\n");
-
}
效果:
格式2:
-
void DisplayBoard(char board[ROWS][COLS], int row, int col)
-
{
-
//打印列号
-
for (
int j =
0; j <= col; ++j)
-
{
-
if (j ==
0)
-
{
-
printf(
" ");
-
continue;
-
}
-
printf(
" %d ", j);
-
}
-
printf(
"\n\n");
-
-
for (
int i =
1; i <= row; ++i)
-
{
-
//打印列号
-
//1.打印第一部分
-
printf(
" %d ", i);
-
-
for (
int j =
1; j <= col; ++j)
-
{
-
printf (
" %c ", board[i][j]);
-
if (j <= col
-1)
-
printf(
"|");
-
}
-
printf(
"\n");
-
//2.打印第二部分
-
if (i <= row -
1)
-
{
-
printf(
" ");
-
for (
int j =
1; j <= col; ++j)
-
{
-
printf(
"---");
-
if (j <= col -
1)
-
printf(
"+");
-
}
-
}
-
printf(
"\n");
-
}
-
-
}
效果:
但是格式2的缺点是如果棋盘是9*9以上大小,那么存在一些对齐问题,当然厉害的同学可以改善一下。这是提供的两个思路
4.布置雷
根据雷的个数n,随机布置n个雷,雷的个数可以根据用户的选择来定,例如
1.简单 -- 10个雷
2.普通 -- 20个雷
3.困难 -- 40个雷
4.疯狂 -- 80个雷
当然以上的布置雷都是限制在了9*9的棋盘中,大家也可以根据难度设计棋盘的大小,尺寸
为了代码的可维护性以及可读性,我们将不同难度下的雷的个数也使用宏来替换,以及可以使用枚举来帮助我们实现难度选择
-
//雷的个数
-
#define COUNT_EASY 10
-
-
#define COUNT_ORD 20
-
-
#define COUNT_DIF 40
-
-
#define COUNT_FRE 80
-
-
-
//难度等级
-
enum
degree
-
{
-
EASY =
1,
//简单
-
ORD,
//普通
-
DIF,
//困难
-
FRE
//疯狂
-
};
-
void menu_degree()//难度选择菜单 -- 跟我们的枚举常量值一致
-
{
-
printf(
"==========1.简单==========\n");
-
printf(
"==========2.普通==========\n");
-
printf(
"==========3.困难==========\n");
-
printf(
"==========4.疯狂==========\n");
-
}
-
-
-
int SetMine(char board[ROWS][COLS], int row, int col)
-
{
-
int count =
0;
//雷的个数
-
-
system(
"cls");
//我们游戏开始部分肯定会要菜单,所以这里使用一个清屏功能
-
int input =
0;
-
-
do
-
{
-
menu_degree();
-
-
printf(
"请选择扫雷难度\n");
-
scanf(
"%d", &input);
-
switch (input)
-
{
-
case EASY:
-
count = COUNT_EASY;
-
break;
-
case ORD:
-
count = COUNT_ORD;
-
break;
-
case DIF:
-
count = COUNT_DIF;
-
break;
-
case FRE:
-
count = COUNT_FRE;
-
break;
-
default:
-
printf(
"输入错误,请重新输入");
-
break;
-
}
-
}
while (input != EASY && input != ORD &&
-
input != DIF && input != FRE);
-
-
int _count = count;
//保存count的值
-
-
while (count)
-
{
-
//随机生成十个雷,放在中间的9*9中
-
int x =
rand() % row +
1;
-
int y =
rand() % col +
1;
-
//判断该位置是否已经布置了雷
-
if (board[x][y] ==
'0')
//没布置
-
{
-
board[x][y] =
'1';
-
--count;
-
}
-
}
-
-
return _count;
//这里我们将布置雷的个数返回了,之后要用
-
}
解释:布置雷只需要在9*9中布置,所以咱们传参传的是row,col而不是rows,cols
5.排查雷
游戏的核心部分:
1.玩家输入需要排查雷的坐标
2.判断是否有效
3.根据雷区判断,如果是雷,游戏结束
4.如果不是雷,则在显示区棋盘中赋上雷的个数
5.如果雷的个数是0,还是考虑将周围8个位置进行递归展开
5.1排查雷的功能
在排查雷的函数中,mine -- 雷区棋盘,show -- 展示区棋盘,是需要配合使用的。例如判断一个位置是否是雷,我们要去雷区棋盘找,而即使修正展示区的位置内容,我们需要去show棋盘中修改,并且即使打印出来给用户观看。
判断输赢:
- 如果输入的坐标位置是雷,则输掉游戏
- 如果不是雷的位置都被排查完了,那么玩家赢得游戏胜利
-
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col,int count)
-
{
-
//雷的个数通过SetMine的返回值可以得到,然后我们通过参数的形式,传给FineMine
-
//因为我们的版本雷的个数不固定
-
-
int chance = row*col - count;
//记录不是雷的个数
-
-
while (chance)
-
{
-
system(
"cls");
-
-
DisplayBoard(show, row, col);
//展示show棋盘
-
-
printf(
"请输入需要排查雷的坐标\n");
-
int x =
0, y =
0;
-
scanf(
"%d %d", &x, &y);
-
//检查坐标的合法性
-
if (x >=
1 && x <= row && y >=
1 && y <= col)
-
{
-
//不是雷--显示雷的个数
-
if (mine[x][y] !=
'1')
//没点到雷
-
{
-
//递归展开该位置
-
boom_mine(mine, show, x, y, &chance);
//将该位置周围的展开,传坐标
-
//展开雷chance是会减少的,使用址传递,在函数里面修改chance
-
}
-
//如何是雷--爆炸
-
else
-
{
-
printf(
"很遗憾,你被炸死了\n");
-
DisplayBoard(mine, row, col);
-
break;
-
}
-
}
-
else
-
{
-
printf(
"坐标不合法\n");
-
}
-
}
-
//判断是否赢了-- 将所有不是雷的地方都排查出来了
-
if (chance ==
0)
-
{
-
printf(
"排雷成功,游戏胜利\n");
-
}
-
Sleep(
2000);
//停顿两秒看下结果
-
}
5.2递归展开功能bool_mine
查出该位置周围8个位置的雷的个数
- 0个以上的雷 -- 将这个show棋盘对应的位置赋值为该数字字符
- 0个雷 -- 先这个show棋盘对应的位置赋值为该数字字符,再对该位置周围8个位置进行递归展开
防止出现死递归,我们思考一下递归限制条件:
- 如果该位置越界,那么不做任何处理
- 如果该位置已经被排查过了就不做任何处理
如果你的代码出现了错误,调式一看发现有个stack overflow这个报错,原因之一就是你写出死递归了
-
void boom_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int* pchance)
-
{
-
//递归限制条件
-
//1.如果越界了,直接返回
-
if (x ==
0 || x == ROW+
1 || y ==
0 || y == COL+
1)
-
{
-
return;
-
}
-
//2.如果该位置已经展开,直接返回
-
if (show[x][y] !=
'*')
-
{
-
return;
-
}
-
//3.剩下的就是需要展开的情况
-
int count =
get_mine(mine, x, y);
//计算该位置的雷的个数
-
-
if (count)
//如果雷的个数不为0,直接展开
-
{
-
show[x][y] = count +
'0';
-
--(*pchance);
//不是雷的个数也减一
-
return;
//这里需要返回,不然无限递归下去了
-
}
-
else
//如果雷的个数是0
-
{
-
//对周围八个位置进行展开操作
-
show[x][y] = count +
'0';
-
--(*pchance);
-
boom_mine(mine, show, x -
1, y -
1, pchance);
-
boom_mine(mine, show, x -
1, y, pchance);
-
boom_mine(mine, show, x -
1, y +
1, pchance);
-
boom_mine(mine, show, x, y -
1, pchance);
-
boom_mine(mine, show, x, y +
1, pchance);
-
boom_mine(mine, show, x +
1, y -
1, pchance);
-
boom_mine(mine, show, x +
1, y, pchance);
-
boom_mine(mine, show, x +
1, y +
1, pchance);
-
}
-
}
5.3计算该位置的雷的个数
这里就很好地体现了我们在雷区棋盘中放字符0和字符1的好处。
我们将 周围8个位置的字符加进来 - 8*字符0 = 雷的个数
-
int get_mine(char mine[ROWS][COLS], int x, int y)
-
{
-
//周围八个位置的内容 - 8 * '0' == 周围雷的个数
-
return (mine[x -
1][y -
1] + mine[x -
1][y] + mine[x -
1][y +
1]
-
+ mine[x][y -
1] + mine[x][y +
1]
-
+ mine[x +
1][y -
1] + mine[x +
1][y] + mine[x +
1][y +
1] -
8 *
'0');
-
}
6.扫雷的全部代码
上述的操作是讲解扫雷的核心函数的实现,下面我们进行分块编程,并且设计一些菜单,将他们整合在一起。
game.h
-
#define _CRT_SECURE_NO_WARNINGS 1
-
-
#pragma once
-
-
#include<stdio.h>
-
#include<stdlib.h>
-
#include<time.h>
-
#include<windows.h>
-
-
//操作棋盘的时候用的
-
#define ROWS 11
-
#define COLS 11
-
-
//用户看到的
-
#define ROW 9
-
#define COL 9
-
-
//雷的个数
-
#define COUNT_EASY 10
-
-
#define COUNT_ORD 20
-
-
#define COUNT_DIF 40
-
-
#define COUNT_FRE 80
-
-
enum
degree
-
{
-
EASY =
1,
//简单
-
ORD,
//普通
-
DIF,
//困难
-
FRE
//疯狂
-
};
-
-
-
//初始化棋盘
-
void InitBoard(char board[ROWS][COLS],int rows,int cols,char set);
-
-
//打印棋盘
-
void DisplayBoard(char board[ROWS][COLS], int row, int col);
-
-
//布置雷
-
int SetMine(char board[ROWS][COLS], int row, int col);
-
-
//排查类
-
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col,int count);
game.c
打印的两个方式都放在里面了
-
#include"game.h"
-
-
-
//初始化棋盘
-
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set)
-
{
-
for (
int i =
0; i < rows; ++i)
-
{
-
for (
int j =
0; j < cols; ++j)
-
{
-
board[i][j] = set;
-
}
-
}
-
}
-
-
//打印棋盘--将行列号也打印出来
-
//void print_mineline(int row)
-
//{
-
// for (int i = 0; i < row; ++i)
-
// {
-
// if (i == row / 2)
-
// {
-
// printf("扫雷");
-
// }
-
// printf("==");
-
// }
-
// printf("\n");
-
//}
-
-
//void DisplayBoard(char board[ROWS][COLS], int row, int col)
-
//{
-
// print_mineline(row);
-
// //打印列号
-
// for (int j = 0; j <= col; ++j)
-
// {
-
// if (0 == j)//将列对齐
-
// {
-
// printf(" ");
-
// continue;
-
// }
-
// printf("%d ", j);
-
// }
-
// 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");
-
// }
-
// print_mineline(row);
-
// printf("\n");
-
//}
-
-
void DisplayBoard(char board[ROWS][COLS], int row, int col)
-
{
-
//打印列号
-
for (
int j =
0; j <= col; ++j)
-
{
-
if (j ==
0)
-
{
-
printf(
" ");
-
continue;
-
}
-
printf(
" %d ", j);
-
}
-
printf(
"\n\n");
-
-
for (
int i =
1; i <= row; ++i)
-
{
-
//打印列号
-
//1.打印第一部分
-
printf(
" %d ", i);
-
-
for (
int j =
1; j <= col; ++j)
-
{
-
printf (
" %c ", board[i][j]);
-
if (j <= col
-1)
-
printf(
"|");
-
}
-
printf(
"\n");
-
//2.打印第二部分
-
if (i <= row -
1)
-
{
-
printf(
" ");
-
for (
int j =
1; j <= col; ++j)
-
{
-
printf(
"---");
-
if (j <= col -
1)
-
printf(
"+");
-
}
-
}
-
printf(
"\n");
-
}
-
-
}
-
-
void menu_degree()
-
{
-
printf(
"==========1.简单==========\n");
-
printf(
"==========2.普通==========\n");
-
printf(
"==========3.困难==========\n");
-
printf(
"==========4.疯狂==========\n");
-
}
-
-
//布置雷
-
-
//mine中,是雷就布置1,不是雷就是0
-
-
//布置雷的函数返回值 -- 返回雷的个数
-
-
int SetMine(char board[ROWS][COLS], int row, int col)
-
{
-
int count =
0;
//雷的个数
-
-
system(
"cls");
-
int input =
0;
-
-
do
-
{
-
menu_degree();
-
-
printf(
"请选择扫雷难度\n");
-
scanf(
"%d", &input);
-
switch (input)
-
{
-
case EASY:
-
count = COUNT_EASY;
-
break;
-
case ORD:
-
count = COUNT_ORD;
-
break;
-
case DIF:
-
count = COUNT_DIF;
-
break;
-
case FRE:
-
count = COUNT_FRE;
-
break;
-
default:
-
printf(
"输入错误,请重新输入");
-
break;
-
}
-
}
while (input != EASY && input != ORD &&
-
input != DIF && input != FRE);
-
-
int _count = count;
//保存count的值
-
-
while (count)
-
{
-
//随机生成十个雷,放在中间的9*9中
-
int x =
rand() % row +
1;
-
int y =
rand() % col +
1;
-
//判断该位置是否已经布置了雷
-
if (board[x][y] ==
'0')
//没布置
-
{
-
board[x][y] =
'1';
-
--count;
-
}
-
//布置了
-
}
-
-
return _count;
-
}
-
-
//设计排查雷 -- 展开功能 -- 返回展开的雷的个数
-
-
-
-
//排查雷
-
int get_mine(char mine[ROWS][COLS], int x, int y)
-
{
-
//周围八个位置的内容 - 8 * '0' == 周围雷的个数
-
return (mine[x -
1][y -
1] + mine[x -
1][y] + mine[x -
1][y +
1]
-
+ mine[x][y -
1] + mine[x][y +
1]
-
+ mine[x +
1][y -
1] + mine[x +
1][y] + mine[x +
1][y +
1] -
8 *
'0');
-
}
-
-
void boom_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int* pchance)
-
{
-
//递归限制条件
-
//1.如果越界了,直接返回
-
if (x ==
0 || x == ROW+
1 || y ==
0 || y == COL+
1)
-
{
-
return;
-
}
-
//2.如果该位置已经展开,直接返回
-
if (show[x][y] !=
'*')
-
{
-
return;
-
}
-
//3.剩下的就是需要展开的情况
-
int count =
get_mine(mine, x, y);
//计算该位置的雷的个数
-
-
if (count)
//如果雷的个数不为0,直接展开
-
{
-
show[x][y] = count +
'0';
-
--(*pchance);
//不是雷的个数也减一
-
return;
//这里需要返回,不然无限递归下去了
-
}
-
else
//如果雷的个数是0
-
{
-
//对周围八个位置进行展开操作
-
show[x][y] = count +
'0';
-
--(*pchance);
-
boom_mine(mine, show, x -
1, y -
1, pchance);
-
boom_mine(mine, show, x -
1, y, pchance);
-
boom_mine(mine, show, x -
1, y +
1, pchance);
-
boom_mine(mine, show, x, y -
1, pchance);
-
boom_mine(mine, show, x, y +
1, pchance);
-
boom_mine(mine, show, x +
1, y -
1, pchance);
-
boom_mine(mine, show, x +
1, y, pchance);
-
boom_mine(mine, show, x +
1, y +
1, pchance);
-
}
-
}
-
-
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col,int count)
-
{
-
int chance = row*col - count;
//记录不是雷的个数
-
-
while (chance)
-
{
-
system(
"cls");
-
-
DisplayBoard(mine, row, col);
-
-
DisplayBoard(show, row, col);
-
printf(
"请输入需要排查雷的坐标\n");
-
int x =
0, y =
0;
-
scanf(
"%d %d", &x, &y);
-
//检查坐标的合法性
-
if (x >=
1 && x <= row && y >=
1 && y <= col)
-
{
-
//如何是雷--爆炸
-
//不是雷--显示雷的个数
-
if (mine[x][y] !=
'1')
//没点到雷
-
{
-
boom_mine(mine, show, x, y, &chance);
//将该位置周围的展开,传坐标
-
}
-
else
-
{
-
printf(
"很遗憾,你被炸死了\n");
-
DisplayBoard(mine, row, col);
-
break;
-
}
-
}
-
else
-
{
-
printf(
"坐标不合法\n");
-
}
-
}
-
//判断是否赢了-- 将所有不是雷的地方都排查出来了
-
if (chance ==
0)
-
{
-
printf(
"排雷成功,游戏胜利\n");
-
}
-
Sleep(
2000);
-
}
-
test.c
-
#include"game.h"
-
-
void game()//游戏函数
-
{
-
srand((
unsigned
int)
time(
NULL));
-
-
char mine[ROWS][COLS] = {
0 };
//11*11的数组便于操作
-
char show[ROWS][COLS] = {
0 };
//show数组与mine数组尺寸类型一样,可以使函数更兼容
-
-
//1.初始化棋盘
-
InitBoard(mine, ROWS, COLS,
'0');
//将mine初始化为字符0
-
InitBoard(show, ROWS, COLS,
'*');
//将show初始化为*
-
-
//3.布置雷
-
int count =
SetMine(mine,ROW,COL);
//布置雷是在mine中布置
-
//是在9*9中布置
-
//4.排查雷
-
FindMine(mine, show, ROW, COL,count);
-
}
-
-
void menu()
-
{
-
printf(
"=================================\n");
-
printf(
"====== 1. play ======\n");
-
printf(
"====== 0. exit ======\n");
-
printf(
"=================================\n");
-
}
-
-
void test()//测试函数
-
{
-
int input =
0;
-
do
-
{
-
system(
"cls");
-
menu();
-
printf(
"请输入->:");
-
scanf(
"%d", &input);
-
switch (input)
-
{
-
case
1:
-
game();
-
break;
-
case
0:
-
printf(
"游戏退出\n");
-
break;
-
default:
-
printf(
"错误输出\n");
-
Sleep(
1000);
-
break;
-
}
-
}
while (input);
-
}
-
-
int main()
-
{
-
test();
-
return
0;
-
}
7.作者试玩环节
打完一把还是需要点时间的,博主就不玩完了。
当然了,上述代码肯定还存在着很多不足的地方,如果大家有什么好的建议欢迎在评论区留言,我们下期见吧。
转载:https://blog.csdn.net/AAxiao_bei/article/details/127963624