简易扫雷小游戏
- 游戏介绍
- 功能设计
- 具体实现
-
- 棋盘的创建
- 初始化
- 布置雷
- 棋盘展示
- 玩家第一次落子时不踩雷
- 统计落子处周围雷的个数
- 递归实现周围无雷时自动展开的功能
- 统计当前棋盘已清扫的空格数,以判断最终扫雷是否成功
- 游戏效果
本文所介绍的扫雷仅仅是用C语言的一些最基本的语法所实现,并没有涉及高深的数据结构与算法,仅仅是为了巩固笔者最近所学的C语言语法。若有错误还望多多指教。
游戏介绍
扫雷是一款十分经典的电脑休闲单机游戏,游戏目标是在最短的时间内根据点击格子出现的数字找出所有非雷格子,同时避免踩雷,踩到一个雷即全盘皆输。
功能设计
- 简易的图形化界面
- 玩家通过输入坐标以选择要扫雷的位置。
- 玩家第一次落子一定不踩雷
- 显示选择的坐标周围雷的个数,若周围没有雷则自动展开。
具体实现
在这里具体介绍几个核心功能,完整代码见Gitee: 传送门.
棋盘的创建
这里创建了两个二维数组,一个用于雷的布置,一个用于图形化的展示.
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS ROW + 2
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
棋盘的格式为9x9,这里创建11x11的二维数组是为了当玩家选择坐标在棋盘周围时,正确显示所落子处四周雷的个数。
因此,这里将存放雷的数组最外层全部初始化为0(0表示无雷,1表示雷)。
用于展示的数组则全部初始化为“ * ” ,通过伪随机数的生成,来生成初始雷的坐标
初始化
Init(mine, ROW, COL, '0');
Init(show, ROW, COL, '*');
void Init(char board[ROWS][COLS], int row, int col, char set)
{
int i = 0 ;
int j = 0 ;
for (i = 0; i < row + 2; i++)
{
for (j = 0; j < col + 2; j++)
{
board[i][j] = set;
}
}
}
布置雷
这里雷的布置采用了生成随机数的方法,具体方式为调用rand()函数,并在调用rand()函数之前,先用srand()为伪随机数生成器播撒种子,种子采用系统时钟来生成。
需要引用的头文件和定义:
#include<stdlib.h>
#include<time.h>
#define COUNT 10 //雷的个数
要注意的是srand()一定要在rand()之前使用,否则每次随机种子就是一定的,那么产生出来的随机数就是固定的。这里把srand()放在主函数一开始实现
雷坐标的生成:
Set_Mine(mine, ROW, COL,COUNT);
void Set_Mine(char mine[ROWS][COLS], int row, int col, int count)
{
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
棋盘展示
Display(show, ROW, COL);
```handlebars
void Display(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("******扫雷游戏*******\n");
for (i = 0; i < row + 1; i++)
{
printf("%d ", i);
for (j = 1; j < col + 1; j++)
{
if (i == 0)
printf("%d ", j);
else
printf("%c ", board[i][j]);
}
printf("\n");
}
}
具体效果:
用于展示的棋盘:(周围的数字为方便玩家输入坐标)
用于放置雷的棋盘:
扫雷
这里将统计落子处四周雷的个数,实现周围无雷时自动展开的功能以及统计已排查的无雷坐标个数以确定玩家赢的条件分别封装为单独的函数,具体实现如下:
Sweep(mine, show, ROW, COL, COUNT);
void Sweep(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int count)
{
int x = 0;
int y = 0;
int judge = 0;
while (judge < row*col - count)
{
printf("请输入要扫除的坐标: \n");
scanf("%d %d", &x, &y);
if (x < 0 || y < 0 || x > row || y > col)
printf("坐标非法,请重新输入:\n");
else if (show[x][y] != '*')
printf("该坐标已被扫除,请重新输入:\n");
if(judge == 0)
No_mine(mine, show, x, y, row, col);
if (mine[x][y] != '1')
{
Spread(x, y, mine, show, row, col);
Display(show, row, col);
}
else
{
printf("你被炸死了\n");
break;
}
Stat_judge(show, &judge, row, col);
}
if (judge == row * col - count)
{
printf("恭喜你排雷成功\n");
}
}
玩家第一次落子时不踩雷
void No_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int i, int j, int row, int col)
{
if (mine[i][j] == '1')
{
while (1)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
break;
}
}
mine[i][j] = '0';
}
}
统计落子处周围雷的个数
int Count_Mine(int x, int y, char mine[ROWS][COLS])
{
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';
}
递归实现周围无雷时自动展开的功能
void Spread(int x, int y, char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
char count = Count_Mine(x, y, mine) + '0';
if (count != '0')
show[x][y] = count;
else
{
show[x][y] = ' ';
if (x - 1 > 0 && show[x - 1][y] == '*')
Spread(x - 1, y, mine, show, row, col);
if (y - 1 > 0 && show[x][y - 1] == '*')
Spread(x, y - 1, mine, show, row, col);
if (y + 1 < col + 1 && show[x][y + 1] == '*')
Spread(x, y + 1, mine, show, row, show);
if (x + 1 < row + 1 && show[x + 1][y] == '*')
Spread(x + 1, y, mine, show, row, col);
}
}
统计当前棋盘已清扫的空格数,以判断最终扫雷是否成功
void Stat_judge(char show[ROWS][COLS], int* judge, int row, int col)
{
int i = 0;
int j = 0;
for (i = 1; i < row + 1; i++)
{
for (j = 1; j < col + 1; j++)
{
if (show[i][j] != '*')
(*judge)++;
}
}
}
游戏效果
自动展开
踩雷
胜利