C语言实现扫雷游戏(进阶篇)

       @C语言实现扫雷游戏(基础篇)的链接在下面@

https://blog.csdn.net/m0_73676323/article/details/129357113https://blog.csdn.net/m0_73676323/article/details/129357113

目录

一)前言

二)爆发式展开  explodespread

三)疑似地雷坐标的标注  signmine

四)代码实现效果

五)完整代码(扫雷)


一)前言

在之前我们讲解了C语言实现扫雷游戏(基础篇)的代码实现,发现程序太过于繁琐,所以今天我们来进行程序的优化。

二)爆发式展开  (explodespread)

 我们知道要完成爆发式展开,我们应该使用函数递归的方式。

而要完成爆发式展开有两个难点:1.当满足什么条件时可以爆发式展开。2.展开是否越界以及重复。下面我们来一一解决。

1)当满足什么条件时可以爆发式展开

 由上图可知只有当我们选择的坐标周围没有地雷的时候我们才可以爆发式展开

2)展开是否越界以及重复访问

 

 

 越界问题:上图是9*9棋盘的一个角落,在基础篇我们知道我们创建了一个11*11的数组,而当我们选择边角的坐标进行展开,由于在初始化数组中我们把mine数组中的元素都初始化为‘0’,所以在展开时会导致访问到了11*11的范围,从而造成越界(如蓝色区域)。

重复问题: 当我们使用递归时我们把周围3*3的无地雷区域展开,应该是把【x】【y】以及周围的8个坐标进行了访问,但是却会导致如:【x-1】【y】坐标会把【x】【y】坐标再一次访问的问题,而导致死循环(如绿色区域)。

解决方案: 1.要限制非法坐标的展开,只需要对坐标进行判断即可

                   2.限制对点位置的重复访问,使得每一个位置只能向四周展开一次

代码展示

void explodespread(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y)
{
	//限制非法坐标的展开
	if (x >= 1 && x <= row && y >= 1 && y <= col)
	{
		int count = getin(mine, x, y);
		if (count == 0)
		{
			show[x][y] = '0';			//无地雷则变为 “0”
            int i = 0;
			for (i = x - 1; i <= x + 1; i++)			
			                                  //向四周共8个位置递归调用
			{
				int j = 0;
				for (j = y - 1; j <= y + 1; j++)
				{
					//限制对点位置的重复访问,使得每一个位置只能向四周展开一次
					if (show[i][j] == '*')
					{
						explodespread(mine, show, row, col, i, j);
					}
				}
			}
		}
		//标注地雷的个数
		else
		{
			show[x][y] = count + '0';
		}
	}
}

三)疑似地雷坐标的标注  (signmine)

 其实疑似地雷坐标的标注的代码实现非常简单,只需要把玩家在show棋盘中认为的疑似地雷的坐标‘*’改成另一个字符就行了,在这里我们选择‘  !’来作为标记。

代码如下

void signmine(char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int n = 1;
	while (n)
	{
		printf("请输入要标记的坐标:\n");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] == '*')
			{
				show[x][y] = '!';
				system("cls");
				printf("已成功标记\n");
				DisPlayBoard(show, row, col);
				printf("请选择是否继续标记(输入“1”继续,输入“0”退出)\n");
				scanf("%d", &n);
			}
			else
			{
				printf("该位置不能被标记,请重新输入:\n");
			}
		}
		else
		{
			printf("输入坐标非法,请重新输入:\n");
		}
	}

}

四)代码实现效果

扫雷 标记

五)完整代码(扫雷)

  game.h

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define minecount 10

//菜单
void menu();
//初始化雷区
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 mine[ROWS][COLS],int row,int col);
//排查雷 
void Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

  game.c

#define _CRT_SECURE_NO_WARNINGS
#include "game.h"

void menu()
{
	printf("<<<<<<<********>>>>>>>\n");
	printf("<<<<<<<*1.play*>>>>>>>\n");
	printf("<<<<<<<*0.exit*>>>>>>>\n");
	printf("<<<<<<<********>>>>>>>\n");
}
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 <= row; 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 mine[ROWS][COLS],int row, int col)
{
	int x = 0;
	int y = 0;
	int count = minecount;
	while (count)
	{
		x = rand() % row+ 1;//1~9
		y = rand() % col+ 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

static int getin(char mine[ROWS][COLS],int x,int y)//static有三个作用 1.修饰局部变量 2.修饰全局变量 3.修饰函数
{
	return mine[x][y] + mine[x][y - 1] + mine[x][y + 1] +
		   mine[x - 1][y] + mine[x - 1][y + 1] + mine[x - 1][y - 1] +
		   mine[x + 1][y] + mine[x + 1][y + 1] + mine[x + 1][y - 1]- 9 * '0';//可用循环
}

void explodespread(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y)
{
	//限制非法坐标的展开
	if (x >= 1 && x <= row && y >= 1 && y <= col)
	{
		//计算该位置附近四周地雷的个数
		int count = getin(mine, x, y);
		//若四周没有一个地雷,则需要向该位置的四周展开,直到展开到某个位置附近存在地雷为止
		if (count == 0)
		{
			//把附近没有地雷的位置变成字符 “0”
			show[x][y] = '0';
			int i = 0;
			//向四周共8个位置递归调用
			for (i = x - 1; i <= x + 1; i++)
			{
				int j = 0;
				for (j = y - 1; j <= y + 1; j++)
				{
					//限制对点位置的重复展开调用,使得每一个位置只能向四周展开一次
					if (show[i][j] == '*')
					{
						explodespread(mine, show, row, col, i, j);
					}
				}
			}
		}
		//若四周存在地雷则应该在这个位置上标注上地雷的个数
		else
		{
			show[x][y] = count + '0';
		}
	}
}

void signmine(char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int n = 1;
	while (n)
	{
		printf("请输入要标记的坐标:\n");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] == '*')
			{
				show[x][y] = '!';
				system("cls");
				printf("已成功标记\n");
				DisPlayBoard(show, row, col);
				printf("请选择是否继续标记(输入“1”继续,输入“0”退出)\n");
				scanf("%d", &n);
			}
			else
			{
				printf("该位置不能被标记,请重新输入:\n");
			}
		}
		else
		{
			printf("输入坐标非法,请重新输入:\n");
		}
	}

}

void Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	int n;
	while (win < row * col - minecount)
	{
		printf("请输入要排查的位置下标:\n");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你失败了!");
				break;
			}
			else
			{
				int count = 0;
				//count = getin(mine, x, y);
				//show[x][y] = count + '0';
				explodespread(mine, show, ROW, COL, x, y);
				win++;
				printf("\n");
				DisPlayBoard(show, row, col);
				if (win == row * col - minecount)
				{
					printf("恭喜你,排雷成功\n");
					DisPlayBoard(mine, ROW, COL);
				}
				else 
				{
					printf("请选择是否标记地雷:(输入“1”进行确认,输入“0”退出)\n");
					scanf("%d", &n);
					switch (n)
					{
					case 1:
						signmine(show, row, col);
						break;
					default:
						break;
					}
				}
			}
		}
		else
		{
			printf("输入错误,请重新输入\n");
		}
	}
	
}

  text.c

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include "game.h"

void game()
{
	//定义数组————字符型 
	char mine[ROWS][COLS] = { 0 };//空格
	char show[ROWS][COLS] = { 0 };
	//初始化雷区
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');
	//打印雷区
	//DisPlayBoard(mine, ROW, COL);//测试用,游戏时不用
	//printf("\n");
	DisPlayBoard(show, ROW, COL);
	//布置地雷
	printf("\n");
	Setmine(mine, ROW, COL);
	//DisPlayBoard(mine, ROW, COL);
	//排查雷 
	Findmine(mine, show, ROW, COL);

}

int main()
{
	int n = 0;
	srand((unsigned int)time(NULL));
	printf("请选择:\n");
	do
	{
		menu();//菜单打印
		scanf_s("%d", &n);
		switch (n)
		{
		case 1:
			system("cls");
			printf("游戏开始》》》》\n");
			game();//游戏函数
			break;
		case 0:
			printf("退出游戏《《《《\n");
			break;
		default:
			printf("erorr\n");
			break;
		}
		printf("请选择是否继续游玩:\n");
	} while (n);
	return 0;
}



  • 14
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值