Move Maze-related scripts into the Hallucinate.GameSetup.Maze namespace and perform a broad refactor and cleanup. Make MapLocation a readonly struct, add Corridor/Wall/Path constants, and convert Maze into a clearer base class with serialized fields (width, depth, scale, mapParentObject), proper initialization, virtual Generate, and safer DrawMap behavior. Update neighbor-count helpers, tighten method visibility, and improve algorithm implementations (Crawler, Prims, Recursive, Wilsons) to use the new constants and more robust logic (including logging and loop guards). Add a Fisher–Yates Shuffle extension with a static RNG under Hallucinate.GameSetup.Maze.Extensions. Also update IDE metadata (.idea encodings.xml and workspace.xml) to record file encodings and some project settings.
133 lines
3.8 KiB
C#
133 lines
3.8 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace Hallucinate.GameSetup.Maze
|
|
{
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public class Wilsons : Maze
|
|
{
|
|
private List<MapLocation> _notUsed = new List<MapLocation>();
|
|
|
|
/// <summary>
|
|
/// Generates the maze using Wilson's algorithm logic.
|
|
/// </summary>
|
|
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();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Counts how many neighbors are already part of the finalized "Path".
|
|
/// </summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Scans the grid for cells that are not yet connected to the maze.
|
|
/// </summary>
|
|
/// <returns>The number of available (unused) cells.</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Performs a random walk from an unused cell until it hits the existing maze.
|
|
/// </summary>
|
|
private void RandomWalk()
|
|
{
|
|
List<MapLocation> inWalk = new List<MapLocation>();
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
}
|