生成和解决一个没有边界的迷宫?

Generating and solving a maze with no boundaries?

好吧,假设您尝试穿过迷宫的 "northern edge",您会回到同一个迷宫,但在 "southern edge"。有点像在迷宫中穿行。

是否可以生成并解决这样的迷宫?我还没有找到关于这个主题的文档...

关键是将迷宫从像素网格重新概念化为图形。然后,您可以连接图表,使其形成环形。

威尔逊算法可能特别容易理解。它也很好,因为它生成一个统一生成树,它是从 space.

的所有可能生成树的集合中统一绘制的生成树。

执行以下操作:

  1. 随机选择任意顶点并将其添加到 UST。
  2. Select 任何不在 UST 中的顶点并执行 loop-erasing random walk 直到遇到 UST 中的顶点。您可以修改此随机游走,使其在遇到边缘时环绕。
  3. 将随机游走中接触到的顶点和边添加到UST。
  4. 重复 2 和 3,直到所有顶点都已添加到 UST。

可以进行讨论here and here

这是算法的草稿(其余粉红色单元格是绘图例程中的人工制品,但不影响结果)。

<!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>