目录
正文前的碎碎念
前言
正文
主函数
game()
各个模块的具体实现
InitBoard()初始化棋盘
DisplayBoard() 打印棋盘
FindMine() 扫雷函数
SetMine()设置雷
UnfoldBoard()展开棋盘函数
get_mine_count()获取周围雷的数量
总结
换一种风格,直接上代码,少一些废话。O.o
用c语言摸了个扫雷,实现了自定义棋盘大小,和埋雷数量,但是没有实装标记雷功能,需要将非雷点全部找出来才能判赢,算是加强版(O.o),下面详细介绍思路和代码实现。
上图是一个经典的扫雷,从里面可以看出扫雷的主体是在一个有很多小格子的方形棋盘,玩家在上面进行选择小格子来实现扫雷。下面我将直接用代码来说明我是如何实现一个简易版的扫雷游戏。
用来控制游戏流程,实现游戏的进入、退出,随机数种子的生成。
int main()
{
int input = 0;//用来接收玩家选择,进入、退出游戏的判定条件
srand((unsigned int)time(NULL));//随机数生成
do
{
menu();
printf("请选择;>");
scanf("%d", &input);
switch(input)
{
case 1:
game();//game() 函数用来实现游戏主体
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("请输入 1 或 0\n");
break;
}
} while (input);
return 0;
}
实现具体的扫雷游戏的流程
void game()
{
int rows = 0;//行数
int cols = 0;//列数
int trap = 0;//雷的个数
printf("请输入棋盘的行;>");
scanf("%d", &rows);
printf("请输入棋盘的列;>");
scanf("%d", &cols);
//提醒玩家输入正确的雷的个数
//雷的个数不能多于棋盘大小,也不能小于等于0;
while (1)
{
printf("请设定雷的个数;>");
scanf("%d", &trap);
if (trap < (rows * cols) && trap > 0)
{
break;
}
printf("雷的个数多于棋盘格子数,或者雷的数为0,请重新输入\n");
}
//为了实现自定义棋盘大小用malloc开辟棋盘所需要的空间。
char* mine = (char*)malloc((rows * cols) * sizeof(char));//布置雷的棋盘
//assert(mine != NULL);
if (mine == NULL)
{
perror("mine");
return;
}
//为了隐藏埋雷棋盘的详情,开辟新空间,作为展示棋盘给晚间看。
char* show = (char*)malloc((rows * cols) * sizeof(char));//存放排查后的棋盘
if (mine == NULL)
{
perror("show");
return;
}
//将埋雷棋盘中的元素初始化为'0'
InitBoard(mine, rows, cols, '0');//初始化埋雷棋盘
//将展示棋盘中的元素初始化为'*'
InitBoard(show, rows, cols, '*');//初始化显示棋盘
//展示棋盘,让玩家可以看着棋盘进行选择扫雷的地点。
DisplayBoard(show, rows, cols);//实现显示棋盘的打印
//实现扫雷流程。
FindMine(mine, show, rows, cols, trap);//扫雷
//释放malloc开辟的空间
free(mine);
mine = NULL;
free(show);
show = NULL;
}
上面介绍了整个游戏的流程,下面将详细介绍各个模块的具体实现。
//用board来接收传进来的棋盘空间,将内部空间初始化为 set 。
void InitBoard(char* board, int rows, int cols, char set)
{
int i = 0;
for (i = 0; i < (rows * cols); i++)//malloc空间大小为 行*列
{
*(board + i) = set;
}
}
因为这里使用的是malloc函数创建的空间,在形式上是一维空间,需要一定的手动调整,显示为二维形式。
board用来接收棋盘,来选择打印 埋雷棋盘还是展示棋盘
void DisplayBoard(char* board, int rows, int cols)
{
int i = 0;
int count = 0;
int j = 1;
printf("---------- 扫雷游戏 ----------\n");
//用来打印列行提示
for (i = 0; i <= cols; i++)
{
printf("%-2d ", i);
}
printf("\n");
//因为malloc()出来的是一维的空间,
//这里我们在打印设定的一行数量之后需要手动换行
for (i = 0; i < (rows * cols); i++)
{
//这里判断是不是一行的开始,用来打印行提示列。
if (count == 0)
{
printf("%-2d ", j);
j++;
}
printf("%-2c ", *(board + i));
if (count == cols - 1)
{
printf("\n");//实现手动换行
count = -1;
}
count++;
}
printf("---------- 扫雷游戏 ----------\n");
}
效果图 :
这里的埋雷棋盘打印出来只是让我们看一下设定有没有问题,正式不需要打印埋雷棋盘。
在这里面将实现扫雷流程,包括接收玩家输入的坐标,设置雷的坐标,展开输入坐标周围没有雷的格子,判断输赢。
void FindMine(char* mine, char* show, int rows, int cols, int trap)
{
int x = 0;//接收玩家输入的横坐标
int y = 0;//接收玩家输入的纵坐标
int one = 1;//用来标记玩家是不是在一局游戏中第一次输入坐标
while (1)
{
printf("请输入排查横坐标;>");
scanf("%d", &x);
printf("请输入排查纵坐标;>");
scanf("%d", &y);
//如果玩家是一局游戏第一次输入坐标,就布置雷
//防止第一次输入坐标就被炸没。
if (one == 1)
{
//布置雷函数,将埋雷棋盘上的'0' 改成'1',
//详细实现在后面。
SetMine(mine, rows, cols, trap, x, y);//在埋雷棋盘上布置雷
one--;
}
//判断坐标的合法性
if (x > 0 && x <= rows && y > 0 && y <= cols)
{
//判断玩家输入的坐标是不是雷,是雷就是玩家输掉游戏。
if (*(mine + (x - 1) * cols + y - 1) == '1')
{
printf("\n");
printf("寄寄寄寄寄寄寄寄寄寄寄\n");
printf("\n");
DisplayBoard(mine, rows, cols);//实现埋雷棋盘的打印
break;
}
else
{
//如果玩家在这次输入没有被炸,就展开周围没有雷的格子
//详细实现在后面。
UnfoldBoard(mine, show, rows, cols, x, y);
DisplayBoard(show, rows, cols);//实现显示棋盘的打印
}
//判断玩家是否赢了
int win = IfWin(show, rows, cols);
if (win == trap)
{
printf("赢了\n");
break;
}
}
else
{
printf("坐标有误,请输入正确坐标\n");
}
}
}
在埋雷棋盘上设置雷的位置。
mine:埋雷棋盘
rows:棋盘的行数
cols:棋盘的列数
trap:需要埋雷的个数
x:玩家首次输入的横坐标
y:玩家首次输入的纵坐标
void SetMine(char* mine, int rows, int cols, int trap, int x, int y)
{
while (trap)
{
int i = rand() % (rows * cols);//用随机数来表示埋雷的地方
//判断随机数生成的位置没有雷,且不是玩家首次输入的坐标。
if (*(mine + i) == '0' && i != (x * y - 1))
{
*(mine + i) = '1';
trap--;
}
}
}
展开输入坐标周围没有雷的格子,并设置周围有雷的格子,周围存在多少雷。
主要思想是用递归以此判定坐标周围有没有雷,或者有多少雷。
mine:埋雷棋盘
show:展示棋盘
rows:棋盘的行数
cols:棋盘的列数
trap:需要埋雷的个数
x:玩家输入的横坐标
y:玩家输入的纵坐标
void UnfoldBoard(char* mine, char* show, int rows, int cols, int x, int y)
{
//用get_mine_count()函数来计算坐标周围有多少雷
int count = get_mine_count(mine, rows, cols, x, y);
if (count != 0)//如果周围有雷就将展示棋盘的相应位置设定为雷的个数
{
//因为get_mine_count返回的是int类型,转换为相应的字符数字,需要加上字符'0';
*(show + (x - 1) * cols + y - 1) = (count + '0');
return;
}
else
{
//坐标周围没有雷,展示棋盘的对应格子设置为 ' ' 空格。
*(show + (x - 1) * cols + y - 1) = ' ';
//坐标正上方在棋盘范围内,且没有被判断过,进入递归判断周围有多少雷。
if ((x - 1) > 0 &&
(*(show + ((x - 1) - 1) * rows + y - 1)) != ' ')
{
UnfoldBoard(mine, show, rows, cols, x - 1, y);//上
}
//坐标左方在棋盘范围内,且没有被判断过,进入递归判断周围有多少雷。
if ((y - 1) > 0 &&
(*(show + (x - 1) * rows + y - 1 - 1)) != ' ')
{
UnfoldBoard(mine, show, rows, cols, x, y - 1);//左
}
//坐标正下方方在棋盘范围内,且没有被判断过,进入递归判断周围有多少雷。
if ((x + 1) <= rows &&
(*(show + ((x + 1) - 1) * rows + y - 1)) != ' ')
{
UnfoldBoard(mine, show, rows, cols, x + 1, y);//下
}
//坐标右方在棋盘范围内,且没有被判断过,进入递归判断周围有多少雷。
if ((y + 1) <= cols &&
(*(show + (x - 1) * rows + (y + 1) - 1)) != ' ')
{
UnfoldBoard(mine, show, rows, cols, x, y + 1);//右
}
//坐标左上方在棋盘范围内,且没有被判断过,进入递归判断周围有多少雷。
if ((x - 1) > 0 && (y - 1) > 0 &&
(*(show + ((x - 1) - 1) * rows + (y - 1) - 1)) != ' ')
{
UnfoldBoard(mine, show, rows, cols, x - 1, y - 1);//左上
}
//坐标右下方在棋盘范围内,且没有被判断过,进入递归判断周围有多少雷。
if ((x + 1) <= rows && (y + 1) <= cols &&
(*(show + ((x + 1) - 1) * rows + (y + 1) - 1)) != ' ')
{
UnfoldBoard(mine, show, rows, cols, x + 1, y + 1);//右下
}
//坐标右上方在棋盘范围内,且没有被判断过,进入递归判断周围有多少雷。
if ((x - 1) > 0 && (y + 1) <= cols &&
(*(show + ((x - 1) - 1) * rows + (y + 1) - 1)) != ' ')
{
UnfoldBoard(mine, show, rows, cols, x - 1, y + 1);//右上
}
//坐标左下方在棋盘范围内,且没有被判断过,进入递归判断周围有多少雷。
if ((x + 1) <= rows && (y - 1) > 0 &&
(*(show + ((x + 1) > -1) * rows + (y - 1) - 1)) != ' ')
{
UnfoldBoard(mine, show, rows, cols, x + 1, y - 1);//左下
}
}
}
用来判断坐标周围有多少雷。
int get_mine_count(char* mine, int rows, int cols, int x, int y)
{
//输入坐标为左上角第一个时
if (x == 1 && y == 1)//左上角第一个
{
return *(mine + 1) +
*(mine + cols) +
*(mine + cols + 1) - 3 * '0';
}
//输入坐标为右上角第一个时
else if (x == 1 && y == cols) //右上角第一个
{
return *(mine + cols - 1 - 1) +
*(mine + cols + cols - 1) +
*(mine + cols + cols - 1 - 1) - 3 * '0';
}
//输入坐标为左下角第一个时
else if (x == rows && y == 1)//左下角第一个
{
return *(mine + (rows - 1) * cols + 1) +
*(mine + (rows - 1) * cols - cols) +
*(mine + (rows - 1) * cols - cols + 1) - 3 * '0';
}
//输入坐标为右下角第一个时
else if (x == rows && y == cols)//右下角
{
return *(mine + rows * cols - 1 - 1) +
*(mine + (rows - 1) * cols - 1) +
*(mine + (rows - 1) * cols - 1 - 1) - 3 * '0';
}
//输入坐标在第一行,除了开头和结尾
else if (x == 1)//第一行 除了开头和结尾
{
return *(mine + y - 1 + 1) +
*(mine + y - 1 - 1) +
*(mine + y - 1 + cols) +
*(mine + y - 1 + cols + 1) +
*(mine + y - 1 + cols - 1) - 5 * '0';
}
//输入坐标在最后一行,除了开头和结尾
else if (x == rows)//最后一行中 除了第一个和最后一个
{
return *(mine + (rows - 1) * cols + y - 1 - 1) +
*(mine + (rows - 1) * cols + y - 1 + 1) +
*(mine + (rows - 1) * cols + y - 1 - rows) +
*(mine + (rows - 1) * cols + y - 1 - rows + 1) +
*(mine + (rows - 1) * cols + y - 1 - rows - 1) - 5 * '0';
}
//输入坐标在第一列,除了开头和结尾
else if (y == 1)//第一列 不包含第一列第一个和最后一个
{
return *(mine + (x - 1) * cols + 1) +
*(mine + (x - 2) * cols) +
*(mine + (x - 2) * cols + 1) +
*(mine + x * cols) +
*(mine + x * cols + 1) - 5 * '0';
}
//输入坐标在最后一列,除了开头和结尾
else if (y == cols)//最后一列,不包含 最后一列的第一个和最后一个
{
return *(mine + x * cols - 1 - 1) +
*(mine + x * cols - 1 - cols) +
*(mine + x * cols - 1 - cols - 1) +
*(mine + x * cols - 1 + cols) +
*(mine + x * cols - 1 + cols - 1) - 5 * '0';
}
//输入坐标为普通情况,即输入坐标周围有8个坐标
else
{
return *(mine + (x - 1) * cols + y - 1 + 1) +
*(mine + (x - 1) * cols + y - 1 - 1) +
*(mine + (x - 1) * cols + y - 1 - cols) +
*(mine + (x - 1) * cols + y - 1 - cols + 1) +
*(mine + (x - 1) * cols + y - 1 - cols - 1) +
*(mine + (x - 1) * cols + y - 1 + cols) +
*(mine + (x - 1) * cols + y - 1 + cols + 1) +
*(mine + (x - 1) * cols + y - 1 + cols - 1) - 8 * '0';
}
}
IfWin()判断输赢
遍历展示棋盘,查看还有多少个格子没有被查找。如果在失败之前,检查到剩下没被查找的格子数等于布置雷的数量,就是玩家获胜。
int IfWin(char* show, int rows, int cols)
{
int win = 0;//用来记录还剩下多少个格子没有被查找。
int i = 0;
//遍历棋盘。
for (i = 0; i < rows * cols; i++)
{
if (*(show + i) == '*')
{
win++;
}
}
return win;//将win值返回,如果win值等于雷的数量,就是玩家获胜。
}
用新风格写了一篇博客,希望大家能喜欢。限于技术这篇博客还有很多不足,希望大家可以多多指出。蓝字为源代码链接扫雷
最后再次感谢阅读,感谢点赞、收藏和评论。