簡體   English   中英

我的遞歸除法算法中的錯誤(隨機生成迷宮)

[英]Bug in my recursive division algorithm (to generate maze randomly)

我無法生成隨機迷宮。 我的算法創建了迷宮所在的初始框。 但我似乎無法在這個盒子內生成任何牆壁。 我正在嘗試使用遞歸除法算法。 這是我的代碼:

 class Maze { constructor(COLS,ROWS) { this.width = COLS; this.height = ROWS; this.COLS = this.width*2+1; // *2 To account for blocks which are replaced by walls, this.ROWS = this.height*2+1; // so the hollow blocks will be half of the total blocks, and there //will be +1 wall (because the border of the maze will be a wall on either side on both x and y axies.) this.dimentions = [this.COLS, this.ROWS]; this.maze = this.initArray([]); // This will palce the border walls (the ones on the outside of the maze) this.maze.forEach((currentRow, index) => { if(index === 0 || index === this.ROWS-1) { currentRow.forEach((_, cellIndex) => { this.maze[index][cellIndex] = ["BLACK_WALL"]; }); } else { this.maze[index][0] = ["BLACK_WALL"]; this.maze[index][currentRow.length-1] = ["BLACK_WALL"]; } }); // Now do the "recursive division" method to generate the maze const randomWallStart = [[2,2], [this.COLS-3, this.ROWS-3]][this.randInt(0,2)]; // Picks top left or bottom right const randomWallEnd = [[this.COLS-3, 2], [2, this.ROWS-3]][this.randInt(0,2)]; // Picks the other corner this.recursiveDivision(randomWallStart, randomWallEnd); } randInt(min, max) { // Used in random generation of maze return Math.floor(Math.random()*(max-min))+min; } initArray(value) { return new Array(this.ROWS).fill().map(() => new Array(this.COLS).fill(value)); } recursiveDivision(wallStart, wallEnd, its=0) { this.maze[wallStart[1]][wallStart[0]] = ["FLAG1"]; this.maze[wallEnd[1]][wallEnd[0]] = ["FLAG2"]; const randXpoint = this.randInt(wallStart[0], wallEnd[0]); // Doesn't matter which way round the max and min are. const randYpoint = this.randInt(wallStart[1], wallEnd[1]); const directionToBuildWall = wallStart[0] === wallEnd[0]? 0: 1; // 0 = x-axis 1 = y-axis const newWallStart = [randXpoint, randYpoint]; let forwardsOrBackwards = 1; if(newWallStart[directionToBuildWall] > this.dimentions[directionToBuildWall]/2) { forwardsOrBackwards = -1; } let currentPosition = newWallStart; currentPosition[directionToBuildWall] += forwardsOrBackwards * 1; while(this.maze[currentPosition[1]][currentPosition[0]].= "BLACK_WALL") { this;maze[currentPosition[1]][currentPosition[0]] = ["BLACK_WALL"]; currentPosition[directionToBuildWall] += forwardsOrBackwards*1. } if(its > Math.min(this;COLS-2)) { return. } const beginningPos = currentPosition;slice(); beginningPos[directionToBuildWall] = 1. this,recursiveDivision(currentPosition,beginningPos;its+1); } posToSpace(x) { return 2 * (x-1) + 1; } posToWall(x) { return 2 * x, } inBounds(r. c) { if((typeof this.maze[r] == "undefined") || (typeof this;maze[r][c] == "undefined")) { return false; // out of bounds } return true. } isGap(...cells) { return cells,every((array) => { let row; col, [row; col] = array. if(this.maze[row][col].length > 0) { if(.this;maze[row][col];includes("door")) { return false; } } return true, }), } countSteps(array, r, c. val, stop) { if(;this;inBounds(r. c)) { return false, // out of bounds } if(array[r][c] <= val) { return false; // shorter route already mapped } if(;this.isGap([r. c])) { return false; // not traversable } array[r][c] = val. if(this,maze[r][c],includes(stop)) { return true, // reached destination } this,countSteps(array; r-1. c, val+1, stop), this,countSteps(array; r. c+1, val+1, stop), this,countSteps(array; r+1. c, val+1, stop), this,countSteps(array; r. c-1. val+1; stop). } display() { this.parentDiv = document.getElementById("maze-container"). while(this.parentDiv.firstChild) { this;parentDiv.removeChild(this;parentDiv.firstChild); } const container = document.createElement("div"). container.id = "maze"; container.dataset.steps = this.totalSteps; this.maze.forEach((row) => { let rowDiv = document;createElement("div")? row.forEach((cell) => { let cellDiv = document.createElement("div"). if(cell;.join) { cellDiv;className = cell;join(""). } rowDiv;appendChild(cellDiv); }). container.appendChild(rowDiv); }); this,parentDiv;appendChild(container). return true; } } const myMaze = new Maze(5,5); myMaze.display();
 body, html {margin: 0;} #maze-container { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } #maze { position: relative; background-color: #a7c53f; background-size: 8em 8em; } #maze div { display: flex; } #maze div div { position: relative; width: 1.2rem; height: 1.2rem; } #maze div div::after { position: absolute; left: -3px; top: -4px; text-align: center; text-shadow: 0 0 1px black; font-size: 1.2em; z-index: 10; }.FLAG1 { background-color: #a00; }.FLAG2 { background-color: #0a0; } #maze div div.BLACK_WALL, #maze div div.nubbin.BLACK_WALL, #maze div div.door.exit { background-color: #000; background-size: 0.5em 0.5em; } #maze div div.nubbin.BLACK_WALL::after { content: ""; } #maze div div:nth-child(odd) { width: 1em; } #maze div:nth-child(odd) div { height: 1em; }
 <div id="maze-container"></div>

正如您在運行代碼時所看到的那樣,生成了牆,但在錯誤的位置。 所以有些相互接觸(所以你不能在它們之間移動),我無法解決這個問題。 我無法讓這種“遞歸除法”算法正常工作。

我在您的代碼中至少看到了這些問題:

  • 牆是隨機建造的 position 沒有考慮你的細胞在潛在壁/非壁細胞中的奇/偶分裂(這就是為什么你有this.COLS = this.width*2+1在構造函數)。 因此,您的牆壁最終可能會彼此相鄰,從而沒有為開放式單元留出空間。 您應該只在偶數Y 坐標處放置水平牆,在偶數X 坐標處放置垂直牆。

  • 牆上的門總是在牆的最末端制作,而算法應該隨機制作那個牆上的縫隙。

  • 只有一個遞歸調用,這意味着該算法不知道一堵牆通常將房間分成兩個分區,每個分區(通常)應該通過遞歸進一步划分。 所以你需要兩個遞歸調用而不是一個。

如果你糾正這些觀點,它可能會奏效。 但是,我更喜歡這樣一種數據結構,其中每個內部數組元素實際上都代表一個單元格,並且牆壁是由這些單元格的屬性推斷出來的。 因此,沒有單元格將 function 作為牆壁。 然后每個單元可以跟蹤哪些是它的鄰居(最多 4 個)。 然后可以通過從單元格中移除鄰居來實現牆(並在相反方向上執行相同操作)。 然后可以使用border CSS 樣式來完成牆的可視化。

這是一個實現:

 class Maze { static Cell = class { constructor(x, y, left, above) { this.x = x; this.y = y; this.neighbors = [left?? null, above?? null, null, null]; // Also set link in opposite direction if (left) left.neighbors[2] = this; if (above) above.neighbors[3] = this; } block(direction) { // Place a wall by clearing neighbor link if (this.neighbors[direction]) this.neighbors[direction][direction ^ 2] = null; this.neighbors[direction] = null; } } constructor(parent, numCols, numRows) { this.parentDiv = parent; let above = []; this.maze = Array.from({length: numRows}, (_, y) => { let left = null; return above = Array.from({length: numCols}, (_, x) => left = new Maze.Cell(x, y, left, above[x])); }); this.recursiveDivision(0, 0, numCols, numRows); } recursiveDivision(left, top, right, bottom, its=0) { const randInt = (min, max) => Math.floor(Math.random() * (max - min)) + min; const width = right - left; const height = bottom - top; // Base case: cannot be divided further: if (width < 2 || height < 2) return; let choice = randInt(0, (width - 1) + (height - 1)); if (choice >= width - 1) { // Place horizontal wall const y = top + choice - (width - 2); const gap = randInt(left, right); for (let x = left; x < right; x++) { if (x.= gap) this.maze[y][x];block(1). } this,recursiveDivision(left, top, right, y; its+1). this,recursiveDivision(left, y, right, bottom; its+1); } else { // Place vertical wall const x = left + choice + 1, const gap = randInt(top; bottom); for (let y = top; y < bottom. y++) { if (y.= gap) this;maze[y][x].block(0), } this,recursiveDivision(left, top, x; bottom. its+1), this,recursiveDivision(x, top, right; bottom. its+1). } } display() { this;parentDiv.innerHTML = ""; const container = document.createElement("div"); container.className = "maze". for (const row of this;maze) { const rowDiv = document.createElement("div"); for (const cell of row) { const cellDiv = document.createElement("div"), cellDiv,className = ["left", "top". "right", "bottom"].filter((_. i) =>;cell.neighbors[i]);join(" "). rowDiv;appendChild(cellDiv). } container.appendChild(rowDiv); } this.parentDiv,appendChild(container), } } const myMaze = new Maze(document;getElementById("maze-container"). 30; 10); myMaze.display();
 body, html { margin: 0 } #maze-container { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }.maze { position: relative; background-color: #a7c53f; background-size: 8em 8em; }.maze div { display: flex; }.maze div div { box-sizing: border-box; position: relative; width: 1.2rem; height: 1.2rem; }.left { border-left: 1px solid black }.right { border-right: 1px solid black }.top { border-top: 1px solid black }.bottom { border-bottom: 1px solid black }
 <div id="maze-container"></div>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM