生成并求解无边界的迷宫吗?

时间:2018-08-03 19:38:37

标签: algorithm maze

好吧,假设您尝试穿越迷宫的“北边”,您将回到同一迷宫,但位于“南边”。金达喜欢在迷宫中穿行。

是否可以生成和解决这样的迷宫?我尚未找到有关此主题的文档...

1 个答案:

答案 0 :(得分:5)

关键是要重新构想从像素网格到图形的迷宫。然后,您可以连接图形,使其形成环形。

威尔逊算法可能特别容易理解。它还可以生成统一生成树,这是一个均匀的生成树,它是从空间的所有可能生成树的集合中统一绘制的生成树。

执行以下操作:

  1. 随机选择任何顶点并将其添加到UST。
  2. 选择UST中尚未存在的任何顶点,然后执行loop-erasing random walk,直到遇到UST中尚未存在的顶点。您可以修改此随机游走,以便在遇到边缘时可以环绕。
  3. 将在随机漫游中触摸的顶点和边添加到UST。
  4. 重复2和3,直到将所有顶点添加到UST。

可以进行讨论herehere

这是算法外观的草稿(剩余的粉红色单元格是绘图例程中某些东西的伪像,但不影响结果)。

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