扫雷这个游戏想必大家都不陌生吧,规则我就不多说了,我们今天就来用C语言来简单实现它。
游戏左上角的数字是雷的数量,上图显示的数字是数字周围一圈含雷的个数,由图我们可以创建一个 9*9 的数组来模拟方格,如果我选择最外的一圈的格子,那么周围格子就没有一圈了,怎么办呢?
就像上图,2周围只有3个格子,怎么样才能让它周围也有8个格子呢?
我们可以多加2行、2列,这样图中每个格子周围都有8个格子,好判断。
那我们就创建一个11*11的数组。用1表示雷,0表示非雷。
问题又来了,如果格子周围只有一个雷,那它把1存进去显示1,那判断的时候岂不是变成雷了,那我们可以再创建一个数组,专门存放显示的数字。
1、创建2个数组,一个数组(a)用来存放布置好的雷的信息,另一个数组(b)存放排查出的雷的信息。
2、a 数组初始化为 ’ 0 ’ ,布置雷的时候改为 ’ 1 ’ 。
3、b 数组初始化为 ’ * ’ ,排除雷后,具体位置改为数字字符,如 ’ 3 ’ 。
void menu()
{
printf("*****************************\n");
printf("******* 1.play ********\n");
printf("******* 0.exit ********\n");
printf("*****************************\n");
}
首先,我们思考一下,本次游戏应该最少执行一次,在执行过程中进行选择,那么我们应该使用 do while 循环来实现。根据选择不同,来执行相应的程序,那么应该使用 switch 语句。
下面我们来看代码:
int main()
{
int input = 0;
do
{
menu(); //每次在选择前打印菜单
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game(); //开始游戏
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,重新选择!\n");
break;
}
} while (input); //根据输入的值来确定是否进行循环,0则退出循环
return 0; //这也是为什么在菜单中把退出游戏设置成0的原因
}
#define M 11
#define N 11
void game()
{
//设计两个数组存放信息
char a[M][N] = { 0 };
char b[M][N] = { 0 };
// 初识化棋盘
// a数组初始化为'0'
// b数组初始化为'*'
chu_shi(a, M, N, '0'); //将 0 和 * 传过去方便初始化
chu_shi(b, M, N, '*');
}
void chu_shi(char p[M][N], int m, int n, char w)
{
int i = 0;
int j = 0;
for (i = 0; i < m; i++)
for (j = 0; j < n; j++)
p[i][j] = w;
}
void game()
{
//设计两个数组存放信息
char a[M][N] = { 0 };
char b[M][N] = { 0 };
//初识化棋盘
// a数组初始化为'0'
// b数组初始化为'*'
chu_shi(a, M, N, '0');
chu_shi(b, M, N, '*');
//打印棋盘
da_yin(a, M - 2, N - 2); //M-2,N-2就是9,因为我们的游戏是9*9的,所以传9过去,方便打印
da_yin(b, M - 2, N - 2);
}
void da_yin(char p[M][N], int m, int n)
{
int i = 0;
int j = 0;
for (j = 0; j <= n; j++) //打印列号
printf("%d ", j);
printf("\n");
for (i = 1; i <= m; i++)
{
printf("%d ", i); //打印行号
for (j = 1; j <= n; j++)
printf("%c ", p[i][j]); //注意:数组p是char型,用%c打印每个元素
printf("\n");
}
}
结果:
打印结果能够一目了然,哪个元素是几行几列,我们棋盘打印已经完成下一步就是布置雷了。
我们要布置10个雷在数组 a 里,怎么样才能做到随机生成呢?
我们可以生成随机数,来达到效果。
随机数的生成:
#include
#include
#include
int main()
{
srand((unsigned int)time(NULL)); //设置一个随机数的生成器
int m = rand()%9; //因为 rand 生成随机数范围0~32767
printf("%d",m); //所以 %9 使随机数生成范围为0~8
}
#include
#include
#include
srand((unsigned int)time(NULL)); //我们需要放在 main 函数中
void game()
{
char a[M][N] = { 0 };
char b[M][N] = { 0 };
chu_shi(a, M, N, '0');
chu_shi(b, M, N, '*');
//布置雷
bu_lei(a, M - 2, N - 2);
da_yin(a, M - 2, N - 2);
}
void bu_lei(char p[M][N], int m, int n)
{
int c = 10;
while (c)
{
int x = rand() % m + 1;
int y = rand() % n + 1;
if (p[x][y] == '0') //避免雷的重复
{
p[x][y] = '1';
c--;
}
}
}
输出:
我们可以观察到10个雷已经布置成功,接下来就是排雷了。
首先,我们排雷是一步一步排的,这样我们就需要一个while循环来首先,我们输入一个行和列都是1到9的坐标,然后显示出这个坐标旁边8个格子的雷的个数,这样我们便可以写出如下代码:
int ji_shu(char a[M][N], int x, int y)
{
return (a[x - 1][y] +
a[x - 1][y - 1] +
a[x][y - 1] +
a[x + 1][y - 1] +
a[x + 1][y] +
a[x + 1][y + 1] +
a[x][y + 1] +
a[x - 1][y + 1] - 8 * '0');
} //每个数字字符减去一个‘0’即可得到相应数字
void pai_lei(char a[M][N], char b[M][N], int m, int n)
{
int x = 0;
int y = 0;
while (1)
{
printf("请输入坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
{
int k = ji_shu(a, x, y); //计算旁边8个格子的雷的个数
b[x][y] = k + '0'; //因为字符0的ASCII码值为48,加上对应数字0到9就能得到对应的数字字符
da_yin(b, M - 2, N - 2); //每次输入完后打印棋盘b
}
else
printf("坐标非法,请重新输入:>\n");
}
}
void pai_lei(char a[M][N], char b[M][N], int m, int n)
{
int x = 0;
int y = 0;
while (1)
{
printf("请输入坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
{
if (a[x][y] == '1') //排到雷了
{
printf("你被炸死了\n");
da_yin(a, M - 2, N - 2); //让你看看雷的位置
break; //退出 while 循环
}
else
{
int k = ji_shu(a, x, y);
b[x][y] = k + '0';
da_yin(b, M - 2, N - 2);
}
}
else
printf("坐标非法,请重新输入:>\n");
}
}
如果排完了,那我们应该要退出呀,我们应该控制 while 循环来退出来:
void pai_lei(char a[M][N], char b[M][N], int m, int n)
{
int x = 0;
int y = 0;
int win = 0;
while (win<(M-2)*(N-2)-10) //排完71个格子退出循环
{
printf("请输入坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
{
if (a[x][y] == '1')
{
printf("你被炸死了\n");
da_yin(a, M - 2, N - 2);
break;
}
else
{
int k = ji_shu(a, x, y);
b[x][y] = k + '0';
da_yin(b, M - 2, N - 2);
win++;
}
}
else
printf("坐标非法,请重新输入:>\n");
}
if (win == (M - 2) * (N - 2) - 10)
{
printf("恭喜你,排雷成功\n");
da_yin(a, M-2, N-2);
}
}
我们这个代码还是不够完善,当我们输入2 2后再输入2 2,它还是会输出,我们应该用 if 语句限制一下:
void pai_lei(char a[M][N], char b[M][N], int m, int n)
{
int x = 0;
int y = 0;
int win = 0;
while (win<(M-2)*(N-2)-10)
{
printf("请输入坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
{
if (b[x][y] == '*') //防止重复排查
{
if (a[x][y] == '1')
{
printf("你被炸死了\n");
da_yin(a, M - 2, N - 2);
break;
}
else
{
int k = ji_shu(a, x, y);
b[x][y] = k + '0';
da_yin(b, M - 2, N - 2);
win++;
}
}
else
printf("该坐标已经被排查过了\n");
}
else
printf("坐标非法,请重新输入:>\n");
}
if (win == (M - 2) * (N - 2) - 10)
{
printf("恭喜你,排雷成功\n");
da_yin(a, M-2, N-2);
}
}
好了,我们游戏具体解析已经完成了,接下来就是游戏封装了。
test.c:扫雷游戏的测试逻辑
game.h:游戏函数的声明
game.c :游戏函数的实现
#pragma once
#include
#include
#include
#define M 11
#define N 11
//初始化棋盘
void chu_shi(char p[M][N], int m, int n, char w);
//打印棋盘
void da_yin(char p[M][N], int m, int n);
//布置雷
void bu_lei(char p[M][N], int m, int n);
//排雷
void pai_lei(char a[M][N], char b[M][N], int m, int n);
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
printf("*****************************\n");
printf("******* 1.play ********\n");
printf("******* 0.exit ********\n");
printf("*****************************\n");
}
void game()
{
char a[M][N] = { 0 };
char b[M][N] = { 0 };
chu_shi(a, M, N, '0');
chu_shi(b, M, N, '*');
bu_lei(a, M - 2, N - 2);
pai_lei(a, b, M - 2, N - 2);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void chu_shi(char p[M][N], int m, int n, char w)
{
int i = 0;
int j = 0;
for (i = 0; i < m; i++)
for (j = 0; j < n; j++)
p[i][j] = w;
}
void da_yin(char p[M][N], int m, int n)
{
int i = 0;
int j = 0;
for (j = 0; j <= n; j++)
printf("%d ", j);
printf("\n");
for (i = 1; i <= m; i++)
{
printf("%d ", i);
for (j = 1; j <= n; j++)
printf("%c ", p[i][j]);
printf("\n");
}
}
void bu_lei(char p[M][N], int m, int n)
{
int c = 10;
while (c)
{
int x = rand() % m + 1;
int y = rand() % n + 1;
if (p[x][y] == '0')
{
p[x][y] = '1';
c--;
}
}
}
int ji_shu(char a[M][N], int x, int y)
{
return (a[x - 1][y] +
a[x - 1][y - 1] +
a[x][y - 1] +
a[x + 1][y - 1] +
a[x + 1][y] +
a[x + 1][y + 1] +
a[x][y + 1] +
a[x - 1][y + 1] - 8 * '0');
}
void pai_lei(char a[M][N], char b[M][N], int m, int n)
{
int x = 0;
int y = 0;
int win = 0;
while (win<(M-2)*(N-2)-10)
{
printf("请输入坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
{
if (b[x][y] == '*')
{
if (a[x][y] == '1')
{
printf("你被炸死了\n");
da_yin(a, M - 2, N - 2);
break;
}
else
{
int k = ji_shu(a, x, y);
b[x][y] = k + '0';
da_yin(b, M - 2, N - 2);
win++;
}
}
else
printf("该坐标已经被排查过了\n");
}
else
printf("坐标非法,请重新输入:>\n");
}
if (win == (M - 2) * (N - 2) - 10)
{
printf("恭喜你,排雷成功\n");
da_yin(a, M-2, N-2);
}
}
跟三子棋一样,都是代码一大堆,看得令人头疼,其实不然,只要我们把它细分下来,就会发现其实都挺简单的,都是我们学过的,只是见的少罢了,这就要求我们多做练习提高见识,这样才能写出好代码。
扫雷游戏也就圆满完成了,希望大家都能有所收获,有所成长。
下期见啦~