迷宫游戏算法

      前两天在慕课网学习c++时,老师留下了一道作业题,关于迷宫算法的:自己设置一个迷宫,里面有起点和终点,然后程序要帮助闯关者找到逃生的路径【起点——出口】。当时老师给出的提示是使用“左手扶墙”或“右手扶墙”策略,大意就是,比如“左手扶墙”,从起点开始走时,就始终保持着左手扶墙的状态,这样一直走,最终你会找到迷宫的出口。嗯,这种方法确实挺实用的,但恐怕不适用于一些特殊的路形,比如“日”字型迷宫,如果我们把起点放在“日”字中间那一横的某一点上,那么无论闯关者使用左手扶墙还是右手扶墙策略,最终他都将陷入一个死循环中。。。

       考虑到上面的这种情况,我觉得有必要新设计一个程序算法。而这种算法,我暂且称之为“灌水法”,顾名思义,就是在起点处一直灌水,最后水一定会从出口流出去。而活水所走的路径,就是程序所要寻找的路径。

      程序的核心算法很简单,首先设计一个迷宫类,类里面开辟两个数组,一个是地图数组,一个是路径数组,除此之外,还需要两个变量,一个代表初始位置,一个代表出口位置。在地图数组中,每个元素用1和0来表示有路和无路;而在路径数组中,每个节点元素只能在这四个数值中选择一个:1、-1、16和-16,这是因为我用的是16*16的一维矩阵,而上面的四哥元素值可以分别对应矩阵中的上下左右。

      程序运行后,首先调用maze类的初始化函数来设置迷宫,至于如何初始化迷宫程序员可以随意设置。重点在下面的两个函数,首先说put_water,它每次先寻找一个单向路径,如果找到,就记录下这个单向路径的走法,直到遇到一个岔路口为止,在此之后它会递归调用自身,依次进入每个岔路口并摸清它们的情况,因为函数调用栈开销的特性,这样当程序从某个死路回溯时,就会重新刷新原先记录的record数组。。。接下来是显示路径函数showpath,这个就要简单多了,无非是先把被破坏的迷宫复位,然后按照record数组记录的走法显示出逃生路径。其中,“森”字表示障碍物,“起”表示起点,“终”字表示出口位置,“人”字代表逃生所走的路径,本来老师要求的是动态显示出逃生过程,但为了本文的简洁,我就不写这些冗余的代码了,以下源代码


此为类声明:

class maze
{
public:
	maze(int m[], int r[], int , int );
	int put_water(int, int);
	void showpath(int *);
protected:
	int map[256], record[256], Start, Exit;
};

此为类定义:

#include <iostream>
#include "maze.h"
using namespace std;

maze::maze(int m[], int r[], int s, int e)
{
	for (int i = 0; i < 256; i++)
	{
		map[i] = m[i];

		if (i % 16 == 0)  printf("\n");

		if (m[i])  printf("  ");
		else printf("森");
	}
	for (int i = 0; i < 256; i++)  record[i] = r[i];
	Start = s;
	Exit = e;
}

int maze::put_water(int p, int count)// p表示当前位置,count配合record记录以寻找的路径
{
	int new_p;

	while (map[p - 1] + map[p - 16] + map[p + 1] + map[p + 16] == 1) //如果当前位置不是岔路口,则一直记录直至到岔路口或出口为止
	{
		if (p == Exit)
        {
            record[count] = 0;
            return 1;
        }
		map[p] = 0; //配合这个while循环,就这样一直把水灌满这个单向路
		new_p = (p - 1)*map[p - 1] + (p - 16)*map[p - 16] + (p + 1)*map[p + 1] + (p + 16)*map[p + 16]; //寻找单向路的下一个节点	
		record[count] = new_p - p; //记录下来这个单向路的走法
		count++;
		p = new_p;
	}
	map[p] = 0;
	if (map[p - 1] + map[p - 16] + map[p + 1] + map[p + 16] > 1) //这里表示这个路口属于岔路口,至少有两个选择,最多有三个选择
	{
		if (1 == map[p - 1]) //先往上寻找,表达式为真表示上面有路
		{
			record[count] = -1;
			if (1 == put_water(p - 1, count + 1)) //既然上面有路,就递归调用自身,看看上面的路情况如何
				return 1;
		}
		if (1 == map[p - 16]) //再往左寻找,表达式为真表示左方有路
		{
			record[count] = -16;
			if (1 == put_water(p - 16, count + 1)) //同上
				return 1;
		}
		if (1 == map[p + 1]) //再往右寻找,表达式为真表示右方有路
		{
			record[count] = 1;
			if (1 == put_water(p + 1, count + 1))
				return 1;
		}
		if (1 == map[p + 16]) //最后往下寻找,表达式为真表示下面有路
		{
			record[count] = 16;
			if (1 == put_water(p + 16, count + 1))
				return 1;
		}
	}

	return 0;
}

void maze::showpath(int m[])
{
	printf("\n\n我们逃跑的路径是:\n");
	for (int i = 0; i < 256; i++) 
	{
		map[i] = m[i];
	}
	map[10] = -9;
	for (int i = 0; record[i]; i++)
	{
		Start += record[i];
		map[Start]++;
	}
	for (int i = 0; i < 256; i++)
	{

		if (i % 16 == 0)  printf("\n");

		switch (map[i])
		{
		case 0:
			printf("森");
			break;
		case 1:
			printf("  ");
			break;
		case 2:
			printf("人");
			break;
		case 9:
			printf("起");
			break;
		case -9:
			printf("终");
		}
	}
}

最后这个是主函数:

#include <iostream>
#include "maze.h"
int MAPARR[256] = {
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
	0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
	0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0,
	0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0,
	0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0,
	0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0,
	0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
	0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0,
	0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0,
	0, 1, 1, 1, 0, 9, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0,
	0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0,
	0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
	0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0,
	0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0,
	0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, RECORD[256];
int main()
{
	maze m(MAPARR,RECORD,149,26);

	if (0 == m.put_water(149, 0))
	{
		printf("迷宫设计有误!");
		return 0;
	}
	else  m.showpath(MAPARR);

	return 0;
}


运行效果如图:









  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值