文章目录
- 完整扫雷
- 1. 说明
- 2. 思路
- 3. 各个功能实现
- 3.1 雷盘初始化与打印
- 1)雷盘定义
- 2) 随机布置雷
- 3.2 玩家排查雷
- 1) 获取坐标周围雷数
- 2) 递归展开
- 3)胜负判断
- 3) 显示雷位置
- 4. 游戏试玩
- 5. 游戏完整代码
- game.h
- test.c
- game.c
完整扫雷
1. 说明
扫雷大家应该都知道,翻开一个格子,显示的数字就是周围 8 格所含的雷数。例如,红色框框里的 1 周围 8 格就只有一个雷。
2. 思路
-
我们定义两个数组来实现,show 数组存放玩家看到的棋盘,mine 数组存放隐藏的雷盘
-
这两个数组搭配使用,就能计算某个位置周围的雷数,并且修改 show 数组来显示这个位置的雷数。
-
但是如果计算边缘格子周围的雷数时,数组会越界。
-
我们只要在周围留一圈就能解决这个问题
- 如下图所示,假设我们要玩 9x9 的大小,我们的数组大小就定义为 11x11
3. 各个功能实现
3.1 雷盘初始化与打印
1)雷盘定义
- 初始化雷盘
- 初始化 show,mine 两个数组,show 存放 ‘*’ , mine 存放 ‘0’
void init_board(char board[ROWS][cols], int rows, int cols, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
打印雷盘
//打印 show 雷盘
void display_board(char board[ROWS][cols], int row, int col)
{
int i = 0;
int j = 0;
//这里打印上面一行数字
printf("|");
for (j = 0; j <= col; j++)
{
printf(" %-2d |", j);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("|");
for (j = 0; j <= col; j++)
{
printf("----|");//打印两行之间的分割线
}
printf("\n");
printf("|");
printf(" %-2d |", i);//打印左边一列数字
for (j = 1; j <= col; j++)
{
printf(" %2c |",board[i][j]);//打印show数组
}
printf("\n");
}
}
效果图:
2) 随机布置雷
-
随机布置雷,将 mine 中的 ‘0’ 改为 ‘1’
-
随机布置雷,在 mine 数组里随机设置 COUNT 个雷
void set_mine(char mine[ROWS][cols], int row, int col)
{
int count = COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1'; //地雷设置为 '1'
count--;
}
}
}
3.2 玩家排查雷
1) 获取坐标周围雷数
1… 计算该坐标在 mine 中 周围 '1’的个数
2… 获取一个格子周围的雷数
int get_mine_count(char mine[ROWS][cols], int x, int y)
{
//mine 中存放的是字符'0' 和 '1'
return (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] +
mine[x + 1][y] +
mine[x + 1][y + 1] - 8 \* '0');
}
2) 递归展开
- 我们玩扫雷时,翻开一个格子会展开一片,如上图所示,翻开黑格子,展开紫色区域。
- 我们可以用递归来实现
- 当这个格子周围没雷时,显示空白,然后继续递归它周围的八个格子
- 有雷时,显示雷数,停止递归。
void expand(char mine[ROWS][cols], char show[ROWS][cols], int x, int y, int\* win)
{
if (x >= 1 && x <= ROW && y >= 1 && y <= COL) //限制在棋盘内展开,防止越界
{
int count = get_mine_count(mine, x, y);//获取雷数
if (count == 0) //四周没雷,进入递归展开
{
show[x][y] = ' ';//四周没雷的改为 空格 ' '
int i = 0;
//向四周共8个位置递归
for (i = x - 1; i <= x + 1; i++)
{
int j = 0;
for (j = y - 1; j <= y + 1; j++)
{
//只对 '*' 进行展开,防止死循环
if (show[i][j] == '*')
{
expand(mine, show, i, j, win);
}
}
}
}
else //四周有雷显示雷数
{
show[x][y] = count + '0';
}
//记录展开的数量
(*win)++;
}
}
3)胜负判断
- 这里定义了一个 win 来表示翻开的格子数,当翻开的格子数量 = 行 x 列 - 雷数 ===> 排雷成功。
- 玩家排查雷
void find_mine(char mine[ROWS][cols], char show[ROWS][cols], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;//初始化 翻开的格子数量
while (win < row * col - COUNT)//当翻开的格子数量 = 行 x 列 - 雷数 ===> 排雷成功
{
printf("请输入要排查的坐标:>");
scanf("%d %d", &x, &y);
//检查坐标是否越界
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
//检查坐标是否排查过了
if (show[x][y] == '*')
{
if (mine[x][y] == '1')
{
system("cls");
show_mine(mine, show, row, col);
printf("-----------很遗憾,你被炸死了-----------\n");
break;
}
else
{
//展开
expand(mine, show, x, y, &win);
system("cls");//清屏
display_board(show, row, col);
printf("--------------还需翻开%d格--------------\n", row * col - COUNT - win);
}
}
else
{
printf("该坐标已排查,请重新输入\n");
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
if (win == row* col - COUNT)
{
system("cls");
show_mine(mine, show, row, col);//展示地雷位置
printf("------------恭喜你,排雷成功-----------\n");
}
}
3) 显示雷位置
显示地雷位置,排雷成功或被炸死后 向玩家展示地雷位置
void show_mine(char mine[ROWS][cols], char show[ROWS][cols], int row, int col)
{
int i = 0;
for (i = 1; i <= row; i++)
{
int j = 0;
for (j = 1; j <= COL; j++)
{
if (mine[i][j] == '1')
{
show[i][j] = '@'; //将地雷改成 '@'
}
}
}
display_board(show, row, col); //打印
}
4. 游戏试玩
- 游戏设置 10 行 ,10 列 ,15 个雷
|
|
|
|
- 排雷成功
- 排雷失败
5. 游戏完整代码
game.h
头文件 常量定义,函数声明
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//玩家看到的大小
#define ROW 10
#define COL 10
//实际数组大小,防止越界
#define ROWS ROW+2
#define COLS COL+2
#define COUNT 10//雷数
//初始化棋盘
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 mine[ROWS][cols], int row, int col);
//获取坐标周围地雷数
int get_mine_count(char mine[ROWS][cols], int x, int y);
//显示地雷位置并打印
void show_mine(char mine[ROWS][cols], char show[ROWS][cols], int row, int col);
//递归展开
void expand(char mine[ROWS][cols], char show[ROWS][cols], int x, int y, int\* win);
//排查雷
void find_mine(char mine[ROWS][cols],char show[ROWS][cols],int row, int col);
test.c
游戏测试文件
#define \_CRT_SECURE_NO_WARNINGS 1
#include "game.h"
//菜单
void menu()
{
printf("======================\n");
printf("|| 扫雷 ||\n");
printf("|| 1-开始游戏 ||\n");
printf("|| 0-退出游戏 ||\n");
printf("======================\n");
}
//游戏流程
void game()
{
char mine[ROWS][cols] = { 0 };//存放布置的雷(隐藏的)
char show[ROWS][cols] = { 0 };//存放排查的雷(游戏看到的)
//初始化棋盘
//mine 全为'0'
//show 全为'*'
init_board(mine, ROWS, COLS, '0');
init_board(show, ROWS, COLS, '*');
//随机布置雷
set_mine(mine,ROW,COL);
//打印棋盘
//display_board(mine, ROW, COL);
display_board(show, ROW, COL);
printf("--------------需要翻开%d格--------------\n", ROW * COL - COUNT);
//排查雷(游戏开始)
find_mine(mine, show, ROW, COL);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
system("cls");
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
game.c
函数定义
#define \_CRT_SECURE_NO_WARNINGS 1
#include "game.h"
//将两个数组初始化,show 全为'\*' ,mine 全为'0'
void init_board(char board[ROWS][cols], int rows, int cols, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
//打印 show 棋盘
void display_board(char board[ROWS][cols], int row, int col)
{
int i = 0;
int j = 0;
//这里打印上面一行数字
printf("|");
for (j = 0; j <= col; j++)
{
printf(" %-2d |", j);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("|");
for (j = 0; j <= col; j++)
{
printf("----|");//打印两行之间的分割线
}
printf("\n");
printf("|");
printf(" %-2d |", i);//打印左边一列数字
for (j = 1; j <= col; j++)
{
printf(" %2c |",board[i][j]);//打印show数组
}
printf("\n");
}
}
//随机设置雷,在 mine 数组里随机设置 COUNT 个雷
void set_mine(char mine[ROWS][cols], int row, int col)
{
int count = COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1'; //地雷设置为 '1'
count--;
}
}
}
//获取一个格子周围的雷数
int get_mine_count(char mine[ROWS][cols], int x, int y)
{
//mine 中存放的是字符'0' 和 '1'
return (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] +
mine[x + 1][y] +
mine[x + 1][y + 1] - 8 \* '0');
}
//递归展开
void expand(char mine[ROWS][cols], char show[ROWS][cols], int x, int y, int\* win)
{
if (x >= 1 && x <= ROW && y >= 1 && y <= COL) //限制在棋盘内展开,防止越界
{
int count = get_mine_count(mine, x, y);//获取雷数
if (count == 0) //四周没雷,进入递归展开
{
show[x][y] = ' ';//四周没雷的改为 空格 ' '
int i = 0;
//向四周共8个位置递归调用
for (i = x - 1; i <= x + 1; i++)
{
int j = 0;
for (j = y - 1; j <= y + 1; j++)
{
//只对 '*' 进行展开,防止死循环
if (show[i][j] == '*')
{
expand(mine, show, i, j, win);
}
}
}
}
else //四周有雷显示雷数
{
show[x][y] = count + '0';
}
//记录展开的数量
(*win)++;
}
}
//显示地雷位置,排雷成功或被炸死后 向玩家展示地雷位置
void show_mine(char mine[ROWS][cols], char show[ROWS][cols], int row, int col)
{
int i = 0;
for (i = 1; i <= row; i++)
{
int j = 0;
for (j = 1; j <= COL; j++)
{
if (mine[i][j] == '1')
{
show[i][j] = '@'; //将地雷改成 '@'
}
}
}
display_board(show, row, col); //打印
}
//玩家排查雷
void find_mine(char mine[ROWS][cols], char show[ROWS][cols], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;//初始化 翻开的格子数量
while (win < row * col - COUNT)//当翻开的格子数量 = 行 x 列 - 雷数 ===> 排雷成功
{
printf("请输入要排查的坐标:>");
scanf("%d %d", &x, &y);
//检查坐标是否越界
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
//检查坐标是否排查过了
if (show[x][y] == '*')
{
if (mine[x][y] == '1')
{
system("cls");
show_mine(mine, show, row, col);
printf("-----------很遗憾,你被炸死了-----------\n");
break;
}
else
{
//展开
expand(mine, show, x, y, &win);
system("cls");//清屏
display_board(show, row, col);
printf("--------------还需翻开%d格--------------\n", row * col - COUNT - win);
}
}
else
{
printf("该坐标已排查,请重新输入\n");
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
if (win == row* col - COUNT)
{
system("cls");
show_mine(mine, show, row, col);//展示地雷位置
printf("------------恭喜你,排雷成功-----------\n");
}
}