Skip to content

Commit b5469b3

Browse files
committed
Adding Monster Maze
1 parent 5942b3f commit b5469b3

24 files changed

+1494
-0
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: Monster Maze Build
2+
on:
3+
push:
4+
paths:
5+
- 'Projects/MonsterMaze/**'
6+
- '!**.md'
7+
pull_request:
8+
paths:
9+
- 'Projects/MonsterMaze/**'
10+
- '!**.md'
11+
workflow_dispatch:
12+
jobs:
13+
build:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v3
17+
- uses: actions/setup-dotnet@v3
18+
with:
19+
dotnet-version: 8.0.x
20+
- run: dotnet build "Projects\MonsterMaze\MonsterMaze.csproj" --configuration Release

.vscode/launch.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,5 +542,15 @@
542542
"console": "externalTerminal",
543543
"stopAtEntry": false,
544544
},
545+
{
546+
"name": "Monster Maze",
547+
"type": "coreclr",
548+
"request": "launch",
549+
"preLaunchTask": "Build Monster Maze",
550+
"program": "${workspaceFolder}/Projects/MonsterMaze/bin/Debug/MonsterMaze.dll",
551+
"cwd": "${workspaceFolder}/Projects/MonsterMaze/bin/Debug",
552+
"console": "externalTerminal",
553+
"stopAtEntry": false,
554+
},
545555
],
546556
}

.vscode/tasks.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,19 @@
704704
],
705705
"problemMatcher": "$msCompile",
706706
},
707+
{
708+
"label": "Build Monster Maze",
709+
"command": "dotnet",
710+
"type": "process",
711+
"args":
712+
[
713+
"build",
714+
"${workspaceFolder}/Projects/MonsterMaze/MonsterMaze.csproj",
715+
"/property:GenerateFullPaths=true",
716+
"/consoleloggerparameters:NoSummary",
717+
],
718+
"problemMatcher": "$msCompile",
719+
},
707720
{
708721
"label": "Build Solution",
709722
"command": "dotnet",
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
public enum EntityAction
2+
{
3+
None,
4+
Left,
5+
Up,
6+
Right,
7+
Down,
8+
Quit
9+
}
10+
11+
public static class EntityActionExtensions
12+
{
13+
public static EntityAction FromConsoleKey(ConsoleKeyInfo key)
14+
{
15+
return key.Key switch
16+
{
17+
ConsoleKey.LeftArrow => EntityAction.Left,
18+
ConsoleKey.RightArrow => EntityAction.Right,
19+
ConsoleKey.UpArrow => EntityAction.Up,
20+
ConsoleKey.DownArrow => EntityAction.Down,
21+
ConsoleKey.Escape => EntityAction.Quit,
22+
_ => key.KeyChar switch {
23+
'a' => EntityAction.Left,
24+
'A' => EntityAction.Left,
25+
'w' => EntityAction.Up,
26+
'W' => EntityAction.Up,
27+
'd' => EntityAction.Right,
28+
'D' => EntityAction.Right,
29+
's' => EntityAction.Down,
30+
'S' => EntityAction.Down,
31+
'q' => EntityAction.Quit,
32+
'Q' => EntityAction.Quit,
33+
_ => EntityAction.None
34+
}
35+
};
36+
}
37+
}

Projects/MonsterMaze/GameUtils.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
public static class GameUtils
2+
{
3+
public static char[,] ConvertToCharMaze(bool[,] maze, char wallCharacter = '#')
4+
{
5+
var result = new char[maze.GetLength(0), maze.GetLength(1)];
6+
for (int i = 0; i < maze.GetLength(0); i++)
7+
{
8+
for (int j = 0; j < maze.GetLength(1); j++)
9+
{
10+
result[i, j] = maze[i, j] ? ' ' : wallCharacter;
11+
}
12+
}
13+
return result;
14+
}
15+
16+
public static bool WaitForEscapeOrSpace()
17+
{
18+
var key = Console.ReadKey(true).Key;
19+
while(key != ConsoleKey.Spacebar && key != ConsoleKey.Escape)
20+
{
21+
key = Console.ReadKey(true).Key;
22+
}
23+
return key == ConsoleKey.Escape;
24+
}
25+
}
26+

Projects/MonsterMaze/MazePoint.cs

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

Projects/MonsterMaze/MazeStep.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
public class MazeStep
2+
{
3+
public MazePoint Position {get; set;}
4+
public EntityAction Direction {get; set;}
5+
6+
public MazeStep(MazePoint position, EntityAction direction)
7+
{
8+
Position = position;
9+
Direction = direction;
10+
}
11+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
<ApplicationIcon>monstermaze.ico</ApplicationIcon>
8+
<Company></Company>
9+
<Authors>Geoff Thompson (geoff.t.nz2 @ gmail.com)</Authors>
10+
<Description>A console window game, where the player has to escape the maze while being chased by monsters.</Description>
11+
</PropertyGroup>
12+
<PropertyGroup Condition="'$(Configuration)'=='Release'">
13+
<Optimize>True</Optimize>
14+
<PublishTrimmed>true</PublishTrimmed>
15+
<SelfContained>true</SelfContained>
16+
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
17+
</PropertyGroup>
18+
</Project>

0 commit comments

Comments
 (0)