一、功能实现
扫雷是一个非常经典的小游戏,今天我将给大家讲解如何用C语言实现扫雷,我的这个扫雷有如下几个功能:
1.显示该点周围雷的个数:
2.坐标周围没雷,可以实现展开:
3.游戏结束后展示雷的位置:
二、设计思路
从上面几张效果图可以看出,我们一共有两种不同的棋盘:
一种是含" * "和数字的棋盘,是显示给玩家看的棋盘,;
另一种是只有0和1的棋盘,是我们设计者看的,它可以显示出当所有雷的分布情况。
我们可以用二维数组来定义这两种棋盘,那么,问题来了,假如我们想打印出 9X9的棋盘,我们的二维数组元素也应该是 9X9个吗?不是的,假如我们定义的二维数组是 9X9的话,在计算边界雷的个数时,要访问该坐标的周围8个坐标,会出现数组越界的问题,所以,我们在定义二维数组的时候,应该比 9X9多定义一圈,也就是定义成11X11的,打印的时候打印中间的9X9个元素就好啦,看看下图可能会更好理解:
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
char real_mine[ROWS][COLS]; //存放雷的棋盘
char show_mine[ROWS][COLS]; //显示的棋盘
1.初始化棋盘
定义好这两个数组以后,我们需要初始化它们:
代码如下:
//初始化棋盘
//该函数传的四个参数分别是:要初始化的数组首地址,要初始化的行数,要初始化的列数,目标字符
void InitBoard(char arr[ROWS][COLS], int rows, int cols, char x)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
arr[i][j] = x;
}
}
}
调用该函数:
//初始化棋盘
InitBoard(real_mine, ROWS, COLS, '0');
InitBoard(show_mine, ROWS, COLS, '*');
2.打印棋盘
代码如下:
//打印棋盘
//该函数传的四个参数分别是:要打印的数组首地址,要打印的行数,要打印的列数
void DisplayBoard(char arr[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
for (j = 0; j <= col; j++)
{
printf("%d ",j); //打印横坐标
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i); //打印纵坐标
for (j = 1; j <= col; j++)
{
printf("%c ", arr[i][j]);
}
printf("\n");
}
}
3.布置雷
初始化好棋盘之后,我们要在存放雷的棋盘上面布置雷了,为了使每一次玩的时候棋盘中的雷分布都不相同,我们采用随机数生成横竖坐标来确定雷的坐标。
代码如下:
void SetMine(char real_mine[ROWS][COLS], int row, int col)
{
int count = MINE_COUNT;
while (count)
{
//随机生成雷的下标
int x = rand() % row + 1;
int y = rand() % col + 1;
if (real_mine[x][y] == '0') //判断该位置是否已经生成雷了
{
real_mine[x][y] = '1';
count--;
}
}
}
注意:
1.我们的雷不能生成在周围一圈,只能生成在中间的9X9的数组内。
2.在使用rand( )之前,需要在主函数调用一次:
srand((unsigned int)time(NULL));
这样每次生成的雷的坐标一定不同。
4.排查雷
布置好雷以后,玩家就可以玩游戏了,每次输入一个坐标,判断存放雷的棋盘上该坐标处是否有雷:
1.有雷:
游戏结束,打印存放雷的棋盘;
2.该坐标处没有雷
a.它的周围有雷:打印周围雷的个数;
a.它的周围没有雷:展开一片。
代码如下:
//计算周围雷的个数
int GetMineCount(char real_mine[ROWS][COLS], int i, int j)
{
return real_mine[i - 1][j - 1] +
real_mine[i - 1][j] +
real_mine[i - 1][j + 1] +
real_mine[i][j - 1] +
real_mine[i][j + 1] +
real_mine[i + 1][j - 1] +
real_mine[i + 1][j] +
real_mine[i + 1][j + 1] - 8 * '0';
//通过ASCII码值,将字符转化成数字
}
//该坐标处没有雷时:
void FindMine_plus(char real_mine[ROWS][COLS], char show_mine[ROWS][COLS], int i, int j,int* pwin)
{
//循环条件:该坐标在中间的9X9的范围内,且该坐标处不是雷,且该坐标处没有排查过
while (i > 0 && i<ROWS && j>0 && j< COLS && real_mine[i][j] == '0' && show_mine[i][j] == '*')
{
if (GetMineCount(real_mine, i, j) == 0)//该坐标周围没有雷
{
show_mine[i][j] = ' ';
(*pwin)++;
FindMine_plus(real_mine, show_mine, i - 1, j, pwin);
FindMine_plus(real_mine, show_mine, i - 1, j + 1, pwin);
FindMine_plus(real_mine, show_mine, i, j + 1, pwin);
FindMine_plus(real_mine, show_mine, i + 1, j + 1, pwin);
FindMine_plus(real_mine, show_mine, i + 1, j, pwin);
FindMine_plus(real_mine, show_mine, i, j - 1, pwin);
FindMine_plus(real_mine, show_mine, i + 1, j - 1, pwin);
FindMine_plus(real_mine, show_mine, i - 1, j - 1, pwin);
}
else//该坐标周围有雷
{
show_mine[i][j] = GetMineCount(real_mine, i, j) + '0';//将数字转化成字符
(*pwin)++;
break;
}
}
}
void FindMine(char real_mine[ROWS][COLS], char show_mine[ROWS][COLS], int row, int col)
{
int i;
int j;
int win = 0;
while (win < row*col - MINE_COUNT) //循环次数:元素个数-雷的个数
{
printf("请输入坐标:>");
scanf("%d%d", &i, &j);
if (i >= 1 && i <= row && j >= 1 && j <= col)
{
if (real_mine[i][j] == '1')
{
printf("很遗憾,你踩到了雷,游戏结束!\n");
DisplayBoard(real_mine, row, col);
break;
}
else
{
FindMine_plus(real_mine, show_mine, i, j,&win);
DisplayBoard(show_mine, row, col);
}
}
else
printf("坐标有误,请重新输入!");
}
if (win == row*col - MINE_COUNT)
{
printf("恭喜你,排查出了所有雷!");
DisplayBoard(show_mine, row, col);
}
}
将以上函数的定义放在 game.c 文件中
三、测试
在头文件game.h中声明各种函数:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MINE_COUNT 10
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
//初始化棋盘
void InitBoard(char arr[ROWS][COLS], int rows, int cols, char x);
//打印棋盘
void DisplayBoard(char arr[ROWS][COLS], int row, int col);
//布置雷
void SetMine(char real_mine[ROWS][COLS], int row, int col);
//排查雷
void FindMine(char real_mine[ROWS][COLS], char show_mine[ROWS][COLS], int row, int col);
在test.c中把这些模块整合起来测试:
#include "game.h"
void menu()
{
printf("****************************\n");
printf("********* 扫雷 *********\n");
printf("********* 1.play *********\n");
printf("********* 0.exit *********\n");
printf("****************************\n");
}
void game()
{
char real_mine[ROWS][COLS]; //存放雷的棋盘
char show_mine[ROWS][COLS]; //显示的棋盘
//初始化棋盘
InitBoard(real_mine, ROWS, COLS, '0');
InitBoard(show_mine, ROWS, COLS, '*');
//打印棋盘
DisplayBoard(show_mine, ROW, COL);
//DisplayBoard(real_mine, ROW, COL);
//布置雷
SetMine(real_mine, ROW, COL);
//DisplayBoard(real_mine, ROW, COL);
//排查雷
FindMine(real_mine, show_mine, ROW, COL);
}
void test()
{
int choice = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请输入:>");
scanf("%d", &choice);
switch (choice)
{
case 1:
game();
break;
case 0:
printf("********* 再见 *********\n");
break;
default:
printf("输入有误,请重新输入!\n");
break;
}
} while (choice);
}
int main()
{
test();
return 0;
}
四、源代码
game.c源代码如下:
#include "game.h"
void InitBoard(char arr[ROWS][COLS], int rows, int cols, char x)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
arr[i][j] = x;
}
}
}
void DisplayBoard(char arr[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
for (j = 0; j <= col; j++)
{
printf("%d ",j);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", arr[i][j]);
}
printf("\n");
}
}
void SetMine(char real_mine[ROWS][COLS], int row, int col)
{
int count = MINE_COUNT;
while (count)
{
//随机生成雷的下标
int x = rand() % row + 1;
int y = rand() % col + 1;
if (real_mine[x][y] == '0')
{
real_mine[x][y] = '1';
count--;
}
}
}
int GetMineCount(char real_mine[ROWS][COLS], int i, int j)
{
return real_mine[i - 1][j - 1] +
real_mine[i - 1][j] +
real_mine[i - 1][j + 1] +
real_mine[i][j - 1] +
real_mine[i][j + 1] +
real_mine[i + 1][j - 1] +
real_mine[i + 1][j] +
real_mine[i + 1][j + 1] - 8 * '0';
//将字符转化成数字
}
void FindMine_plus(char real_mine[ROWS][COLS], char show_mine[ROWS][COLS], int i, int j,int* pwin)
{
while (i > 0 && i<ROWS && j>0 && j< COLS && real_mine[i][j] == '0' && show_mine[i][j] == '*')
{
if (GetMineCount(real_mine, i, j) == 0)
{
show_mine[i][j] = ' ';
(*pwin)++;
FindMine_plus(real_mine, show_mine, i - 1, j, pwin);
FindMine_plus(real_mine, show_mine, i - 1, j + 1, pwin);
FindMine_plus(real_mine, show_mine, i, j + 1, pwin);
FindMine_plus(real_mine, show_mine, i + 1, j + 1, pwin);
FindMine_plus(real_mine, show_mine, i + 1, j, pwin);
FindMine_plus(real_mine, show_mine, i, j - 1, pwin);
FindMine_plus(real_mine, show_mine, i + 1, j - 1, pwin);
FindMine_plus(real_mine, show_mine, i - 1, j - 1, pwin);
}
else
{
show_mine[i][j] = GetMineCount(real_mine, i, j) + '0';//将数字转化成字符
(*pwin)++;
break;
}
}
}
void FindMine(char real_mine[ROWS][COLS], char show_mine[ROWS][COLS], int row, int col)
{
int i;
int j;
int win = 0;
while (win < row*col - MINE_COUNT)
{
printf("请输入坐标:>");
scanf("%d%d", &i, &j);
if (i >= 1 && i <= row && j >= 1 && j <= col)
{
if (real_mine[i][j] == '1')
{
printf("很遗憾,你踩到了雷,游戏结束!\n");
DisplayBoard(real_mine, row, col);
break;
}
else
{
FindMine_plus(real_mine, show_mine, i, j,&win);
DisplayBoard(show_mine, row, col);
}
}
else
printf("坐标有误,请重新输入!");
}
if (win == row*col - MINE_COUNT)
{
printf("恭喜你,排查出了所有雷!");
DisplayBoard(show_mine, row, col);
}
}