From f6f81c16b37b1e3da8eaf28e8aa3ac22baa04982 Mon Sep 17 00:00:00 2001 From: buddhisthead Date: Fri, 11 Oct 2019 11:17:52 -0700 Subject: [PATCH] Change A* implementation to use an interface and allow easier adapting to existing grid systems * Change Form1 to use interface GridI * Separate interface to a different file --- .../2dTileBasedPathFinding/Grid.cs | 198 ------------------ .../2dTileBasedPathFinding/GridI.cs | 21 ++ .../2dTileBasedPathFinding/PathFinding.cs | 12 +- .../2dTileBasedPathFinding/SquareGrid.cs | 190 +++++++++++++++++ .../TestProject/TestProject/Form1.cs | 2 +- .../TestProject/TestProject.csproj | 11 +- 6 files changed, 225 insertions(+), 209 deletions(-) delete mode 100755 Controls/PathFinding/2dTileBasedPathFinding/Grid.cs create mode 100644 Controls/PathFinding/2dTileBasedPathFinding/GridI.cs mode change 100755 => 100644 Controls/PathFinding/2dTileBasedPathFinding/PathFinding.cs create mode 100644 Controls/PathFinding/2dTileBasedPathFinding/SquareGrid.cs diff --git a/Controls/PathFinding/2dTileBasedPathFinding/Grid.cs b/Controls/PathFinding/2dTileBasedPathFinding/Grid.cs deleted file mode 100755 index 8c2ee03..0000000 --- a/Controls/PathFinding/2dTileBasedPathFinding/Grid.cs +++ /dev/null @@ -1,198 +0,0 @@ -/** - * Represent a grid of nodes we can search paths on. - * Based on code and tutorial by Sebastian Lague (https://www.youtube.com/channel/UCmtyQOKKmrMVaKuRXz02jbQ). - * - * Author: Ronen Ness. - * Since: 2016. -*/ -using System.Collections.Generic; - -namespace NesScripts.Controls.PathFind -{ - /// - /// A 2D grid of nodes we use to find path. - /// The grid mark which tiles are walkable and which are not. - /// - public class Grid - { - // nodes in grid - public Node[,] nodes; - - // grid size - int gridSizeX, gridSizeY; - - /// - /// Create a new grid with tile prices. - /// - /// A 2d array of tile prices. - /// 0.0f = Unwalkable tile. - /// 1.0f = Normal tile. - /// > 1.0f = costy tile. - /// < 1.0f = cheap tile. - /// - public Grid(float[,] tiles_costs) - { - // create nodes - CreateNodes(tiles_costs.GetLength(0), tiles_costs.GetLength(1)); - - // init nodes - for (int x = 0; x < gridSizeX; x++) - { - for (int y = 0; y < gridSizeY; y++) - { - nodes[x, y] = new Node(tiles_costs[x, y], x, y); - } - } - } - - /// - /// Create a new grid without tile prices, eg with just walkable / unwalkable tiles. - /// - /// A 2d array representing which tiles are walkable and which are not. - public Grid(bool[,] walkable_tiles) - { - // create nodes - CreateNodes(walkable_tiles.GetLength(0), walkable_tiles.GetLength(1)); - - // init nodes - for (int x = 0; x < gridSizeX; x++) - { - for (int y = 0; y < gridSizeY; y++) - { - nodes[x, y] = new Node(walkable_tiles[x, y] ? 1.0f : 0.0f, x, y); - } - } - } - - /// - /// Create the nodes grid and set size. - /// - /// Nodes grid width. - /// Nodes grid height. - private void CreateNodes(int width, int height) - { - gridSizeX = width; - gridSizeY = height; - nodes = new Node[gridSizeX, gridSizeY]; - } - - /// - /// Updates the already created grid with new tile prices. - /// - /// true, if grid was updated, false otherwise. - /// Tiles costs. - public void UpdateGrid (float[,] tiles_costs) - { - // check if need to re-create grid - if (nodes == null || - gridSizeX != tiles_costs.GetLength(0) || - gridSizeY != tiles_costs.GetLength(1)) - { - CreateNodes(tiles_costs.GetLength(0), tiles_costs.GetLength(1)); - } - - // update nodes - for (int x = 0; x < gridSizeX; x++) - { - for (int y = 0; y < gridSizeY; y++) - { - nodes[x, y].Update(tiles_costs[x, y], x, y); - } - } - } - - /// - /// Updates the already created grid without new tile prices, eg with just walkable / unwalkable tiles. - /// - /// true, if grid was updated, false otherwise. - /// Walkable tiles. - public void UpdateGrid (bool[,] walkable_tiles) - { - // check if need to re-create grid - if (nodes == null || - gridSizeX != walkable_tiles.GetLength(0) || - gridSizeY != walkable_tiles.GetLength(1)) - { - CreateNodes(walkable_tiles.GetLength(0), walkable_tiles.GetLength(1)); - } - - // update grid - for (int x = 0; x < gridSizeX; x++) - { - for (int y = 0; y < gridSizeY; y++) - { - nodes[x, y].Update(walkable_tiles[x, y] ? 1.0f : 0.0f, x, y); - } - } - } - - /// - /// Get all the neighbors of a given tile in the grid. - /// - /// Node to get neighbors for. - /// List of node neighbors. - public System.Collections.IEnumerable GetNeighbours(Node node, Pathfinding.DistanceType distanceType) - { - int x = 0, y = 0; - switch (distanceType) - { - case Pathfinding.DistanceType.Manhattan: - y = 0; - for (x = -1; x <= 1; ++x) - { - var neighbor = AddNodeNeighbour(x, y, node); - if (neighbor != null) - yield return neighbor; - } - - x = 0; - for (y = -1; y <= 1; ++y) - { - var neighbor = AddNodeNeighbour(x, y, node); - if (neighbor != null) - yield return neighbor; - } - break; - - case Pathfinding.DistanceType.Euclidean: - for (x = -1; x <= 1; x++) - { - for (y = -1; y <= 1; y++) - { - var neighbor = AddNodeNeighbour(x, y, node); - if (neighbor != null) - yield return neighbor; - } - } - break; - } - } - - /// - /// Adds the node neighbour. - /// - /// true, if node neighbour was added, false otherwise. - /// The x coordinate. - /// The y coordinate. - /// Node. - /// Neighbours. - Node AddNodeNeighbour(int x, int y, Node node) - { - if (x == 0 && y == 0) - { - return null; - } - - int checkX = node.gridX + x; - int checkY = node.gridY + y; - - if (checkX >= 0 && checkX < gridSizeX && checkY >= 0 && checkY < gridSizeY) - { - return nodes[checkX, checkY]; - } - - return null; - } - } - -} \ No newline at end of file diff --git a/Controls/PathFinding/2dTileBasedPathFinding/GridI.cs b/Controls/PathFinding/2dTileBasedPathFinding/GridI.cs new file mode 100644 index 0000000..9e6ba04 --- /dev/null +++ b/Controls/PathFinding/2dTileBasedPathFinding/GridI.cs @@ -0,0 +1,21 @@ +/** + * Represent a grid of nodes we can search paths on. + * Based on code and tutorial by Sebastian Lague (https://www.youtube.com/channel/UCmtyQOKKmrMVaKuRXz02jbQ). + * + * Author: Ronen Ness. + * Since: 2016. +*/ +using System.Collections.Generic; + +namespace NesScripts.Controls.PathFind { + + /// + /// A 2D grid of nodes we use to find paths with the PathFinding class. + /// The grid supplies Nodes which mark tiles with traversal costs. + /// The grid can be square or hexagonal (using offset coordinate system). + /// + public interface GridI { + Node GetNode (int x, int y); + IEnumerable GetNeighbours (Node currentNode, Pathfinding.DistanceType distance); + } +} \ No newline at end of file diff --git a/Controls/PathFinding/2dTileBasedPathFinding/PathFinding.cs b/Controls/PathFinding/2dTileBasedPathFinding/PathFinding.cs old mode 100755 new mode 100644 index 8282a75..4cdbdd3 --- a/Controls/PathFinding/2dTileBasedPathFinding/PathFinding.cs +++ b/Controls/PathFinding/2dTileBasedPathFinding/PathFinding.cs @@ -37,13 +37,13 @@ public enum DistanceType /// /// Find a path between two points. /// - /// Grid to search. + /// GridI to search. /// Starting position. /// Ending position. /// The type of distance, Euclidean or Manhattan. /// If true, will ignore tile price (how much it "cost" to walk on). /// List of points that represent the path to walk. - public static List FindPath(Grid grid, Point startPos, Point targetPos, DistanceType distance = DistanceType.Euclidean, bool ignorePrices = false) + public static List FindPath(GridI grid, Point startPos, Point targetPos, DistanceType distance = DistanceType.Euclidean, bool ignorePrices = false) { // find path List nodes_path = _ImpFindPath(grid, startPos, targetPos, distance, ignorePrices); @@ -69,10 +69,10 @@ public static List FindPath(Grid grid, Point startPos, Point targetPos, D /// The type of distance, Euclidean or Manhattan. /// If true, will ignore tile price (how much it "cost" to walk on). /// List of grid nodes that represent the path to walk. - private static List _ImpFindPath(Grid grid, Point startPos, Point targetPos, DistanceType distance = DistanceType.Euclidean, bool ignorePrices = false) + private static List _ImpFindPath(GridI grid, Point startPos, Point targetPos, DistanceType distance = DistanceType.Euclidean, bool ignorePrices = false) { - Node startNode = grid.nodes[startPos.x, startPos.y]; - Node targetNode = grid.nodes[targetPos.x, targetPos.y]; + Node startNode = grid.GetNode(startPos.x, startPos.y); + Node targetNode = grid.GetNode(targetPos.x, targetPos.y); List openSet = new List(); HashSet closedSet = new HashSet(); @@ -127,7 +127,7 @@ private static List _ImpFindPath(Grid grid, Point startPos, Point targetPo /// Starting node. /// Ending (target) node. /// Retraced path between nodes. - private static List RetracePath(Grid grid, Node startNode, Node endNode) + private static List RetracePath(GridI grid, Node startNode, Node endNode) { List path = new List(); Node currentNode = endNode; diff --git a/Controls/PathFinding/2dTileBasedPathFinding/SquareGrid.cs b/Controls/PathFinding/2dTileBasedPathFinding/SquareGrid.cs new file mode 100644 index 0000000..ff45dac --- /dev/null +++ b/Controls/PathFinding/2dTileBasedPathFinding/SquareGrid.cs @@ -0,0 +1,190 @@ +/** + * Represent a grid of nodes we can search paths on. + * Based on code and tutorial by Sebastian Lague (https://www.youtube.com/channel/UCmtyQOKKmrMVaKuRXz02jbQ). + * + * Author: Ronen Ness. + * Since: 2016. +*/ +using System.Collections.Generic; + +namespace NesScripts.Controls.PathFind { + /// + /// A 2D grid of nodes we use to find path. + /// The grid mark which tiles are walkable and which are not. + /// + public class SquareGrid : GridI { + // nodes in grid + private Node [,] nodes; + + // grid size + int gridSizeX, gridSizeY; + + /// + /// Create a new grid with tile prices. + /// + /// A 2d array of tile prices. + /// 0.0f = Unwalkable tile. + /// 1.0f = Normal tile. + /// > 1.0f = costy tile. + /// < 1.0f = cheap tile. + /// + public SquareGrid (float [,] tiles_costs) + { + // create nodes + CreateNodes (tiles_costs.GetLength (0), tiles_costs.GetLength (1)); + + // init nodes + for (int x = 0; x < gridSizeX; x++) { + for (int y = 0; y < gridSizeY; y++) { + nodes [x, y] = new Node (tiles_costs [x, y], x, y); + } + } + } + + /// + /// Create a new grid without tile prices, eg with just walkable / unwalkable tiles. + /// + /// A 2d array representing which tiles are walkable and which are not. + public SquareGrid (bool [,] walkable_tiles) + { + // create nodes + CreateNodes (walkable_tiles.GetLength (0), walkable_tiles.GetLength (1)); + + // init nodes + for (int x = 0; x < gridSizeX; x++) { + for (int y = 0; y < gridSizeY; y++) { + nodes [x, y] = new Node (walkable_tiles [x, y] ? 1.0f : 0.0f, x, y); + } + } + } + + /// + /// Create the nodes grid and set size. + /// + /// Nodes grid width. + /// Nodes grid height. + private void CreateNodes (int width, int height) + { + gridSizeX = width; + gridSizeY = height; + nodes = new Node [gridSizeX, gridSizeY]; + } + + /// + /// Updates the already created grid with new tile prices. + /// + /// true, if grid was updated, false otherwise. + /// Tiles costs. + public void UpdateGrid (float [,] tiles_costs) + { + // check if need to re-create grid + if (nodes == null || + gridSizeX != tiles_costs.GetLength (0) || + gridSizeY != tiles_costs.GetLength (1)) { + CreateNodes (tiles_costs.GetLength (0), tiles_costs.GetLength (1)); + } + + // update nodes + for (int x = 0; x < gridSizeX; x++) { + for (int y = 0; y < gridSizeY; y++) { + nodes [x, y].Update (tiles_costs [x, y], x, y); + } + } + } + + /// + /// Updates the already created grid without new tile prices, eg with just walkable / unwalkable tiles. + /// + /// true, if grid was updated, false otherwise. + /// Walkable tiles. + public void UpdateGrid (bool [,] walkable_tiles) + { + // check if need to re-create grid + if (nodes == null || + gridSizeX != walkable_tiles.GetLength (0) || + gridSizeY != walkable_tiles.GetLength (1)) { + CreateNodes (walkable_tiles.GetLength (0), walkable_tiles.GetLength (1)); + } + + // update grid + for (int x = 0; x < gridSizeX; x++) { + for (int y = 0; y < gridSizeY; y++) { + nodes [x, y].Update (walkable_tiles [x, y] ? 1.0f : 0.0f, x, y); + } + } + } + + /// + /// Get all the neighbors of a given tile in the grid. + /// + /// Node to get neighbors for. + /// List of node neighbors. + public IEnumerable GetNeighbours (Node node, Pathfinding.DistanceType distanceType) + { + int x = 0, y = 0; + switch (distanceType) { + case Pathfinding.DistanceType.Manhattan: + y = 0; + for (x = -1; x <= 1; ++x) { + var neighbor = AddNodeNeighbour (x, y, node); + if (neighbor != null) + yield return neighbor; + } + + x = 0; + for (y = -1; y <= 1; ++y) { + var neighbor = AddNodeNeighbour (x, y, node); + if (neighbor != null) + yield return neighbor; + } + break; + + case Pathfinding.DistanceType.Euclidean: + for (x = -1; x <= 1; x++) { + for (y = -1; y <= 1; y++) { + var neighbor = AddNodeNeighbour (x, y, node); + if (neighbor != null) + yield return neighbor; + } + } + break; + } + } + + /// + /// Adds the node neighbour. + /// + /// true, if node neighbour was added, false otherwise. + /// The x coordinate. + /// The y coordinate. + /// Node. + /// Neighbours. + Node AddNodeNeighbour (int x, int y, Node node) + { + if (x == 0 && y == 0) { + return null; + } + + int checkX = node.gridX + x; + int checkY = node.gridY + y; + + if (checkX >= 0 && checkX < gridSizeX && checkY >= 0 && checkY < gridSizeY) { + return nodes [checkX, checkY]; + } + + return null; + } + + /// + /// Get Node at grid coordinates. + /// + /// The x coordinate. + /// The y coordinate. + /// Node with costs at x,y + public Node GetNode (int x, int y) + { + return nodes [x, y]; + } + } + +} diff --git a/Controls/PathFinding/2dTileBasedPathFinding/TestProject/TestProject/Form1.cs b/Controls/PathFinding/2dTileBasedPathFinding/TestProject/TestProject/Form1.cs index b36930b..f9635a0 100644 --- a/Controls/PathFinding/2dTileBasedPathFinding/TestProject/TestProject/Form1.cs +++ b/Controls/PathFinding/2dTileBasedPathFinding/TestProject/TestProject/Form1.cs @@ -40,7 +40,7 @@ private void DrawMapWhenReady_Tick(object sender, EventArgs e) } // create a grid - PathFind.Grid grid = new PathFind.Grid(tilesmap); + PathFind.GridI grid = new PathFind.SquareGrid(tilesmap); // create source and target points PathFind.Point _from = new PathFind.Point(1, 1); diff --git a/Controls/PathFinding/2dTileBasedPathFinding/TestProject/TestProject/TestProject.csproj b/Controls/PathFinding/2dTileBasedPathFinding/TestProject/TestProject/TestProject.csproj index 90fed6d..0b533e3 100644 --- a/Controls/PathFinding/2dTileBasedPathFinding/TestProject/TestProject/TestProject.csproj +++ b/Controls/PathFinding/2dTileBasedPathFinding/TestProject/TestProject/TestProject.csproj @@ -1,4 +1,4 @@ - + @@ -45,8 +45,11 @@ - - Source\Grid.cs + + Source\GridI.cs + + + Source\SquareGrid.cs Source\Node.cs @@ -82,4 +85,4 @@ --> - \ No newline at end of file +