# 四種迷宮生成算法的實現和可視化

（上圖是使用隨機化`Prim`算法生成一個200x200的迷宮的過程）

Github項目地址：maze

# 前言

1. 將所有相鄰格子連接起來，形成一個無向圖`G={V,E}`
2. 構造`G`的一個子圖`G'={V',E'}``G'`要求是一顆樹，且`V'=V`

# 數據結構

``````class AdjacencyList
{
public:
AdjacencyList(int row = 0, int column = 0);

void connect(int i, int j);
void disconnect(int i, int j);

int row() const { return m_row; }
int column() const { return m_column; }

std::vector<int> &neighbor(int i);
std::vector<int> &surround(int i);

private:
int m_row;
int m_column;

std::vector<std::vector<int>> m_index2neighbor;
std::vector<std::vector<int>> m_index2surround;
};
``````

# 深度優先算法

``````AdjacencyList DeepFirstSearch::generate()
{
enum Color
{
White,
Gray,
Black
};

vector<int> color(static_cast<size_t>(m_row * m_column), White);
vector<int> current;
current.reserve(static_cast<size_t>(m_row * m_column));

color[0] = Gray;
current.push_back(0);

while (current.size())
{
int last = current.back();
random_shuffle(result.surround(last).begin(), result.surround(last).end());

auto iter = result.surround(last).cbegin();

for (; iter != result.surround(last).cend(); iter++)
{
if (color[static_cast<size_t>(*iter)] == White)
{
color[static_cast<size_t>(*iter)] = Gray;
result.connect(last, *iter);
current.push_back(*iter);
break;
}
}

// all adjacent points are found
if (iter == result.surround(last).cend())
{
current.pop_back();
color[static_cast<size_t>(last)] = Black;
}
}

return result;
}
``````

# 隨機化`Kruskal`算法

`Kruskal`原本是構造最小生成樹的算法，但迷宮中的邊都沒有權重（或者說權重都是0），因此在把新邊加入樹中時隨機選擇一個就可以。在選擇邊時需要引入隨機性，否則每次都會得到相同的結果。算法代碼爲：

``````AdjacencyList Kruskal::generate()
{

UnionFind uf(m_row * m_column);

vector<pair<int, int>> edges;
for (int i = 0; i < m_row * m_column; i++)
{
for (auto iter : result.surround(i))
{
// avoid duplicate edge
if (i > iter)
{
edges.push_back(pair<int, int>(i, iter));
}
}
}
random_shuffle(edges.begin(), edges.end());
for (auto iter : edges)
{
if(!uf.connected(iter.first, iter.second))
{
uf.connect(iter.first, iter.second);
result.connect(iter.first, iter.second);
}
}
return result;
}
``````

# 隨機化`Prim`算法

`Prim`同樣是構造最小生成樹的算法，注意事項和`Kruskal`相同。算法代碼爲：

``````AdjacencyList Prim::generate()
{

set<pair<int ,int>> paths;
paths.insert(pair<int, int>(0, 1));
paths.insert(pair<int, int>(0, m_column));

static default_random_engine e(static_cast<unsigned>(time(nullptr)));

while (!paths.empty())
{
// random select a path in paths
int pos = static_cast<int>(e() % paths.size());
auto iter = paths.begin();

// connect the two node of path
result.connect(iter->first, iter->second);

// get the node not in linked
int current = 0;
{
current = iter->first;
}
else
{
current = iter->second;
}

// add all not accessed path to paths, and delete all invalid path from paths
for (auto i : result.surround(current))
{
pair<int, int> currentPath = makeOrderedPair(i, current);
{
paths.insert(currentPath);
}
else
{
paths.erase(paths.find(currentPath));
}
}
}

return result;
}
``````

# 遞歸分割算法

``````AdjacencyList RecursiveDivision::generate()
{
result.connectAllSurround();

divide(result, 0, 0, m_column - 1, m_row - 1);
return result;
}

void RecursiveDivision::divide(AdjacencyList &list, int left, int top, int right, int bottom)
{
// the x range of input is [left, right]
// the y range of input is [top, bottom]

if ((right - left < 1) || (bottom - top < 1))
{
return;
}

static default_random_engine e(static_cast<unsigned>(time(nullptr)));
int x = static_cast<int>(e() % static_cast<unsigned>(right - left)) + left;
int y = static_cast<int>(e() % static_cast<unsigned>(bottom - top)) + top;

vector<pair<int, int>> toDisconnect;

for (int i = left; i <= right; i++)
{
int p = y * m_column + i;
int q = (y + 1) * m_column + i;
toDisconnect.emplace_back(p, q);
}

for (int i = top; i <= bottom; i++)
{
int p = i * m_column + x;
int q = i * m_column + x + 1;
toDisconnect.emplace_back(p, q);
}

// the position of no gap wall relative to (x, y), 0:top 1:bottom 2:left 3:right
int position = e() % 4;

int gapPos[4] = {0};

// get the gap position
gapPos[0] = static_cast<int>(e() % static_cast<unsigned>(y - top + 1)) + top;
gapPos[1] = static_cast<int>(e() % static_cast<unsigned>(bottom - y)) + y + 1;
gapPos[2] = static_cast<int>(e() % static_cast<unsigned>(x - left + 1)) + left;
gapPos[3] = static_cast<int>(e() % static_cast<unsigned>(right - x)) + x + 1;

for (int i = 0; i <= 3; i++)
{
if (position != i)
{
int p = 0;
int q = 0;
if (i <= 1) // the gap is in top or bottom
{
p = gapPos[i] * m_column + x;
q = gapPos[i] * m_column + x + 1;
}
else // the gap is in left or right
{
p = y * m_column + gapPos[i];
q = (y + 1) * m_column + gapPos[i];
}
pair<int, int> pair(p, q);
toDisconnect.erase(find(toDisconnect.begin(), toDisconnect.end(), pair));
}
}

for (auto &pair : toDisconnect)
{
list.disconnect(pair.first, pair.second);
}

divide(list, left, top, x, y);
divide(list, x + 1, top, right, y);
divide(list, left, y + 1, x, bottom);
divide(list, x + 1, y + 1, right, bottom);
}
``````

# 參考鏈接

Maze generation algorithm：https://en.wikipedia.org/wiki/Maze_generation_algorithm