Generating maze is amazing! This algorithm is also widely used in lots of computer games.
Given a 2D array, generate a maze in it. There are several requirements of this maze:
- There are no circles in the maze, which means all roads in the maze lead to an dead end or to the exit.
- There are no wall blocks in the maze. Each wall is 1 unit in width, each road is also 1 unit in width.
For example,
//Following is not a legal maze, it contains wall block and circle road. //***********| //********* *| //******* *| //****** * *| //******* *| //***********|
A legal maze is something like,
// * * | // * | //** *| // ** | // * * *| // |
There is a simple and clear way to solve this problem, less than 100 lines of code.
Imaging an area full of rocks (wall) and you are a miner digging path through it.
When you are standing at a cell, you first randomly pick up an adjacent cell (up, down, left, right), try to dig it. When return, try another adjacent cell randomly, until all the 4 directions are tried.
The strategy for digging it with coordinate (i, j) or not is as follows:
- if this cell is out of boundary, do not dig it, return to last step
- if this cell is already digged, do not dig it again, return to last step
- if there are more than 1 of its 4 neighbor cells are digged, do not dig it, return to last step (since it will cause circles in path)
- else dig it. and move to this cell, jump to step 1.
Algorithm, a DFS approach:
map = [][] # initialize the map with full of walls
for each (i, j) cell in map:
DFS(i,j)
def DFS(i, j):
if i, j is out of boundary:
return
if (i,j) is visited:
return
if number of visited neighbors of (i, j) > 1:
return
mark cell as visited
for each neighbor (ni, nj) of cell(random order):
DFS(ni, nj)
C++ code is as follows. The complexity is O(n*n), n is the edge length of map.
#include "string" #include "vector" #include "unordered_map" #include "queue" #include "cstdlib" #include "ctime" #include <iostream> #include "algorithm" #include "stack" using namespace std; class Solution{ public: // Definition for singly-linked list. void maze(vector<vector<char>>& map){ //U unvisited, ' ' visited for (int i = 0; i < map.size(); ++i) { for (int j = 0; j < map[0].size(); ++j) { map[i][j] = '*'; } } _maze(map, 0, 0); } void showMaze(vector<vector<char>>& map){ for (int i = 0; i < map.size(); ++i) { for (int j = 0; j < map[0].size(); ++j) { cout<<map[i][j]; } cout<<endl; } } //Use DFS void _maze(vector<vector<char>>& map, int i, int j){ int direct[][2] = {{0,1}, {0,-1}, {-1,0}, {1,0}}; int visitOrder[] = {0,1,2,3}; //out of boundary if(i < 0 || j < 0 || i >= map.size() || j >= map[0].size()) return ; #ifdef DEBUG cout<<"("<<i<<", "<<j<<")"<<endl; #endif //visited, go back to the coming direction, return if(map[i][j] == ' ') return ; //some neightbors are visited in addition to the coming direction, return //this is to avoid circles in maze if(countVisitedNeighbor(map, i, j) > 1) return ; map[i][j] = ' '; // visited //shuffle the visitOrder shuffle(visitOrder, 4); for (int k = 0; k < 4; ++k) { int ni = i + direct[visitOrder[k]][0]; int nj = j + direct[visitOrder[k]][1]; _maze(map, ni, nj); } } int countVisitedNeighbor(vector<vector<char>>& map, int i, int j){ int direct[][2] = {{1,0}, {-1,0}, {0,1}, {0,-1}}; int count = 0; for (int k = 0; k < 4; ++k) { int ni = i + direct[k][0]; int nj = j + direct[k][1]; //out of boundary if(ni < 0 || nj < 0 || ni >= map.size() || nj >= map[0].size()) continue; if(map[ni][nj] == ' ') count++;//visited } return count; } void shuffle(int a[], int n){ for (int i = 0; i < n; ++i) { swap(a[i], a[rand() % n]); } } void swap(int & a, int &b){ int c = a; a = b; b = c; } }; int main(int argc, char const *argv[]) { Solution s; int height = 6; int width = 6; srand(time(0)); vector<char> row(width); vector<vector<char>> map; for (int i = 0; i < height; ++i) { map.push_back(row); } s.maze(map); s.showMaze(map); return 0; }
wow that was raely cool
really*