扫雷——一款大众类的益智小游戏,一款在小居童年时一直以为是纯靠运气取胜的游戏,导致从没玩赢过。直到高中才晓得真正的游戏规则
初学C语言的小居开始豪横起来,计划用一些简单的C语言知识来实现一个"要啥有啥"的扫雷,话不多说,讲起!
扫雷游戏是在9*9,16*16,16*30或者自定义大小的矩形方阵中实现的,此次就来整一个9*9的扫雷简易版玩玩。
玩家通过选择1(玩游戏)或0(结束游戏)来决定游戏与否。
输入想要扫的雷的坐标。若踩到雷,那就输了;若未踩到雷,显示该坐标上下左右以及四个角上所含雷的个数(如果周围八个都没有雷,则向外展开,直到周围有雷为止),游戏继续。直到矩形方阵中只剩下雷时,恭喜!你赢了。
玩家遇见疑似雷的时候可以选择去标记它,选择1(标记)或0(不标记),输入想要标记的坐标,即可完成标记。
在计算机编程的过程中,为了使代码看起来井然有序,遂将游戏主体分在三个文件里,两个源文件,一个头文件
游戏界面的打印以及游戏的进入与结束都放在主函数(main函数)中
代码展示:
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:> ");
scanf("%d", &input);
switch (input)
{
case 1:
system("cls");//执行系统命令-cls-清空屏幕
game();//选择进入游戏
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
}
说明:
在main函数当中,小居运用了do…while语句来实现游戏的进入与结束,保证游戏界面至少进入一次。
在do…while语句中首先进行了游戏菜单的打印,1代表玩游戏,0代表退出游戏。
代码展示:
void menu()
//打印菜单
{
printf("**********************************\n");
printf("********** 1、Play ********\n");
printf("********** 0、Exit ********\n");
printf("**********************************\n");
}
代码效果:
当选择数字1进入游戏函数(game函数)后,便进入了游戏的主体,为了使代码分工明确,实现游戏过程中的函数的详细内容将会被放在game.c之中。
代码展示:
void game()
{
char mine[ROWS][COLS] = {
0 };//存放雷的数组
char show[ROWS][COLS] = {
0 };//扫雷的数组
//初始化两个数组
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
//打印扫雷的界面
PrintBoard(show, ROW, COL);
//布置雷的位置
SetMine(mine, ROW, COL);
//排查雷
FindMine(mine, show, ROW, COL);
}
说明:
进入游戏之后先创建了两个字符型二维数组,一个是mine[ROWS][COLS]用来存放雷的数组,另一个是show[ROWS][COLS]用于进行扫雷,ROWS(11)和COLS(11)代表着行和列,在game.h这个头文件里进行了宏定义,这样未来想要修改行和列只需要在头文件里进行修改即可,方便大大地增加。
问题1、在这里为啥要创建两个数组呢?
解:当出现1时,表示的是雷?还是排雷时坐标周围雷的个数1?这种时候就会产生分歧,所以需要创建两个数组来存放信息。
问题2、既然1会混淆分不清,为何不让雷用@代替,不是雷用#代替呢?
解:这样会让棋盘上的信息过多,不方便打印。打印的时候只想要打印排查出来的雷的信息,其他位置不显示。如果这样做就需要每个坐标去判断是否为数字,可读性差,难以理解。
问题3、明明就是9*9的扫雷游戏为啥行和列都是11?
解:在扫雷的过程中有一步需要统计坐标(x,y)(坐标x和y的区间为[1,9])周围8个坐标的雷的个数,如果mine数组的大小刚好是9*9,那坐标方阵最外一圈就没有办法统计全雷的个数,还会出现越界访问现象。show数组的大小也是11*11是为了和mine数组一一对应,方便将mine数组中的内容反映到show数组中。
代码展示:
void InitBoard(char Board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0, j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
Board[i][j] = set;
}
}
}
说明:
通过两层for循环对数组的内容进行初始化,这里用字符型变量set来接受初始化的内容’0’或’*’,将set赋值给初始化的数组。
代码展示:
void PrintBoard(char Board[ROWS][COLS], int row, int col)
{
printf("----------扫雷游戏----------\n");
int i = 0;
for (i = 0; i <= col; i++)//列号
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)//行号
{
int j = 0;
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 count = NumMine;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (Board[x][y] == '0')
{
Board[x][y] = '1';//雷为'1'
count--;
}
}
}
说明:
NumMine是整个方阵中雷的个数,这里通过while循环来实现雷的一个一个布置。此时需要让电脑随机生成横纵坐标,运用到了rand函数随机生成1到9(雷布置的区域)的数,该函数的头文件为#include
srand((unsigned int)time(NULL));
这是拿时间戳来设置随机数的生成起始点,里面包含time函数,其头文件为#include
如果生成的坐标先前没有布置过雷(坐标里是’0’),就布置一个雷,雷用’1’表示,每不止一个count- -,直到count等于0,跳出循环,结束布置雷这一个操作。
代码展示:
void FindMine(char Mine[ROWS][COLS], char Show[ROWS][COLS], int row, int col)
{
int win = 0;//排雷区域'*'或'&'个数
int x = 0, y = 0;
int a = 0, b = 0;
while (1)
{
//PrintBoard(Show, ROW, COL);
int sign = 0;//标记与否
printf("请输入想要排雷的坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (Mine[x][y] == '1')
{
printf("很遗憾,你被炸死了\n");
PrintBoard(Mine, row, col);
break;
}
else
{
//计算该坐标周围有多少雷,并进行递归展开
Count_Spread(Mine, Show, x, y);
system("cls");
PrintBoard(Show, ROW, COL);
win = To_Win(Show, ROW, COL);
if (win == NumMine)
{
printf("恭喜你,扫雷成功!\n");
PrintBoard(Mine, row, col);
break;
}
}
}
else
{
printf("坐标不合法,请重新输入\n");
continue;
}
}
int To_Win(char Show[ROWS][COLS], int row, int col)
{
int i = 0, j = 0;
int count = 0;
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++)
{
if (Show[i][j] == '*'|| Show[i][j] == '&')
{
count++;
}
}
}
return count;
}
说明:
代码展示:
//x,y坐标周围8个坐标的雷的个数
static int RoundMine(char Mine[ROWS][COLS], int x, int y)
{
return Mine[x - 1][y - 1] +
Mine[x - 1][y] +
Mine[x - 1][y + 1] +
Mine[x][y + 1] +
Mine[x + 1][y + 1] +
Mine[x + 1][y] +
Mine[x + 1][y - 1] +
Mine[x][y - 1] - 8 * '0';
}
//递归展开
void Count_Spread(char Mine[ROWS][COLS], char Show[ROWS][COLS], int x, int y)
{
int count = RoundMine(Mine, x, y);
if (count != 0)
{
Show[x][y] = count + '0';
}
else
{
Show[x][y] = '0';
if ((x - 1) > 0 && (y - 1) > 0 && Show[x - 1][y - 1] == '*')
Count_Spread(Mine, Show, x - 1, y - 1);
if ((x - 1) > 0 && (y + 0) > 0 && Show[x - 1][y + 0] == '*')
Count_Spread(Mine, Show, x - 1, y + 0);
if ((x - 1) > 0 && (y + 1) <= COL && Show[x - 1][y + 1] == '*')
Count_Spread(Mine, Show, x - 1, y + 1);
if ((x + 0) > 0 && (y + 1) <= COL && Show[x + 0][y + 1] == '*')
Count_Spread(Mine, Show, x + 0, y + 1);
if ((x + 1) <= ROW && (y + 1) <= COL && Show[x + 1][y + 1] == '*')
Count_Spread(Mine, Show, x + 1, y + 1);
if ((x + 1) <= ROW && (y - 0) > 0 && Show[x + 1][y - 0] == '*')
Count_Spread(Mine, Show, x + 1, y - 0);
if ((x + 1) <= ROW && (y - 1) > 0 && Show[x + 1][y - 1] == '*')
Count_Spread(Mine, Show, x + 1, y - 1);
if ((x - 0) > 0 && (y - 1) > 0 && Show[x - 0][y - 1] == '*')
Count_Spread(Mine, Show, x - 0, y - 1);
}
}
说明:
代码展示:
do
{
printf("标记与否(1表示标记,0表示不标记):>");
scanf("%d", &sign);
printf("\n");
switch (sign)
{
case 1:
while (1)
{
printf("请输入想要标记的坐标:>");
scanf("%d %d", &a, &b);
if (a >= 1 && a <= row && b >= 1 && b <= col)
{
Show[a][b] = '&';
system("cls");
PrintBoard(Show, ROW, COL);
break;
}
else
{
printf("坐标不合法,请重新输入\n");
}
}
break;
case 0:
break;
default:
printf("输入错误请重新输入\n");
break;
}
} while (sign);
说明:
代码效果:
#pragma once
#include
#include
#include
#define NumMine 10//布置雷的个数
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
void InitBoard(char Board[ROWS][COLS], int rows, int cols, char set);//初始化
void PrintBoard(char Board[ROWS][COLS], int row, int col);//打印数组
void SetMine(char Board[ROWS][COLS], int row, int col);//布置雷的位置
void FindMine(char Mine[ROWS][COLS], char Show[ROWS][COLS], int row, int col);//排查雷
#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 mine[ROWS][COLS] = {
0 };//存放雷的数组
char show[ROWS][COLS] = {
0 };//扫雷的数组
//初始化两个数组
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
//打印扫雷的界面
PrintBoard(show, ROW, COL);
//布置雷的位置
SetMine(mine, ROW, COL);
//排查雷
FindMine(mine, show, ROW, COL);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:> ");
scanf("%d", &input);
switch (input)
{
case 1:
system("cls");
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误请重新输入\n");
break;
}
} while (input);
}
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void InitBoard(char Board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0, j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
Board[i][j] = set;
}
}
}
void PrintBoard(char Board[ROWS][COLS], int row, int col)
{
printf("----------扫雷游戏----------\n");
int i = 0;
for (i = 0; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
int j = 0;
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 count = NumMine;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (Board[x][y] == '0')
{
Board[x][y] = '1';//雷为'1'
count--;
}
}
}
//x,y坐标周围有的雷的个数
static int RoundMine(char Mine[ROWS][COLS], int x, int y)
{
return Mine[x - 1][y - 1] +
Mine[x - 1][y] +
Mine[x - 1][y + 1] +
Mine[x][y + 1] +
Mine[x + 1][y + 1] +
Mine[x + 1][y] +
Mine[x + 1][y - 1] +
Mine[x][y - 1] - 8 * '0';
}
void Count_Spread(char Mine[ROWS][COLS], char Show[ROWS][COLS], int x, int y)
{
int count = RoundMine(Mine, x, y);
if (count != 0)
{
Show[x][y] = count + '0';
}
else
{
Show[x][y] = '0';
if ((x - 1) > 0 && (y - 1) > 0 && Show[x - 1][y - 1] == '*')
Count_Spread(Mine, Show, x - 1, y - 1);
if ((x - 1) > 0 && (y + 0) > 0 && Show[x - 1][y + 0] == '*')
Count_Spread(Mine, Show, x - 1, y + 0);
if ((x - 1) > 0 && (y + 1) <= COL && Show[x - 1][y + 1] == '*')
Count_Spread(Mine, Show, x - 1, y + 1);
if ((x + 0) > 0 && (y + 1) <= COL && Show[x + 0][y + 1] == '*')
Count_Spread(Mine, Show, x + 0, y + 1);
if ((x + 1) <= ROW && (y + 1) <= COL && Show[x + 1][y + 1] == '*')
Count_Spread(Mine, Show, x + 1, y + 1);
if ((x + 1) <= ROW && (y - 0) > 0 && Show[x + 1][y - 0] == '*')
Count_Spread(Mine, Show, x + 1, y - 0);
if ((x + 1) <= ROW && (y - 1) > 0 && Show[x + 1][y - 1] == '*')
Count_Spread(Mine, Show, x + 1, y - 1);
if ((x - 0) > 0 && (y - 1) > 0 && Show[x - 0][y - 1] == '*')
Count_Spread(Mine, Show, x - 0, y - 1);
}
}
//通过计算Show[ROWS][COLS]中有多少'*'或'&',该个数等于雷的个数时,获得胜利
int To_Win(char Show[ROWS][COLS], int row, int col)
{
int i = 0, j = 0;
int count = 0;
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++)
{
if (Show[i][j] == '*'|| Show[i][j] == '&')
{
count++;
}
}
}
return count;
}
void FindMine(char Mine[ROWS][COLS], char Show[ROWS][COLS], int row, int col)
{
int win = 0;//排雷区域'*'和'&'的总数
int x = 0, y = 0;
int a = 0, b = 0;
while (1)
{
int sign = 0;//标记与否
printf("请输入想要排雷的坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (Mine[x][y] == '1')
{
printf("很遗憾,你被炸死了\n");
PrintBoard(Mine, row, col);
break;
}
else
{
//计算该坐标周围有多少雷,并进行递归展开
Count_Spread(Mine, Show, x, y);
system("cls");
PrintBoard(Show, ROW, COL);
win = To_Win(Show, ROW, COL);
if (win == NumMine)
{
printf("恭喜你,扫雷成功!\n");
PrintBoard(Mine, row, col);
break;
}
}
}
else
{
printf("坐标不合法,请重新输入\n");
continue;
}
do
{
printf("标记与否(1表示标记,0表示不标记):>");
scanf("%d", &sign);
printf("\n");
switch (sign)
{
case 1:
while (1)
{
printf("请输入想要标记的坐标:>");
scanf("%d %d", &a, &b);
if (a >= 1 && a <= row && b >= 1 && b <= col)
{
Show[a][b] = '&';
system("cls");
PrintBoard(Show, ROW, COL);
break;
}
else
{
printf("坐标不合法,请重新输入\n");
}
}
break;
case 0:
break;
default:
printf("输入错误请重新输入\n");
break;
}
} while (sign);
}
}
本次的扫雷游戏的实现充满着不易,游戏颜值虽不高,但也算是圆满实现扫雷基本功能,希望技术多多长进,能实现颜值更高,玩起来更加便捷的扫雷游戏…