Created
January 10, 2024 20:42
-
-
Save pjmagee/88fca76fa778710f5972ba612b16131e to your computer and use it in GitHub Desktop.
flat nodes and edges
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| void Main() | |
| { | |
| var g = new Graph(); | |
| var p = new Player(); | |
| p.Location = g.Nodes[0]; | |
| var destination = g.Edges[3]; | |
| PathFinder finder = new PathFinder(); | |
| finder.FindPath(g, p.Location, destination).Dump(); | |
| } | |
| public class Graph | |
| { | |
| public List<Node> Nodes { get; } = new(); | |
| public List<Edge> Edges { get; } = new(); | |
| public Graph() | |
| { | |
| var context = new Context(){ Name = "Starting zone" }; | |
| var node1 = new Node() { Name = "N1", Context = context }; | |
| var node2 = new Node() { Name = "N2", Context = context }; | |
| var node3 = new Node() { Name = "N3", Context = context }; | |
| var node4 = new Node() { Name = "N4", Context = context }; | |
| var node4_1 = new Node() { Name = "N4N1", Context = context }; | |
| var node5 = new Node() { Name = "N5", Context = context }; | |
| var edge1 = new Edge() { Name = "N1 -> N2", From = node1, To = node2, Context = context }; | |
| var edge2 = new Edge() { Name = "N2 -> N3", From = node2, To = node3, Context = context }; | |
| var edge3 = new Edge() { Name = "N3 -> N4", From = node3, To = node4, Context = context }; | |
| var edge4 = new Edge() { Name = "N4 -> N5", From = node4, To = node5, Context = context }; | |
| var edge5 = new Edge() { Name = "N5 -> NULL", From = node5, To = null, Context = context }; | |
| var edge6 = new Edge() { Name = "N4 -> N4N1", From = node4, To = node4_1, Context = context }; | |
| Nodes.AddRange(new[] { node1, node2, node3, node4, node4_1, node5 }); | |
| Edges.AddRange(new[] { edge1, edge2, edge3, edge4, edge5, edge6 }); | |
| } | |
| } | |
| /// <summary> | |
| /// Context about the Location, is it a building, an area, etc. | |
| /// A context can be shared by nodes and edges (A location) | |
| /// </summary> | |
| public class Context | |
| { | |
| public string Name { get; set; } | |
| } | |
| public class Player | |
| { | |
| public Location? Location { get; set; } | |
| } | |
| public class PathFinder | |
| { | |
| public List<Location> FindPath(Graph graph, Location start, Location goal) | |
| { | |
| var queue = new Queue<Location>(); | |
| var visited = new HashSet<Location>(); | |
| var cameFrom = new Dictionary<Location, Location>(); | |
| if (start == null || goal == null) | |
| { | |
| throw new ArgumentNullException("Start or goal location is null."); | |
| } | |
| queue.Enqueue(start); | |
| visited.Add(start); | |
| cameFrom[start] = null; // The start location came from nowhere. | |
| while (queue.Count > 0) | |
| { | |
| var current = queue.Dequeue(); | |
| if (current.Equals(goal)) | |
| { | |
| return ReconstructPath(cameFrom, current); | |
| } | |
| foreach (var neighbor in GetNeighbors(graph, current, visited)) | |
| { | |
| if (!visited.Contains(neighbor)) | |
| { | |
| queue.Enqueue(neighbor); | |
| visited.Add(neighbor); | |
| cameFrom[neighbor] = current; | |
| } | |
| } | |
| } | |
| return null; // Path not found | |
| } | |
| private IEnumerable<Location> GetNeighbors(Graph graph, Location current, HashSet<Location> visited) | |
| { | |
| var neighbors = new List<Location>(); | |
| foreach (var edge in graph.Edges) | |
| { | |
| if (edge.From == current && !visited.Contains(edge.To) && !edge.IsBlocked) | |
| { | |
| neighbors.Add(edge); | |
| } | |
| else if (edge.To == current && !visited.Contains(edge.From)) | |
| { | |
| neighbors.Add(edge); | |
| } | |
| } | |
| if (current is Edge e) | |
| { | |
| if (!visited.Contains(e.From)) | |
| { | |
| neighbors.Add(e.From); // Add the node where the edge starts | |
| } | |
| if (!visited.Contains(e.To)) | |
| { | |
| neighbors.Add(e.To); // Add the node where the edge ends | |
| } | |
| } | |
| return neighbors; | |
| } | |
| private List<Location> ReconstructPath(Dictionary<Location, Location> cameFrom, Location? current) | |
| { | |
| var path = new List<Location>(); | |
| while (current != null) | |
| { | |
| path.Insert(0, current); | |
| current = cameFrom.TryGetValue(current, out var previous) ? previous : null; | |
| } | |
| return path; | |
| } | |
| } | |
| public class Location | |
| { | |
| public Context Context { get; set; } | |
| public string Name { get; set; } | |
| public string Description { get; set; } | |
| public string Type => this.GetType().Name; | |
| } | |
| /// <summary> | |
| /// A node is similar to an Edge, except can have Child Nodes. | |
| /// An Edge, cannot have Child edges. | |
| /// </summary> | |
| public class Node : Location, IEquatable<Node> | |
| { | |
| /// <summary> | |
| /// Optional edges (more for organisation and structure purposes) | |
| /// </summary> | |
| public List<Edge> Edges { get; set; } = new(); | |
| public bool Equals(Node? other) | |
| { | |
| return this.Name.Equals(other?.Name); | |
| } | |
| } | |
| /// <summary> | |
| /// An edge is a type of location that connects two other locations together | |
| /// Similar to a Graph of Nodes/Vertices and Edges/Connections | |
| /// The difference is that, a player can also be located at an Edge | |
| /// An edge in this case, can be anything from a path, ladder, street or even another building, but provides semantics to how two Nodes are related. | |
| /// </summary> | |
| public class Edge : Location, IEquatable<Edge> | |
| { | |
| /// <summary> | |
| /// Where this edge begins | |
| /// </summary> | |
| public Location From { get; init; } | |
| /// <summary> | |
| /// It is possible for an edge to lead to another edge | |
| /// Generally, an Edge would link two 'Nodes' however, it's possible that an Edge could lead to another Edge | |
| /// </summary> | |
| public Location? To { get; init; } | |
| /// <summary> | |
| /// Sometimes, an Edge may be blocked due to various factors: Player level, skill, environment, quest, dynamic event etc. | |
| /// </summary> | |
| public bool IsBlocked { get; init; } | |
| public bool Equals(Edge? other) | |
| { | |
| return this.Name.Equals(other?.Name); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment