using System.Collections.Generic;
using UnityEngine;
namespace Hallucinate.GameSetup.Maze
{
///
/// Implements Wilson's algorithm for generating a Uniform Spanning Tree of the grid.
/// It uses loop-erased random walks to connect unvisited cells to the existing maze.
///
public class Wilsons : Maze
{
private List _notUsed = new List();
///
/// Generates the maze using Wilson's algorithm logic.
///
public override void Generate()
{
// Create a starting cell
int x = Random.Range(2, width - 1);
int z = Random.Range(2, depth - 1);
map[x, z] = Path;
while (GetAvailableCells() > 1)
{
RandomWalk();
}
}
///
/// Counts how many neighbors are already part of the finalized "Path".
///
private int CountSquareMazeNeighbours(int x, int z)
{
int count = 0;
for (int d = 0; d < directions.Count; d++)
{
int nx = x + directions[d].x;
int nz = z + directions[d].z;
if (map[nx, nz] == Path)
{
count++;
}
}
return count;
}
///
/// Scans the grid for cells that are not yet connected to the maze.
///
/// The number of available (unused) cells.
private int GetAvailableCells()
{
_notUsed.Clear();
for (int z = 1; z < depth - 1; z++)
{
for (int x = 1; x < width - 1; x++)
{
if (CountSquareMazeNeighbours(x, z) == 0)
{
_notUsed.Add(new MapLocation(x, z));
}
}
}
return _notUsed.Count;
}
///
/// Performs a random walk from an unused cell until it hits the existing maze.
///
private void RandomWalk()
{
List inWalk = new List();
int rStartIndex = Random.Range(0, _notUsed.Count);
int cx = _notUsed[rStartIndex].x;
int cz = _notUsed[rStartIndex].z;
inWalk.Add(new MapLocation(cx, cz));
int loopCount = 0;
bool validPath = false;
while (cx > 0 && cx < width - 1 && cz > 0 && cz < depth - 1 && loopCount < 5000 && !validPath)
{
map[cx, cz] = Corridor;
if (CountSquareMazeNeighbours(cx, cz) > 1)
{
break;
}
int rd = Random.Range(0, directions.Count);
int nx = cx + directions[rd].x;
int nz = cz + directions[rd].z;
if (CountSquareNeighbours(nx, nz) < 2)
{
cx = nx;
cz = nz;
inWalk.Add(new MapLocation(cx, cz));
}
validPath = CountSquareMazeNeighbours(cx, cz) == 1;
loopCount++;
}
if (validPath)
{
map[cx, cz] = Corridor;
Debug.Log("Path Found");
foreach (MapLocation m in inWalk)
{
map[m.x, m.z] = Path;
}
inWalk.Clear();
}
else
{
foreach (MapLocation m in inWalk)
{
map[m.x, m.z] = Wall;
}
inWalk.Clear();
}
}
}
}