前些天逛书店,发现一本叫做<<opengl超级宝典第5版>>的书,买了下来回家慢慢看.感觉这本书比<<opengl编程指南>>更适合入门学习,因为里面有许多例子.后来才知道,这本书就是所谓"opengl蓝宝书".看了这本书,许多看红宝书时不明白的地方突然就明白了,可能是因为这本书翻译的质量更好,或者说这本书解释得更具体.
无意间发现了一个博客,http://blog.csdn.net/jiangcaiyang123.该博客主人还是个学生,技术却不错.代码写得清晰,注重复用,在学校写的代码就用上了泛型和跨平台,对opengl 和 dx的理解也比我强..跟他比起来,我很惭愧.上班这些年,总有许多生活上的琐事,让自己学习时间并不充足.即使在学生时代时间充足的时候,自己的水平也比人家差的太远.我不禁感慨时间飞逝,惋惜以往浪费的时间.
今天就弄个迷宫自动生成的控制台程序,过些天想办法移植到 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;
}