大家好,今天我们来实现扫雷小游戏,对就是windows自带的扫雷小游戏,不过这里是用代码实现的。废话不多讲,我们开搞!
首先,我们是从网上搞来一张扫雷的图片,它是这样的:
很经典对吧,好的,接下来我们进入学习阶段,我先介绍一下,我用的编译器是vs2019,大家如果是初学建议用这个,大佬看个乐呵,哈哈。
第一步:代码模块化+小菜单
我们创建三个文件,一个头文件game.h,两个源文件game.c和test.c。它们的作用如下:
//game.h主要用来放函数的声明、宏定义以及所用到的库里的头文件。
//game.c主要用来放实现函数的的代码。
//test.c主要进行函数的调用,检测代码的实现。
好了,我们上来打开test.c,写下如下的代码:
#include"game.h"
void menu()
{
printf("****************************************\n");
printf("****************************************\n");
printf("************* 1.play ***************\n");
printf("************* 0.exit ***************\n");
printf("****************************************\n");
printf("****************************************\n");
}
int main()
{
int num = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &num);
switch (num)
{
case 1:
game();
break;
case 0:
printf("退出成功\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (num);
return 0;
}
上面这部分是由三部分构成,熟悉C语言的同学应该知道,首先是主函数main,其次是一个do…while循环语句,再就是switch的运用了。这部分很简单,我接着下一个部分。
第二步:分析扫雷游戏
首先哈,我们先看一下我写的game.h里面的东西,我们对扫雷进行分析:
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 9//行
#define COL 9//列
#define ROWS ROW+2//行+2
#define COLS COL+2//列+2
#define EASY_RET 10//雷的个数
//函数的声明
void InitBoard(char board[ROWS][COLS], int rows, int cols, char tmp);//初始化每一个方块
void DisPlay(char board[ROWS][COLS], int row, int col);//显示界面
void SetMine(char board[ROWS][COLS], int row, int col);//设置雷的个数
ClearMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);//扫雷过程
好了,我们先对扫雷进行分析,扫雷游戏是有一个界面的,界面上有一个一个的小方块,我们假定有81个小方块,10个雷。那我们需要定义一个二维数组,这个二维数组最好是char类型的,因为我们要把方块放上一个’*',表示方块未被点开的状态。当然,我们发现,靠近边角的的方块我们也需要计算它们附近雷的个数,但是,如果数组定义为81个小方块,那就会出现越界访问的问题;所以我们给左右各加上一列,上下各加上一行;这就是ROWS和COLS的被定义出来的意义。除了二维数组,我们还需要进行,小方块初始化,雷的设置,扫雷的实现等等。接下来我们来搞game.c。
第三步:游戏逐步实现
大家都知道,我们在test.c里面放了一个switch语句,我们把它带过来:
switch (num)
{
case 1:
game();//这就是我们要实现的部分
break;
case 0:
printf("退出成功\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
我们开始的菜单就是,num=1我们就是开始游戏,所以case 1我们进入游戏,回想一下,游戏开始是怎么样的,哦,小方块摆在你面前,你要进行选择,接下来,开始:
void game()
{
//设置两个数组,一个是存放雷的信息,另一个是排查雷的信息
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
//初始化数组,将两个数组运用函数进行初始化。
//mine数组是存放雷的数组,当没有雷的时候,我们初始化为全是'0'
InitBoard(mine, ROWS, COLS, '0');
//show数组是排查雷的数组,未排查时初始化为'*'
InitBoard(show, ROWS, COLS, '*');
//显示一下我的每个数组,检查是否初始化成功
//DisPlay(mine, ROW, COL);
DisPlay(show, ROW, COL);
}
当然,我们不能瞎搞,所以得一步一步来,先定义两个二维数组,对二维数组进行初始化;好的,这里有同学就问了,数组初始化那不就用for循环,简简单单就搞定,哪还要写函数实现?先别急,我们一步一步来,定义数组用的是ROWS和COLS,是因为避免数组越界,我们为什么要写一个函数进行初始化,我们来看看这个初始化函数吧:
void InitBoard(char board[ROWS][COLS], int rows, int cols)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = ?;
}
}
}
就是嘛,你这还不一样是两for循环,大家注意,如果简单写两个循环,我们需要设定的值就乱了,我们一共有两个数组,一个数组放雷,没有雷初始化为’0’,另一个数组排查雷,没有排查时初始化为’*';这就需要我们对函数传一个值,把需要初始化成什么样的值传递过来,让我们初始化可以进行多个数组的初始化。改成下面这样:
void InitBoard(char board[ROWS][COLS], int rows, int cols, char tmp)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = tmp;
}
}
}
好了,初始化搞完了,我们需要进行整个界面的显示,这个时候我们是怎么做的呢?
void DisPlay(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
这时候有人又问,这显示没什么东西,就是一个二维数组的遍历呗,咔的一下写了出来。你说的确实没错,但是,我们虽然没有UI,我们也要尽量去考虑一下观感,所以我们要进行代码的调整:
void DisPlay(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("-----扫雷小游戏-----\n");//加上一个小修饰
for (j = 0; j <= col; j++)
{
printf("%d ", j);//打印一下列的序号
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);//打印一下行的序号
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("-----扫雷小游戏-----\n");
}
这样搞一下,肯定比之前那个显示效果好一点:
好了,接下来,还要实现雷的设置,和排雷:
第四步:雷的设置,排雷(核心)
好了,我们先思考一下,设置雷是不是要考虑随机性,所以这里我们要用到srand,rand两个关于随机数的库函数。其次,排雷的时候,点开一个方块之后,中间有一个数字,表示周围有几个雷,这是我们要实现的,最后还要判断输赢,不能一直排,那就没有意义了。接下来,看代码:
void game()
{
//设置雷
SetMine(mine, ROW, COL);
//扫雷
ClearMine(mine, show, ROW, COL);
}
void SetMine(char board[ROWS][COLS], int row, int col)
{
int ret = EASY_RET;
while (ret)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
ret--;
}
}
}
get_mine_count(char board[ROWS][COLS], int x, int y)
{
return (board[x - 1][y] +
board[x - 1][y - 1] +
board[x][y - 1] +
board[x + 1][y - 1] +
board[x + 1][y] +
board[x + 1][y + 1] +
board[x][y + 1] +
board[x - 1][y + 1] - 8 * '0');
}
ClearMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int m = 0;
int n = 0;
int w = 0;//排查到的非雷的个数
while (w < row * col - EASY_RET)
{
printf("请输入你要排查的坐标:>");
scanf("%d %d", &m, &n);
if (m >= 1 && m <= row && n >= 1 && n <= col)//判断坐标合法性
{
if (show[m][n] != '*')
{
printf("该坐标已经被排查,不能重复排查\n");//判断坐标是否二次排查
}
else
{
if (mine[m][n] == '1')
{
printf("Boom,你被炸死了!\n");
DisPlay(mine, ROW, COL);
break;
}
else
{
w++;
//统计mine数组m,n坐标附近雷的个数
int count = get_mine_count(mine, m, n);
show[m][n] = count + '0';
DisPlay(show, ROW, COL);
}
}
}
else
{
printf("输入的坐标非法,请重新输入\n");
}
}
if (w == row * col - EASY_RET)
{
printf("恭喜你,排雷成功\n");
DisPlay(mine, ROW, COL);
}
}
好了,接下来有很多人就问,为什么要在设置雷的函数里面,传的值是ROW、COL呢?这个很简单,因为我们至始至终都是在那81个格子里面玩的,外面那两行两列只是用来防止数组越界,所以不需要在那两行布置雷。
剩下的就很简单,解释一下部分的条件,row * col - EASY_RET意思是非雷的个数,然后就是get_mine_count函数是用来统计周围雷的个数,最后的-'0’是把字符型转换为整型。
最后,本次的扫雷小游戏到这就结束了,接下来,附上完整代码,感谢大家!!!
game.h
#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_RET 10
//函数的声明
void InitBoard(char board[ROWS][COLS], int rows, int cols, char tmp);//初始化每一个方块
void DisPlay(char board[ROWS][COLS], int row, int col);//显示界面
void SetMine(char board[ROWS][COLS], int row, int col);//设置雷的个数
ClearMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);//扫雷过程
game.c
#include"game.h"
void InitBoard(char board[ROWS][COLS], int rows, int cols, char tmp)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = tmp;
}
}
}
void DisPlay(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("-----扫雷小游戏-----\n");
for (j = 0; j <= col; j++)
{
printf("%d ", j);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("-----扫雷小游戏-----\n");
}
void SetMine(char board[ROWS][COLS], int row, int col)
{
int ret = EASY_RET;
while (ret)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
ret--;
}
}
}
get_mine_count(char board[ROWS][COLS], int x, int y)
{
return (board[x - 1][y] +
board[x - 1][y - 1] +
board[x][y - 1] +
board[x + 1][y - 1] +
board[x + 1][y] +
board[x + 1][y + 1] +
board[x][y + 1] +
board[x - 1][y + 1] - 8 * '0');
}
ClearMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int m = 0;
int n = 0;
int w = 0;//非雷的个数
while (w < row * col - EASY_RET)
{
printf("请输入你要排查的坐标:>");
scanf("%d %d", &m, &n);
if (m >= 1 && m <= row && n >= 1 && n <= col)
{
if (show[m][n] != '*')
{
printf("该坐标已经被排查,不能重复排查\n");
}
else
{
if (mine[m][n] == '1')
{
printf("Boom,你被炸死了!\n");
DisPlay(mine, ROW, COL);
break;
}
else
{
w++;
//统计mine数组m,n坐标附近雷的个数
int count = get_mine_count(mine, m, n);
show[m][n] = count + '0';
DisPlay(show, ROW, COL);
}
}
}
else
{
printf("输入的坐标非法,请重新输入\n");
}
}
if (w == row * col - EASY_RET)
{
printf("恭喜你,排雷成功\n");
DisPlay(mine, ROW, COL);
}
}
test.c
#include"game.h"
void menu()
{
printf("****************************************\n");
printf("****************************************\n");
printf("************* 1.play ***************\n");
printf("************* 0.exit ***************\n");
printf("****************************************\n");
printf("****************************************\n");
}
void game()
{
//1.设置两个数组,一个是存放雷的信息,另一个是排查雷的信息
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
//2.初始化数组,将两个数组运用函数进行初始化。
//mine数组是存放雷的数组,当没有雷的时候,我们初始化为全是'0'
InitBoard(mine, ROWS, COLS, '0');
//show数组是排查雷的数组,未排查时初始化为'*'
InitBoard(show, ROWS, COLS, '*');
//4.设置雷
SetMine(mine, ROW, COL);
//3.显示一下我的每个数组,检查是否初始化成功
//DisPlay(mine, ROW, COL);
DisPlay(show, ROW, COL);
//5.扫雷
ClearMine(mine, show, ROW, COL);
}
int main()
{
int num = 0;
//设置随机值
srand((size_t)time(NULL));
do
{
menu();
printf("请选择:>");
scanf("%d", &num);
switch (num)
{
case 1:
game();
break;
case 0:
printf("退出成功\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (num);
return 0;
}