目录
导语:
1. 游戏界面和流程
2. 代码实现
2.1 思维导图
2.2 游戏菜单
2.3 雷盘及展示区
2.31 初始化
2.32 打印显示区
2.4 置雷
2.5 排雷
2.6 赢的判定
3. 完整代码
头文件game.h --> 函数声明
test.c文件 --> 逻辑测试
game.c文件 --> 功能实现
4.结语
在大家的记忆里是否有扫雷、蜘蛛纸牌、植物大战僵尸的这样的游戏呢?玩过这些游戏的小屁孩,想必也都长大了吧。还记得当时玩扫雷这个游戏的时候,不知道游戏逻辑,全凭运气,那结果肯定是,踩雷小王子。
那这次,我们就用代码来实现这个扫雷游戏,刨析内部原理,重拾童年回忆吧(当然,拾起过程会有点艰难,要做好准备哦)
我们先来看我们要实现的结果(如图):
初始逻辑与上一篇文字三子棋一样
链接:三子棋https://blog.csdn.net/Dirty_artist/article/details/127780924?spm=1001.2014.3001.5502
我们在test.c文件中,设置main函数,将逻辑测试函数放入test()函数里面
int main()
{
test();
return 0;
}
然后我们开始设置菜单,将此功能写一个函数完成,函数之前,最好是问一下自己三个问题
①函数用来干嘛?
②是否需要传参?
③是否需要返回值?
void menu()
{
printf("|==========================|\n");
printf("|========= 1.play =========|\n");
printf("|========= 0.exit =========|\n");
printf("|==========================|\n");
}
void test()
{
int input = 0;
do
{
menu();
printf("请选择:");
scanf("%d", &input);
if (input == 1)
{
printf("扫雷开始!\n");
//break;
}
else if (input == 0)
{
printf("退出游戏!\a\n");
break;
}
else
{
printf("选择错误,重新选择!\n");
}
} while (input);
}
我们怕排雷的时候,数组越界,所以就将棋盘扩大一圈,这样就不用担心在判定的时候数组越界了
所以我们在game.h文件里面先声明雷盘大小
#define Row 9
#define Col 9
#define Rows Row+2
#define Cols Col+2
然后,将其初始化,雷区里面先全部初始化为'0',展示区初始化为'*'
void init_board(char board[Rows][Cols], int rows, int cols, char set)
{
int i = 0;
for (i = 1; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
因为扫雷游戏的时候,我们看不到雷区,所以,只显示展示区
//展示棋盘
void display_board(char board[Rows][Cols], int row, int col)
{
int i = 0;
int j = 0;
printf("---------------------\n");
for (i = 0; i <= row; i++)
{
printf("%d ", i);
}
printf("|\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("|\n");
}
printf("---------------------\n");
}
效果如图:
置雷要求:
①生成的雷要有随机性;
②设置的雷要在9*9的格子里面;
③一个格子只能置一个雷。
我们先去将雷盘全部初始化为'0',那我们置雷,就设为'1'。
//置雷
void set_mine(char board[Rows][Cols], int row, int col)
{
int count = Easy;
while (count != 0)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
整体思想如图:
代码如下:
//排雷
void fine_mine(char mine[Rows][Cols], char show[Rows][Cols], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (1)
{
printf("输入查找的坐标: ");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (show[x][y] != '*')
{
printf("该坐标已排查\n");
continue;
}
if (mine[x][y] == '1')
{
printf("踩雷,游戏结束\n");
display_board(mine, Row, Col);
Sleep(2500);
system("cls");//清空屏幕
break;
}
else
{
//排空
find_safe_place(mine, show, x, y,row,col,&win);
system("cls");
display_board(show, Row, Col);
}
}
else
{
printf("坐标非法,重新输入\n");
}
}
}
这里唯一有难点的地方在于递归的排空。
我们将查找过的区域,标记为C,防止无限查找,如果周围无雷,将show区域标记为' '(空格)
有雷,那我们统计周围雷的数量。
递归排空:
//排空
void find_safe_place(char mine[Rows][Cols], char show[Rows][Cols], int x, int y,int row,int col,int* win)
{
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
mine[x][y] = 'C';//排查标记
if (get_mine_count(mine, x, y))//是否有雷
{
show[x][y] = get_mine_count(mine, x, y) + '0';//显示雷的数量
}
else
{
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] == '*')
find_safe_place(mine, show, i, j,row,col,win);
}
}
}
(*win)++;
}
}
统计雷的数量:
因为我们里面放的是char类型的字符,所以返回的时候,还需要减去8*'0'换算。
代码如下:
//周围雷的数量
int get_mine_count(char mine[Rows][Cols], int x, int y)
{
int i = 0;
int count = 0;
for (i = -1; i <= 1; i++)
{
int j = 0;
for (j = -1; j <= 1; j++)
{
if (mine[x + i][y + j] == '1')
{
count++;
}
}
}
return count;
}
失败很简单,只要踩雷,就是失败;那么赢,如何计算?
比如说,我们的地图是9*9,设置了9个9个雷,那么,如果我们最后地图还剩9个格子,那么,游戏就是胜利了,因为剩下的9个都是雷。
if (win == row * col - Easy)
{
printf("胜利\n");
display_board(mine, Row, Col);
Sleep(2500);
system("cls");
break;
}
#pragma once
#define Row 9
#define Col 9
#define Rows Row+2
#define Cols Col+2
#define Easy 3
#include
#include
#include
#include
//初始化棋盘
void init_board(char board[Rows][Cols],int rows,int cols,char set);
//展示棋盘
void display_board(char board[Rows][Cols],int row,int col);
//置雷
void set_mine(char board[Rows][Cols], int row, int col);
//排雷
void fine_mine(char mine[Rows][Cols], char show[Rows][Cols], int row, int col);
#define _CRT_SECURE_NO_WARNINGS 1
#pragma warning(disable:6031)
#include"game.h"
//菜单
void menu()
{
printf("|==========================|\n");
printf("|========= 1.play =========|\n");
printf("|========= 0.exit =========|\n");
printf("|==========================|\n");
}
//游戏测试
void game()
{
//雷盘、显示盘
char mine[Rows][Cols] = { 0 };
char show[Rows][Cols] = { 0 };
//初始化
init_board(mine, Rows, Cols, '0');
init_board(show, Rows, Cols, '*');
//打印
display_board(show, Row, Col);
//置雷
set_mine(mine, Row, Col);
//display_board(mine, Row, Col);
//排雷
fine_mine(mine, show, Row, Col);
}
void test()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:");
scanf("%d", &input);
if (input == 1)
{
printf("扫雷开始!\n");
game();
//break;
}
else if (input == 0)
{
printf("退出游戏!\a\n");
break;
}
else
{
printf("选择错误,重新选择!\n");
}
} while (input);
}
int main()
{
test();
return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#pragma warning(disable:6031)
#include"game.h"
//初始化棋盘
void init_board(char board[Rows][Cols], int rows, int cols, char set)
{
int i = 0;
for (i = 1; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
//展示棋盘
void display_board(char board[Rows][Cols], int row, int col)
{
int i = 0;
int j = 0;
printf("---------------------\n");
for (i = 0; i <= row; i++)
{
printf("%d ", i);
}
printf("|\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("|\n");
}
printf("---------------------\n");
}
//置雷
void set_mine(char board[Rows][Cols], int row, int col)
{
int count = Easy;
while (count != 0)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
//周围雷的数量
int get_mine_count(char mine[Rows][Cols], int x, int y)
{
int i = 0;
int count = 0;
for (i = -1; i <= 1; i++)
{
int j = 0;
for (j = -1; j <= 1; j++)
{
if (mine[x + i][y + j] == '1')
{
count++;
}
}
}
return count;
}
//排空
void find_safe_place(char mine[Rows][Cols], char show[Rows][Cols], int x, int y,int row,int col,int* win)
{
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
mine[x][y] = 'C';//排查标记
if (get_mine_count(mine, x, y))//是否有雷
{
show[x][y] = get_mine_count(mine, x, y) + '0';//显示雷的数量
}
else
{
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] == '*')
find_safe_place(mine, show, i, j,row,col,win);
}
}
}
(*win)++;
}
}
//排雷
void fine_mine(char mine[Rows][Cols], char show[Rows][Cols], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (1)
{
printf("输入查找的坐标: ");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (show[x][y] != '*')
{
printf("该坐标已排查\n");
continue;
}
if (mine[x][y] == '1')
{
printf("踩雷,游戏结束\n");
display_board(mine, Row, Col);
Sleep(2500);
system("cls");//清空屏幕
break;
}
else
{
//排空
find_safe_place(mine, show, x, y,row,col,&win);
system("cls");
display_board(show, Row, Col);
}
}
else
{
printf("坐标非法,重新输入\n");
}
if (win == row * col - Easy)
{
printf("胜利\n");
display_board(mine, Row, Col);
Sleep(2500);
system("cls");
break;
}
}
}
关于扫雷,重难点在于递归排空,如果想象不出,可以尝试拿笔自己在草稿纸上推演。过程中可能会出现程序能运行但没按逻辑来(自己写了三遍,出现最多的问题就是,不报错,但是没按自己的逻辑来,这是最难受的),这就需要自己慢慢调试,看看程序的执行流程,然后一步一步改bug。这其实还有优化的空间,可以自己添加一些趣味项目,这可以下去再拓展拓展。
好了,本次分享就到这里,有帮助的话,不妨点个赞再走。
下机,再见!