好吧,假设您尝试穿越迷宫的“北边”,您将回到同一迷宫,但位于“南边”。金达喜欢在迷宫中穿行。
是否可以生成和解决这样的迷宫?我尚未找到有关此主题的文档...
答案 0 :(得分:5)
关键是要重新构想从像素网格到图形的迷宫。然后,您可以连接图形,使其形成环形。
威尔逊算法可能特别容易理解。它还可以生成统一生成树,这是一个均匀的生成树,它是从空间的所有可能生成树的集合中统一绘制的生成树。
执行以下操作:
这是算法外观的草稿(剩余的粉红色单元格是绘图例程中某些东西的伪像,但不影响结果)。
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
background: #000;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var width = 200,
height = 200;
var N = 1 << 0,
S = 1 << 1,
W = 1 << 2,
E = 1 << 3;
var cellSize = 4,
cellSpacing = 4,
cellWidth = Math.floor((width - cellSpacing) / (cellSize + cellSpacing)),
cellHeight = Math.floor((height - cellSpacing) / (cellSize + cellSpacing)),
cells = new Array(cellWidth * cellHeight), // each cell’s edge bits
remaining = d3.range(cellWidth * cellHeight), // cell indexes to visit
previous = new Array(cellWidth * cellHeight), // current random walk path
i0, x0, y0; // end of current random walk
var canvas = d3.select("body").append("canvas")
.attr("width", width)
.attr("height", height);
var context = canvas.node().getContext("2d");
context.translate(
Math.round((width - cellWidth * cellSize - (cellWidth + 1) * cellSpacing) / 2),
Math.round((height - cellHeight * cellSize - (cellHeight + 1) * cellSpacing) / 2)
);
// Add the starting cell.
context.fillStyle = "white";
var start = remaining.pop();
cells[start] = 0;
fillCell(start);
// While there are remaining cells,
// add a loop-erased random walk to the maze.
context.fillStyle = "magenta";
d3.timer(function() {
for (var k = 0; k < 50; ++k) {
if (loopErasedRandomWalk()) {
return true;
}
}
});
function loopErasedRandomWalk() {
var i1;
// Pick a location that’s not yet in the maze (if any).
if (i0 == null) {
do if ((i0 = remaining.pop()) == null) return true;
while (cells[i0] >= 0);
previous[i0] = i0;
fillCell(i0);
x0 = i0 % cellWidth;
y0 = i0 / cellWidth | 0;
return;
}
// Perform a random walk starting at this location,
// by picking a legal random direction.
i1 = Math.random() * 4 | 0;
if (i1 === 0) {
if (y0 <= 0){
y0 = cellHeight-1;
i1 = i0 - cellWidth + cellWidth*cellHeight;
} else{
--y0;
i1 = i0 - cellWidth;
}
} else if (i1 === 1) {
if (y0 >= cellHeight - 1){
y0 = 0;
i1 = i0 + cellWidth - cellWidth*cellHeight;
} else {
++y0;
i1 = i0 + cellWidth;
}
} else if (i1 === 2) {
if (x0 <= 0){
x0 = cellWidth-1;
i1 = i0+cellWidth-1;
} else {
--x0;
i1 = i0 - 1;
}
} else {
if (x0 >= cellWidth - 1) {
x0 = 0;
i1 = i0-cellWidth+1;
} else {
++x0;
i1 = i0 + 1;
}
}
// If this new cell was visited previously during this walk,
// erase the loop, rewinding the path to its earlier state.
if (previous[i1] >= 0) eraseWalk(i0, i1);
// Otherwise, just add it to the walk.
else {
previous[i1] = i0;
fillCell(i1);
if (i1 === i0 - 1) fillEast(i1);
else if (i1 === i0 + 1) fillEast(i0);
else if (i1 === i0 - cellWidth) fillSouth(i1);
else fillSouth(i0);
}
// If this cell is part of the maze, we’re done walking.
if (cells[i1] >= 0) {
// Add the random walk to the maze by backtracking to the starting cell.
// Also erase this walk’s history to not interfere with subsequent walks.
context.save();
context.fillStyle = "#fff";
fillCell(i1);
while ((i0 = previous[i1]) !== i1) {
fillCell(i0);
if (i1 === i0 + 1) cells[i0] |= E, cells[i1] |= W, fillEast(i0);
else if (i1 === i0 - 1) cells[i0] |= W, cells[i1] |= E, fillEast(i1);
else if (i1 === i0 + cellWidth) cells[i0] |= S, cells[i1] |= N, fillSouth(i0);
else cells[i0] |= N, cells[i1] |= S, fillSouth(i1);
previous[i1] = NaN;
i1 = i0;
}
context.restore();
previous[i1] = NaN;
i0 = null;
} else {
i0 = i1;
}
}
function eraseWalk(i0, i2) {
var i1;
context.save();
context.globalCompositeOperation = "destination-out";
do {
i1 = previous[i0];
if (i1 === i0 - 1) fillEast(i1);
else if (i1 === i0 + 1) fillEast(i0);
else if (i1 === i0 - cellWidth) fillSouth(i1);
else fillSouth(i0);
fillCell(i0);
previous[i0] = NaN;
i0 = i1;
} while (i1 !== i2);
context.restore();
}
function fillCell(i) {
var x = i % cellWidth, y = i / cellWidth | 0;
context.fillRect(x * cellSize + (x + 1) * cellSpacing, y * cellSize + (y + 1) * cellSpacing, cellSize, cellSize);
}
function fillEast(i) {
var x = i % cellWidth, y = i / cellWidth | 0;
context.fillRect((x + 1) * (cellSize + cellSpacing), y * cellSize + (y + 1) * cellSpacing, cellSpacing, cellSize);
}
function fillSouth(i) {
var x = i % cellWidth, y = i / cellWidth | 0;
context.fillRect(x * cellSize + (x + 1) * cellSpacing, (y + 1) * (cellSize + cellSpacing), cellSize, cellSpacing);
}
d3.select(self.frameElement).style("height", height + "px");
</script>