前言
实现扫雷小游戏,我们应该先来了解它的游戏规则,再来理解它的实现逻辑。
游戏规则
- 我们随便找到一个棋盘上的坐标,打开该坐标的格子可能是数字,可能是地雷,也可能是空白。
- 如果我们打开的是地雷的话,那我们就输了
- 如果我们打开的是数字,则代表周围会有雷,数字几则代表以该坐标为中心周围的八个坐标内有几个雷
- 而如果是空白的话,那么就会往周围炸开一片,直至遇到是数字的坐标则不继续炸开
如图:
实现逻辑
- 我们要先建立一个菜单,来实现玩家与电脑之间的交互
- 我们需要有两个棋盘,其中一个棋盘用来实现玩家与电脑之间的交互,另外一个用来埋雷
- 输入一个坐标,随后该坐标对应的点展开,展开的可能是数字,可能是地雷,也可能是空白。
- 如果是空白的话,需要用递归的思想来进行展开一片连续的空白。
- 同时,我们要有一个用来计数的变量,这个变量用来判断我们是否将雷全部排完。
1. 实现步骤
由于涉及到的函数较多,所以我们可以用一个头文件(game.h),两个源文件(分别是main函数的运行(test.c) 和 我们要实现的游戏逻辑函数(game.c) )来完成这些操作。
1.1 源文件(test.c)
#include "game.h"
void meun()
{
printf("**************************\n");
printf("***** 1.play *****\n");
printf("***** 0.exit *****\n");
printf("**************************\n");
}
void game()
{
char show[ROW][COL] = { 0 };
char mine[ROW][COL] = { 0 };
//初始化棋盘
Init_chessboard(show, ROW, COL, '*');
Init_chessboard(mine, ROW, COL, '0');
//打印棋盘
Print_chessboard(show, ROW, COL);
Print_chessboard(mine, ROW, COL);
//埋雷
Lay_mines(mine, ROW, COL);
Print_chessboard(mine, ROW, COL);
//玩家操作
Player_move(show, mine, ROW, COL);
}
int main()
{
//生成随机数种子,在main函数中使用一次以后,
//后面使用到rand函数就会随机生成数值
srand((unsigned int)time(NULL));
int input = 0;
//我们do...while语句可以方便我们能自主控制开始/退出游戏
do
{
meun();//打印菜单,以方便人机互动
scanf("%d", &input);
switch (input)
{
case 1:game();该函数则是我们要实现的一个函数
break;
case 0:printf("退出游戏\n");break;
default:printf("输入有误,请重新输入\n");break;
}
} while (input);
return 0;
}
1.2 头文件(game.h)的准备
那么在头文件(game.h)中我们应该做哪些准备呢:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//定义棋盘的横纵坐标数量
#define ROWS 9
#define COLS 9
//棋盘的横纵坐标数量是用来方便玩家操作的,
//而我们应该在此基础上多增加两格,
//目的是为了操作数组的时候防止越界
#define ROW (ROWS + 2)
#define COL (COLS + 2)
#define NUM 10//雷的个数
//我们可以调节雷的个数和横纵坐标的大小来设计游戏的难度
//初始化棋盘
void Init_chessboard(char chessboard[ROW][COL],int row,int col,char ch);
//打印棋盘
void Print_chessboard(char chessboard[ROW][COL], int row, int col);
//埋雷
void Lay_mines(char chessboard[ROW][COL], int row, int col);
//玩家操作
void Player_move(char show[ROW][COL], char mine[ROW][COL], int row, int col);
//周围雷的个数
int around_mines(char mine[ROW][COL], int x, int y);
//实现扫雷中炸开一片的效果
void view_clear(char show[ROW][COL], char mine[ROW][COL], int x, int y);
我们在头文件game.h中同时也包含了一些头文件,而我们包含在头文件中的目的是为了方便能让两个源文件同时能够用到我们定义的头文件(game.h)中的内容。
而后面的函数,则是我们需要实现的一些游戏逻辑函数,在头文件里面声明一下,在game.c中来实现这些函数。
1.3 game.c的实现
#include "game.h"
//定义一个全局变量,每展开一个坐标就减一,
//直到该变量等于雷的个数
int count_mine = ROWS * COLS;
//初始化棋盘
void Init_chessboard(char chessboard[ROW][COL], int row, int col, char ch)
{
int i = 0;
for (i = 0;i < row;i++)
{
int j = 0;
for (j = 0;j < col;j++)
{
chessboard[i][j] = ch;
}
}
}
//打印棋盘
void Print_chessboard(char chessboard[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0;i < row - 1;i++)
{
if (i == 0)
{
printf(" | ");
}
else
printf("%d ", i);
}
printf("\n");
for (i = 0;i < row - 1;i++)
{
printf("--");
}
printf("\n");
for (i = 1;i < row - 1;i++)
{
int j = 0;
for (j = 0;j < col - 1;j++)
{
if (j == 0)
{
printf("%d| ", i);
}
else
printf("%c ", chessboard[i][j]);
}
printf("\n");
}
printf("\n");
}
//埋雷
void Lay_mines(char chessboard[ROW][COL], int row, int col)
{
int count = NUM;
while (count)
{
int x = rand() % ROWS + 1;//结果是 1 - 9
int y = rand() % COLS + 1;//结果是 1 - 9
if (chessboard[x][y] == '0') //确保能埋满我们需要埋的雷的个数
{
chessboard[x][y] = '1';
count--;
}
}
}
//玩家操作
void Player_move(char show[ROW][COL], char mine[ROW][COL], int row, int col)
{
while (1)
{
int x = 0, y = 0;
printf("请输入坐标:\n");
scanf("%d %d", &x, &y);
if ((x >= 1 && x <= row - 1) && (y >= 1 && y <= col - 1))
{
if (show[x][y] == '*')
{
if (mine[x][y] == '1')
{
printf("很遗憾,你已被炸死\n");
Print_chessboard(mine, row, col);
//失败以后重置这个用来计数的变量,保证游戏胜利以后还能继续玩
count_mine = ROWS * COLS;
break;
}
else
{
view_clear(show, mine, x, y);
Print_chessboard(show, row, col);
//Print_chessboard(mine, row, col);//该代码是用于测试时判断雷所在位置
if (count_mine == NUM)
{
printf("恭喜你,以将所有雷排除\n");
//胜利以后重置这个用来计数的变量,保证游戏胜利以后还能继续玩
count_mine = ROWS * COLS;
break;
}
}
}
else
{
printf("该坐标已被使用,请重新输入\n");
}
}
else
{
printf("输入的坐标有误,请重新输入\n");
}
}
}
//周围雷的个数
int around_mines(char mine[ROW][COL], int x, int y)
{
//前面埋雷函数中,我们让字符'1'为雷,
//到这里我们就可以以x,y为中心去判断周围八个坐标有几个雷
char ret = (mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y - 1] +
mine[x][y + 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 7 * '0');
return ret;//返回的就是雷的个数
}
//实现扫雷中炸开一片的效果
void view_clear(char show[ROW][COL], char mine[ROW][COL], int x, int y)
{
if ((x >= 1 && x < ROW - 1) && (y >= 1 && y < COL - 1))
{
if (around_mines(mine, x, y) == '0')
{
count_mine--;
show[x][y] = ' ';
int i = 0, j = 0;
for (i = x - 1;i <= x + 1;i++)
{
for (j = y - 1;j <= y + 1;j++)
{
if (show[i][j] == '*')
view_clear(show, mine, i, j);
}
}
}
else
{
count_mine--;
show[x][y] = around_mines(mine, x, y);
}
}
}