鱼式疯言:此疯言非彼疯言,而是理解过并总结出来通俗易懂的大白话,我会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的.
可能说的不是那么严谨.但小编初心是能让更多人能接受我们这个概念]
在本篇中,主要讲解扫雷的
- 雷盘的初始化,
- 雷盘的打印
- 雷的布置
- 周围雷信息的打印
- 展开一片和标记的重要解析
前言
按照小伙伴都思路雷盘是一个够了,其实不竟然,为什么呢?
主要是因为我们这边既要隐藏雷的区域,又要明面显示周围雷的个数,
那我们就一个叫mine的自身雷盘,一个叫show的玩家看到的雷盘。
鱼式疯言😍
就show好比题目,mine好比答案,必须要同时有题目和答案才能保证我们的信息运行和准确性。
一.游戏菜单设定
首先来个函数包装我们菜单才进行游戏的选择,可选择棋盘大小
void meau()
{
printf("****************************\n");
printf("请选择以下数字确定游戏规格:>\n");
printf("*** 1. 9 * 9雷盘********\n");
printf("*** 2. 16 * 16雷盘 ******\n");
printf("*** 3. 30 * 16雷盘 ******\n");
printf("*** 0. 退出游戏 ******\n");
printf("****************************\n");
}
do
{
meau();
scanf("%d", &n);
switch (n)
{
case 1:
printf("初级扫雷游戏\n");
game1();
break;
case 2:
printf("中级扫雷游戏\n");
game2();
break;
case 3:
printf("高级扫雷游戏\n");
game3();
break;
case 0:
break;
default:
printf("输入失败,请重新输入:\n");
break;
}
}
while (n);
二.初始化雷盘
我们要布置雷盘即要有行也有列的,所以我们就要先初始化两个二维数组,在我们二维数组中,一个是我们的答案mine数组全部放入 ‘0’,一个是显示的show数组全部放入 ‘*’。
char mine[ROWS2][COLS2];
//这里引用了常量:ROW2 ROWS2 COL2,COLS2
char show[ROWS2][COLS2];
initboard(mine, ROWS2, COLS2, '0');
initboard(show, ROWS2, COLS2, '*');
//初始化棋盘
//这里引入常量,以便修改雷盘规格
//这里初始化'*'和'0'只要引入形参ch,就可以两次调用initboard
{
for (int i = 0; i < x; i++)
{
for (int j = 0; j < y; j++)
{
arr[i][j] = ch;
//放入元素
}
}
}
三.打印雷盘
普通打印二维数组是一般的,但这里要注意的细节,因为我们的棋盘是没有行列标志的,所以我们要用坐标定位时必须加上对应的行和列. 那我们就要带上序号来确定行数和列数。
void displayboard(char arr[ROWS][COLS], int x, int y)
{
printf("\n--------------------------------------扫雷-----------------------------------\n");
//前后标志一下
for (int i = 0; i <= y; i++)
//首行打印列数
{
printf("%d ", i);
}
printf("\n\n");
//换到下一行
for (int i = 1; i <=x; i++)
{
if (i<=9)
{
printf("%d ", i);
}
//打印行数
else
{
printf("%d ", i);
}
for (int j = 1; j <= y; j++)
{
if (j < 9)
{
printf("%c ", arr[i][j]);
}
else if (j >= 9)
{
printf("%c ", arr[i][j]);
}
}
system("color 74");
//这里用改变了雷盘颜色,让玩家避免视觉疲劳
printf("\n\n");
}
printf("%d ",x+1);
for (int i = 1; i <= y; i++)
{
printf("%d ", i);
}
//最后列数再打印一行
printf("\n--------------------------------------扫雷-----------------------------------\n");
}
四.布置地雷
布置地雷的话,大家可以思考下怎么布置呢?
鱼式疯言😍
- 首先我们的雷是随机的,就得用到随机数,随机一个x坐标和随机一个y坐标。
- 其次我们布置好的雷应该放在答案mine数组里面就够啦,我们自己知道哪边有雷就够了,不需要在展示show数组里面,
- 最后这样到时候在答案mine数组里面把一个安全点中附近的雷统计到show就OK啦
setmine(mine, ROW2, COL2, EASY_COUNT);
//这里就应用常量EASY_COUNT
srand((unsigned int)time(NULL));
//随机数初始化
void setmine(char arr[ROWS][COLS], int m, int n,int count)
{
while (count)
{
int x = rand() % m + 1;
int y = rand() % n + 1;
if (arr [x][y] == '0')
{
arr [x][y] = '1';
count--;
}
}
}
五.查找和标记雷
在这里就提一点细节:主要还是看坐标合不合法,会不会重复来构想的,然后在以上情况都满足的情况下,就可以判断你是排查还是标记。
注意哦,这里标记了是不算查找点里面的,所以一旦标记错了,就要取消标记才可以查找
鱼式疯言 😍
安全区是指没有雷的地方,所以只要你排查的点达到了安全区的点数就算你扫雷成功
void displaymine(char mine[ROWS][COLS], char show[ROWS][COLS],int row, int col,int easy)
{
int win = 0;
//win表示排查的点数
while (win < row * col - easy)
//当排查的点数< (雷盘总数 — 地雷数)-->即安全区的个数
//发生循环
{
int x, y = 0;
printf("请输入的坐标:>\n");
scanf("%d%*c%d", &x, &y);
if (x > 0 && x <= row &&y > 0&& y <= col)
//保证坐标在雷盘内合法
{
int w = 0;
printf("要排查请按:1 标记或取消标记请按:2\n请选择:>\n");
scanf("%d", &w);
//这里加入分支:可选择排查,标记,或取消标记。
switch (w)
{
case 1:
//选择一:排查
if (mine[x][y] == '1')
//只要这个坐标在mine数组中你碰到1的话就被炸死。
{
printf("你被炸死了,游戏结束\n");
displayboard(mine, row, col);
//炸死啦就要让玩家明白怎么炸死的
win = (row * col -easy) + 1;
//最终只有win比安全区大就会跳出循环。
break;
}
else
{
if (show[x][y] == '*')
//注意哦,这里要小心坐标有没有被排查过哦
{
win = unfold(mine, show, row,col,x,y,win);
//这里是展开一片和统计数字我们就都放在unfold实现
//setboard(mine, show, x , y,win);
displayboard(show, row, col);
}
else
{
printf("已排查或标记过,请重新输入\n");
}
}
break;
case 2:
//选择二:标记或取消标记
if (show[x][y] == '*')
{
show[x][y] = '!';
displayboard(show, row, col);
}
else if (show[x][y] == '!')
{
show[x][y] = '*';
displayboard(show, row, col);
}
else
{
printf("已排查过,请重新输入\n");
}
break;
default:
printf("输入错误,请重新输入:>");
break;
}
}
else
{
printf("坐标非法,请重新输入:>\n");
}
}
if (win==row*col-easy)
//排查的实际数目等于安全区就算胜利。
//只要把安全区都排查完啦,游戏就胜利。
{
printf("恭喜你,排雷成功\n");
displayboard(mine, row,col);
}
}
六.展开一片详解
首先我们不管三七二十一就统计该点的周围的雷的个数,
然后以是否周围有雷的为标志
如果周围没有雷,那么就把周围的放入继续排查直到没有雷的就停止
最后一定要注意返回你排查完无雷点的个数
鱼式疯言😍
字符和整数之间的转化:相同类型就转化,不同类型换数字:‘1’-‘0’=1,‘2’-‘1’=2,3+‘0’=‘3’,4+‘0’=4
int unfold(char mine[ROWS][COLS], char show[ROWS][COLS], int row,int col,int x, int y, int win)
{
//首先统计雷的个数
int sum = 0;
//先初始化雷的个数为0。
if ((x <=row && x > 0) && (y <= col && y > 0) && (show[x][y] == '*')&& (mine[x][y] != '1') )
{
for (int i = x-1; i <= x+1; i++)
{
for (int j = y-1; j <= y+1 ; j++)
{
sum = sum + mine[i][j] - '0';
//利用循环每查找一个点就减去一个'0'就可以得到一个整数
//前面我们前面雷存放的字符是'1',所以为了得到算出雷的个数必须用整数表示
//比如'1'-'0'=1,'2'-'0'=2.
}
}
if (sum == 0)
//一旦身边没有雷,那我们就反复查询展开没有雷的区域
{
show[x][y] = '0';
win++;
//安全区展开一次,win就要++.
for (int i = x-1; i <= x+1; i++)
{
for (int j = y - 1; j <= y + 1; j++)
{
if ((i == x) && (j == y))
{
continue;
//除去自身不调用.
}
if (show[i][j] == '*')
{
unfold(mine, show, row, col, i, j, win);
//坐标八格内无雷返回函数继续调用.
}
}
}
}
else
{
show[x][y] = sum + '0';
//一旦身边有雷,最终要把每个点的坐标的雷的个数放在show展示
win++;
}
}
else
{
//只要不符合条件,就说明调用结束,那么就返回查找的点数
return win;
}
}
总结
通过上面的的解析,友友们应该都熟悉了扫雷大致分为五个模块
游戏逻辑:菜单的设定
游戏载物:雷盘的初始
游戏背景:雷盘的显示
游戏内核:布置地雷
游戏反馈:雷的信息
游戏结果:查的无雷个数与实际安全处的个数相等.
因为我们要设定游戏逻辑的实现—>我们就把它放在test.c为一个文件
设定游戏内容的实现---->我们就把它放在game.c为一个文件
前面两个文件我们可能要调用到的函数可能要包含的头文件,函数声明和常量的定义,所以我们这边就要放在game.h
game.h文件
#define _CRT_SECURE_NO_WARNINGS
#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 EASY_COUNT 10
void initboard(char arr[ROWS][COLS], int x, int y, char ch);
void displayboard(char arr[ROWS][COLS], int x, int y);
void setmine(char arr[ROWS][COLS], int m, int n);
int unfold(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* win);
void displaymine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col,int easy);
#define ROW1 16
#define COL1 16
#define ROWS1 ROW1+2
#define COLS1 COL1+2
#define EASY_COUNT1 40
#define ROW2 30
#define COL2 16
#define ROWS2 ROW2+2
#define COLS2 COL2+2
#define EASY_COUNT2 99
test.c文件
#include "game.h"
void meau()
{
printf("****************************\n");
printf("请选择以下数字确定游戏规格:>\n");
printf("*** 1. 9 * 9雷盘********\n");
printf("*** 2. 16 * 16雷盘 ******\n");
printf("*** 3. 30 * 16雷盘 ******\n");
printf("*** 0. 退出游戏 ******\n");
printf("****************************\n");
}
void game1()
{
char mine[ROWS][COLS];
char show[ROWS][COLS];
initboard(mine, ROWS, COLS, '0');
initboard(show, ROWS, COLS, '*');
//初始化棋盘
//displayboard(mine, ROW, COL);
displayboard(show, ROW, COL);
//打印棋盘
setmine(mine, ROW, COL,EASY_COUNT);
//布置雷
displayboard(mine, ROW, COL);
displaymine(mine, show, ROW , COL,EASY_COUNT);
}
void game2()
{
char mine[ROWS1][COLS1];
char show[ROWS1][COLS1];
initboard(mine, ROWS1, COLS1, '0');
initboard(show, ROWS1, COLS1, '*');
//初始化棋盘
displayboard(mine, ROW1, COL1);
displayboard(show, ROW1, COL1);
//打印棋盘
setmine(mine, ROW1, COL1, EASY_COUNT1);
//布置雷
displayboard(mine, ROW1, COL1);
displaymine(mine, show, ROW1, COL1, EASY_COUNT1);
}
void game3()
{
char mine[ROWS2][COLS2];
//这里引入常量的概念,以便修改雷盘规格
char show[ROWS2][COLS2];
initboard(mine, ROWS2, COLS2, '0');
initboard(show, ROWS2, COLS2, '*');
//初始化棋盘
displayboard(mine, ROW2, COL2);
displayboard(show, ROW2, COL2);
//打印棋盘
setmine(mine, ROW2, COL2, EASY_COUNT2);
//布置雷
displayboard(mine, ROW2, COL2);
displaymine(mine, show, ROW2, COL2, EASY_COUNT2);
}
int main()
{
srand((unsigned int)time(NULL));
int n = 0;
//system("cls");
do
{
meau();
scanf("%d", &n);
switch (n)
{
case 1:
printf("初级扫雷游戏\n");
game1();
break;
case 2:
printf("中级扫雷游戏\n");
game2();
break;
case 3:
printf("高级扫雷游戏\n");
game3();
break;
case 0:
break;
default:
printf("输入失败,请重新输入:\n");
break;
}
}
while (n);
}
game.c文件
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
void initboard(char arr[ROWS][COLS], int x, int y, char ch)
//这里初始化'*'和'0'只要引入形参ch,就可以两次调用initboard
{
for (int i = 0; i < x; i++)
{
for (int j = 0; j < y; j++)
{
arr[i][j] = ch;
//放入元素
}
}
}
void displayboard(char arr[ROWS][COLS], int x, int y)
{
printf("\n--------------------------------------扫雷-----------------------------------\n");
//前后标志一下
for (int i = 0; i <= y; i++)
//首行打印列数
{
printf("%d ", i);
}
printf("\n\n");
//换到下一行
for (int i = 1; i <=x; i++)
{
if (i<=9)
{
printf("%d ", i);
}
//打印行数
else
{
printf("%d ", i);
}
for (int j = 1; j <= y; j++)
{
if (j < 9)
{
printf("%c ", arr[i][j]);
}
else if (j >= 9)
{
printf("%c ", arr[i][j]);
}
}
system("color 74");
printf("\n\n");
}
printf("%d ",x+1);
for (int i = 1; i <= y; i++)
{
printf("%d ", i);
}
//最后列数再打印一行
printf("\n--------------------------------------扫雷-----------------------------------\n");
}
void setmine(char arr[ROWS][COLS], int m, int n,int count)
{
while (count)
{
int x = rand() % m + 1;
int y = rand() % n + 1;
if (arr [x][y] == '0')
{
arr [x][y] = '1';
count--;
}
}
}
int unfold(char mine[ROWS][COLS], char show[ROWS][COLS], int row,int col,int x, int y, int win)
{
//首先统计雷的个数
int sum = 0;
//先初始化雷的个数为0。
if ((x <=row && x > 0) && (y <= col && y > 0) && (show[x][y] == '*')&& (mine[x][y] != '1') )
{
for (int i = x-1; i <= x+1; i++)
{
for (int j = y-1; j <= y+1 ; j++)
{
sum = sum + mine[i][j] - '0';
//利用循环每查找一个点就减去一个'0'就可以得到一个整数
//前面我们前面雷存放的字符是'1',所以为了得到算出雷的个数必须用整数表示
//比如'1'-'0'=1,'2'-'0'=2.
}
}
if (sum == 0)
//一旦身边没有雷,那我们就反复查询展开没有雷的区域
{
show[x][y] = '0';
win++;
//安全区展开一次,win就要++.
for (int i = x-1; i <= x+1; i++)
{
for (int j = y - 1; j <= y + 1; j++)
{
if ((i == x) && (j == y))
{
continue;
//除去自身不调用.
}
if (show[i][j] == '*')
{
unfold(mine, show, row, col, i, j, win);
//坐标八格内无雷返回函数继续调用.
}
}
}
}
else
{
show[x][y] = sum + '0';
//一旦身边有雷,最终要把每个点的坐标的雷的个数放在show展示
win++;
}
}
else
{
//只要不符合条件,就说明调用结束,那么就返回查找的点数
return win;
}
}
void displaymine(char mine[ROWS][COLS], char show[ROWS][COLS],int row, int col,int easy)
{
int win = 0;
//win表示排查的点数
while (win < row * col - easy)
//当排查的点数< 雷盘总数 — 地雷数
//发生循环
{
int x, y = 0;
printf("请输入的坐标:>\n");
scanf("%d%*c%d", &x, &y);
if (x > 0 && x <= row &&y > 0&& y <= col)
//保证坐标在雷盘内合法
{
int w = 0;
printf("要排查请按:1 标记或取消标记请按:2\n请选择:>\n");
scanf("%d", &w);
//这里加入分支:可选择排查,标记,或取消标记。
switch (w)
{
case 1:
//选择一:排查
if (mine[x][y] == '1')
//只要这个坐标在mine数组中你碰到1的话就被炸死。
{
printf("你被炸死了,游戏结束\n");
displayboard(mine, row, col);
//炸死啦就要让玩家明白怎么炸死的
win = (row * col -easy) + 1;
//最终只有win比安全区大就会跳出循环。
break;
}
else
{
if (show[x][y] == '*')
//注意哦,这里要小心坐标有没有被排查过哦
{
win = unfold(mine, show, row,col,x,y,win);
//这里是展开一片和统计数字我们就都放在unfold实现
//setboard(mine, show, x , y,win);
displayboard(show, row, col);
}
else
{
printf("已排查或标记过,请重新输入\n");
}
}
break;
case 2:
//选择二:标记或取消标记
if (show[x][y] == '*')
{
show[x][y] = '!';
displayboard(show, row, col);
}
else if (show[x][y] == '!')
{
show[x][y] = '*';
displayboard(show, row, col);
}
else
{
printf("已排查过,请重新输入\n");
}
break;
default:
printf("输入错误,请重新输入:>");
break;
}
}
else
{
printf("坐标非法,请重新输入:>\n");
}
}
if (win==row*col-easy)
//排查的实际数目等于安全区就算胜利。
//只要把安全区都排查完啦,游戏就胜利。
{
printf("恭喜你,排雷成功\n");
displayboard(mine, row,col);
}
}
鱼式疯言😍
为什么我们要三个文件呢?主要是为了区分,像这种大文件,
- 一个是能分清条理保证保证我们他人能继续操作我们的代码,
- 第二个是重要文件可进行保证,以保证我们自身文件的安全性
❤️❤️❤️创作不易❤️❤️❤️ ,最后希望友友们看到这篇博文,觉得还不错的咱可支持三关下,不妥当的咱评论区指正,希望我的文章能给各位家人们带来哪怕一点点的收获就是小编创作的最大动力