c语言实现扫雷(简易版)
前言:写完三子棋后,慢慢地熟悉了这种写小游戏的过程,之前对于代码的分区存储都还是懵懵的,现在终于有点得心应手啦,话不多说,这次我写的是扫雷,但是博主水平有限,目前还不能做到图形化界面,日后有能力时定来修改这篇文章,实现一个带有图形化界面的扫雷!
文章目录
1.游戏准备
先来看看扫雷的样子
与三子棋一样,我们可以把要排雷的雷盘?(是这个名字不?)看成一个二维数组,当我们点击格子时,格子会显示周围一圈的雷数。先要有一个二维数组来放我们的雷,但我们不可能把这个数组展示给玩家看(这样直接开透视了呀)。我们得再创建一个二维数组展示给玩家,这个数组开始时全是’ * ’ ,当玩家输入一个坐标时,’ * ’ 会变成周围一圈的雷数。
经过梳理,游戏的逻辑就清晰了:开始时我们要初始化两个数组,一个用来放雷(我们叫这个数组mine),另一个用来向玩家展示(我们叫它show)。接着我们会放置地雷,把show数组打印给玩家看,玩家输入坐标,开始排雷。每成功排一次雷,我们都要展示雷盘当场上剩下的格子数等于雷数时,游戏胜利,玩家踩雷时,游戏结束,所以我们这是一个判断输赢的函数。大概的过程我们就理清楚了。
还是像三子棋一样,我们把整个游戏分成三个部分:
test.c — > 程序的运行逻辑
game.c — > 游戏的实现逻辑
game.c — > 存放游戏相关函数的代码与库函数的头文件
接着是游戏代码
2.主函数
int main()
{
srand((unsigned int)time(NULL));//设置随机数种子,用来随机生成地雷
test();
return 0;
}
为了让代码看起来整洁,在主函数中调用test函数
3.test函数
test函数是游戏运行的逻辑
void test()
{
int input = 0;
do
{
menu();//开始游戏前打印菜单
printf("请选择 > 1/0n");
scanf("%d", &input);//input变量存放玩家的选择
switch (input)//根据input的值switch进入不同的分支
{
case 1:
game();//游戏逻辑的实现函数
break;
case 0:
printf("退出游戏n");
default :
printf("输入错误,请重新输入n");
break;
}
} while (input);//玩家可以反复玩游戏,玩家选0时退出游戏
}
代码实现与三子棋的类似,这次写这部分代码时就很熟练了。
5.menu函数
游戏菜单(这个就和三子棋一模一样了)
void menu()
{
printf("***********************n");
printf("***** 1.开始游戏 ****n");
printf("***** 0.退出游戏 ****n");
printf("***********************n");
}
6.game函数
void game()
{
//雷棋盘与展示棋盘的初始化
char mine[ROWS][COLS] = { '0' };
char show[ROWS][COLS] = { '0' };
//初始化放雷棋盘
InitBoard(mine, ROWS, COLS,'0');
//初始化展示棋盘
InitBoard(show, ROWS, COLS,'*');
//放置地雷
SetMine(mine, ROW, COL);
//打印展示棋盘
DisplayBoard(show, ROW, COL);
//玩家排雷
Find(mine, show, ROW, COL);
}
game函数就是我们刚刚梳理的游戏逻辑,接着我们就要一个个实现这些函数了。
7.初始化雷盘
首先呢,与三子棋不一样的是,我们这次要初始化两个棋盘,但是两个棋盘的初始化内容是不一样的,这就要求我们的初始化函数要有相对的独立性来实现对不同数组的初始化。要怎么做呢?我们可以在函数设置四个参数
- 一个二维数组
- 数组的行
- 数组的列
- 要初始化的内容(mine要初始化的内容就是’0’,show的是’*’)
我们来看看函数的实现
//初始化棋盘
void InitBoard(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;//将所有元素初始化
}
}
}
这里解释为什么定义ROWS,COLS,ROW,COL这四个宏。
mine数组中,除了雷是用1表示,其他的元素都是用0表示,计算雷数时,把周围八个元素的值相加,得到的数刚好就是雷数。但是我们对边界上的元素进行排雷时,会出先数组的越界访问,产生意想不到的结果,这当然不是我们想要的。所以,对数组增加两行与两列(假设二维数组是9 * 9的,增加后变为11 * 11),对11 * 11的数组初始化,但我们使用的是9 * 9 的数组,在计算雷数时就不会出现错误了。
所以,我们把ROWS与COLS设置成11,ROW与COL设值成9。
8.放置地雷
初始化后,我们就要放地雷了。
//放置地雷
//这里有个小细节,我们的数组大小是ROWS * COLS
//但放地雷时只能在ROW * COL大小的数组中
void SetMine(char board[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int count = CON;
//CON又是一个宏,指的是我们要放置的雷数
//CON在头文件中定义
//用count变量接收CON常量的值,放一次雷count减一
while (count)
{
//利用随机数产生坐标
x = rand() % row + 1;
y = rand() % col + 1;
//坐标的值可能相同,所以要判断坐标是否合理
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;//当count为0时,循环结束
}
}
}
由于我们要用到rand函数,所以在主函数中,我们调用了srand函数设置随机数种子。
9.展示雷盘
展示雷盘的时候,要是直接把雷盘打印出来,玩家输入坐标时对行号与列号可能会不太确定(毕竟数量有点多嘛),
看得清不?哈哈哈,所以我们要对行与列加上序号,方便玩家确定坐标
//同时打印行号与列号
//我们展示的雷盘大小是ROW * COL
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
//列号的打印
for (i = 0; i <= 9; i++)
{
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("n");
}
}
这样就好认多了
10.玩家排雷
接着就到了玩家进行排雷了,通过玩家输入坐标,程序把对应的数组元素赋值成周围一圈的雷数,要是踩到雷了,提示玩家玩得太菜 游戏结束。但这又有一个细节,我们创建的数组是字符数组,放置的是字符,把字符相加得到的是对应的ASCII码值。所以我们把相加后的值减去八个字符0的ASCII码值,得到的就是雷数了。我写了一个num函数来计算雷数。
//num函数返回类型是整形
int num(char board[ROWS][COLS], int x, int y)
{
//返回八个周围元素的相加值,但返回的得是整形,所以减去八个字符0
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';
}
接着是玩家排雷的函数
void Find(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
//设置变量接收玩家输入的值
int win = 0;
//用win变量判断玩家是否胜利
//win初始值为0,每排一次雷win加一
while (win < ROW * COL - CON)
{
printf("请输入坐标n");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= 9 && y >= 1 && y <= 9 && show[x][y] == '*')
//判断玩家输入的坐标在雷盘中并且没有对坐标进行过扫雷
{
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了n");
DisplayBoard(mine, ROW, COL);
//玩家被炸死后展示雷盘
break;
}
else
{
//计算雷的数量
int n = num(mine, x, y);
//把展示雷盘对应的元素赋值成周围雷数
show[x][y] = n + '0';
//但展示雷盘是字符数组,雷数要加上字符0
DisplayBoard(show, ROW, COL);
//每次排雷展示雷盘
win++;
}
}
else
printf("输入错误,请重新输入n");
}
//当win等于雷盘大小减去雷数时,玩家排雷成功,游戏结束
if (win == ROW * COL - CON)
{
printf("恭喜你,排雷成功n");
}
}
至此所有的代码我们就完成了,接着是所有文件代码的整体展示
11.完整代码展示
11.1 game.c
#include"game.h"
void InitBoard(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 DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
//列号的打印
for (i = 0; i <= 9; i++)
{
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("n");
}
}
void SetMine(char board[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int count = CON;
while (count)
{
x = rand() % row + 1;
y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
int num(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';
}
void Find(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win < ROW * COL - CON)
{
printf("请输入坐标n");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
{
if (mine[x][y] == '1')
{
printf("恭喜你,你被炸死了n");
DisplayBoard(mine, ROW, COL);
break;
}
else
{
//计算雷的数量
int n = num(mine, x, y);
show[x][y] = n + '0';
DisplayBoard(show, ROW, COL);
win++;
}
}
else
printf("输入错误,请重新输入n");
}
if (win == ROW * COL - CON)
{
printf("恭喜你,排雷成功n");
}
}
11.3 game.h
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define ROWS 11
#define COLS 11
#define ROW 9
#define COL 9
#define CON 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 Find(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
11.3 test.c
#include"game.h"
void menu()
{
printf("***********************n");
printf("***** 1.开始游戏 ****n");
printf("***** 0.退出游戏 ****n");
printf("***********************n");
}
void game()
{
char mine[ROWS][COLS] = { '0' };
char show[ROWS][COLS] = { '0' };
//初始化放雷棋盘
InitBoard(mine, ROWS, COLS,'0');
//初始化展示棋盘
InitBoard(show, ROWS, COLS,'*');
//放置地雷
SetMine(mine, ROW, COL);
//打印展示棋盘
DisplayBoard(show, ROW, COL);
//玩家排雷
Find(mine, show, ROW, COL);
}
void test()
{
int input = 0;
do
{
menu();
printf("请选择 > 1/0n");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏n");
default :
printf("输入错误,请重新输入n");
break;
}
} while (input);
}
int main()
{
srand((unsigned int)time(NULL));
test();
return 0;
}
12 效果展示
排雷成功的效果就是这样啦
最后:博主水平有限,写的程序还有不够完善的地方,对程序的优化这几天会写完(虽然不能实现图形化界面),xdm动动小手点个赞呗