Skip to content

Instantly share code, notes, and snippets.

@raydog
Created February 25, 2013 09:42
Show Gist options
  • Save raydog/5028769 to your computer and use it in GitHub Desktop.
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.
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