Here’s a random maze generator class based on Kruskal’s algorithm for creating a minimum spanning tree. Of course I didn’t write this from scratch… It’s a cleaned up and modified version of some code I found here written by smart_idiot. At least I think that’s his page… This version creates a maze that is closed in on the bounding walls. If you add the missing cases to the switch statement in Update(), it will create a maze that “wraps”, where “exiting” an opening in one exterior wall will “enter” on the opposite side of the maze.
Yeah there is no drawing code so have fun. I did write a small application that will give a 3D walk through of the maze. I might post it some time… but the source is relatively large…
#ifndef _MAZE_H_
#define _MAZE_H_
#include <vector>
class Cell
{
public:
// This is the constructor for the Cell. As you can see, it starts off with
// a new group containing a pointer to its self, and all the edges are set
// to true, meaning there are walls up.
Cell();
// And this is the destructor. It removes it's self from the group, and
// if the group ends up becoming empty, it deletes it.
~Cell();
// Write about here I should have a copy constructor, and an assignment opperator.
// The problem with that however is that if I do that, they should get new groups.
// If they get new groups, they obviously aren't connected to anything. And if
// they aren't connected to anything, there isn't much point in trying to make
// a copy of them. So long story short, no trying to copy cells. And as a side
// effect of this, Mazes too.
// This applies the rules between two cells.
void ApplyRules(unsigned int edge, Cell &neighbor);
bool GetEdge (int index);
private:
// And the cells have edges. Four of them. Imagine that.
bool Edge[4];
// This is the most important part of the maze generator. Each cell belongs
// to a group. In the beginning each cell has its own group. If 2 neighboring
// cells are in different groups, they can connect, and in the process become
// part of the same group. Thats pretty much all there is to it.
std::vector *group;
friend class Maze;
};
class Maze
{
public:
// This is the maze constructor. By default a maze is 32x32 cells, though you can use any size
// you want, though 8x8 is the minumum.
Maze(unsigned int w = 32, unsigned int h = 32);
// This is the destructor, which is only here to destroy the cells and avoid a nasty memory leak.
~Maze();
// This is a helper for finding a cell in the maze. You may choose a cell outside of the maze,
// in which case it wraps back to the other side, though trying to look up a cell farther than
// 2 maze lengths away will cause serious problems and probably a crash if x or y is negetive.
Cell & At(int x, int y);
// This function should be called repeatedly to construct the maze. The maze is done
// when all the cells are in the same group, and hence the group size of any of the
// cells is equal to width times height, but there currently isn't a check for it.
void Update();
unsigned int GetWidth ();
unsigned int GetHeight ();
bool MazeDone ();
void Generate ();
private:
Cell *cells; // And now, back in the context of the Maze class, we have a pointer to some cells.
unsigned int width, height; // Width and height of the maze.
};
#endif //_MAZE_H_
#include "Maze.h"
#include <algorithm>
Cell::Cell()
{
group = new std::vector <cell*> (1, this);
Edge[0] = Edge[1] = Edge[2] = Edge[3] = true;
}
Cell::~Cell()
{
group->erase(std::find(group->begin(), group->end(), this));
if(!group->size())
delete group;
}
bool Cell::GetEdge (int index)
{
return Edge [index];
}
void Cell::ApplyRules(unsigned int edge, Cell &cell)
{
if(group != cell.group) // If we are in different groups. . .
{
std::vector *old_group = cell.group; // Remember the old group so we can delete it later.
// Change all the cells in that group to point to our group.
for(std::vector::iterator c = old_group->begin(); c != old_group->end(); c++)
(*c)->group = group;
// Add them all to out group.
group->insert(group->end(), old_group->begin(), old_group->end());
// And finally destroy the old group.
delete old_group;
// All thats left to do now is hide the edges of the cells.
Edge[edge] = false;
cell.Edge[(edge + 2) % 4] = false;
}
}
Maze::Maze(unsigned int w, unsigned int h)
{
width = w < 8 ? 8 : w;
height = h < 8 ? 8 : h;
cells = new Cell [width * height];
}
Maze::~Maze()
{
delete [] cells;
}
unsigned int Maze::GetWidth()
{
return width;
}
unsigned int Maze::GetHeight ()
{
return height;
}
Cell & Maze::At(int x, int y)
{
return cells[(x+width)%width + ((y+height)%height)*width];
}
bool Maze::MazeDone ()
{
return (At (0,0).group->size () == width * height);
}
void Maze::Update()
{
int edge;
for(unsigned int y = 0; y < height; ++y)
{
for(unsigned int x = 0; x < width; ++x)
{
edge = 1 + rand () % 2;
switch (edge)
{
case 1:
if (x + 1 < width)
At (x, y).ApplyRules (edge, At(x+1, y));
break;
case 2:
if (y + 1 < height)
At (x, y).ApplyRules (edge, At(x, y+1));
break;
}
}
}
}
void Maze::Generate ()
{
while (!MazeDone ())
{
Update ();
}
}