摸鱼小游戏其二:扫雷

目录

正文前的碎碎念

前言

正文

主函数

game()

各个模块的具体实现

InitBoard()初始化棋盘

DisplayBoard() 打印棋盘

FindMine() 扫雷函数

SetMine()设置雷

UnfoldBoard()展开棋盘函数

 get_mine_count()获取周围雷的数量

总结


正文前的碎碎念

换一种风格,直接上代码,少一些废话。O.o

前言

        用c语言摸了个扫雷,实现了自定义棋盘大小,和埋雷数量,但是没有实装标记雷功能,需要将非雷点全部找出来才能判赢,算是加强版(O.o),下面详细介绍思路和代码实现。

正文

摸鱼小游戏其二:扫雷_第1张图片

        上图是一个经典的扫雷,从里面可以看出扫雷的主体是在一个有很多小格子的方形棋盘,玩家在上面进行选择小格子来实现扫雷。下面我将直接用代码来说明我是如何实现一个简易版的扫雷游戏。

主函数

用来控制游戏流程,实现游戏的进入、退出,随机数种子的生成。

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;
}

game()

实现具体的扫雷游戏的流程

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;
}

上面介绍了整个游戏的流程,下面将详细介绍各个模块的具体实现。

各个模块的具体实现

InitBoard()初始化棋盘

//用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;
	}
}

DisplayBoard() 打印棋盘

        因为这里使用的是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");
}

效果图 :

        这里的埋雷棋盘打印出来只是让我们看一下设定有没有问题,正式不需要打印埋雷棋盘。

摸鱼小游戏其二:扫雷_第2张图片

FindMine() 扫雷函数

        在这里面将实现扫雷流程,包括接收玩家输入的坐标,设置雷的坐标,展开输入坐标周围没有雷的格子,判断输赢。

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");
		}
	}
}

SetMine()设置雷

        在埋雷棋盘上设置雷的位置。

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--;
		}
	}
}

UnfoldBoard()展开棋盘函数

        展开输入坐标周围没有雷的格子,并设置周围有雷的格子,周围存在多少雷。

        主要思想是用递归以此判定坐标周围有没有雷,或者有多少雷。

 摸鱼小游戏其二:扫雷_第3张图片

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);//左下
		}
	}
}

 get_mine_count()获取周围雷的数量

        用来判断坐标周围有多少雷。

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值等于雷的数量,就是玩家获胜。
}

总结

        用新风格写了一篇博客,希望大家能喜欢。限于技术这篇博客还有很多不足,希望大家可以多多指出。蓝字为源代码链接扫雷

        最后再次感谢阅读,感谢点赞、收藏和评论。

你可能感兴趣的:(算法,c++,开发语言)