利用C语言实现简单的扫雷
一,扫雷的游戏逻辑
1,扫雷简介
《扫雷》是一款大众类的益智小游戏,想必大家应该都玩过,它
于1992年发行。游戏目标是在最短的时间内根据点击格子出现的数字找出所有非雷格子,同时避免踩雷,踩到一个雷即全盘皆输。如果没玩过,可以参考下面网站
网页版扫雷
2,游戏逻辑
1.根据用户输入进行游戏操作:
根据用户输入的坐标,对对应的格子进行操作(翻开、标记)。
判断操作的合法性,例如坐标是否越界,格子是否已经被翻开等。
如果输入的坐标是空白格子(周围没有雷),则递归翻开周围的格子;如果周围有雷,则显示这个坐标周围雷的个数
2.判断游戏是否结束:
如果用户输入的坐标是雷,游戏结束,显示雷的位置,并游戏结束,提示失败。
如果用户成功翻开所有非雷格子,游戏结束,显示所有雷的位置,并提示胜利。
3.显示游戏界面:
根据格子的状态,将相应的字符显示到屏幕上,未翻开的格子显示为 “*” ,翻开的数字格子显示为数字,雷格子显示为 “1” 。
PS!!!:本博客的目的是使用控制台实现简单的扫雷游戏,(相比完整的扫雷游戏仍有许多需要补充的地方,如没有实现递归翻开格子等等,读者略作参考即可)
二,前期准备工作
本博客的要求是可以通过打印的菜单来决定是否继续游戏或退出游戏。棋盘的大小是9X9(可更改),共布置有10个雷(也可根据需求而更改雷的个数)
首先我们需要三个文件来实现扫雷游戏
test.c
game.c
game.h
其中
test.c ——游戏逻辑
game.c ——游戏实现
game.h ——相关函数的声明
1,初始化菜单 menu()
test.c
void menu(){
printf("**********************\n");
printf("****** 1.play ******\n");
printf("****** 0.exit ******\n");
printf("**********************\n");
} //玩家可以通过输入0或1来决定是否继续游戏
2,初始化棋盘 InitBoard()
首先,我们需要定义棋盘的行和列,需要注意的一点是,行和列大小的确定,本博客需要的是一个9X9大小的棋盘,而实现后面的代码的时候,我们需要访问某一个格子周围八个格子的信息,不难想到,当我们需要对棋盘边界上的某个格子进行操作时可能会导致数组的越界访问,所以,在我们创建数组时,需要将这个二维数组扩大一圈,即行+2,列+2。
如下:
game.h
#define ROW 9 //行
#define COL 9 //列
#define ROWS ROW+2
#define COLS COL+2
#define MINE 10 //雷的个数
这些宏定义在 game.h 中存放。
接下来我们在 test.c 中可以创建两个二维数组来生成棋盘,其中一个查看布置雷的信息,一个用来查看排查雷的的信息,展示给玩家。
test.c
char mine[ROWS][COLS]; //布置雷的信息
char show[ROWS][COLS]; //排查雷的信息
将mine数组中所有的元素初始化为字符 ’ 0 ’ 方便后面布置雷,将show数组中所有元素初始化为 ’ * ’ (初始化过程在 game.c 中实现)。
InitBoard(mine, ROWS, COLS,'0');
InitBoard(show, ROWS, COLS,'*');
然后我们利用放在 game.c 中的函数来实现相关功能。
game.c
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set)
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
InitBoard() 也需要在 game.h 声明
game.h
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set);//初始化棋盘
三,打印棋盘与布置雷
1,打印棋盘 DisplayBoard()
同样的,我们仍需要两个数组来打印棋盘,而其中 mine 是用来埋雷的,故不展示出来。
test.c
//DisplayBoard(mine, ROW, COL);
DisplayBoard(show, ROW, COL);
棋盘的打印方法如下:
game.c
void DisplayBoard(char board[ROWS][COLS], int row, int col)//只打印1-9行,1-9列
{
printf("-----扫雷游戏-----\n");
for (int c = 0; c <= col; c++)//打印列号
printf("%d ", c);
printf("\n");
for (int i = 1; i <= row; i++)
{
printf("%d ", i);//打印行号
for (int j = 1; j <= col; j++)
printf("%c ", board[i][j]);
printf("\n");
}
printf("-----扫雷游戏-----\n");
}
为了后面排查方便,于是加上了行号,列号。
DisplayBoard() 需要在 game.h 中声明
game.h
void DisplayBoard(char board[ROWS][COLS], int rows, int cols);//打印棋盘
2,布置雷 SetMine()
由于生成雷的位置是随机的,所以在布置雷的时候,需要用到随机数函数 rand() 和 srand() 所以需要引用头文件 time.h。
需要在 test.c 中用到 srand((unsigned int)time(NULL))。
布置雷的函数:
test.c
SetMine(mine, ROW, COL);
下面是给mine数组布置雷的函数实现
game.c
void SetMine(char mine[ROWS][COLS], int row, int col)//布置雷
{
int count = MINE;
while (count)//随机生成雷
{
int x = rand() % row + 1;
int y = rand() % col + 1;
//布置雷的循环次数有可能多于十次,因为生成的坐标可能已经生成过
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
该函数也需要在 game.h 中声明
game.h
void SetMine(char mine[ROWS][COLS], int row, int col);//布置雷
四,排查雷 FineMine()
现在从 test.c 中创建一个函数,在这个函数中,会将当前已排查的情况展示给玩家,于是多出一个 show 参数。
test.c
FineMine(mine, show, ROW, COL);
玩家通过键盘输入一个坐标来排查雷。
由于本文所展现的排雷方式不完整,所以只能通过如下方式来排雷。如下所示,当次数 times == row*col -MINE时,这时排雷就已经成功。
game.c
void FineMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0,y = 0,times = 0;
while (times<row*col-MINE)//最大排雷次数
{
printf("请输入要排查的坐标:");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了\n");
DisplayBoard(mine, ROW, COL);
break;
}
else
{
int count = GetMineCount(mine, x, y);//得到雷的个数
show[x][y] = count + '0';//加上字符'0'得到对应的字符
DisplayBoard(show, ROW, COL);
times++;
}
}
else
printf("坐标非法,重新输入\n");
}
if (times == row * col - MINE)
{
printf("恭喜你,排雷成功\n");
DisplayBoard(mine, ROW, COL);
}
}
FineMine() 在统计各自周围雷的个数的时候,调用了一个函数 GetMineCount() ,如下:
int GetMineCount(char mine[ROWS][COLS], int x, int y)//返回格子周围雷的个数
{
return (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] +
mine[x][y + 1] +
mine[x - 1][y + 1] - 8 * '0');//因为共有8个格子,于是减去8*'0'得到个数
}
同样的,FineMine() 函数也需要在 game.h 中声明。
game.h
void FineMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col);//排查雷
五,完整代码
game.h ——相关函数的声明
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define MINE 10
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set);
void DisplayBoard(char board[ROWS][COLS], int rows, int cols);
void SetMine(char mine[ROWS][COLS], int row, int col);
void FineMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col);
game.c ——游戏实现
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set)
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
printf("-----扫雷游戏-----\n");
for (int c = 0; c <= col; c++)
printf("%d ", c);
printf("\n");
for (int i = 1; i <= row; i++)
{
printf("%d ", i);
for (int j = 1; j <= col; j++)
printf("%c ", board[i][j]);
printf("\n");
}
printf("-----扫雷游戏-----\n");
}
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int count = MINE;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
return (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] +
mine[x][y + 1] +
mine[x - 1][y + 1] - 8 * '0');
}
void FineMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0,y = 0,times = 0;
while (times<row*col-MINE)
{
printf("请输入要排查的坐标:");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了\n");
DisplayBoard(mine, ROW, COL);
break;
}
else
{
int count = GetMineCount(mine, x, y);
show[x][y] = count + '0';
DisplayBoard(show, ROW, COL);
times++;
}
}
else
printf("坐标非法,重新输入\n");
}
if (times == row * col - MINE)
{
printf("恭喜你,排雷成功\n");
DisplayBoard(mine, ROW, COL);
}
}
test.c ——游戏逻辑
因为考虑到玩家可能玩耍多次,所以在 main() 用到了 do while 循环。
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu() {
printf("**********************\n");
printf("****** 1.play ******\n");
printf("****** 0.exit ******\n");
printf("**********************\n");
}
void game()
{
char mine[ROWS][COLS];
char show[ROWS][COLS];
InitBoard(mine, ROWS, COLS,'0');
InitBoard(show, ROWS, COLS,'*');
DisplayBoard(show, ROW, COL);
SetMine(mine, ROW, COL);
FineMine(mine, show, ROW, COL);
}
int main()
{
srand((unsigned int)time(NULL));
int input = 0;
do
{
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,重新选择\n");
break;
}
} while (input);
return 0;
}
以上就是一段简单的扫雷代码实现。