-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMazeRecursiveGenerator.cs
140 lines (123 loc) · 4.44 KB
/
MazeRecursiveGenerator.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
public class MazeRecursiveGenerator
{
public enum MazeMode {
OnePath,
FilledDeadEnds,
Loops
};
private static readonly (int, int)[] Directions = { (0, -1), (1, 0), (0, 1), (-1, 0) }; // Up, Right, Down, Left
private static Random random = new Random();
public static bool[,] GenerateMaze(int width, int height, MazeMode mazeMode = MazeMode.OnePath)
{
if (width % 2 == 0 || height % 2 == 0)
throw new ArgumentException("Width and height must be odd numbers for a proper maze.");
bool[,] maze = new bool[width, height]; // by default, everything is a wall (cell value == false)
// Start the maze generation
GenerateMazeRecursive(maze, 1, 1);
// Make sure the entrance and exit are open
maze[0, 1] = true; // Entrance
maze[width - 1, height - 2] = true; // Exit
if(mazeMode == MazeMode.FilledDeadEnds)
FillDeadEnds(maze);
else if(mazeMode == MazeMode.Loops)
RemoveDeadEnds(maze);
return maze;
}
private static void GenerateMazeRecursive(bool[,] maze, int x, int y)
{
maze[x, y] = true;
// Shuffle directions
var shuffledDirections = ShuffleDirections();
foreach (var (dx, dy) in shuffledDirections)
{
int nx = x + dx * 2;
int ny = y + dy * 2;
// Check if the new position is within bounds and not visited
if (IsInBounds(maze, nx, ny) && !maze[nx, ny])
{
// Carve a path
maze[x + dx, y + dy] = true;
GenerateMazeRecursive(maze, nx, ny);
}
}
}
private static List<(int, int)> ShuffleDirections()
{
var directions = new List<(int, int)>(Directions);
for (int i = directions.Count - 1; i > 0; i--)
{
int j = random.Next(i + 1);
(directions[i], directions[j]) = (directions[j], directions[i]);
}
return directions;
}
private static bool IsInBounds(bool[,] maze, int x, int y)
{
return x > 0 && y > 0 && x < maze.GetLength(0) - 1 && y < maze.GetLength(1) - 1;
}
private static void FillDeadEnds(bool[,] maze)
{
bool removed;
do
{
removed = false;
for (int x = 1; x < maze.GetLength(0) - 1; x++)
{
for (int y = 1; y < maze.GetLength(1) - 1; y++)
{
if (maze[x, y]) // If it's a path
{
int neighbors = 0;
foreach (var (dx, dy) in Directions)
{
if (maze[x + dx, y + dy])
neighbors++;
}
if (neighbors <= 1) // If it's a dead end
{
maze[x, y] = false;
removed = true;
}
}
}
}
} while (removed);
}
private static void RemoveDeadEnds(bool[,] maze)
{
bool removed;
do
{
removed = false;
for (int x = 1; x < maze.GetLength(0) - 1; x++)
{
for (int y = 1; y < maze.GetLength(1) - 1; y++)
{
if (maze[x, y]) // If it's a path
{
int neighbors = 0;
foreach (var (dx, dy) in Directions)
{
if (maze[x + dx, y + dy])
neighbors++;
}
if (neighbors <= 1) // If it's a dead end
{
// Pick a random neighbor to keep open
var shuffledDirections = ShuffleDirections();
foreach(var (dx, dy) in shuffledDirections)
{
if(IsInBounds(maze, x + dx, y + dy) && !maze[x + dx, y + dy])
{
maze[x + dx, y + dy] = true;
break;
}
}
removed = true;
}
}
}
}
} while (removed);
}
}