diff --git a/Paths/Assets/Scripts/Algorithms/AStar.cs b/Paths/Assets/Scripts/Algorithms/AStar.cs index cacfeb1..90b1e05 100644 --- a/Paths/Assets/Scripts/Algorithms/AStar.cs +++ b/Paths/Assets/Scripts/Algorithms/AStar.cs @@ -1,68 +1,99 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Numerics; -using Algorithms; -public class AStar : IPathfinding { - private NodeGrid _nodeGrid; +namespace Algorithms { + public class AStar : IPathfinding { + private NodeGrid _nodeGrid; - private Stack _path; - private List _openList; - private List _closedList; - - public AStar(NodeGrid nodeGrid) { - this._nodeGrid = nodeGrid; - } + private Stack _path; + private List _openList; + private List _closedList; + private List _states; - public Tuple, Stack> FindPath(Vector2 Start, Vector2 End) { - var start = new Node(Start, true); - var end = new Node(End, true); + private Vector2 _start; + private Vector2 _end; - _path = new Stack(); - _openList = new List(); - _closedList = new List(); - Node current = start; + public AStar(NodeGrid nodeGrid) { + this._nodeGrid = nodeGrid; + _states = new List(); + } - // add start node to Open List - _openList.Add(start); + public Stack FindPath(Vector2 Start, Vector2 End) { + this._start = Start; + this._end = End; - while (_openList.Count != 0 && !_closedList.Exists(x => x.Position == end.Position)) { - current = _openList[0]; - _openList.Remove(current); - _closedList.Add(current); - List adjacentNodes = this._nodeGrid.GetAdjacentNodes(current); + var start = new Node(Start, true); + var end = new Node(End, true); + + RecordState(); + _path = new Stack(); + _openList = new List(); + _closedList = new List(); + Node current = start; - foreach (Node n in adjacentNodes) { - if (!_closedList.Contains(n) && n.Walkable) { - if (!_openList.Contains(n)) { - n.Parent = current; - n.DistanceToTarget = NodeGrid.Manhattan(n, end); - n.Cost = n.Weight + n.Parent.Cost; - _openList.Add(n); - _openList = _openList.OrderBy(node => node.F).ToList(); + // add start node to Open List + _openList.Add(start); + + while (_openList.Count != 0 && !_closedList.Exists(x => x.Position == end.Position)) { + current = _openList[0]; + _openList.Remove(current); + _closedList.Add(current); + RecordState(); + + IEnumerable adjacentNodes = this._nodeGrid.GetAdjacentNodes(current); + + foreach (Node n in adjacentNodes) { + if (!_closedList.Contains(n) && n.Walkable) { + if (!_openList.Contains(n)) { + n.Parent = current; + n.DistanceToTarget = NodeGrid.Manhattan(n, end); + n.Cost = n.Weight + n.Parent.Cost; + + _openList.Add(n); + _openList = _openList.OrderBy(node => node.F).ToList(); + + RecordState(); + } } } } + + // construct path, if end was not closed return null + if (!_closedList.Exists(x => x.Position == end.Position)) { + return null; + } + + // if all good, return path + Node temp = _closedList[_closedList.IndexOf(current)]; + if (temp == null) return null; + do { + _path.Push(temp); + temp = temp.Parent; + } while (temp != start && temp != null); + + return _path; } - // construct path, if end was not closed return null - if (!_closedList.Exists(x => x.Position == end.Position)) { - return null; + /// + /// Records the current state of the pathfinding algorithm in the grid. + /// + public void RecordState() { + // TODO: Record basic grid node types + // TODO: Record timing information + // TODO: Record pathfinding state information (stages, heuristic, statistical info) + this._states.Add( + new GridState(this._nodeGrid, this._openList, this._closedList, _start, _end, _path) + ); } - // if all good, return path - Node temp = _closedList[_closedList.IndexOf(current)]; - if (temp == null) return null; - do { - _path.Push(temp); - temp = temp.Parent; - } while (temp != start && temp != null); - - return _path; - } - - private StateGrid compileState() { + /// + /// Returns the current list of grid states of the pathfinding algorithm. + /// + /// A list of GridState objects representing the pathfinding algorithm's progress + public List GetStates() { + return this._states; + } } } \ No newline at end of file diff --git a/Paths/Assets/Scripts/Algorithms/NodeGrid.cs b/Paths/Assets/Scripts/Algorithms/NodeGrid.cs index f799960..7465665 100644 --- a/Paths/Assets/Scripts/Algorithms/NodeGrid.cs +++ b/Paths/Assets/Scripts/Algorithms/NodeGrid.cs @@ -2,59 +2,76 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; -using Algorithms; -using NUnit.Framework.Constraints; -using UnityEngine.SocialPlatforms; -public class NodeGrid { - private List> grid; - private readonly int _width; - private readonly int _height; +namespace Algorithms { + public class NodeGrid { + private List> grid; + public readonly int Width; + public readonly int Height; - public NodeGrid(int width, int height) { - if (width <= 0) - throw new ArgumentOutOfRangeException(nameof(width), - $"The width of the grid must be a positive non-zero integer."); - if (height <= 0) - throw new ArgumentOutOfRangeException(nameof(height), - $"The height of the grid must be a positive non-zero integer."); + public NodeGrid(int width, int height) { + if (width <= 0) + throw new ArgumentOutOfRangeException(nameof(width), + $"The width of the grid must be a positive non-zero integer."); + if (height <= 0) + throw new ArgumentOutOfRangeException(nameof(height), + $"The height of the grid must be a positive non-zero integer."); - this.grid = new List>(width); - // Fill grid with width*height nodes, zero-indexed - foreach (int x in Enumerable.Range(0, width - 1)) { - List list = new List(height); - foreach (int y in Enumerable.Range(0, height)) - list.Add(new Node(new Vector2(x, y), true)); + this.grid = new List>(width); + // Fill grid with width*height nodes, zero-indexed + foreach (int x in Enumerable.Range(0, width - 1)) { + List list = new List(height); + foreach (int y in Enumerable.Range(0, height)) + list.Add(new Node(new Vector2(x, y), true)); - this.grid.Add(list); + this.grid.Add(list); + } + + this.Width = width; + this.Height = height; } - this._width = width; - this._height = height; - } + public NodeGrid(List> grid) { + this.grid = grid; - public NodeGrid(List> grid) { - this.grid = grid; + this.Height = this.grid[0].Count; + this.Width = this.grid.Count; + } - this._height = this.grid[0].Count; - this._width = this.grid.Count; - } + public IEnumerable GetAdjacentNodes(Node node) { + List temp = new List(); - public List GetAdjacentNodes(Node node) { - List temp = new List(); + int row = (int) node.Position.Y; + int col = (int) node.Position.X; - int row = (int) node.Position.Y; - int col = (int) node.Position.X; + if (row + 1 < Height) temp.Add(this.grid[col][row + 1]); + if (row - 1 >= 0) temp.Add(this.grid[col][row - 1]); + if (col - 1 >= 0) temp.Add(this.grid[col - 1][row]); + if (col + 1 < Width) temp.Add(this.grid[col + 1][row]); - if (row + 1 < _height) temp.Add(this.grid[col][row + 1]); - if (row - 1 >= 0) temp.Add(this.grid[col][row - 1]); - if (col - 1 >= 0) temp.Add(this.grid[col - 1][row]); - if (col + 1 < _width) temp.Add(this.grid[col + 1][row]); + return temp; + } - return temp; - } + public bool IsValid(int x, int y) { + return x > 0 && x < this.Width && y > 0 && y < this.Height; + } + + /// + /// Retrieves a node at a given coordinate. + /// + /// the X (column) coordinate + /// the Y (row) coordinate + /// A Node object + /// when the coordinate given does not exist on the grid + public Node GetNode(int x, int y) { + if(!IsValid(x, y)) + throw new ArgumentOutOfRangeException(); + + return this.grid[x][y]; + } - public static float Manhattan(Node first, Node second) { - return Math.Abs(first.Position.X - second.Position.X) + Math.Abs(first.Position.Y - second.Position.Y); + public static float Manhattan(Node first, Node second) { + return Math.Abs(first.Position.X - second.Position.X) + Math.Abs(first.Position.Y - second.Position.Y); + } } } \ No newline at end of file diff --git a/Paths/Assets/Scripts/GridState.cs b/Paths/Assets/Scripts/GridState.cs new file mode 100644 index 0000000..657e091 --- /dev/null +++ b/Paths/Assets/Scripts/GridState.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using Algorithms; + +public class GridState { + private List> _grid; + + public GridState(NodeGrid grid, IEnumerable seen, IEnumerable expanded, Vector2 start, Vector2 end, IReadOnlyCollection path) { + this._grid = new List>(grid.Width); + + // Add walls and empty tiles + foreach (var x in Enumerable.Range(0, grid.Width - 1)) { + this._grid.Add(new List(grid.Height)); + foreach (var y in Enumerable.Range(0, grid.Height - 1)) { + Node node = grid.GetNode(x, y); + this._grid[x].Add(!node.Walkable ? GridNodeType.Wall : GridNodeType.Empty); + } + } + + // Add 'seen' tiles + foreach (Node seenNode in seen) { + this._grid[(int) seenNode.Position.X][(int) seenNode.Position.Y] = GridNodeType.Seen; + } + + // Add 'expanded' tiles + foreach (Node expandedNode in expanded) { + this._grid[(int) expandedNode.Position.X][(int) expandedNode.Position.Y] = GridNodeType.Expanded; + } + + // Set start and end tiles + this._grid[(int) start.X][(int) start.Y] = GridNodeType.Start; + this._grid[(int) end.X][(int) end.Y] = GridNodeType.End; + + // Add 'path' tiles + if (path != null) + foreach (Node pathNode in path) + this._grid[(int) pathNode.Position.X][(int) pathNode.Position.Y] = GridNodeType.Path; + } +} \ No newline at end of file diff --git a/Paths/Assets/Scripts/GridState.cs.meta b/Paths/Assets/Scripts/GridState.cs.meta new file mode 100644 index 0000000..14a2e8b --- /dev/null +++ b/Paths/Assets/Scripts/GridState.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1a26aa7e514043e8911382088176258b +timeCreated: 1604887099 \ No newline at end of file diff --git a/Paths/Assets/Scripts/Pathfinding.cs b/Paths/Assets/Scripts/Pathfinding.cs index 21f7557..bc62ddc 100644 --- a/Paths/Assets/Scripts/Pathfinding.cs +++ b/Paths/Assets/Scripts/Pathfinding.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Numerics; using Algorithms; @@ -13,5 +12,12 @@ public interface IPathfinding { /// The position from which pathfinding begins /// The position trying to be found via pathfinding /// A List of NodeGridGrid objects representing the timeline of Pathfinding - Tuple, Stack> FindPath(Vector2 start, Vector2 end); + Stack FindPath(Vector2 start, Vector2 end); + + /// + /// Records the current state of the pathfinding algorithm. Internal usage only. + /// + void RecordState(); + + List GetStates(); } \ No newline at end of file