假设你想在N x M的网格上创建一个简单的迷宫,有一条路径通过,以及很多死路,但同时看起来“对”(即像是由手工制作而成,没有太多小巧的死路等)。是否已知有方法可以实现这个目标?
想要了解更多信息,请访问 GitHub 上的mazelib,这是一个实现了所有标准迷宫生成/求解算法的 Python 库。
从 http://www.astrolog.org/labyrnth/algrithm.htm
递归回溯:这与下面描述的递归回溯解决方法有些相关,并需要堆栈大小为迷宫的大小。在雕刻时,尽可能贪婪,并始终在当前单元格旁边有未制作的部分时进行雕刻。每次移动到新单元格时,将前一个单元格推入堆栈。如果当前位置旁边没有未制作的单元格,则弹出堆栈到上一个位置。当您弹出堆栈中的所有内容时,迷宫完成。该算法会产生河流因子尽可能高但死胡同较少且更长的迷宫,通常具有非常长而曲折的解决方案。它运行得相当快,尽管Prim算法稍微快一些。递归回溯不适用于添加墙壁,因为这样做往往会导致解决路径遵循外边缘,其中整个迷宫的内部都通过单个茎连接到边界。
它们仅生成10%的死胡同
一个相当简单的解决方案是为图的边分配随机权重并运用Kruskal算法来寻找最小生成树。
迄今为止关于迷宫生成算法的最佳讨论:http://www.jamisbuck.org/presentations/rubyconf2011/index.html(几天前在HN上)。
我最喜欢使用Kruskal算法,但在随机选择一条边进行删除时,根据它所连接的边的类型来加权选择。
通过为不同的边类型变化权重,您可以生成带有许多不同特征或“个性”的迷宫。请参见我的示例:
奇怪的是,通过略微改变“规范”规则,并从随机配置开始,康威生命游戏似乎能够生成相当不错的迷宫!
(我不记得确切的规则,但这是一种非常简单的修改,倾向于“密集化”单元格的人口...)
递归回溯是最容易实现的算法。
以下是Java实现:
这里的Cell表示2D网格中的一个单元格,cells是Cell对象的2D数组。Cell具有布尔变量top、bottom、left和right,用于指示单元格是否在这些边上有墙,布尔变量visited用于检查我们是否已经遍历它,并且还有两个整数变量row和col,用于指示其在网格中的位置。
Cell current = cells[0][0] , next;
current.visited=true;
do{
next = getNeighbour(current);
if(next!=null){
removeWall(current , next);
st.push(current);
current = next;
current.visited = true;
}
else {
current = st.pop();
}
}
while (!st.empty());
private Cell getNeighbour(Cell cell){
ArrayList<Cell> ara = new ArrayList<>();
if(cell.col>0 && !cells[cell.col-1][cell.row].visited)
ara.add(cells[cell.col-1][cell.row]);
if(cell.row>0 && !cells[cell.col][cell.row-1].visited)
ara.add(cells[cell.col][cell.row-1]);
if(cell.col<col-1 && !cells[cell.col+1][cell.row].visited)
ara.add(cells[cell.col+1][cell.row]);
if(cell.row<row-1 && !cells[cell.col][cell.row+1].visited)
ara.add(cells[cell.col][cell.row+1]);
if(ara.size()>0){
return ara.get(new Random().nextInt(ara.size()));
}else{
return null;
}
}
private void removeWall(Cell curr , Cell nxt){
if((curr.col == nxt.col) && (curr.row == nxt.row+1)){/// top
curr.top=nxt.botttom=false;
}
if(curr.col==nxt.col && curr.row == nxt.row-1){///bottom
curr.botttom = nxt.top = false;
}
if(curr.col==nxt.col-1 && curr.row==nxt.row ){///right
curr.right = nxt.left = false;
}
if(curr.col == nxt.col+1 && curr.row == nxt.row){///left
curr.left = nxt.right = false;
}
}
以下是以伪代码形式书写的DFS算法:
创建一个CellStack(LIFO)来保存单元格位置列表
设置TotalCells = 网格中的单元格数量
随机选择一个单元格并将其命名为CurrentCell
设置VisitedCells = 1
while VisitedCells < TotalCells
找到所有保持完整的墙壁的CurrentCell邻居
如果找到一个或多个
随机选择一个
拆除它与CurrentCell之间的墙壁
将CurrentCell位置推入CellStack
使新单元格成为CurrentCell
将VisitedCells加1
否则
从CellStack中弹出最近的单元格条目
将其设置为CurrentCell
endIf
endWhile