目录
1.设计思路分析
2.准备工作
2.1 所需文件
3.扫雷的具体实现
3.1 打印菜单
3.2 核心内容实现
3.2.1 初始化棋盘
3.2.2 打印棋盘
3.2.3 (核心)布置雷
3.2.4 (核心2)排查雷
4.总代码
game.h
game.c
test.c
1992年4月6日,扫雷和纸牌、空当接龙等小游戏搭载在Windows 3.1系统中与用户见面,主要目的是让用户训练使用鼠标。这个游戏的玩法很简单,有初级、中级、高级和自定义等模式,雷区中随机布置一定数量的地雷。游戏目标是在最短的时间内根据点击格子出现的数字找出所有非雷格子,同时避免踩雷,踩到一个雷即全盘皆输。
(1)玩家可以通过菜单选择游戏还是退出;
(2)扫雷的棋盘格(Board)定为9*9大小;
(3)默认设置10个地雷(Mine);
(4)输入需要排雷的坐标:
如果该位置不是雷,则显示周围有几个雷;
如果该位置是雷,则提示玩家被炸死,结束游戏,并且显示完整的地雷位置;
将所有雷全部找出,排雷成功,结束游戏。
要完成一个简易版的扫雷游戏,其代码量是不小的,如果将所有代码都放在一个源文件里,写着写着估计就懵了,最终的代码也不美观。所以,就需要分文件编写。我们采用分模块的思想,将所写的代码进行功能的划分,把每个功能不同的代码放在一个源文件里,方便查看和修改。
test.c——完成扫雷游戏的测试,打印菜单,调用函数等
game.c——声明函数
game.h——函数的具体实现
game.h 是自定义的,所以在另外两个.c文件中使用需要:
#include "game.h"
game.h 中包含的头文件将在后面慢慢讲。
熟悉的操作:
#include
#include "game.h"
void menu()
{
printf("******************\n");
printf("***** 1.play *****\n");
printf("***** 0.exit *****\n");
printf("******************\n");
}
int main()
{
int input = 0;
do
{
menu();
printf("请输入:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("进入游戏\n");
printf("———— game ————\n");
game();
printf("———— game ————\n");
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
}
while(input);
return 0;
}
效果如下:
编写一个game()函数作为游戏代码,可以用之前学习的二维数组来构成棋盘。
规定:有雷的方格用1表示,没有雷就用0表示。但是玩过扫雷的我们都知道,打开一个没有雷的方格时,周围八个格会显示这个方格周围的雷的个数。这就要想到一点,如果周围一圈只有一个雷,那这个1和表示雷规定的1就会混淆在一起。所以我们就需要建立两个棋盘,一个用于布置雷,一个用于显示雷的个数。
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
比如这样(布置雷):
图1中排查雷的时候发现第一个红框内的数都在这个二维数组里,但是第二个红框超出了数组的范围,溢出。因此,需要扩建数组,将9*9变为11*11(如图2),但显示和游戏还是9*9。
所以,在 game.h 中定义棋盘大小
#define ROW 9;
#define COL 9;
#define ROWS ROW + 2;
#define COLS COL + 2;
进行初始化棋盘
game.h
//初始化声明
void InitBoard(char arr[ROWS][COLS], int rows, int cols);//形参
game.c
#include "game.h"
void InitBoard(char arr[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++)
{
arr[i][j] = set;//set可以是'*'或'0'
}
}
}
这里是“char set” 是因为 * 是字符,而 0 是数字,‘0’ 是字符,显示出的结果和数字一样,但这样我们就没有必要再写一个函数。同理设置 set 这个变量的目的也是为了方便。
test.c
void game()
{
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
//初始化棋盘
InitBoard(mine, ROWS, COLS, '0');//布雷
InitBoard(show, ROWS, COLS, '*');//界面
}
游戏界面打印出的一定是*的界面(show)。上面讲过我们将9*9的棋盘变为11*11,但是玩家感知到的只要是9*9的就行。
为了方便玩家定位,我们可以加上列号和行号:
game.h
//打印声明
void DisplayBoard(char arr[ROWS][COLS], int row, int col);
game.c
void DisplayBoard(char arr[ROWS][COLS], int row, int col)
{
int i = 0;
//打印列号
for (i = 0; i <= row; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
int j = 0;
//打印行号
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", arr[i][j]);
}
printf("\n");
}
}
test.c
//打印棋盘
DisplayBoard(show, ROW, COL);
输出效果如下:
雷的位置是随机的,所以又要用到可以产生随机数的函数 rand了(详见http://t.csdnimg.cn/5ONtv)
game.h
//10个雷
#define EASY_COUNT 10
//布置雷
void SetMine(char arr[ROWS][COLS], int row, int col);
game.c
void SetMine(char arr[ROWS][COLS], int row, int col)
{
//布置10个雷
int count = EASY_COUNT;
while (count)
{
int x = rand()%row + 1;//1到9 rand()%9=0到8
int y = rand()%col + 1;//1到9
//放过雷的地方不用重复
if(arr[x][y] == '0')
{
arr[x][y] = '1';
count--;
}
}
}
test.c
//(核心1)布置雷
SetMine(mine, ROW, COL);
效果如下:是随机的雷
排雷涉及到两个数组,先在mine数组里排查,得到的信息放在show里面,同样的,不管是查还是传,还是在9*9里。
game.h
//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
game.c
这里再创建一个用来统计周围一圈雷个数的函数——GetMineCount(),按如图顺序。这个函数只用于FindMine()函数,所以可以在前面加上 static。
static 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] - 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 n = GetMineCount(mine, x, y);
show[x][y] = n + '0';//知识点
DisplayBoard(show, ROW, COL);
win++;
}
}
else
{
printf("坐标非法,请重新输入!\n");
}
}
if (win == row * col - EASY_COUNT)
{
printf("恭喜你,排雷成功!!!");
DisplayBoard(mine, ROW, COL);
}
}
test.c
//(核心2)排查雷
FindMine(mine, show, ROW, COL);
#pragma once
#include
#include
#include
#define EASY_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);//形参
//打印声明
void DisplayBoard(char arr[ROWS][COLS], int row, int col);
//布置雷
void SetMine(char arr[ROWS][COLS], int row, int col);
//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
void InitBoard(char arr[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++)
{
arr[i][j] = set;//set可以是'*'或'0'
}
}
}
void DisplayBoard(char arr[ROWS][COLS], int row, int col)
{
int i = 0;
//打印列号
for (i = 0; i <= row; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
int j = 0;
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", arr[i][j]);
}
printf("\n");
}
}
void SetMine(char arr[ROWS][COLS], int row, int col)
{
//布置10个雷
int count = EASY_COUNT;
while (count)
{
int x = rand()%row + 1;//1到9 rand()%9=0到8
int y = rand()%col + 1;//1到9
//放过雷的地方不用重复
if(arr[x][y] == '0')
{
arr[x][y] = '1';
count--;
}
}
}
static 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] - 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 n = GetMineCount(mine, x, y);
show[x][y] = n + '0';//知识点
DisplayBoard(show, ROW, COL);
win++;
}
}
else
{
printf("坐标非法,请重新输入!\n");
}
}
if (win == row * col - EASY_COUNT)
{
printf("恭喜你,排雷成功!!!");
DisplayBoard(mine, ROW, COL);
}
}
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
void menu()
{
printf("******************\n");
printf("***** 1.play *****\n");
printf("***** 0.exit *****\n");
printf("******************\n");
}
void game()
{
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
//初始化棋盘
InitBoard(mine, ROWS, COLS, '0');//布雷
InitBoard(show, ROWS, COLS, '*');//界面
//打印棋盘
DisplayBoard(show, ROW, COL);
//(核心1)布置雷
SetMine(mine, ROW, COL);
//(核心2)排查雷
FindMine(mine, show, ROW, COL);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请输入:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("进入游戏\n");
printf("———— game ————\n");
game();
printf("———— game ————\n");
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
}
while(input);
return 0;
}
本文写的是扫雷游戏的基础实现,与网页版的扫雷还有很大差距,展开一片和标记雷将会在后续实现(研究研究——)