Skip to content

Instantly share code, notes, and snippets.

@hankhan425
Created August 7, 2013 10:46
Show Gist options
  • Save hankhan425/6173027 to your computer and use it in GitHub Desktop.
Save hankhan425/6173027 to your computer and use it in GitHub Desktop.
Automatic Maze Generation
#canvas, #dimensions {
margin: auto;
display: block;
text-align: center;
}
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
<body>
<div id = 'dimensions'>
Dimensions:
<select id = 'X'>
</select></br>
<button id = 'btn'>Generate!</button>
</div>
<canvas id = 'canvas' tabindex = '0'></canvas>
</body>
</html>
// Load canvas
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');
canvas.width = 500;
canvas.height = 500;
// Load other html stuff
var X = document.getElementById('X');
for (var i = 5; i < 26; i++) {
var child = document.createElement('option');
child.value = i;
child.innerHTML = i + " x " + i;
X.appendChild(child);
}
btn = document.getElementById('btn');
btn.addEventListener('click', onClick);
function onClick(event) {
var xLength = parseInt(X.value, 10) + 2;
var yLength = parseInt(X.value, 10) + 2;
var map = purgeMaze(map, xLength, yLength);
var visited = purgeVisited(visited);
mazeGen(1, 1, map, visited);
//console.log(map);
//console.log(visited);
ctx.clearRect(0, 0, canvas.width, canvas.height);
draw(map, xLength);
/*
Originally had the following code ----
xLength = parseInt...
yLength = parseInt...
purgeMaze(map. xLength, yLength);
purgeVisited(visited)...
...
and I had set global variables (map, visited, lengths) outside
but that didn't work because although you can fix global variables
within functions you can't just pass global variables without first
having the global variables passed to the parent function.
*/
}
function purgeMaze(map, xLength, yLength) {
map = [];
map.length = yLength;
for (var i=0; i < map.length; i++) {
map[i] = new Array(xLength);
for (var j=0; j < map[i].length; j++) {
map[i][j] = [];
}
}
return map;
}
function purgeVisited(visited) {
visited = [];
return visited;
}
// generating the maze using depth-first algorithm
// helper constants
// when viewing the array U is going to point down
// and D is going to point up. that's just how
// things look like in reality vs arrays.
var dX = {R: 1, L: -1, D: 0, U: 0};
var dY = {R: 0, L: 0, D: -1, U: 1};
var dO = {R: 'L', L: 'R', D: 'U', U: 'D'};
function mazeGen(currX, currY, map, visited) {
// randomizing directions to test for cell validity
var directions = ['R', 'L', 'D', 'U'].sort(function() {
return 0.5 - Math.random();
});
// console.log("directions: " + directions);
visited.push(currX + ", " + currY);
for (var i in directions) {
// setting new coordinates for each
// i is 0, 1, 2 ...
// directions[i] is 'R', 'L', 'U', 'D'
var newX = currX + dX[directions[i]];
var newY = currY + dY[directions[i]];
// for debugging - uncomment console.log lines
// console.log("current coordinates: " + currX + ", " + currY);
// console.log("direction: " + directions[i]);
// console.log("new coordinates: " + newX + ", " + newY);
// testing the validity of new coordinates
if (inMaze(newX, newY, map) && unvisitedCell(newX, newY, visited)) {
// new coordinates are valid. coordinates locked in.
// paving way from current cell to new cell
// console.log("passed the validity test");
// console.log(currX + ", " + currY + " is now " + directions[i]);
map[currY][currX].push(directions[i]);
// need to take care of edge cases where
// cells that have no way to go remain as 0.
// here we assign opposite direction values to
// edge cases so when we draw the maze
// it doesn't destroy a wall that should be standing.
// draw the maze urself using any other edge case solution
// and you'll see why this is like this.
map[newY][newX].push(dO[directions[i]]);
// recursively call mazeGen method
mazeGen(newX, newY, map, visited);
}
}
}
function inMaze(x, y, map) {
// TRICKY!!! important edge case shit here
// note how it's x>0 not x>=0 because if you allow
// x>=0 then when the current coordinates are on the edge
// its next coordinates are likely to be off the map
// hence undefined
return (x > 0 && x < map.length - 1 &&
y > 0 && y < map[y].length - 1);
}
//helper methods
function unvisitedCell(x, y, visited) {
for (var i = 0; i < visited.length; i++)
if (visited[i] === x + ", " + y)
return false;
return true;
}
function contains(a, obj) {
for (var i = 0; i < obj.length; i++)
if (obj[i] === a)
return true;
return false;
}
// drawing the maze on canvas
function draw(map, xLength) {
ctx.fillStyle = "black";
var length = 500/(xLength);
// Going to draw only the bottom and the right walls if applicable
for (var i = 0; i < map.length-1; i++) {
for (var j = 0; j < map[i].length-1; j++) {
// the outermost layer is not drawn
if (!contains('U', map[i][j]) && !contains('D', map[i+1][j]) && j !== 0)
ctx.fillRect(length*j, length*(i+1), length, 1);
if (!contains('R', map[i][j]) && !contains('L', map[i][j+1]) && i !== 0)
ctx.fillRect(length*(j+1), length*i, 1, length);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment