Created
August 7, 2013 10:46
-
-
Save hankhan425/6173027 to your computer and use it in GitHub Desktop.
Automatic Maze Generation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#canvas, #dimensions { | |
margin: auto; | |
display: block; | |
text-align: center; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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