目录
一、游戏介绍
二、游戏框架
三、游戏实现
1.搭建游戏运行逻辑(test.c)
2.game.h要包含的头文件和定义的宏
3.game.c函数的实现
扫雷的玩法:在一个方块矩阵中随机布置一定量的地雷,再由玩家排雷,以找出所有地雷为最终游戏目标。如果玩家排除所有地雷或翻开的地方有地雷,则游戏结束。
test.c:主函数所在,游戏逻辑的测试,包含游戏菜单的打印,游戏设计的基本逻辑的展示。
game.c:游戏功能的具体实现,功能都封装成函数。
game.h:相关头文件的包含、符号的声明以及函数的声明。
#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] = { 0 };
char show[ROWS][COLS] = { 0 };
//初始化
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
//清除菜单,美观整洁
system("cls");
//埋雷
SetMine(mine, ROW, COL);
//DisplayBoard(mine, ROW, COL); //用于自己调试观察,在发布时注释掉
//打印游戏
DisplayBoard(show, ROW, COL);
//排雷
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:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,重新选择:\n");
break;
}
} while (input);
return 0;
}
我们定义两个数组(雷盘),show(展示给玩家的),mine(方便我们埋雷和统计周围雷的个数)
接下来只要实现game函数中的InitBoard(初始化),SetMine(埋雷),DisplayBoard(打印游戏), FindMine(排雷)。
#pragma once
#include
#include
#include
#include //system("cls")要用到
#define ROW 9 //行数
#define COL 9 //列数
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10 //埋下雷的数量
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.h将雷盘的行数,列数和埋雷数用宏定义,这样以后改变大小只需要在game.h改就行了,增加了代码的可维护性。
假设是9*9的雷盘,埋了10个雷,如图
我们发现最外面一圈坐标在排查周围有多少雷时会发生数组越界,所以为了方便统计,周围增加一圈没有雷的坐标。
ROW和COL确定游戏的雷盘大小,ROWS和COLS确定实际数组的大小。
而且这样玩家输入的行列数就是实际的行列数(数组下标从0开始)。
初始化(show传 ‘ * ’,mine传‘ 0 ’)
埋雷
生成rand函数的种子的srand函数只需要在main函数中声明一次即可。
打印游戏
排雷(递归和标记借鉴http://t.csdn.cn/0rCVY)
先判断有没有赢,然后让玩家输入,判断玩家输入的坐标合法性(是不是在范围内),再判断该坐标是否有雷,再判断这个坐标有没有排查过,如果没有,就递归检查它周围的坐标,直到遇到周围有雷的坐标才停止递归,再让用户选择是否需要标记雷的信息。
接下来只要实现ExplosionSpread(炸开一片)和MarkMine(标记雷)
ExplosionSpread
炸开一片的条件:
1.这个坐标不是雷
2.这个坐标(及周围的坐标)周围没有雷且没有排查过其中有一个统计周围雷数量的函数get_mine_count(mine, x, y)
因为是埋的雷是字符' 1 ',所以可以用' 1 ' - ' 0 '=1的方法快速统计。
当然还可以用循环。
MarkMine
完整的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, j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;//set表示要初始化的字符
}
}
}
void DisplayBoard(char board[ROWS][COLS], int row, int col)//打印雷盘
{
int i = 0, j = 0;
printf("------扫雷游戏------\n");
printf(" ");
for (i = 1; i <= col; i++)//打印列号
printf("%d ", i);
printf("\n ");
for (i = 1; i <= col; i++)
printf("- ");
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d|", i);//打印行号
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j] );
}
printf("\n");
}
}
void SetMine(char board[ROWS][COLS], int row, int col)
{
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';
}
}
}
//周围有多少雷
int get_mine_count(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 ExplosionSpread(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* win)
{
if (x >= 1 && x <= row && y >= 1 && y <= col) //判断坐标是否为排查范围内
{
int num = get_mine_count(mine, x, y); //获取坐标周围雷的个数
if (num == 0)
{
(*win)++;
show[x][y] = ' '; //如果该坐标周围没有雷,就把该坐标置成空格(防止死递归),并向周围八个坐标展开
int i = 0;
int j = 0;
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
if (show[i][j] == '*'|| show[i][j] == '!') //限制递归条件,防止已经排查过的坐标再次递归,从而造成死递归
ExplosionSpread(mine, show, row, col, i, j, win);
}
}
}
else
{
(*win)++;
show[x][y] = num + '0';
}
}
}
//标记雷的位置
void MarkMine(char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("请输入你想要标记位置的坐标->");
while (getchar()!= '\n')//清理缓冲区
;
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col) //判断该坐标是否合法
{
if (show[x][y] == '*') //判断该坐标是否被排查
{
show[x][y] = '!';
break;
}
else
{
system("cls");
DisplayBoard(show, ROW, COL);
printf("该位置不能被标记,请重新输入!\n");
}
}
else
{
system("cls");
DisplayBoard(show, ROW, COL);
printf("不在范围内,请重新输入!\n");
}
}
}
//排雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int win = 0;//统计不是雷的坐标
while (win");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)//判断坐标合法性
{
if (mine[x][y] == '1')
{
system("cls");
printf("很遗憾,你被炸死了\n");
DisplayBoard(mine, row, col);//让玩家知道雷在哪
break;
}
else
{
if (show[x][y] != '*'&& show[x][y] != '!') //判断是否重复排查
printf("该坐标已被排查,请重新输入!\n");
else
{
ExplosionSpread(mine, show, row, col, x, y, &win);
system("cls");
DisplayBoard(show, ROW, COL);
printf("需要标记雷的位置请输入Y,否则请按任意键->");
while (getchar() != '\n')//清理缓冲区
;
char c;
scanf("%c", &c);
while (c == 'Y')
{
MarkMine(show, ROW, COL); //标记雷
system("cls");
DisplayBoard(show, ROW, COL);
printf("继续标记请输入Y,否则请按任意键->");
while ( getchar() != '\n')
;
scanf("%c", &c);
}
}
}
}
else
printf("输入坐标非法,请重新输入\n");
}
if (win == row * col - EASY_COUNT)//判断是为了防止被炸死还打印“排雷成功”
{
system("cls");
printf("恭喜你,排雷成功\n");
DisplayBoard(mine, ROW, COL);
}
}
还有其他功能可以添加,如:去除标记。我就不献丑了。