您的位置:首页 > 其它

深度优先搜索生成迷宫

2013-03-29 17:09 726 查看
前些天逛书店,发现一本叫做<<opengl超级宝典第5版>>的书,买了下来回家慢慢看.感觉这本书比<<opengl编程指南>>更适合入门学习,因为里面有许多例子.后来才知道,这本书就是所谓"opengl蓝宝书".看了这本书,许多看红宝书时不明白的地方突然就明白了,可能是因为这本书翻译的质量更好,或者说这本书解释得更具体.

无意间发现了一个博客,http://blog.csdn.net/jiangcaiyang123.该博客主人还是个学生,技术却不错.代码写得清晰,注重复用,在学校写的代码就用上了泛型和跨平台,对opengl 和 dx的理解也比我强..跟他比起来,我很惭愧.上班这些年,总有许多生活上的琐事,让自己学习时间并不充足.即使在学生时代时间充足的时候,自己的水平也比人家差的太远.我不禁感慨时间飞逝,惋惜以往浪费的时间.

那个博客里有个opengl 3d的迷宫,我最近也在学习opengl,打算不参考其他人代码的前提下,自己也搞一个.

今天就弄个迷宫自动生成的控制台程序,过些天想办法移植到 opengl上.

关于迷宫自动生成参考了一些资料,主要是维基百科

http://en.wikipedia.org/wiki/Maze_generation_algorithm

里面介绍了几种常用的算法,我这里的代码暂时只参考了深度优先搜索算法.算法本身里面描述的比较清楚 ,不再赘述.在过程

中遇到了一个转不过弯的地方,记录如下.

迷宫的存储结构是一个2维数组,在采用深度优先搜索的时候,会遍历这个数组的每个元素.

然而,最终迷宫存储的数组,并不是这个数组,而是一个"扩展数组".

即,如果把原数组看作是一个方格,该方格仅代表一个数组元素的话,扩展数组则把这一个方格扩展为9个元素,分别是

左上,正上,右上,左侧,正中,右侧,左下,正下,右下.

这样才能把深度优先搜索的矩阵,与最终的迷宫矩阵建立联系.

就像下面代码 Maze类中的 MazePoint** m_arrMaze 是最终迷宫的矩阵,

MazePoint** m_arrRaw 是用来做遍历生成路径的辅助矩阵。

现在不太方便具体绘图说明,上面一段话表达不够清晰,但是却是生成迷宫时困扰我时间最长的问题.

直接上代码和截图吧.

Maze.h

#ifndef	_MAZE_H_
#define	_MAZE_H_

#include <vector>
#define	DEBUG_MAZE_GENERATE
struct MazePoint
{
	int _x,_y;
	bool _bIsVisisted;

	MazePoint()
	{		
		_x = _y = 0;
		_bIsVisisted = false;
		_state = E_State_Wall;
	}

	MazePoint(int x,int y)
	{
		_x = x;
		_y = y;
		_bIsVisisted = false;
	}
	
	void setCoord(int x,int y)
	{
		_x = x;
		_y = y;
	}

	void operator = (const MazePoint& pt)
	{
		_x = pt._x;
		_y = pt._y;
		_bIsVisisted = pt._bIsVisisted;
	}

	bool IsVisited()
	{
		return _bIsVisisted;
	}

	void setVisited(bool visited = true)
	{
		_bIsVisisted = visited;
	}

	enum
	{
		E_State_Path,
		E_State_Wall,
		E_State_Entry,
		E_State_Dest,
		E_State_Max,
	};
	int _state;
};

class Maze
{
protected:
	int		m_nCol,m_nRow,m_nRawCol,m_nRawRow;

	//	generate helper
	MazePoint**	m_arrMaze;
	MazePoint** m_arrRaw;
	MazePoint m_ptStart;
	MazePoint m_ptCur;
	std::vector<MazePoint> m_ptStack;
public:
	Maze();
	virtual ~Maze();

	void initMazeArray(int rownum,int colnum);
	void printMazeArray();
	
	//	generate helper
	void autoGenerate();
	bool isThereUnvisited();
	bool isThePointInBoundary(int x,int y);
	void reset();

	enum
	{
		E_Dir_Up,
		E_Dir_Down,
		E_Dir_Left,
		E_Dir_Right,
		E_Dir_Max,
	};
	double random(double start,double end);
	bool getNeighbor(int &x,int &y);
};
#endif	//_MAZE_H_


Maze.cpp

#include "Maze.h"
#include <stdio.h>
#include <time.h>
#include <stdlib.h>

Maze::Maze() :
	m_nCol(0),
	m_nRow(0)
{
	m_arrRaw = NULL;
	m_arrMaze = NULL;
	
	srand((int)time(0));
	rand();
}

Maze::~Maze()
{
	reset();
}

void Maze::initMazeArray(int rownum,int colnum)
{
	//	raw data
	m_nRawRow = rownum;
	m_nRawCol = colnum;
	m_arrRaw = new MazePoint*[m_nRawRow];
	for(int i=0;i<m_nRawRow;i++)
	{
		m_arrRaw[i] = new MazePoint[m_nRawCol]();
		for(int j=0;j<m_nRawCol;j++)
		{
			m_arrRaw[i][j].setCoord(j,i);
		}
	}
	
	//	generated data
	m_nRow = 2 * rownum + 1;
	m_nCol = 2 * colnum + 1;
	m_arrMaze = new MazePoint*[m_nRow];
	for(int i=0;i<m_nRow;i++)
	{
		m_arrMaze[i] = new MazePoint[m_nCol]();
		for(int j=0;j<m_nCol;j++)
		{
			m_arrMaze[i][j].setCoord(j,i);
		}
	}
}

void Maze::printMazeArray()
{
	printf("Maze Map:\n");
	for(int i = 0; i < m_nRow; i++ )
	{
		for(int j = 0 ;j < m_nCol; j++ )
		{
			printf("%d",m_arrMaze[i][j]._state);
		}
		printf("\n");
	}
#ifdef	DEBUG_MAZE_GENERATE
	printf("Raw data:\n");
	for(int i = 0; i < m_nRawRow; i++ )
	{
		for(int j = 0 ;j < m_nRawCol; j++ )
		{
			printf("%d",m_arrRaw[i][j]._state);
		}
		printf("\n");
	}	
#endif
}

void Maze::autoGenerate()
{
	int curx,cury;
	int nextx,nexty;
	curx = cury = 0;

	m_arrRaw[cury][curx].setVisited(true);
	m_ptStack.push_back(m_arrRaw[cury][curx]);
	while(isThereUnvisited())
	{
		nextx = curx;
		nexty = cury;

		if(getNeighbor(nextx,nexty))
		{
#ifdef	DEBUG_MAZE_GENERATE
			printf("nextx: %d,nexty: %d\n",nextx,nexty);
#endif	
			m_arrRaw[nexty][nextx].setVisited();
			m_ptStack.push_back(m_arrRaw[nexty][nextx]);

			//	modfiy arrMaze[][] ,mark it passable between (curx,cury) => (nextx,nexty)
			m_arrMaze[2*cury+1][2*curx+1]._state = MazePoint::E_State_Path;
			m_arrMaze[2*nexty+1][2*nextx+1]._state = MazePoint::E_State_Path;
			m_arrMaze[(2*cury+1 + 2*nexty+1)/2][(2*curx+1 + 2*nextx+1)/2]._state = MazePoint::E_State_Path;

			curx = nextx;
			cury = nexty;
		}
		else if(m_ptStack.size() > 1)
		{
			m_ptStack.pop_back();
			curx = m_ptStack[m_ptStack.size()-1]._x;
			cury = m_ptStack[m_ptStack.size()-1]._y;
		}
		else if(m_ptStack.size() == 1)
		{
			curx = m_ptStack[m_ptStack.size()-1]._x;
			cury = m_ptStack[m_ptStack.size()-1]._y;
			m_ptStack.pop_back();
		}
		else
		{
			//	is it possible ?
			printf("Stack is NULL\n");
		}
	}
}

bool Maze::isThereUnvisited()
{
	for(int i = 0; i < m_nRawRow; i++ )
	{
		for(int j = 0 ;j < m_nRawCol; j++ )
		{
			if(!m_arrRaw[i][j].IsVisited())
				return true;
		}
	}
	return false;
}

bool Maze::isThePointInBoundary(int x,int y)
{
	return x >= 0 && x < m_nRawCol &&  y >=0 && y < m_nRawRow;
}

double Maze::random(double start,double end)
{
	return start+(end-start)*rand()/(RAND_MAX + 1.0);
}

bool Maze::getNeighbor(int &x,int &y)
{
	int neighborX[E_Dir_Max];
	int neighborY[E_Dir_Max];
	bool isValid[E_Dir_Max];

	neighborX[E_Dir_Right] = x + 1;
	neighborY[E_Dir_Right] = y;
	isValid[E_Dir_Right] = (isThePointInBoundary(neighborX[E_Dir_Right],neighborY[E_Dir_Right])
								&& !m_arrRaw[neighborY[E_Dir_Right]][neighborX[E_Dir_Right]].IsVisited());
	neighborX[E_Dir_Left] = x - 1;
	neighborY[E_Dir_Left] = y;
	isValid[E_Dir_Left] = (isThePointInBoundary(neighborX[E_Dir_Left],neighborY[E_Dir_Left])
								&& !m_arrRaw[neighborY[E_Dir_Left]][neighborX[E_Dir_Left]].IsVisited());
	neighborX[E_Dir_Down] = x;
	neighborY[E_Dir_Down] = y + 1;
	isValid[E_Dir_Down] = (isThePointInBoundary(neighborX[E_Dir_Down],neighborY[E_Dir_Down])
								&& !m_arrRaw[neighborY[E_Dir_Down]][neighborX[E_Dir_Down]].IsVisited());
	neighborX[E_Dir_Up] = x;
	neighborY[E_Dir_Up] = y - 1;
	isValid[E_Dir_Up] = (isThePointInBoundary(neighborX[E_Dir_Up],neighborY[E_Dir_Up])
								&& !m_arrRaw[neighborY[E_Dir_Up]][neighborX[E_Dir_Up]].IsVisited());
	
	//	there's no valid direction 
	bool isThereValidDir = false;
	for(int i=0;i<E_Dir_Max;i++)
	{
		isThereValidDir = isThereValidDir || isValid[i];
	}
	if(!isThereValidDir)
	{
		return false;
	}

	//	there's valid direction ,get random one
	int randnum = random(0,4);
	int finalDir = 0;	
	for(int i=0;i<E_Dir_Max;i++)
	{
		if(isValid[i])
		{
			finalDir = i;
			break;
		}
	}

	while(randnum)
	{
		randnum--;
		finalDir++;
		finalDir = finalDir == E_Dir_Max ? 0 : finalDir;

		while(!isValid[finalDir])
		{
			finalDir++;
			finalDir = finalDir == E_Dir_Max ? 0 : finalDir;
		}
	}
	x = neighborX[finalDir];
	y = neighborY[finalDir];
	return true;
	
}

void Maze::reset()
{
	//	delete generated data
	if( m_arrMaze != NULL)
	{
		for(int i=0;i<m_nRow;i++)
		{
			delete[] (m_arrMaze[i]);
			m_arrMaze[i] = NULL;
		}
		delete[] m_arrMaze;
		m_arrMaze = NULL;
	}
	
	//	delete raw data
	if( m_arrMaze != NULL)
	{
		for(int i=0;i<m_nRawRow;i++)
		{
			delete[] (m_arrRaw[i]);
			m_arrRaw[i] = NULL;
		}
		delete[] m_arrRaw;
		m_arrRaw = NULL;
	}
}


main.cpp

#include <stdio.h>
#include "Maze.h"

Maze*	pMaze = new Maze();

int main(int argc,char** argv)
{
	pMaze->initMazeArray(20,20);
	pMaze->printMazeArray();
	pMaze->autoGenerate();
	pMaze->printMazeArray();

	pMaze->reset();
	pMaze->initMazeArray(3,8);
	pMaze->autoGenerate();
	pMaze->printMazeArray();

	if(pMaze)
	{
		delete pMaze;
	}
	return 0;
}



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: