I’m having trouble generating a random maze. My algorithm creates the initial box where the maze will be. But I cannot seem to generate any walls inside of this box. I’m trying to use the recursive division algorithm. Here is my code:
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>
As you can see when you run the code, the walls are generated, but in the wrong places. So some touch each other (so you can’t move between them) and I cannot solve this problem.
I cannot get this “recursive division” algorithm to work properly.