一、前导
想必大家都玩过扫雷游戏吧!不管怎样,我们看一下扫雷游戏的基本规则:
- 游戏目标:找出所有的地雷! 更准确地说是——在避免踩到地雷的情况下探索所有的格子。 扫雷游戏胜利的条件并不是标记处所有包含地雷的格子,而是 揭示所有不包含地雷的格子 。
- 如何避免踩雷:利用安全格子提供的信息,我们可以推断一个格子附近的格子有没有雷。
- 安全格子的类型: 数字:表示它 周围八个格子 中地雷的数量。
这是一个简易的网页版扫雷,我们今天就用C语言的数组和函数知识来实现这样一个扫雷游戏。
二、分析与思路
棋盘
由上图我们发现,游戏有一个9*9的棋盘,棋盘中包含着数字信息,雷的位置信息等。由此我们想到用二维数组来完成棋盘的制作。
雷的布置
- 我们决定用二维数组来制作棋盘,那么二维数组的每个元素可以用来表示每个区域,区域分为有雷区和无雷区。两种雷区意味着需要两种元素来分别表示,那么我们选择什么元素来表示呢?
有和无,我们容易想到C语言中的 0 和 1 ,0表示无雷区,1表示有雷区。这样,一个由1和0组成的二维数组就产生了。注意,这个二维数组不能打印在屏幕上,不能泄露天机呀! - 在9*9棋盘上随机放置n(下面代码放10个)个雷,需要产生随机数,我们需要调用rand函数、srand函数、time函数,这就需要头文件stdlib.h、time.h。
数字的显示
- 数字表示它 周围八个格子 中地雷的数量。这个数字要计算出并显示出来,且第一个二维数组记录了雷区情况,所以我们需要另一个二维数组。
- 在我们排雷并显示数字前,屏幕上总得显示点什么吧,不妨就用 字符* 来初始化第二个数组。
- 还有一点:
如果排棋盘边缘的区域,会出现数组越界访问的问题。我们考虑将棋盘扩大一圈,但是游戏区域仍然是9*9的区域。
为了使两个数组元素下标一一对应,我们规定它们都是char[11][11]类型,规定同类型方便函数的使用。
三、代码实现
我们用多个文件的形式对函数进行声明和定义,来实现扫雷游戏:
1. test.c //记录游戏的测试逻辑
2. game.c //记录游戏中函数的实现等
3. game.h //记录游戏需要的数据类型和函数声明等
game.c
#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 COUNT_MINE 10//雷的数量
//初始化场地
void Initarray(char arr[ROWS][COLS], int row, int col, char ch);
//查看打印场地
void Showarray(char arr[ROWS][COLS], int row, int col);
//布置雷区
void Layoutmine(char arr[ROWS][COLS], int row, int col);
//扫雷
void Findmine(char arr1[ROWS][COLS], char arr2[ROWS][COLS],int row,int col);
game.c
#include"Mine.h"
//初始化
void Initarray(char arr[ROWS][COLS], int row, int col, char ch)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
arr[i][j] = ch;
}
}
}
//查看打印场地,只打印中间游戏部分
void Showarray(char arr[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("---------------------------------------\n");//类似的场地布置随意发挥
//打印行标号,便于观察坐标
for (i = 0; i <= ROW; i++)
{
printf("%d ", i);
}
printf("\n");
printf("____________________\n");
//打印游戏棋盘
for (i = 1; i < row - 1; i++)
{
printf("%d|", i);//穿插打印列标号,‘|’ 将标号和棋盘分隔开
for (j = 1; j < col - 1; j++)
{
printf("%c ", arr[i][j]);
}
printf("\n");
}
}
//布置雷
void Layoutmine(char arr[ROWS][COLS], int row, int col)
{
int count = COUNT_MINE;
while (count)
{
int x = rand() % 9 + 1;//产生随机数x(1~9)
int y = rand() % 9 + 1;//产生随机数y(1~9)
if (arr[x][y] == '0')//字符0代表无雷区
{
arr[x][y] = '1';//无雷区布置一个雷
count--;
}
}
}
//计算周围雷的数量
int Tip(char arr[ROWS][COLS],int x,int y)
{
return arr[x][y + 1] + arr[x + 1][y + 1] +
arr[x + 1][y] + arr[x + 1][y - 1] +
arr[x][y - 1] + arr[x - 1][y - 1] +
arr[x - 1][y] + arr[x - 1][y + 1] - 8 * '0';//字符数字转化为数字,用字符数字减去字符0,周围有几个雷,就返回数字几
}
//扫雷
void Findmine(char arr1[ROWS][COLS], char arr2[ROWS][COLS],int row,int col)
{
int x = 0;
int y = 0;
int z = 0;//结束标志,记录扫出的无雷区数量
while (z<(row*col- COUNT_MINE))
{
printf("请输入要查询的坐标->:");
scanf("%d %d", &x, &y);
if (((x > 0) && (x <= row)) && ((y > 0) && (y <= col)))
{
if (arr1[x][y] == '1')
{
printf("你被炸飞了,扫雷失败!\n本局游戏雷分布状况:\n");
Showarray(arr1, ROWS, COLS);
break;
}
else if (arr2[x][y] != '*')
{
printf("这个坐标已经查询过了,请重新输入要查询的坐标\n");
}
else
{
int count = Tip(arr1,x,y);
arr2[x][y] = count+'0';//整数化为字符数
Showarray(arr2, ROWS, COLS);
z++;
}
}
else
{
printf("输入的坐标非法,请重新输入!\n");
}
}
if(z == (row * col - COUNT_MINE))
{
printf("恭喜你过关!扫雷大师就是你!\n");
Showarray(arr1, ROWS, COLS);
}
}
test.c
#include"Mine.h"
//打印菜单
void menu()
{
printf("-----------扫雷游戏-----------\n");
printf("******************************\n");
printf("********** 1.开始 **********\n");
printf("********** 0.退出 **********\n");
printf("******************************\n");
printf("请选择->:");
}
//游戏实现的核心
void Game()
{
char mine[ROWS][COLS] = { 0 };//用于布置雷的数组,也就是上述的第一个数组
char show[ROWS][COLS] = { 0 };//用于打印的数组,也就是上述的第二个数组
Initarray(mine, ROWS, COLS, '0');
Initarray(show, ROWS, COLS, '*');
//Showarray(mine, ROWS, COLS); 这里打印场地是为了在书写代码时检验初始化函数的正确性
//Showarray(show, ROWS, COLS);
Layoutmine(mine, ROW, COLS);
//Showarray(mine, ROWS, COLS); 检查布置雷的效果
Showarray(show, ROWS, COLS);
Findmine(mine, show,ROW,COL);
}
//主函数
int main()
{
srand((unsigned int)time(NULL));//rnad函数产生伪随机数,用时间戳实现真随机数
int input = 0;//用户根据菜单选择输入
do
{
menu();
scanf("%d", &input);
switch (input)
{
case 1:Game();
break;
case 0:printf("游戏结束");
break;
default:printf("输入非法,请重新输入\n");
break;
}
} while (input);
return 0;
}
代码完成:
代码已经测试过了,未发现bug,如果有,期待你的指正。如果有哪里不太明白,我很乐意为大家服务!
赶紧动手试一试吧。