一,游戏介绍
扫雷是一款很大众的益智类游戏,游戏规则也很简单,在短时间内把所有的雷排查出来即为胜利,如果在排雷期间踩到雷则游戏失败。本次制作的是一个初级的(10颗雷),玩家需要翻开所有不是雷的格子才能取得胜利,如果翻开的是有雷的格子则游戏失败。如果翻开的格子没有雷,则这个格子会显示周围8个格子中雷的个数。
二,游戏思路
首先,我们需要思考应该怎样构建这个游戏的基本框架呢?
看到这个图时,可以明显感觉和方格图有点类似,那我们就可以尝试用一个二维数组来构建这个游戏的主体,那需要一个多大的二维数组呢,我们继续分析。
接下来我们先思考,如何把雷和非雷区分开呢,这时候大家首先能想到的应该是雷用‘*’号,非雷用‘#’号,等等这种想法都没有任何问题,但这里给大家提一个思路就是,雷用字符‘1’表示,非雷用字符‘0’表示,这样建议肯定有一定的好处,而且会对后面我们排查雷的时候,这样容易计算格子周围一圈雷的个数。如果这个格子周围没有雷就可以显示‘0’,如果有一个雷则显示‘1’,以此类推可以更方便的为排雷提供信息。
假设当我们布置完十个雷之后,开始游戏,然后随机点一个格子,如果这个格子是雷,则游戏结束,如果这个格子不是雷,那么就会显示这个格子周围一圈的雷的个数,如果周围是一个雷的话,则显示‘1’,那么问题来了,这里的‘1’,到底是该格子周围雷的个数呢,还是雷呢?所以为了解决这个问题,我们需要建立两个二维数组,一个用来我们布置雷‘1’和非雷‘0’(mine),这个是用户看不见的,另一个用于展示给玩家看的棋盘(show),存放的是排查雷的信息,这样两边就不会冲突了。展示给玩家看的数组为了保持神秘感,我们最开始放的全是✳,这时我们创建的数组就是字符数组。
紧接着,我们思考一下,我们翻格子的时候,如果这个格子不是雷,那么就会排查这个格子周围八个格子存在雷的个数,假设我们翻开最外围的格子的时候,但是该格子外围时没有格子的怎么办?这是侯就会出现越界访问的情况,为了避免这个情况出现,我们只需要把9*9的格子扩张成11*11的格子就可以了,如图所示,这样遍历的时候就不会出现越界访问的情况了。
三,游戏的实现
为了使游戏代码逻辑清晰可以文件形式创作,创建game.c文件(用于测试函数),创建game.h文件(用于游戏函数的声明和头文件的引用),创建test.c文件(用于游戏的框架和实现)。
1,菜单的打印
菜单的打印采用do-while循环,其中运用switch语句可以更好的控制游戏的开始与结束。
2,初始化数据
首先我们定义两个数组,一个是用来存放布置雷的信息,另一个是存放排查雷的信息。为了方便我们以后对代码的修改以及对游戏的优化,我们这里在头文件中把行和列分别定义出来,如果以后想玩更大的棋盘,我们只需在头文件中进行修改即可。
然后就可以对两个数组进行初始化了,并且要把参数传进去。
3,打印棋盘
当我们初始完之后,我们想要看看棋盘到底是什么样的,那么我们就需要打印棋盘,打印棋盘的时候,展示给我们的其实是9*9的棋盘,我们最开始设成11*11的棋盘只是为了防止越界访问。
打印的时候行和列都是1-9,这样方便后续通过坐标来排雷。
4,布置雷
接下来我们需要布置雷,大家思考一下,我们布置雷实际上还是把雷放在中间的9*9的数组中去,也就是说我们这里布置雷只需要操作mine数组。怎么实现呢,这里我们假设布置十个雷,那么我们就需要生成十个随机坐标,来放置雷。但是为了以后方便更改数据,所以这里我们可以把雷的数量在头文件中。在放置雷的时候,需要随机,这就需要我们对count进行改造,运用时间戳来产生随机值。
5,排雷
我们有两个数组mine,show。show数组我们可以看见,但是我们排雷是对mine数组进行的,并且要把排查结果反应到show中让我们看到。
计算周围雷的个数时要注意,我们mine数组中存的时字符‘0’,‘1’,我们直接找出周围8个格子的坐标,并且相加再减去8个‘0’,最后所得的ASCII值即为8个格子中的雷的数目。
四,总结
扫雷游戏简单但是代码较复杂,还需要多加理解,才能够真正的掌握理解,也许这就是C语言的魅力,可以通过一行行的字母来完成一个简单的小游戏。并且这个扫雷游戏需要将71个不是雷的坐标全部翻开才算游戏胜利。游戏设计比较简单还可以添加更多功能,希望小伙伴们将其完善的更完美。
五,源码
game.h
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define EASY_COUNT 10
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
//棋盘的初始化
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 board[ROWS][COLS], int row, int col);
//排查雷
void FindMine(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)
{
int i = 0;
printf("--------扫雷游戏--------\n");
for (i = 0; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
int j = 0;
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
void SetMine(char board[ROWS][COLS], int row, int col)
{
//布置10个雷
//生成随即坐标,布置雷
int count = EASY_COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[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 FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win < row * col - EASY_COUNT)
{
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);
win++;
}
}
else
{
printf("坐标输入非法,请重新输入\n");
}
}
if (win == row * col - EASY_COUNT)
{
printf("恭喜你,排雷成功\n");
DisplayBoard(mine, ROW, COL);
}
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//设置主页面
void menu()
{
printf("*****************************\n");
printf("*****************************\n");
printf("**********1. play ***********\n");
printf("**********0. exit ***********\n");
printf("*****************************\n");
printf("*****************************\n");
}
//完成游戏的整个过程
void game()
{
char mine[ROWS][COLS];//布置好的雷的信息
char show[ROWS][COLS];//存放排查出的雷的信息用于显示
//初始化棋盘
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
//打印棋盘
//DisplayBoard(mine, ROW, COL);
DisplayBoard(show, ROW, COL);
//布置雷
SetMine(mine, ROW, COL);
//DisplayBoard(mine, ROW, COL);
//排查雷
FindMine(mine, show, ROW, COL);
}
int main()
{
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;
}