using System.Collections; using System.Collections.Generic; using UnityEngine; namespace Hallucinate.GameSetup.Maze { /// /// Wilson's Algorithm implementation based on the original provided logic. /// Ensures paths are sparse and correctly finalized using specific neighbor constraints. /// public class WilsonsAlgorithm : IMazeAlgorithm { private const int MinBoundary = 2; private const int MaxIterationSafety = 5000; private const int MaxWalkSteps = 5000; private readonly List _directions = MapLocation.Directions; private List _notUsed = new List(); public void Generate(MazeGrid grid) { // 1. Create a starting finalized cell (Type.Corridor represents state 2) int x = Random.Range(MinBoundary, grid.Width - 1); int z = Random.Range(MinBoundary, grid.Depth - 1); grid.SetCell(x, z, MazeCellType.Corridor); int safety = 0; while (GetAvailableCells(grid) > 1 && safety < MaxIterationSafety) { RandomWalkSync(grid); safety++; } } public IEnumerator GenerateStepByStep(MazeGrid grid, float interval) { int x = Random.Range(MinBoundary, grid.Width - 1); int z = Random.Range(MinBoundary, grid.Depth - 1); grid.SetCell(x, z, MazeCellType.Corridor); yield return new WaitForSeconds(interval); int safety = 0; while (GetAvailableCells(grid) > 1 && safety < MaxIterationSafety) { yield return RandomWalk(grid, interval); safety++; } } /// /// Counts neighbors that are already part of the finalized maze (State 2 / Corridor). /// private int CountFinalizedNeighbours(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 int GetAvailableCells(MazeGrid grid) { _notUsed.Clear(); for (int z = 1; z < grid.Depth - 1; z++) { for (int x = 1; x < grid.Width - 1; x++) { if (CountFinalizedNeighbours(grid, x, z) == 0) { _notUsed.Add(new MapLocation(x, z)); } } } return _notUsed.Count; } private void RandomWalkSync(MazeGrid grid) { if (_notUsed.Count == 0) return; 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 loop = 0; bool validPath = false; while (cx > 0 && cx < grid.Width - 1 && cz > 0 && cz < grid.Depth - 1 && loop < MaxWalkSteps && !validPath) { // Mark as temporary walk (State 0 / Processing) // Note: We don't set grid cell here in sync mode to avoid triggering events unnecessarily // but we keep track of neighbors. if (CountFinalizedNeighbours(grid, cx, cz) > 1) break; MapLocation rd = _directions[Random.Range(0, _directions.Count)]; int nx = cx + rd.x; int nz = cz + rd.z; // User's original constraint: CountSquareNeighbours (nx, nz) < 2 if (CountFinalizedNeighbours(grid, nx, nz) < 2) { cx = nx; cz = nz; inWalk.Add(new MapLocation(cx, cz)); } validPath = CountFinalizedNeighbours(grid, cx, cz) == 1; loop++; } if (validPath) { foreach (MapLocation m in inWalk) grid.SetCell(m.x, m.z, MazeCellType.Corridor); } } private IEnumerator RandomWalk(MazeGrid grid, float interval) { if (_notUsed.Count == 0) yield break; 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 loop = 0; bool validPath = false; while (cx > 0 && cx < grid.Width - 1 && cz > 0 && cz < grid.Depth - 1 && loop < MaxWalkSteps && !validPath) { grid.SetCell(cx, cz, MazeCellType.Processing); // State 0 if (interval > 0) yield return new WaitForSeconds(interval); if (CountFinalizedNeighbours(grid, cx, cz) > 1) break; MapLocation rd = _directions[Random.Range(0, _directions.Count)]; int nx = cx + rd.x; int nz = cz + rd.z; if (CountFinalizedNeighbours(grid, nx, nz) < 2) { cx = nx; cz = nz; inWalk.Add(new MapLocation(cx, cz)); } validPath = CountFinalizedNeighbours(grid, cx, cz) == 1; loop++; } if (validPath) { foreach (MapLocation m in inWalk) { grid.SetCell(m.x, m.z, MazeCellType.Corridor); // State 2 } } else { foreach (MapLocation m in inWalk) grid.SetCell(m.x, m.z, MazeCellType.Wall); // State 1 } inWalk.Clear(); } } }