Created
February 25, 2013 09:42
-
-
Save raydog/5028769 to your computer and use it in GitHub Desktop.
A simple maze generation algorithm. Currently only has an inefficient variant of the Kruskal's maze algorithm coded, but it works, and new algorithms can be easily implemented. Can be required as a node.js module.
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
var Maze = (function () { | |
// Fisher-Yates shuffle: | |
function shuffle_list(l) { | |
for(var i=l.length-1; i>0; i--) { | |
var j = Math.floor(Math.random() * (i+1)); | |
var temp = l[i]; | |
l[i] = l[j]; | |
l[j] = temp; | |
} | |
} | |
// Quick-n-dirty isConnected implementation: | |
function isConnected(data, start) { | |
start = start || [0,0]; | |
var worklist = [start], marked = 0; | |
data.map(function (r) { r.map(function (x) { x.mark = false; }); }); | |
while (worklist.length) { | |
var item = worklist.shift(); | |
var x = item[0], y = item[1]; | |
var next = []; | |
// Just skip if this node is a dupe: | |
if (data[y][x].mark) { continue; } | |
data[y][x].mark = true; | |
marked ++; | |
if (x+1 < data[y].length && !data[y][x+1].mark && !data[y][x].wall('r')) { next.push([x+1, y]); } | |
if (x-1 >= 0 && !data[y][x-1].mark && !data[y][x-1].wall('r')) { next.push([x-1, y]); } | |
if (y+1 < data.length && !data[y+1][x].mark && !data[y][x].wall('b')) { next.push([x, y+1]); } | |
if (y-1 >= 0 && !data[y-1][x].mark && !data[y-1][x].wall('b')) { next.push([x, y-1]); } | |
next.forEach(function (coord) { | |
worklist.push(coord); | |
}); | |
} | |
return marked === (data.length * data[0].length); | |
} | |
function default_map_gen(data) { | |
var worklist = []; | |
// Push all our work onto the queue: | |
for(var i=0; i<data.length; i++) { | |
for(var j=0; j<data[i].length; j++) { | |
worklist.push({x:j, y:i, d:"r"}); | |
worklist.push({x:j, y:i, d:"b"}); | |
} | |
} | |
shuffle_list(worklist); | |
worklist.forEach(function (item) { | |
var x = item.x, y = item.y, d = item.d; | |
data[y][x].wall(d, true); | |
if (!isConnected(data, [x,y])) { | |
data[y][x].wall(d, false); | |
} | |
}); | |
} | |
function Maze (width, height) { | |
if (typeof width !== "number" || typeof height !== "number") { | |
throw new Error("Bad types in dimensions."); | |
} | |
if (isNaN(width) || isNaN(height) || width < 1 || height < 1) { | |
throw new Error("Invalid dimensions."); | |
} | |
// Set up the data array (contains the walls): | |
var data = new Array(height); | |
for (var i=0; i<height; i++) { | |
data[i] = new Array(width); | |
for(var j=0; j<width; j++) { | |
data[i][j] = new Node(); | |
} | |
} | |
// Function that will apply the mapping algorithm: | |
this.generate = function (fn) { | |
fn = fn || default_map_gen; | |
fn.call({}, data); | |
} | |
// Will call the given function for every square: | |
this.forEach = function (cb) { | |
for(var i=0; i<height; i++) { | |
for(var j=0; j<width; j++) { | |
var t = data[i][j]; | |
cb.call({x:j, y:i, right:t.wall("r"), bottom:t.wall("b")}); | |
} | |
} | |
}; | |
// Function that will dump the map into console: | |
this.debug = function () { | |
var firstrow = " "; | |
for(var i=0; i<width; i++) { firstrow += "_ "; } | |
console.log(firstrow); | |
data.map(function (row) { | |
console.log(" |" + row.join("")); | |
}); | |
} | |
// Function to bring the maze to a starting state: | |
this.reset = function () { | |
data.map(function (row) { row.map(function (x) { x.reset(); }); }); | |
for(i = 0; i<width; i++) { data[height-1][i].wall("b", true); } | |
for(i = 0; i<height; i++) { data[i][width-1 ].wall("r", true); } | |
}; | |
// Make sure we're in a sane state: | |
this.reset(); | |
} | |
function Node() { | |
var walls = {r: false, b: false}; | |
// Set/get the boolean value of a wall: | |
this.wall = function (wall, state) { | |
if (! (wall in walls) ) { throw new Error("Bad wall value"); } | |
if ( state === undefined) { return walls[wall]; } | |
walls[wall] = !!state; | |
}; | |
// Will reset this node to a decent starting state: | |
this.reset = function () { | |
var walls = {r: false, b: false}; | |
}; | |
this.toString = function () { | |
return (walls.b ? "_" : " ") + (walls.r ? "|" : " "); | |
}; | |
} | |
return Maze; | |
})(); | |
// Export it correctly for node.js: | |
if (typeof module !== 'undefined' && "exports" in module) { | |
module.exports = Maze; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment