🧸🧸🧸各位巨佬大家好,我是猪皮兄弟
这里是下面要讲的知识内容🥳🥳🥳
文章目录
🚒前言
今天我们学习的内容是扫雷游戏(可炸开一片和可标记)。是不是发现现在电脑上没有系统自带的扫雷游戏,那有问题吗?没有问题,今天手把手得带大家来实现一下扫雷游戏🐷,废话不多说,我们直接开始
一、🚀头文件部分(头文件的包含和一些函数接口)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define SET_NUM 10
//初始化雷盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char init);
//随机埋雷操作
void SetMine(char board[ROWS][COLS], int row, int col, int num);
//打印雷盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);
//排雷操作
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS],char state[ROWS][COLS]);
//数该雷块的周围八个雷块埋了多少个累
int get_mine_count(char board[ROWS][COLS], int x, int y);
//重点!!递归实现展开一片
void Boom(char show[ROWS][COLS],char mine[ROWS][COLS],char state[ROWS][COL], int x, int y);
二、🚀主函数部分(main)
首先说下为什么使用9x9的雷盘要初始化化成11x11呢?
1.首先,找周围的八个雷会简单很多。因为只在需要的9x9的二维数组里进行埋雷,所以多出来的这两行两列(雷盘上下,左右各多一行)是没有雷的,所以不影响。
2.其次,方便打印棋盘的行号和列号,如图:
下面是主函数和菜单的实现
void menu()
{
printf("*******************\n");
printf("*******0.EXIT******\n");
printf("*******1.PLAY******\n");
printf("*******2.RULE******\n");
printf("*******************\n");
}
void Rule_Menu()
{
printf("1.输入坐标进行雷的排查\n");
printf("2.输入b进行雷的标记\n");
printf("3.显示数字表示包括自己在内的九宫格中雷的数目\n");
system("pause");
}
void game()
{
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
char state[ROWS][COLS] = { 0 };
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
InitBoard(state, ROWS, COLS, '0');
SetMine(mine, ROW, COL, SET_NUM);
DisplayBoard(show, ROW, COL);
/*DisplayBoard(mine, ROW, COL);*/
FindMine(mine, show,state);
}
int main()
{
int input;
srand((unsigned int)time(NULL));
do
{
menu();
printf("\n");
printf("请选择:> ");
rewind(stdin);
scanf("%d", &input);
switch (input)
{
case 1:
printf("进入游戏......\n");
game();
break;
case 0:
printf("退出游戏......\n");
exit(-1);
break;
case 2:
printf("查看规则......\n");
Rule_Menu();
break;
default:
printf("输入错误,请重新输入......\n");
break;
}
} while (1);
return 0;
}
三、🚀自定义函数的实现
声明一下
show是玩家可以看到的数组
mine是用于埋雷和排雷的数组
state是用于后面递归展开时定义是否递归的状态的数组
一、⛄初始化雷盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char init)
{
//char init说明你想把数组里面定义成什么内容
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
board[i][j] = init;
}
}
}
二、⛄随机埋雷
注意:要使用rand函数,要在主函数中加入
srand(unsigned int)time(NULL);
注意srand写的位置,你比如说要写一个才随机数字大小的游戏,那么久只能写在主函数里,不然每次猜数字都会生成一个随机值,这不是拿阳寿猜答案嘛。
void SetMine(char board[ROWS][COLS], int row, int col, int num)
{
while (num)
{
int x = (rand() % 9) + 1;
int y = (rand() % 9) + 1;
if (board[x][y] != 1)
{
board[x][y] = '1';
num--;
}
}
}
三、⛄打印雷盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
printf("------扫雷游戏------\n");
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");
}
}
效果展示
四、⛄获取周围雷的数目
这里需要说一下字符和数字之间的相互转换
int get_mine_count(char board[ROWS][COLS], int x, int y)
{
return(board[x - 1][y] +
board[x - 1][y - 1] +
board[x][y - 1] +
board[x + 1][y - 1] +
board[x + 1][y] +
board[x + 1][y + 1] +
board[x][y + 1] +
board[x - 1][y + 1]
-8 * '0');
}
五、⛄自定义Boom函数递归实现炸开一片(重点!)
注意!!!
如果不进行任何条件的约束,只是往周围进行递归的话,就会死循环
这是哇网页版的扫雷,我们来看卡看它的边界条件,是不是遇到数字就停止这一块雷块的递归,好,我们下面来模仿实现一下
蓝色圈代表排雷点,黑色线代表向周围八个展开,如下图,演示了展开方向
大家想象一下,约束条件也就当某一个雷块周围有雷时,会调用get_mine_count函数数周围的雷,然后在show数组中会显示出来,当遇到这一块显示数字了的,就停止递归,而且不能超过边界,也就是说x>=1&&x<=ROW;y>=1&&y<=COL
下面是对Boom的实现
void Boom(char show[ROWS][COLS], char mine[ROWS][COLS], char state[ROWS][COLS],int x, int y)
{
state[x][y] = '1';
if (x<1 || x>ROW || y<1 || y>COL)
return;
if (show[x][y] != ' ')
return;
else
{
/*if (get_mine_count(mine, x, y) == 0)
show[x][y] = ' ';
else
show[x][y] = get_mine_count(mine, x, y) + '0';*/
//将周围8个先show出来,然后递归boom
if (get_mine_count(mine, x - 1, y) == 0)
show[x - 1][y] = ' ';
else
show[x - 1][y] = get_mine_count(mine, x - 1, y) + '0';
if (get_mine_count(mine, x - 1, y - 1) == 0)
show[x - 1][y - 1] = ' ';
else
show[x - 1][y - 1] = get_mine_count(mine, x - 1, y - 1) + '0';
if (get_mine_count(mine, x, y - 1) == 0)
show[x][y - 1] = ' ';
else
show[x][y - 1] = get_mine_count(mine, x, y - 1) + '0';
if (get_mine_count(mine, x + 1, y - 1) == 0)
show[x + 1][y - 1] = ' ';
else
show[x + 1][y - 1] = get_mine_count(mine, x + 1, y - 1) + '0';
if (get_mine_count(mine, x + 1, y) == 0)
show[x + 1][y] = ' ';
else
show[x + 1][y] = get_mine_count(mine, x + 1, y) + '0';
if (get_mine_count(mine, x + 1, y + 1) == 0)
show[x + 1][y + 1] = ' ';
else
show[x + 1][y + 1] = get_mine_count(mine, x + 1, y + 1) + '0';
if (get_mine_count(mine, x, y + 1) == 0)
show[x][y + 1] = ' ';
else
show[x][y + 1] = get_mine_count(mine, x, y + 1) + '0';
if (get_mine_count(mine, x - 1, y + 1) == 0)
show[x - 1][y + 1] = ' ';
else
show[x - 1][y + 1] = get_mine_count(mine, x - 1, y + 1) + '0';
if(show[x-1][y]==' '&& state[x - 1][y] != '1')
Boom(show, mine,state, x - 1, y);
if (show[x - 1][y-1] == ' ' && state[x - 1][y-1] != '1')
Boom(show, mine,state, x - 1, y - 1);
if (show[x][y-1] == ' ' && state[x ][y-1] != '1')
Boom(show, mine, state,x, y - 1);
if (show[x + 1][y-1] == ' ' && state[x + 1][y-1] != '1')
Boom(show, mine,state, x + 1, y - 1);
if (show[x + 1][y] == ' '&& state[x + 1][y] != '1')
Boom(show, mine,state, x + 1, y);
if (show[x + 1][y+1] == ' '&& state[x + 1][y+1] != '1')
Boom(show, mine,state, x + 1, y + 1);
if (show[x][y+1] == ' '&& state[x][y+1] != '1')
Boom(show, mine, state,x, y + 1);
if (show[x - 1][y+1] == ' '&& state[x - 1][y+1] != '1')
Boom(show, mine,state ,x - 1, y + 1);
}
}
特别注意,不能够去递归 递归过的雷块,因为我弟归你,你递归我,我再递归你,是不是就死循环了啊,所以我们重新定义了一个state数组表示这一块是否被递归过。
六、⛄排雷和标记雷
这里用到的排雷的思想是,有雷的雷块在mine数组上是1.遇到1则炸死了,打印mine数组(让玩家死得瞑目),这里会用到Boom函数进行递归,可以先看看上面讲得递归
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS],char state[ROWS][COLS])
{
int x, y;
int input;
while (1)
{
printf("1.排雷\n");
printf("2.标记雷\n");
printf("3.取消标记\n");
printf("请输入操作选项:>");
scanf("%d",&input);
switch (input)
{
case 1:
{printf("请输入排雷坐标:>");
rewind(stdin);
scanf("%d", &x);
scanf("%d", &y);
if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
{
if (show[x][y] != '*')
{
printf("该坐标已查找过,请重新输入.....\n");
}
else
{
if (mine[x][y] == '1')
{
printf("你被炸死了,loser\n");
DisplayBoard(mine, ROW, COL);
system("pause");
exit(-1);
}
else
{
int count = get_mine_count(mine, x, y);
if (count == 0)
{
show[x][y] = ' ';
Boom(show, mine, state, x, y);
DisplayBoard(show, ROW, COL);
}
else
{
show[x][y] = count + '0';
DisplayBoard(show, ROW, COL);
}
}
}
}
else
{
printf("error position\n");
}
break;
}
case 2:
{
printf("请输入标记雷坐标;>");
rewind(stdin);
scanf("%d", &x);
scanf("%d", &y);
show[x][y] = '#';
DisplayBoard(show, ROW, COL);
break;
}
case 3:
{
printf("请输入取消标记坐标;>");
rewind(stdin);
scanf("%d", &x);
scanf("%d", &y);
show[x][y] = '*';
DisplayBoard(show, ROW, COL);
break;
}
default:
printf("输入错误,请重新输入.....\n");
break;
}
}
}
好了,今天的扫雷游戏就到这里,感谢大家对猪皮的支持!!!
四、🚀五彩斑斓的一些废话
当你看到这里,相信上面的内容已经倒背如流了吧😶🌫️😶🌫️😶🌫️。各位巨佬如果觉得有帮助的话,还望各位父老乡亲动动小手指👈👇👉点点点。一键三连有问题吗?没有问题,这都是什么?人情世故