119 lines
3.9 KiB
C#
119 lines
3.9 KiB
C#
|
|
using System.Collections;
|
||
|
|
using System.Collections.Generic;
|
||
|
|
using UnityEngine;
|
||
|
|
|
||
|
|
namespace Hallucinate.GameSetup.Maze
|
||
|
|
{
|
||
|
|
public class WilsonsAlgorithm : IMazeAlgorithm
|
||
|
|
{
|
||
|
|
private const int MinBoundary = 2;
|
||
|
|
private const int MaxIterationSafety = 5000;
|
||
|
|
private const int TargetPathNeighbours = 1;
|
||
|
|
|
||
|
|
private readonly List<MapLocation> _directions = new List<MapLocation>()
|
||
|
|
{
|
||
|
|
new MapLocation(1, 0), new MapLocation(0, 1), new MapLocation(-1, 0), new MapLocation(0, -1)
|
||
|
|
};
|
||
|
|
|
||
|
|
public void Generate(MazeGrid grid)
|
||
|
|
{
|
||
|
|
int x = Random.Range(MinBoundary, grid.Width - MinBoundary);
|
||
|
|
int z = Random.Range(MinBoundary, grid.Depth - MinBoundary);
|
||
|
|
grid.SetCell(x, z, MazeCellType.Corridor);
|
||
|
|
|
||
|
|
while (GetAvailableCells(grid).Count > 1)
|
||
|
|
{
|
||
|
|
PerformRandomWalk(grid, null, 0);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public IEnumerator GenerateStepByStep(MazeGrid grid, float interval)
|
||
|
|
{
|
||
|
|
int x = Random.Range(MinBoundary, grid.Width - MinBoundary);
|
||
|
|
int z = Random.Range(MinBoundary, grid.Depth - MinBoundary);
|
||
|
|
grid.SetCell(x, z, MazeCellType.Corridor);
|
||
|
|
yield return new WaitForSeconds(interval);
|
||
|
|
|
||
|
|
while (true)
|
||
|
|
{
|
||
|
|
var available = GetAvailableCells(grid);
|
||
|
|
if (available.Count <= 1) break;
|
||
|
|
|
||
|
|
yield return PerformRandomWalk(grid, available, interval);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private List<MapLocation> GetAvailableCells(MazeGrid grid)
|
||
|
|
{
|
||
|
|
List<MapLocation> available = new List<MapLocation>();
|
||
|
|
for (int z = 1; z < grid.Depth - 1; z++)
|
||
|
|
{
|
||
|
|
for (int x = 1; x < grid.Width - 1; x++)
|
||
|
|
{
|
||
|
|
if (CountPathNeighbours(grid, x, z) == 0)
|
||
|
|
available.Add(new MapLocation(x, z));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return available;
|
||
|
|
}
|
||
|
|
|
||
|
|
private int CountPathNeighbours(MazeGrid grid, int x, int z)
|
||
|
|
{
|
||
|
|
int count = 0;
|
||
|
|
foreach (var d in _directions)
|
||
|
|
{
|
||
|
|
if (grid.GetCell(x + d.x, z + d.z) == MazeCellType.Corridor) count++;
|
||
|
|
}
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
private IEnumerator PerformRandomWalk(MazeGrid grid, List<MapLocation> available, float interval)
|
||
|
|
{
|
||
|
|
List<MapLocation> inWalk = new List<MapLocation>();
|
||
|
|
int rStart = Random.Range(0, available.Count);
|
||
|
|
int cx = available[rStart].x;
|
||
|
|
int cz = available[rStart].z;
|
||
|
|
|
||
|
|
inWalk.Add(new MapLocation(cx, cz));
|
||
|
|
bool pathFound = false;
|
||
|
|
|
||
|
|
int safety = 0;
|
||
|
|
while (grid.IsInBounds(cx, cz) && !pathFound && safety < MaxIterationSafety)
|
||
|
|
{
|
||
|
|
grid.SetCell(cx, cz, MazeCellType.Processing);
|
||
|
|
if (interval > 0) yield return new WaitForSeconds(interval);
|
||
|
|
|
||
|
|
if (CountPathNeighbours(grid, cx, cz) > 1) break;
|
||
|
|
|
||
|
|
int rd = Random.Range(0, _directions.Count);
|
||
|
|
int nx = cx + _directions[rd].x;
|
||
|
|
int nz = cz + _directions[rd].z;
|
||
|
|
|
||
|
|
if (grid.CountSquareNeighbours(nx, nz, MazeCellType.Processing) < 2)
|
||
|
|
{
|
||
|
|
cx = nx;
|
||
|
|
cz = nz;
|
||
|
|
inWalk.Add(new MapLocation(cx, cz));
|
||
|
|
}
|
||
|
|
|
||
|
|
pathFound = CountPathNeighbours(grid, cx, cz) == TargetPathNeighbours;
|
||
|
|
safety++;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (pathFound)
|
||
|
|
{
|
||
|
|
foreach (var m in inWalk)
|
||
|
|
{
|
||
|
|
grid.SetCell(m.x, m.z, MazeCellType.Corridor);
|
||
|
|
if (interval > 0) yield return new WaitForSeconds(interval * 0.5f);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
foreach (var m in inWalk)
|
||
|
|
grid.SetCell(m.x, m.z, MazeCellType.Wall);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|