From 304218151a57928b137334499cdebd8ec0dca7d2 Mon Sep 17 00:00:00 2001 From: Pablu23 <43807157+Pablu23@users.noreply.github.com> Date: Tue, 10 Jan 2023 22:41:29 +0100 Subject: [PATCH] Add project files. --- .gitignore | 7 + AStar Pathfinding.csproj | 53 +++++++ AStar Pathfinding.sln | 25 +++ Input.txt | 20 +++ Program.cs | 322 +++++++++++++++++++++++++++++++++++++++ README.md | 5 + 6 files changed, 432 insertions(+) create mode 100644 .gitignore create mode 100644 AStar Pathfinding.csproj create mode 100644 AStar Pathfinding.sln create mode 100644 Input.txt create mode 100644 Program.cs create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..926e4d2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.idea/ +.vs/ +bin/ +obj/ +Properties/ +App.config +*.DotSettings.user \ No newline at end of file diff --git a/AStar Pathfinding.csproj b/AStar Pathfinding.csproj new file mode 100644 index 0000000..5501bbf --- /dev/null +++ b/AStar Pathfinding.csproj @@ -0,0 +1,53 @@ + + + + + Debug + AnyCPU + {A7DEE69C-2567-45A5-9694-795832A800E0} + Exe + AStar_Pathfinding + AStar Pathfinding + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AStar Pathfinding.sln b/AStar Pathfinding.sln new file mode 100644 index 0000000..d218314 --- /dev/null +++ b/AStar Pathfinding.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29411.108 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AStar Pathfinding", "AStar Pathfinding.csproj", "{A7DEE69C-2567-45A5-9694-795832A800E0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A7DEE69C-2567-45A5-9694-795832A800E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A7DEE69C-2567-45A5-9694-795832A800E0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7DEE69C-2567-45A5-9694-795832A800E0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A7DEE69C-2567-45A5-9694-795832A800E0}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E46F2074-9111-48FB-8443-52F330576EAE} + EndGlobalSection +EndGlobal diff --git a/Input.txt b/Input.txt new file mode 100644 index 0000000..5a3b82a --- /dev/null +++ b/Input.txt @@ -0,0 +1,20 @@ +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +X X XX X X X X +X XXX X X X X X +X X X XXXXXXXXXXXXX XXXXXXX +X XX XX X X X +X XXX XX X X X +X X X XXXX X +X X E XXXXXX X +X X X XXXXXXX X +X XX XXXX X +X X X +X X X +X X X +X X XXXXXXXXXXXX X +X X XXXXXXXXXXXXX X +X XXXX XXXX X X XXXXX X +X X X XXXXXS X X +X X X X X X +X X X X XX XX +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \ No newline at end of file diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..9f9682a --- /dev/null +++ b/Program.cs @@ -0,0 +1,322 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AStar_Pathfinding +{ + public enum TypeOfNode { Start, Normal, End, Wall }; + + public readonly struct Coordinates + { + public readonly int X; + public readonly int Y; + + public Coordinates(int x, int y) + { + X = x; + Y = y; + } + } + + public class Node + { + public TypeOfNode TypeOfNode; + //Vorherige Node / weg zu dieser Node + public Node Previous; + // X und Y position + public Coordinates Coordinates; + //G = Distance from starting node / H = Distance from end node / F = G + H + public int G, H, F; + + public Node(Coordinates coordinates) + { + Coordinates = coordinates; + TypeOfNode = TypeOfNode.Normal; + } + } + + public class Map + { + private int Width { get; set; } + private int Height { get; set; } + private Node _start; + private Node _end; + private readonly Node[,] _map; + + // Auch offene Liste gennant + private readonly List _searching = new List(); + // Auch geschlossene Liste gennant + public readonly List Searched = new List(); + + public Map(int wi, int he) + { + Width = wi; + Height = he; + + _map = new Node[wi, he]; + for (int i = 0; i < Width; i++) + { + for (int j = 0; j < Height; j++) + { + _map[i, j] = new Node(new Coordinates(i, j)); + } + } + } + public void SetStart(Coordinates cor) + { + _map[cor.X, cor.Y].Coordinates = cor; + _map[cor.X, cor.Y].TypeOfNode = TypeOfNode.Start; + _start = _map[cor.X, cor.Y]; + } + public void SetEnd(Coordinates cor) + { + _map[cor.X, cor.Y].Coordinates = cor; + _map[cor.X, cor.Y].TypeOfNode = TypeOfNode.End; + _end = _map[cor.X, cor.Y]; + } + + public Node GetEnd() + { + return _end; + } + public void SetWalls(List walls) + { + foreach (int[] t in walls) + { + _map[t[0], t[1]].TypeOfNode = TypeOfNode.Wall; + } + } + + //Errechnet die G Cost / die Distanz zu dem Startpunkt + private void CalculateGCost(int x, int y, Node previous) + { + int cost = 10; + + if (_map[x, y].TypeOfNode == TypeOfNode.Start || _map[x, y].TypeOfNode == TypeOfNode.End) return; + if (previous.Coordinates.X != x && previous.Coordinates.Y != y) + { + cost += 4; + } + + _map[x, y].G = previous.G + cost; + _map[x, y].Previous = previous; + } + + //Errechnet für die ganze Karte die H Cost / die Distanz zu dem Endpunkt + public void CalculateAllHCost() + { + for (int i = 0; i < Width; i++) + { + for (int j = 0; j < Height; j++) + { + CalculateHCost(i, j); + } + } + } + + //Errechnet für dieses eine Feld die H Cost / die Distanz zu dem Endpunkt + private void CalculateHCost(int x, int y) + { + if (_map[x, y].TypeOfNode == TypeOfNode.End) return; + //Wie weit das ende von dem jetzigen Punkt entfernt ist + _map[x, y].H = (Math.Abs(_end.Coordinates.X - x) + Math.Abs(_end.Coordinates.Y - y)) * 10; + } + + //F Cost ist H Cost + G Cost + private void CalculateFCost(int x, int y) => _map[x, y].F = _map[x, y].G + _map[x, y].H; + public void StartPathFinding() + { + _searching.Add(_start); + ShowMap(); + Search(); + } + //Findet die benachbarten Nodes und fügt sie in die offene Liste hinzu, wenn sie dort nicht schon sind mit einem besseren vorgänger. + private void PathFinding(int x, int y) + { + for (int i = -1; i < 2; i++) + { + for (int j = -1; j < 2; j++) + { + if (!((x + i >= 0 && x + i < Width) & (y + j >= 0 && y + j < Height))) continue; + if (_map[x + i, y + j].TypeOfNode == TypeOfNode.Wall) continue; + if (_map[x + i, y + j].G == 0 & _map[x + i, y + j].TypeOfNode != TypeOfNode.Start) + { + _map[x + i, y + j].Previous = _map[x, y]; + _searching.Add(_map[x + i, y + j]); + } + else if (_map[x + i, y + j].G > _map[x, y].G + (i == 0 && j == 0 ? 10 : 14)) + { + _map[x + i, y + j].Previous = _map[x, y]; + var t = Searched.Find( + item => item.Coordinates.X == x + + i && item.Coordinates.Y == y + j); + Searched.Remove(t); + _searching.Add(_map[x + i, y + j]); + } + } + } + } + //Anweisungen wie der Weg gefunden wird + private void Search() + { + Node node; + do + { + foreach (var item in _searching) + { + CalculateGCost(item.Coordinates.X, item.Coordinates.Y, item.Previous); + CalculateFCost(item.Coordinates.X, item.Coordinates.Y); + } + + //Sucht die niedrigste F Cost + int lowestF = _searching.Min(c => c.F); + //Sucht in der offenen Liste nach den Nodes mit der niedrigsten F Cost + var nodes = _searching.FindAll(c => c.F == lowestF); + //Sucht unter den niedrigsten F Cost Nodes nach der niedrigsten H Cost + int lowestH = nodes.Min(c => c.H); + //Wählt die Node mit der niedrigsten F und H Cost aus + node = nodes.Find(c => c.H == lowestH); + + //Sollte die Node zufälligerweise die End-Node sein, wird die Suche nach der End-Node abgebrochen + if (node.TypeOfNode == TypeOfNode.End) + break; + + //Sucht für die Node mit der niedrigsten F und H Cost die Nachbarn und fügt sie der offenen Liste hinzu + PathFinding(node.Coordinates.X, node.Coordinates.Y); + //Entfernt die Node von der offenen Liste + _searching.Remove(node); + //und fügt sie der geschlossenen Liste hinzu + Searched.Add(node); + } while (_searching.Count != 0 && node.TypeOfNode != TypeOfNode.End); + } + public void ShowMap() + { + for (int i = 0; i < Width; i++) + { + for (int j = 0; j < Height; j++) + { + Console.SetCursorPosition(i, j); + + switch (_map[i,j].TypeOfNode) + { + case TypeOfNode.Start: + Console.ForegroundColor = ConsoleColor.Blue; + break; + case TypeOfNode.End: + Console.ForegroundColor = ConsoleColor.Yellow; + break; + default: + Console.ForegroundColor = ConsoleColor.Gray; + break; + } + + //Schreibt ein X wenn es sich bei dem Feld um eine Wand / das Ende / den Start handelt + Console.WriteLine(_map[i, j].TypeOfNode == TypeOfNode.Wall | + _map[i, j].TypeOfNode == TypeOfNode.End | + _map[i, j].TypeOfNode == TypeOfNode.Start ? "X" : " "); + } + } + } + + public void ShowPath(Node node) + { + while (true) + { + System.Threading.Thread.Sleep(100); + Console.SetCursorPosition(node.Coordinates.X, node.Coordinates.Y); + Console.BackgroundColor = ConsoleColor.White; + Console.WriteLine("*"); + Console.BackgroundColor = ConsoleColor.Black; + switch (node.TypeOfNode) + { + case TypeOfNode.Start: + //Die Reihe ist fertig, es wird an das ende der Map gesetzt um weiter Infos + //auf der Konsole auszugeben ohne die Karte zu überschreiben + Console.SetCursorPosition(0, Height + 1); + break; + default: + //Zeigt die nächste Node in der Reihe + node = node.Previous; + continue; + } + + break; + } + } + } + + internal static class Program + { + private static void Main() + { + Console.ReadKey(true); + + int lineCount = 0; + int longestLine = 0; + string line; + + List walls = new List(); + + Coordinates start = new Coordinates(); + Coordinates end = new Coordinates(); + + System.IO.StreamReader file = + new System.IO.StreamReader(@"..\..\Input.txt"); + while ((line = file.ReadLine()) != null) + { + if (line.Length > longestLine) + { + longestLine = line.Length; + } + + int counter2 = 0; + foreach (char x in line) + { + switch (x) + { + case 'X': + walls.Add(new[] { counter2, lineCount }); + break; + + case 'S': + start = new Coordinates(counter2, lineCount); + break; + + case 'E': + end = new Coordinates(counter2, lineCount); + break; + } + counter2++; + } + lineCount++; + } + + var map = new Map(longestLine, lineCount); + + map.SetStart(start); + map.SetEnd(end); + map.SetWalls(walls); + + file.Dispose(); + var sw = new System.Diagnostics.Stopwatch(); + sw.Start(); + map.CalculateAllHCost(); + map.StartPathFinding(); + sw.Stop(); + + map.ShowMap(); + map.ShowPath(map.GetEnd()); + + Console.WriteLine("The Path took {0} ms to calculate", sw.ElapsedMilliseconds); + + foreach (var node in map.Searched) + { + //Alle untersuchten Nodes werden einmal aufgeführt + Console.WriteLine("{0,-6} : G = {1,-4}| H = {2,-4}| F = {3,-4}", + node.Coordinates.X + ", " + node.Coordinates.Y, node.G, node.H, node.F); + } + + Console.ReadKey(true); + } + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..29118c7 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# AStar Pathfinding + +A Project i did for fun awhile ago. +Its still fun to run sometimes. +Probably not the fastest Astar out there \ No newline at end of file