编程环境:【Visual Studio 2019】
标题传送门
扫雷怎么玩
编程思路
创建地图即初始化
打印地图
埋雷
玩家排雷
全部代码(传送门)
演示效果
扫雷怎么玩
扫雷是一款经典的小游戏。当地图上随机点击一个格子,如果不是则雷,检查以该格子为中心的8个坐标有多少个雷。如果附近8个坐标都不是雷,则展开一片不是雷的区域。当玩家排查完所有地雷,则玩家胜利。如果玩家踩雷,则游戏失败。
编程思路
创建地图即初始化
首先创建两个字符类型的二维数组(mine,show),一个是存放雷的数组,另一个给玩家看的排雷信息数组。如果当玩家输入了处于边缘的坐标,当程序检查附近的雷时导致数组越界,所以两个数组大小应该是地图大小N+2。
假设表示雷为1,非雷为0,给存放雷的数组全部初始化字符0。排雷信息数组全部初始化为星号。
#define ROW 9
#define COL 9
#define ROWS 9+2
#define COLS 9+2
char mine[ROWS][COLS] = { 0 };
//存放排雷信息的数组
char show[ROWS][COLS] = { 0 };
InitBoard(mine, ROWS, COLS,'0');
InitBoard(show, ROWS, COLS, '*');
//初始化
void InitBoard(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;
}
}
}
打印地图
为了能更好观察想要输入的坐标在哪一行哪一列,可以为函数的坐标都标记上编号。
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0;i <= col;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");
}
}
埋雷
需要传的是存放雷的数组和地图大小的长和宽。因为需要随机埋雷,需要用到时间戳函数生成随机值。要注意,因为数组大小是9+2×9+2的,而地图则是9×9的,地图边缘不能埋雷,所以在生成随机值时,不能将雷埋在地图外的坐标。当埋的雷满足个数程序结束。
//设置雷个数
#define EASILY_MINE 10
SetMine(mine, ROW, COL);
void SetMine(char mine[ROWS][COLS], int row, int col)
{
//计数
int count = EASILY_MINE;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
玩家排雷
将雷埋好之后,就要开始玩家排雷了,排雷需要用到存放数组和排雷信息数组,让玩家输入排雷坐标。接着判断该坐标的合法性,若坐标非法则让玩家重新输入,若坐标合法,则判断该坐标是不是地雷,如果是雷,game over,不是就以该坐标为中心,计算周围的8个坐标有多少个雷,如果附近8个坐标都不是雷,则向外递归展开一片都不是雷的区域。而main函数内并没有调用过这个展开函数和检查雷函数,它们只是为了支持我们的排雷函数存在的,所以在函数前加一个static让函数失去外部链接属性,只能在这个工程内调用。在头文件也不再需要函数声明了。
//检查雷个数
static int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
//返回坐标周围的八个坐标
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';
}
//展开函数
static void ExcludeMine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
int count = get_mine_count(mine, x, y);
if (count != 0)
{
//当周围8个坐标有雷,计算雷个数
show[x][y] = count + '0';
}
else
{
//当周围8个坐标没有雷
show[x][y] = ' ';
if ('*' == show[x - 1][y - 1])
ExcludeMine(mine, show, x - 1, y - 1);
if ('*' == show[x - 1][y])
ExcludeMine(mine, show, x - 1, y);
if ('*' == show[x - 1][y + 1])
ExcludeMine(mine, show, x - 1, y + 1);
if ('*' == show[x][y - 1])
ExcludeMine(mine, show, x, y - 1);
if ('*' == show[x][y + 1])
ExcludeMine(mine, show, x - 1, y + 1);
if ('*' == show[x + 1][y - 1])
ExcludeMine(mine, show, x + 1, y - 1);
if ('*' == show[x + 1][y])
ExcludeMine(mine, show, x - 1, y + 1);
if ('*' == show[x + 1][y + 1])
ExcludeMine(mine, show, x - 1, y + 1);
}
}
//玩家排雷
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (1)
{
//外挂(方便测试)
//DisplayBoard(mine, row, col);
printf("请输入排雷坐标:");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] == '*')
{
//坐标是雷
if (mine[x][y] == '1')
{
system("cls");
printf("Peng!!!GAME OVER!n");
break;
}
//不是雷
else
{
system("cls");
ExcludeMine(mine, show, x, y);
DisplayBoard(show, row, col);
//DisplayBoard(mine, row, col);
win = CheckShow(show, row, col);
win = CheckShow(show, row, col);
if (win == EASILY_MINE)//当只剩雷的个数,排雷成功
{
system("cls");
break;
}
}
}
else
{
printf("坐标非法,请重新输入n");
}
}
if (win == EASILY_MINE)
printf("恭喜你排雷成功!n");
//查看所有雷的位置
DisplayBoard(mine, row, col);
system("pause");
system("cls");
}
将雷埋好之后,就要开始玩家排雷了,排雷需要用到存放数组和排雷信息数组,让玩家输入排雷坐标。接着判断该坐标的合法性,若坐标非法则让玩家重新输入,若坐标合法,则判断该坐标是不是地雷,如果是雷,game over,不是就以该坐标为中心,计算周围的8个坐标有多少个雷,如果附近8个坐标都不是雷,则向外递归展开一片都不是雷的区域。而main函数内并没有调用过这个展开函数和检查雷函数,它们只是为了支持我们的排雷函数存在的,所以在函数前加一个static让函数失去外部链接属性,只能在这个工程内调用。在头文件也不再需要函数声明了。
当玩家排雷解决后就要解决如何判定玩家是否胜利,玩家的结束条件有踩雷和排雷成功。踩雷已经解决完毕,接下来就是如何判断玩家的输赢。我的方法是用循环判断还有多少个未排坐标,然后返回一个变量,用变量来和雷的个数比较,如果雷的个数等于未排个数说明排雷成功。当游戏结束后给玩家打印雷的位置,让玩家看看怎么赢或输的。
//判赢
static char CheckShow(char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
for (x = 1;x <= row;x++)
{
for (y = 1;y <= col;y++)
{
if (show[x][y] == '*')
{
//计算还有几个未排坐标
win++;
}
}
}
return win;
}
全部代码(传送门)
#pragma once
#include
#include
#include
#include
#define ROW 9
#define COL 9
#define ROWS 9+2
#define COLS 9+2
//设置雷个数
#define EASILY_MINE 10
//初始化
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
//打印
void DisplayBoard(char board[ROWS][COLS], int row, int col);
//埋雷
void SetMine(char mine[ROWS][COLS], int row, int col);
//排雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
void InitBoard(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;
}
}
}
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0;i <= col;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");
}
}
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int count = EASILY_MINE;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
//检查雷个数
static int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
//返回坐标周围的八个坐标
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';
}
static void ExcludeMine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
int count = get_mine_count(mine, x, y);
if (count != 0)
{
//当周围8个坐标有雷,计算雷个数
show[x][y] = count + '0';
}
else
{
//当周围8个坐标没有雷
show[x][y] = ' ';
if ('*' == show[x - 1][y - 1])
ExcludeMine(mine, show, x - 1, y - 1);
if ('*' == show[x - 1][y])
ExcludeMine(mine, show, x - 1, y);
if ('*' == show[x - 1][y + 1])
ExcludeMine(mine, show, x - 1, y + 1);
if ('*' == show[x][y - 1])
ExcludeMine(mine, show, x, y - 1);
if ('*' == show[x][y + 1])
ExcludeMine(mine, show, x - 1, y + 1);
if ('*' == show[x + 1][y - 1])
ExcludeMine(mine, show, x + 1, y - 1);
if ('*' == show[x + 1][y])
ExcludeMine(mine, show, x - 1, y + 1);
if ('*' == show[x + 1][y + 1])
ExcludeMine(mine, show, x - 1, y + 1);
}
}
//判赢
static char CheckShow(char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
for (x = 1;x <= row;x++)
{
for (y = 1;y <= col;y++)
{
if (show[x][y] == '*')
{
//计算还有几个未排坐标
win++;
}
}
}
return win;
}
//玩家排雷
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (1)
{
//外挂(方便测试)
//DisplayBoard(mine, row, col);
printf("请输入排雷坐标:");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] == '*')
{
//坐标是雷
if (mine[x][y] == '1')
{
system("cls");
printf("Peng!!!GAME OVER!n");
break;
}
//不是雷
else
{
system("cls");
ExcludeMine(mine, show, x, y);
DisplayBoard(show, row, col);
//DisplayBoard(mine, row, col);
win = CheckShow(show, row, col);
win = CheckShow(show, row, col);
if (win == EASILY_MINE)//当只剩雷的个数,排雷成功
{
system("cls");
break;
}
}
}
else
{
printf("坐标非法,请重新输入n");
}
}
if (win == EASILY_MINE)
printf("恭喜你排雷成功!n");
//查看所有雷的位置
DisplayBoard(mine, row, col);
system("pause");
system("cls");
}
#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 };
InitBoard(mine, ROWS, COLS,'0');
InitBoard(show, ROWS, COLS, '*');
//观察两个数组
//DisplayBoard(show, ROW, COL);
//DisplayBoard(mine, ROW, COL);
//埋雷
SetMine(mine, ROW, COL);
//DisplayBoard(mine, ROW, COL);
system("cls");//清空屏幕
DisplayBoard(show, ROW, COL);
//排雷
FindMine(mine, show, ROW, COL);
}
void test()
{
int input = 0;
//时间戳
srand((unsigned int)time(NULL));
do
{
menu();
printf("请输入你的选择:");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏n");
break;
default:
printf("输入错误,请重新输入n");
}
} while (input);
}
int main()
{
test();
return 0;
}
演示效果
因为是测试运行效果,所以用外挂来辅助测试~~



