扫雷(可展开)

如果是对展开部分不知道怎么写,我个人总结就是一句话“可以让计算机多判断,但是不能让他多算”,添加一个判断部分需不要要计算,就可以很好的避免栈溢出

下面我从最开始开始说起(只是一个计算机新生,欢迎大佬指点,也欢迎提问)

目录

 主函数部分(源文件)

1.各种菜单的打印

 2.初始化棋盘

3.打印棋盘

4.生成地雷

5.数雷,并赋值给trueboard(与展开有关)

6.排查雷区

7.雷区展开

8.判断输赢


 头文件部分

简单对游戏做了一个提升,添加了可自行选择棋盘的大小,以及游戏时可以选择如何排查,存疑,确定操作

#pragma once
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include<windows.h>
#define hang  11
#define lie   11
#define mines 10

//是否进入游戏
void menu();

//游戏难度选择菜单
void menu2();

//游戏操作选择菜单
void menu3();

//初始化棋盘
void star(char arr[100][100], int g, int h);

//打印棋盘
void prin(char arr[100][100],int g,int h);

//生成地雷
void mkmine(char arr[100][100], int g, int h, int f);

//数雷并且赋值给mine棋盘
void countmine(char arr[100][100],int g, int h);

//初步排查是否炸雷
int  paic(char arr1[100][100], char arr2[100][100], int x, int y, int g, int h);

//进一步实现非雷区展开(展开一片全是0的格子)
void paic2(char arr1[100][100], char arr2[100][100], int x, int y, int g, int h);

//判断输赢
int  pand(char arr[100][100], char arr2[100][100], int g, int h,int f);

 主函数部分(源文件)

其实写完之后才发现hang,lie,mines没必要提前宏替换

#define _CRT_SECURE_NO_WARNINGS
#include"game.h"

int main()
{
	char mine[100][100], show[100][100];
	int key = 0, x, y, nandu, i, j, o, v, b;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		scanf("%d", &key);
		if (key == 1)
		{
			menu2();
			scanf("%d", &nandu);
			switch (nandu)
			{
			case 1:
				i = hang, j = lie, o = mines;
				break;
			case 2:
				i = hang + 7, j = lie + 7, o = mines + 30;
				break;
			case 3:
				i = hang + 7, j = lie + 31, o = mines + 89;
				break;
			case 4:
				printf("请输入行数:");
				scanf("%d", &i);
				i += 2;
				printf("请输入列数:");
				scanf("%d", &j);
				j += 2;
				printf("请输入雷数:");
				scanf("%d", &o);
				break;
			}
			star(mine, i, j);
			star(show, i, j);
			mkmine(mine, i, j, o);
			countmine(mine, i, j);
			while (1)
			{
				system("cls");
				prin(show, i, j);
				loop:menu3();
				loop1:scanf("%d", &b);
				if (b == 2)
				{
					printf("请输入确定的雷区-> ");
					scanf("%d%d", &x, &y);
					show[x][y] = '@';
				}
				else if (b == 3)
				{
					printf("请输入存疑的雷区-> ");
					scanf("%d%d", &x, &y);
					show[x][y] = '?';
				}
				else if (b == 1)
				{
					printf("请输入要排查的雷区-> ");
					scanf("%d%d", &x, &y);
					v = (paic(mine, show, x, y, i, j));
					if (v == 1)
					{
						if (pand(show, mine, i, j, o))
						{
							printf("You win!\n");
							break;
						}
						else;
					}
					else if (v == 0)
						break;
					else if (v == 2)
						goto loop;
				}
				else
				{
					printf("worng input,again?\n");
					goto loop1;
				}
			}
		}
		else if (key != 0)
		{
			system("cls");
			printf("wrong input,again?\n\n\n");
		}
	} while (key);
	return 0;
}

下面是函数的定义,为了方便看不同的函数,我会拆开一部分,同属于源文件

1.菜单的打印

#define _CRT_SECURE_NO_WARNINGS
#include"game.h"

void menu()
{
	printf("#################################\n");
	printf("###########0.结束游戏############\n");
	printf("###########1.开始游戏############\n");
	printf("#################################\n");
}

void menu2()
{
	printf("#################################\n");
	printf("######## 请选择游戏难度 #########\n");
	printf("########## 1.简单模式 ###########\n");
	printf("########## 2.普通模式 ###########\n");
	printf("########## 3.困难模式 ###########\n");
	printf("########## 4.自定义模式 #########\n");
	printf("#################################\n");
}

void menu3()
{
	printf("#################################\n");
	printf("###### 请选择您要进行的操作 #####\n");
	printf("########## 1.排查雷区 ###########\n");
	printf("########## 2.雷区确定 ###########\n");
	printf("########## 3.雷区存疑 ###########\n");
	printf("#################################\n");
}

 2.初始化棋盘

void star(char arr[100][100], int g, int h)
//初始化的时候比真实棋盘大一圈,方便后面的paic函数,避免出现越界现象
{
	int m, n;
	for (m = 0; m < g; m++)
		for (n = 0; n < h; n++)
			arr[m][n] = ' ';
}

3.打印棋盘

void prin(char arr[100][100], int g, int h)
//打印的时候只需要打印真实棋盘即可
{
	int m, n;
	for (m = 0; m <= h - 2; m++)
		printf("%3d", m);//第一行的坐标,方便玩家找扫雷位置
	printf("\n");
	for (m = 1; m <= g - 2; m++)
	{
		printf("%3d", m);//行号
		for (n = 1; n <= h - 2; n++)
		{
			printf("%3c", arr[m][n]);
		}
		printf("\n");
		//%3d ,%3c都是为了美观
	}
}

4.生成地雷

void mkmine(char arr[100][100], int g, int h,int f)
//种子一定要放在外面,主函数里面,如果放在函数里面生成会特别慢(暂不清楚原因)
{
	int i, k=0, x, y;
	for (i = 0; k < f; i++)
	{
		x = rand() % (g - 2) + 1;
		y = rand() % (h - 2) + 1;//生成的数范围从 1 开始
		if (arr[x][y] == ' ')
		{
			arr[x][y] = '*';//布置雷‘*’
			k++;//计数雷的个数
		}
	}
}

5.数雷,并赋值给trueboard

这里是为了后面的展开部分,先计算好每一个格子周围的雷应该是多少,赋值给trueboar,玩家排查的时候只需要从trueboard中找对应点位就好了,避免了每次排查时需要计算周围的雷数,也避免了计算导致的栈溢出

void countmine(char arr[100][100], int g, int h)
{
	int m, n, m1, n1, k;
	for ( m = 1; m <= g -2; m++)
		for (n = 1; n <= h - 2; n++)//只用给真实棋盘范围赋值
		{
			k = 0;
			if (arr[m][n] == '*')
				continue;
			else//如果arr[m][n]不是雷
			{
				for (m1 = m - 1; m1 <= m + 1; m1++)
					for (n1 = n - 1; n1 <= n + 1; n1++)
						if (arr[m1][n1] == '*')
							k++;//统计九宫格内‘*’的个数
				arr[m][n] = k+48;//arr是字符数组,所以记录字符‘1’-‘9’,k+48即将数字1-9变为字符‘1’-‘9’对应的ascll码值
			}
		}
}

6.排查雷区

如果扫到雷,就把雷的位置给到showboard上面,再打印showboard就可以知道所有雷的位置,死的明明白白,如果没有排到雷,就调用paic2函数,这部分主要就是展开

int  paic(char arr1[100][100], char arr2[100][100], int x, int y, int g, int h)
{
	int m, n;
	if (arr1[x][y] == '*')
	{
		system("cls");//清空屏幕,为了美观
		for (m = 0; m < g; m++)
			for (n = 0; n < h; n++)
				if(arr1[m][n]=='*')
					arr2[m][n] = arr1[m][n];//将余下的雷的位置赋给show棋盘,再进行展示
		prin(arr2, g, h);
		printf("You lose!\n\n\n");
		return 0;//输了之后回到主函数就重新开始,所以返回0
	}
	else if (x > g - 2 || x < 1 || y > h - 2 || y < 1)//错误地址(超出棋盘限制)
	{
		printf("wrong location,again?\n");
		return 2;
	}
	else//排查正确
	{
		paic2(arr1,arr2,x,y, g, h);
		return 1;
	}
}

7.展开

只要排查点对应0时才需要判断是否展开,是则必定九宫格内没有雷,展开九宫格,然后对九宫格进行一个判断,如果存在0并且,在showboard上九宫格内存在没有排查的,就进入递归,这样的话,最多递归次数就是棋盘的长,到达边界后就会返回上一层paic2,不会出现指数增长式的递归导致栈溢出

void paic2(char arr1[100][100], char arr2[100][100], int x, int y, int g, int h)
{
	int m1, n1, m, n;
	arr2[x][y] = arr1[x][y];//先赋值,判断是不是0==>有没有展开的必要
	if (arr2[x][y] == '0')
	{
		//展开九宫格内
		for (m1 = x - 1; m1 <= x + 1; m1++)
			for (n1 = y - 1; n1 <= y + 1; n1++)
			{
					arr2[m1][n1] = arr1[m1][n1];
			}
		for (m1 = x - 1; m1 <= x + 1; m1++)
			for (n1 = y - 1; n1 <= y + 1; n1++)
				if (arr2[m1][n1] == '0' && m1 >= 1 && n1 >= 1 && m1 <= g - 2 && n1 <= h - 2)//判断九宫格内是否存在0==>是否可以进一步展开
				{
					for (m = m1 - 1; m <= m1 + 1; m++)
						for (n = n1 - 1; n <= n1 + 1; n++)
							if (arr2[m][n] == ' ' && m >= 1 && n >= 1 && m <= g - 2 && n <= h - 2)
							//进一步筛选如果该 0 九宫格内存在空格(未排查的地方)则展开,若不存在则说明周围已经排查过了,没必要展开,避免了栈溢出
								paic2(arr1, arr2, m, n, g, h);//递归实现展开
				}
	}
}

8.判断输赢

如果showboard上面存在没有被排查的地方,也就是对应字符小于0或大于9且不为‘@’,并且trueboard对应点位不是雷;或者已经排查,但是排查错误,也就是对应字符‘@’,但trueboard对应字符不是雷,就说明没有排查完,均会返回0,表示需要继续

int  pand(char arr1[100][100], char arr2[100][100], int g, int h,int f)
{
	int m, n, count=0,count2=0;
	for (m = 1; m <= g - 2; m++)
		for (n = 1; n <= h - 2; n++)
			if (((arr1[m][n] == ' ' || arr1[m][n] > '?') && arr2[m][n] != '*') || (arr1[m][n] == '@' && arr2[m][n] != '*'))//非雷区必须排查
				return 0;
	return 1;
}

上一篇:mongoDB内嵌文档返回匹配的数组元素


下一篇:Java数组