导语
这一篇带领大家来一起完成扫雷游戏的基础版,虽然与我们电脑自带的扫雷有些差别,不过也是可以玩的。
本章和三子棋有些类似,主要是二维数组的使用,函数,函数声明,循环等等等
然后就是分模块写,我们依然分为三个文件,game.h(头文件,声明函数)game.c(源文件,游戏主要实现位置)test.c(源文件,main函数的位置和菜单的位置)
扫雷游戏的逻辑是什么?
我们打开一个网页搜索一下扫雷就可以了。
左上角的是记录你用了多少时间,右上角是一共有几颗雷。
蓝色格子的地方是你需要排查的部分,当然里面就有那10颗雷。
游戏规则
排查出所有的雷(除了雷的地方其他蓝色方块都被你排除)。
只能点击蓝色方块。
鼠标右击是你感觉这里有雷,再次右击是不确定这里是什么(标记用的)。
不能重复的点击一个位置。
点击一次不是雷的地方这个方块会消失并且查找周围有没有雷,如果33的范围有雷就会在你点击的地方生成一个数,1到8,数字代表雷的是周围雷的数量。
雷是随机分布的。
如果你不幸找点击了雷的地方你就被炸死了。
看,空白的地方是你点击的地方周围都没有雷,所以向周围继续扩散,直到周围33的位置有雷。
嗯,我被雷炸死了。
下面我们来想想怎么用C语言来实现这个游戏。
分析C语言如何实现扫雷游戏
和三子棋一样需要游戏菜单,可以反复游戏,退出游戏等等操作。
其次打印游戏棋盘,肯定又要用到二维数组。
然后是布置随机雷,看到随机二字我们不禁想到时间戳,rand函数。
最重要的就是查找,查找的就是3*3的范围,(这里先不去实现扩散的效果)如过我们查找的是边界的方块,那么不就越界了吗?
就像这个样子,外面你根本不直到它是什么,这是不可控制的范围,所以我们只能想办法。
还有就是判断输赢的逻辑。
(标记的逻辑我们这里也不实现)。
那么怎么实现这些呢?我们往下看。
C语言实现扫雷游戏
菜单与游戏的循环
首先在test.c里面来实现菜单和反复游戏与结束游戏的逻辑等等。
假设输入1是开始游戏,0是推出游戏。
参考代码如下:
//test.c文件
#include "game.h"
void menu()
{
printf("**************************\n");
printf("***** 1.开始游戏 *****\n");
printf("***** 0.退出游戏 *****\n");
printf("**************************\n");
}
int main()
{
int n;
do
{
menu();
scanf("%d", &n);
switch (n)
{
case 1:printf("游戏开始\n");
break;
case 0:printf("游戏结束\n");
break;
default:printf("输入错误,请重新输入\n");
}
} while (n);
return 0;
}
我们这次用的是do while循环搭配switch语句,如果输入1,不需要任何判断就进入循环,然后按照输入的数字进入分支,如果是0,就从case 0:进入然后跳出,之后再while的地方判断为假跳出循环;如果是1或者是其他数字那么就会在循环里一直运行。
//game.c
#include "game.h"
//game.h
#include <stdio.h>
这里我们就不进行代码的演示了。
创建游戏棋盘
下面就是游戏的基本代码运行了,我们自定义一个函数来放在switch里面的case 1:的后面。
首先我们船舰一个99的棋盘,但是上面有一个问题就是,如果你查找的时候数组有可能会越界访问。
我们不可能让他查找的时候只访问合法范围内的数组,因为这样非常麻烦,那么我们就创建一个1111的游戏棋盘。
假设我们定义普通方块是0,雷区为1。
首先初始化游戏棋盘(不布置雷)。
和三子棋一样,我们要定义常量标识符,因为后面方便更改这些标识符的大小。
我们要定义两个为9的常量标识符,因为我们游戏的定义为99的游戏棋盘,无论是布雷还是打印游戏棋盘都是需要在99的游戏棋盘里。
并且,打印游戏棋盘的时候,为了让棋盘更有神秘感,我们让棋盘布满 * 。
当然我们要创建两个棋盘才可以做到。
参考代码如下:
//test.c
void game()
{
char arr1[ROWS][COLS];//布置雷的游戏棋盘
char arr2[ROWS][COLS];//显示排查雷信息的棋盘
chessboard(arr1, ROWS, COLS, '0');//初始化游戏棋盘
chessboard(arr2, ROWS, COLS, '*');
}
//game.h
#include <stdio.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
void chessboard(char arr[ROWS][COLS], int rows, int cols, char ret);
//game.c
#include "game.h"
void chessboard(char arr[ROWS][COLS], int rows, int cols, char ret)
{
int i, j;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
arr[i][j] = ret;//给arr数组初始化值
}
}
}
这里我们还是只用一个函数来完成两个棋盘的初始化,所以最后的参数就是你要初始化的内容。
打印棋盘
然后是打印,也是自定义函数,不过我们要注意,因为打印棋盘不可能是11*11的范围,所以传参时候要注意。
参考代码如下:
//test.c
void game()
{
char arr1[ROWS][COLS];//布置雷的游戏棋盘
char arr2[ROWS][COLS];//显示排查雷信息的棋盘
chessboard(arr1, ROWS, COLS, '0');//初始化游戏棋盘
chessboard(arr2, ROWS, COLS, '*');
print(arr2, ROW, COL);//打印游戏棋盘
}
//game.c
void print(char arr[ROWS][COLS], int row, int col)
{
int i, j;
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++)
{
printf("%c ", arr[i][j]);//打印棋盘内容
}
printf("\n");//换行
}
}
//game.h
#include <stdio.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
void chessboard(char arr[ROWS][COLS], int rows, int cols, char ret);
void print(char arr[ROWS][COLS], int row, int col);
带引运行之后是这个样子:
不过我们发现,如果是这样玩家想知道坐标位置很麻烦,所以可以让横竖都有数字。
//game.c
void print(char arr[ROWS][COLS], int row, int col)
{
int i, j;
for (i = 0; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", arr[i][j]);//打印棋盘内容
}
printf("\n");//换行
}
}
布置雷
这里要布置雷, 我们之前说了,雷是1,当然布置雷的游戏棋盘是arr1。
因为是随机布置雷,所以我们用到时间戳,rand函数,头文件要引用一下。
我们这里把布置好的雷的棋盘打印一下。
注意,布置雷的时候要合法,不要让一个地方重复布置雷超过一次。
//test.c
void game()
{
char arr1[ROWS][COLS];//布置雷的游戏棋盘
char arr2[ROWS][COLS];//显示排查雷信息的棋盘
chessboard(arr1, ROWS, COLS, '0');//初始化游戏棋盘
chessboard(arr2, ROWS, COLS, '*');
layout(arr1, ROW, COL);//布置雷
print(arr1, ROW, COL);//打印游戏棋盘
print(arr2, ROW, COL);//打印游戏棋盘
}
//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 THUNDER 10//雷的数量
void chessboard(char arr[ROWS][COLS], int rows, int cols, char ret);
void print(char arr[ROWS][COLS], int row, int col);
void layout(char arr[ROWS][COLS], int row, int col);
//game.c
void layout(char arr[ROWS][COLS], int row, int col)
{
int c = THUNDER;
while (c)
{
int a = rand() % row + 1;
int b = rand() % col + 1;
if (arr[a][b] == '0')//判断这个位置是不是合法
{
arr[a][b] = '1';//给arr1数组里面随机赋值1
c--;
}
}
}
我们来看一下代码运行:
经过查看之后,确实是10个1。
排查雷
接下来就是最核心的排查部分了。
当然排查的时候也是要在arr1当中进行的。
//game.c
int judge(char arr1[ROWS][COLS], int x, int y)
{
return (arr1[x - 1][y - 1] +
arr1[x - 1][y] +
arr1[x - 1][y + 1] +
arr1[x][y - 1] +
arr1[x][y + 1] +
arr1[x + 1][y - 1] +
arr1[x + 1][y] +
arr1[x + 1][y + 1] - 8 * '0');
}
void troubleshoot(char arr1[ROWS][COLS], char arr2[ROWS][COLS], int row, int col)
{
int x, y;
while (1)
{
printf("请输入要排查的坐标》");
scanf("%d %d", &x, &y);
if (x <= row && x >= 1 && y >= 1 && y <= col)//判断坐标是否合法
{
if (arr1[x][y] == '1')//如果是字符1就会被炸死
{
printf("你被炸死了,游戏失败\n");
print(arr1, ROW, COL);//让你死个明白
break;
}
else
{
int sum = judge(arr1, x, y);
arr2[x][y] = sum + '0';//注意是把周围雷数量信息给了arr2,不会与arr1冲突
print(arr2, ROW, COL);//打印的是显示周围有几颗雷的信息展示棋盘
}
}
else
{
printf("坐标不合法,请重新输入\n");
}
}
}
我们这里首先肯定是判断坐标是否合法,然后再去排查周围有几颗雷,至于排查重复的地方是否合法,游戏也没说不让你排查你排查过的地方,就是鼠标点击之后没反应。
我们知道排查雷的是个循环的过程,这里我们用while循环,暂时不考虑输赢的问题。
我这里自定义了一个judge的函数,就是排查周围有几颗雷用的,当我们选定了X,Y的坐标时候,周围的坐标是这样的:
我们可以发现,周围的坐标相加等于8*周围的字符,arr1里面无非就是字符1和字符0,它们的ASCII码值是49和48,那么我们想知道周围有几颗雷就单多了,把周围的加起来最后减去8个ASCII值为48的字符0然后返回就可以了。
最后,我们需要把这个结果存入arr2中并且展示给玩家,因为返回值是整形,所以我们需要加上一个字符0,这样就能转换为数字ASCII值存进arr2数组里。
注意:arr1和arr2并不冲突,请仔细看两个数组的区别。
这里运行代码查看一下:
当然,就全都找到了雷也不会停止,下面我们来完成判断输赢的逻辑。
判断输赢
我们判断条件是排查雷以外的空间完毕才算胜利,也就是说要排查9*9-雷的数量。
参考代码如下:
void troubleshoot(char arr1[ROWS][COLS], char arr2[ROWS][COLS], int row, int col)
{
int x, y;
int win = 0;//剩余未排查的安全方块
while (win < row * col - THUNDER)
{
printf("请输入要排查的坐标>");
scanf("%d %d", &x, &y);
if (x <= row && x >= 1 && y >= 1 && y <= col)
{
if (arr1[x][y] == '1')
{
printf("你被炸死了,游戏失败\n");
print(arr1, ROW, COL);
break;
}
else
{
win++;
int sum = judge(arr1, x, y);
arr2[x][y] = sum + '0';
print(arr2, ROW, COL);
}
}
else
{
printf("坐标不合法,请重新输入\n");
}
}
if (win == row * col - THUNDER)//游戏胜利的条件
{
printf("你赢了\n");
print(arr1, ROW, COL);
}
}
上面的是排查一个win就+1。
我们这里肯定是要测试一下,但是想测试你需要很久并且还需要运气,所以说要像个办法,之前我们定义雷的常量标识符就有了很大的作用,改一下就好,我们将他改为80,然后我们把arr1这个棋盘也打印一下。
代码如下:
//test.c
void game()
{
char arr1[ROWS][COLS];//布置雷的游戏棋盘
char arr2[ROWS][COLS];//显示排查雷信息的棋盘
chessboard(arr1, ROWS, COLS, '0');//初始化游戏棋盘
chessboard(arr2, ROWS, COLS, '*');
layout(arr1, ROW, COL);//布置雷
print(arr1, ROW, COL);//打印的是arr1棋盘,查看雷在哪里
print(arr2, ROW, COL);//打印游戏棋盘
troubleshoot(arr1, arr2, ROW, COL);
}
//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 THUNDER 80
void chessboard(char arr[ROWS][COLS], int rows, int cols, char ret);
void print(char arr[ROWS][COLS], int row, int col);
void layout(char arr[ROWS][COLS], int row, int col);
void troubleshoot(char arr1[ROWS][COLS], char arr2[ROWS][COLS], int row, int col);
运行结果:
代码没问题。
完整代码
这里就是我们完整版的扫雷代码了,虽然有一些逻辑没实现,不过也可以玩了。
//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 THUNDER 10
void chessboard(char arr[ROWS][COLS], int rows, int cols, char ret);
void print(char arr[ROWS][COLS], int row, int col);
void layout(char arr[ROWS][COLS], int row, int col);
void troubleshoot(char arr1[ROWS][COLS], char arr2[ROWS][COLS], int row, int col);
//game.c
#include "game.h"
int judge(char arr1[ROWS][COLS], int x, int y)
{
return (arr1[x - 1][y - 1] +
arr1[x - 1][y] +
arr1[x - 1][y + 1] +
arr1[x][y - 1] +
arr1[x][y + 1] +
arr1[x + 1][y - 1] +
arr1[x + 1][y] +
arr1[x + 1][y + 1] - 8 * '0');
}
void chessboard(char arr[ROWS][COLS], int rows, int cols, char ret)
{
int i, j;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
arr[i][j] = ret;//给arr数组初始化值
}
}
}
void print(char arr[ROWS][COLS], int row, int col)
{
int i, j;
for (i = 0; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", arr[i][j]);//打印棋盘内容
}
printf("\n");//换行
}
}
void layout(char arr[ROWS][COLS], int row, int col)
{
int c = THUNDER;
while (c)
{
int a = rand() % row + 1;
int b = rand() % col + 1;
if (arr[a][b] == '0')//判断这个位置是不是合法
{
arr[a][b] = '1';//给arr1数组里面随机赋值1
c--;
}
}
}
void troubleshoot(char arr1[ROWS][COLS], char arr2[ROWS][COLS], int row, int col)
{
int x, y;
int win = 0;
while (win < row * col - THUNDER)
{
printf("请输入要排查的坐标>");
scanf("%d %d", &x, &y);
if (x <= row && x >= 1 && y >= 1 && y <= col)
{
if (arr1[x][y] == '1')
{
printf("你被炸死了,游戏失败\n");
print(arr1, ROW, COL);
break;
}
else
{
win++;
int sum = judge(arr1, x, y);
arr2[x][y] = sum + '0';
print(arr2, ROW, COL);
}
}
else
{
printf("坐标不合法,请重新输入\n");
}
}
if (win == row * col - THUNDER)
{
printf("你赢了\n");
print(arr1, ROW, COL);
}
}
#include "game.h"
void menu()
{
printf("**************************\n");
printf("***** 1.开始游戏 *****\n");
printf("***** 0.退出游戏 *****\n");
printf("**************************\n");
}
void game()
{
char arr1[ROWS][COLS];//布置雷的游戏棋盘
char arr2[ROWS][COLS];//显示排查雷信息的棋盘
chessboard(arr1, ROWS, COLS, '0');//初始化游戏棋盘
chessboard(arr2, ROWS, COLS, '*');
layout(arr1, ROW, COL);//布置雷
print(arr2, ROW, COL);//打印游戏棋盘
troubleshoot(arr1, arr2, ROW, COL);
}
int main()
{
int n;
srand((unsigned int)time(NULL));
do
{
menu();
scanf("%d", &n);
switch (n)
{
case 1:printf("游戏开始\n");
game();
break;
case 0:printf("游戏结束\n");
break;
default:printf("输入错误,请重新输入\n");
}
} while (n);
return 0;
}
这里我就不运行了。
结束语
简化版的扫雷小游戏的到这里就结束了,相信经过扫雷和三子棋的代码学习我们对于二维数组的运用更加娴熟。
那么,路过的家人们请点个赞,大佬们觉得哪里不足或者是有错误请多多指点!!!