Implement strategy pattern

This commit is contained in:
Stone_Red
2023-05-31 09:24:32 +02:00
parent 1b1b568bb0
commit 1775929ced
10 changed files with 80 additions and 43 deletions
-6
View File
@@ -1,6 +0,0 @@
namespace Maze.Nodes;
internal class BfsNode : BaseMazeNode<BfsNode>
{
public BfsNode? Parent { get; set; }
}
-4
View File
@@ -1,4 +0,0 @@
namespace Maze.Nodes;
internal class DfsNode : BaseMazeNode<DfsNode>
{
}
+3 -2
View File
@@ -1,11 +1,12 @@
namespace Maze.Nodes; namespace Maze.Nodes;
internal abstract class BaseMazeNode<T> where T : BaseMazeNode<T> internal class MazeNode
{ {
public bool Visited { get; set; } public bool Visited { get; set; }
public bool Wall { get; set; } public bool Wall { get; set; }
public bool Start { get; set; } public bool Start { get; set; }
public bool Goal { get; set; } public bool Goal { get; set; }
public bool Updated { get; set; } = true; public bool Updated { get; set; } = true;
public List<T> Neighbors { get; set; } = new List<T>(); public MazeNode? Parent { get; set; }
public List<MazeNode> Neighbors { get; set; } = new List<MazeNode>();
} }
+30 -3
View File
@@ -7,11 +7,11 @@ using Stone_Red_Utilities.ConsoleExtentions;
MazeParser parser = new MazeParser(); MazeParser parser = new MazeParser();
MazePrinter printer = new MazePrinter(); MazePrinter printer = new MazePrinter();
IMazeSolver<DfsNode> solver = new DepthFirstSearchMazeSolver(); PathFinder pathFinder = new PathFinder(new DepthFirstSearchMazeSolver());
try try
{ {
DfsNode[,] maze = parser.FromFile("input.txt", out DfsNode startNode); MazeNode[,] maze = parser.FromFile("input.txt", out MazeNode startNode);
if (Console.WindowHeight < maze.GetLength(0) || Console.WindowWidth < maze.GetLength(1)) if (Console.WindowHeight < maze.GetLength(0) || Console.WindowWidth < maze.GetLength(1))
{ {
@@ -20,7 +20,34 @@ try
Console.Clear(); Console.Clear();
} }
List<DfsNode> solution = solver.Solve(startNode, maze, GraphicsMode.Colored); Console.WriteLine("Choose an algorithm:");
Type type = typeof(IMazeSolver);
IEnumerable<Type> types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes())
.Where(t => type.IsAssignableFrom(t) && t.IsClass && !t.IsAbstract);
int index = 0;
foreach (Type t in types)
{
Console.WriteLine($"[{index++}] {t.Name}");
}
int algorithmIndex = -1;
do
{
if (!int.TryParse(Console.ReadLine(), out algorithmIndex))
{
ConsoleExt.WriteLine("Invalid input. Please try again.", ConsoleColor.Red);
algorithmIndex = -1;
}
}
while (algorithmIndex < 0 || algorithmIndex >= types.Count());
pathFinder.MazeSolver = (IMazeSolver)Activator.CreateInstance(types.ElementAt(algorithmIndex))!;
List<MazeNode> solution = pathFinder.FindPath(startNode, maze, GraphicsMode.Colored);
printer.PrintColored(maze, solution, redrawAll: true); printer.PrintColored(maze, solution, redrawAll: true);
+8 -8
View File
@@ -3,17 +3,17 @@ using Maze.Utlis;
namespace Maze.Solvers; namespace Maze.Solvers;
internal class BreadthFirstSearchMazeSolver : IMazeSolver<BfsNode> internal class BreadthFirstSearchMazeSolver : IMazeSolver
{ {
public List<BfsNode> Solve(BfsNode node, BfsNode[,] maze, GraphicsMode graphicsMode = GraphicsMode.None) public List<MazeNode> Solve(MazeNode node, MazeNode[,] maze, GraphicsMode graphicsMode = GraphicsMode.None)
{ {
if (!node.Start) if (!node.Start)
{ {
throw new MazeException("Node is not a start node."); throw new MazeException("Node is not a start node.");
} }
BfsNode currentNode; MazeNode currentNode;
Queue<BfsNode> queue = new Queue<BfsNode>(); Queue<MazeNode> queue = new Queue<MazeNode>();
queue.Enqueue(node); queue.Enqueue(node);
@@ -28,8 +28,8 @@ internal class BreadthFirstSearchMazeSolver : IMazeSolver<BfsNode>
if (currentNode.Goal) if (currentNode.Goal)
{ {
List<BfsNode> path = new List<BfsNode>(); List<MazeNode> path = new List<MazeNode>();
BfsNode? pathNode = currentNode; MazeNode? pathNode = currentNode;
while (pathNode is not null && !pathNode.Start) while (pathNode is not null && !pathNode.Start)
{ {
@@ -41,7 +41,7 @@ internal class BreadthFirstSearchMazeSolver : IMazeSolver<BfsNode>
return path; return path;
} }
foreach (BfsNode child in currentNode.Neighbors.Where(n => !n.Visited && !n.Wall)) foreach (MazeNode child in currentNode.Neighbors.Where(n => !n.Visited && !n.Wall))
{ {
child.Parent = currentNode; child.Parent = currentNode;
child.Visited = true; child.Visited = true;
@@ -65,6 +65,6 @@ internal class BreadthFirstSearchMazeSolver : IMazeSolver<BfsNode>
} }
} }
return new List<BfsNode>(); return new List<MazeNode>();
} }
} }
+8 -8
View File
@@ -3,18 +3,18 @@ using Maze.Utlis;
namespace Maze.Solvers; namespace Maze.Solvers;
internal class DepthFirstSearchMazeSolver : IMazeSolver<DfsNode> internal class DepthFirstSearchMazeSolver : IMazeSolver
{ {
public List<DfsNode> Solve(DfsNode node, DfsNode[,] maze, GraphicsMode graphicsMode = GraphicsMode.None) public List<MazeNode> Solve(MazeNode node, MazeNode[,] maze, GraphicsMode graphicsMode = GraphicsMode.None)
{ {
if (!node.Start) if (!node.Start)
{ {
throw new MazeException("Node is not a start node."); throw new MazeException("Node is not a start node.");
} }
DfsNode? currentNode = node; MazeNode? currentNode = node;
DfsNode? tempNode = null; MazeNode? tempNode = null;
Stack<DfsNode> path = new Stack<DfsNode>(); Stack<MazeNode> path = new Stack<MazeNode>();
while (!currentNode.Goal) while (!currentNode.Goal)
{ {
@@ -25,7 +25,7 @@ internal class DepthFirstSearchMazeSolver : IMazeSolver<DfsNode>
throw new MazeException("Node has no neighbors."); throw new MazeException("Node has no neighbors.");
} }
if (path.TryPeek(out DfsNode? prevNode)) if (path.TryPeek(out MazeNode? prevNode))
{ {
prevNode.Updated = true; prevNode.Updated = true;
} }
@@ -39,7 +39,7 @@ internal class DepthFirstSearchMazeSolver : IMazeSolver<DfsNode>
throw new MazeException("No path found."); throw new MazeException("No path found.");
} }
if (path.TryPeek(out DfsNode? prevNode2)) if (path.TryPeek(out MazeNode? prevNode2))
{ {
prevNode2.Updated = true; prevNode2.Updated = true;
} }
@@ -76,7 +76,7 @@ internal class DepthFirstSearchMazeSolver : IMazeSolver<DfsNode>
} }
} }
List<DfsNode> solution = path.ToList(); List<MazeNode> solution = path.ToList();
solution.ForEach(n => n.Updated = true); solution.ForEach(n => n.Updated = true);
return solution; return solution;
} }
+2 -2
View File
@@ -2,7 +2,7 @@
namespace Maze.Solvers; namespace Maze.Solvers;
internal interface IMazeSolver<T> where T : BaseMazeNode<T>, new() internal interface IMazeSolver
{ {
public List<T> Solve(T node, T[,] maze, GraphicsMode graphicsMode = GraphicsMode.None); public List<MazeNode> Solve(MazeNode node, MazeNode[,] maze, GraphicsMode graphicsMode = GraphicsMode.None);
} }
+6 -6
View File
@@ -4,18 +4,18 @@ namespace Maze.Utlis;
internal class MazeParser internal class MazeParser
{ {
public T[,] FromFile<T>(string filePath, out T startNode) where T : BaseMazeNode<T>, new() public MazeNode[,] FromFile(string filePath, out MazeNode startNode)
{ {
return FromString(File.ReadAllText(filePath), out startNode); return FromString(File.ReadAllText(filePath), out startNode);
} }
public T[,] FromString<T>(string input, out T startNode) where T : BaseMazeNode<T>, new() public MazeNode[,] FromString(string input, out MazeNode startNode)
{ {
string[] lines = input.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None); string[] lines = input.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);
startNode = null!; startNode = null!;
T? goalNode = null; MazeNode? goalNode = null;
T[,] nodes = new T[lines.Length, lines[0].Length]; MazeNode[,] nodes = new MazeNode[lines.Length, lines[0].Length];
// Create nodes from input // Create nodes from input
for (int y = 0; y < lines.Length; y++) for (int y = 0; y < lines.Length; y++)
@@ -24,7 +24,7 @@ internal class MazeParser
{ {
char c = lines[y][x]; char c = lines[y][x];
T node = new T MazeNode node = new MazeNode
{ {
Wall = c == '+', Wall = c == '+',
Start = c == 'S', Start = c == 'S',
@@ -69,7 +69,7 @@ internal class MazeParser
{ {
for (int x = 0; x < lines[0].Length; x++) for (int x = 0; x < lines[0].Length; x++)
{ {
T node = nodes[y, x]; MazeNode node = nodes[y, x];
if (x < lines[0].Length - 1) if (x < lines[0].Length - 1)
{ {
+4 -4
View File
@@ -6,13 +6,13 @@ namespace Maze.Utlis;
internal class MazePrinter internal class MazePrinter
{ {
public void Print<T>(T[,] nodes, List<T>? solution = null, bool redrawAll = false) where T : BaseMazeNode<T> public void Print(MazeNode[,] nodes, List<MazeNode>? solution = null, bool redrawAll = false)
{ {
for (int y = 0; y < nodes.GetLength(0); y++) for (int y = 0; y < nodes.GetLength(0); y++)
{ {
for (int x = 0; x < nodes.GetLength(1); x++) for (int x = 0; x < nodes.GetLength(1); x++)
{ {
T node = nodes[y, x]; MazeNode node = nodes[y, x];
if (!node.Updated && !redrawAll) if (!node.Updated && !redrawAll)
{ {
@@ -51,7 +51,7 @@ internal class MazePrinter
} }
} }
public void PrintColored<T>(T[,] nodes, List<T>? solution = null, bool redrawAll = false) where T : BaseMazeNode<T> public void PrintColored(MazeNode[,] nodes, List<MazeNode>? solution = null, bool redrawAll = false)
{ {
Console.BackgroundColor = ConsoleColor.Black; Console.BackgroundColor = ConsoleColor.Black;
Console.CursorVisible = false; Console.CursorVisible = false;
@@ -60,7 +60,7 @@ internal class MazePrinter
{ {
for (int x = 0; x < nodes.GetLength(1); x++) for (int x = 0; x < nodes.GetLength(1); x++)
{ {
T node = nodes[y, x]; MazeNode node = nodes[y, x];
if (!node.Updated && !redrawAll) if (!node.Updated && !redrawAll)
{ {
+19
View File
@@ -0,0 +1,19 @@
using Maze.Nodes;
using Maze.Solvers;
namespace Maze.Utlis;
internal class PathFinder
{
public IMazeSolver MazeSolver { get; set; }
public PathFinder(IMazeSolver mazeSolver)
{
MazeSolver = mazeSolver;
}
public List<MazeNode> FindPath(MazeNode node, MazeNode[,] maze, GraphicsMode graphicsMode = GraphicsMode.None)
{
return MazeSolver.Solve(node, maze, graphicsMode);
}
}