思维导图
首先我们需要多文件进行编写这个游戏,创建三个文件一个 .h头文件(game.h),两个.c源文件(main.c和test.c)
game.h头文件用来存放函数的声明,宏定义,和包含的头文件,main.c用来存放测试的代码,test.c用来实现函数体。
目录
1.制作一个简易菜单
输入1开始游戏,输入0退出游戏,如果输入其他数字,则提示选择错误,请重新输入。
2.制作游戏过程
在这里添加游戏实现函数。
扫雷棋盘大小的标准是9行9列,但是由于考虑到后面在计算周围的有多少个雷时以防越界,我们需要把棋盘扩大一圈11行11列。
这里我们可以使用宏定义减少维护的成本,定义一个ROW 9 (行) 和COL 9 (列) 的大小,只需要在这个基础上在定义宏+2就能扩大一圈,ROWS ROW + 2,COLS COL + 2。最后定义雷的个数EASY_COUNT 10。
2.1初始化棋盘和打印棋盘
我们需要定义两个二维数组,一个用来存放布置雷的信息,一个用来存放排雷的信息,为了知道这是几行几列我们可以在周围打印一些数字,打印出来看一下效果
为什么把存放雷棋盘初始为全'0'?其实这也是为了方便后面计算以坐标为中心周围的八个格子有多少颗雷。
2.2布置雷
把雷设置成'1'。
函数实现:
布置雷我们需要用到库函数rand(),但是在这之前需要使用srand()库函数设置一个伪随机数种子,然后让rand() % row + 1,生成1-9的数字。为了以防在同一个位置布置雷我们需要判断一下该格子等不等于'0',如果等于在布置然后count--,这样就完成一个雷的布置。效果如图:
2.3排查雷和展开周围没有雷的区域
通过这个函数知道为什么把存放雷的棋盘初始化为全'0',这里巧妙用到了ASCII码值来计算周围雷的个数,'0'的ASCII码值是48,'1'的ASCII码值是49,49 - 48 = 1就得出一个雷,然后递归函数则可以利用这一点,48 - 48 = 0 周围没有雷则让坐标x和y各减一来到输入坐标的左上角,在以此为当前坐标中心计算周围八个格子有没有雷,如果有几颗雷则显示对应的数字,没有则把'0'变成' '空格。
每次都会像这样展开,计算以坐标为中心的周围八个格子,那个格子周围没有雷,且show[i][j] == '*'没有被排查过的坐标,则以这个坐标为中心x和y-1,的左上角递归出去依次计算。x-1小于等于x+1,y-1小于等于y+1
则可以计算三行三列。
效果如图:
3.完整代码
main.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
static void menu()
{
printf("******************************\n");
printf("******* 1.paly *******\n");
printf("******* 0.exit *******\n");
printf("******************************\n");
}
static void game()
{
char mine[ROWS][COLS] = { 0 };//存放雷的信息
char show[ROWS][COLS] = { 0 };//存放排雷的信息
//初始化棋盘
inti_board(mine, ROWS, COLS, '0');
inti_board(show, ROWS, COLS, '*');
//打印棋盘
//print_board(mine, ROW, COL);
print_board(show, ROW, COL);
//布置雷
setmine(mine, ROW, COL, '1');
//print_board(mine, ROW, COL);
//排查雷
findmine(mine, show, ROW, COL);
}
static void test()
{
int input = 0;
srand((unsigned)time(NULL));
do
{
menu();
printf("请选择开始游戏:\n");
scanf("%d", &input);
switch (input)
{
case 1:
printf("游戏开始\n");
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
}
int main()
{
test();
return 0;
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//初始化棋盘
void inti_board(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
//打印棋盘
void print_board(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("-------扫雷--------\n");
for (int i = 0; i <= row; i++)
{
if (i == 0)
printf(" ");
else
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("%d ", i);
printf("\n");
}
for (int i = 0; i <= row; i++)
{
if (i == 0)
printf(" ");
else
printf("%d ", i);
}
printf("\n");
printf("-------扫雷--------\n");
}
//布置雷
void setmine(char board[ROWS][COLS], int row, int col, char set)
{
int count = EASY_COUNT;
while (count)
{
int i = rand() % row + 1;
int j = rand() % row + 1;
if (board[i][j] == '0')
{
board[i][j] = set;
count--;
}
}
}
//计算周围雷的数量
static int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
return (mine[x][y + 1] + mine[x][y - 1] + mine[x + 1][y] + mine[x - 1][y] +
mine[x - 1][y - 1] + mine[x + 1][y + 1] + mine[x + 1][y - 1] + mine[x - 1][y + 1]
+ mine[x][y] - 9 * '0');
}
//循环判断以mine[x][y]为中心的九宫格有没有雷
//以mine[x][y]为中心使x和y各-1从九宫格的左上角开始依次判断计算四周有没有雷
//下面的递归函数explode_board其实和写的get_mine_count函数很像
//展开相邻没有雷的范围
void explode_board(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 n = get_mine_count(mine, x, y);//计算周围雷的个数
if (n == 0)
{
(*win)++;
show[x][y] = ' ';
int i = 0;
for (i = x - 1; i <= x + 1; i++)
{
int j = 0;
for (j = y - 1; j <= y + 1; j++)
{
if (show[i][j] == '*')
{
explode_board(mine, show, row, col, i, j, win);//这里要传i和j,不要传成x和y
//为什么这里win不用传地址?因为它本身就是指针
}
}
}
}
else
{
(*win)++;
show[x][y] = n + '0';//显示周围雷的个数
}
}
}
//排查雷
void findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
//输入坐标
int x = 0;
int y = 0;
int win = 0;//统计没有雷的格子,如果等于 row * col - 10,则扫雷成功
while (win < (row * col - EASY_COUNT))
{
printf("请输入坐标>:\n");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)//检查坐标有效性
{
if (show[x][y] != '*')
{
printf("坐标已被占用,请重新输入!\n");
continue;
}
if (mine[x][y] == '1')
{
printf("游戏结束,恭喜你被炸死了!\n");
print_board(mine, ROW, COL);//打印棋盘
break;
}
else
{
explode_board(mine, show, row, col, x, y, &win);
print_board(show, ROW, COL);//打印棋盘
//print_board(mine, ROW, COL);
}
}
else
{
printf("坐标非法\n");
}
}
if (win == (row * col - EASY_COUNT))
{
printf("扫雷成功\n");
print_board(mine, ROW, COL);
}
}
game.h
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
#define EASY_COUNT 10
//初始化棋盘
extern void inti_board(char board[ROWS][COLS], int rows, int cols, char set);
//打印棋盘
extern void print_board(char board[ROWS][COLS], int row, int col);
//布置雷
extern void setmine(char board[ROWS][COLS], int row, int col, char set);
//排查雷
extern void findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);