/// <summary> /// Given an array of tiles and a starting coordinate, /// find all reachable tiles (and paths to get to those tiles). /// This is implemented using a modified form of Djikstra's algorithm. /// /// We return a dictionary of <option, tuple<cost-to-option, path-to-option>>. /// </summary> /// <param name="tiles"></param> /// <param name="startCoord"></param> public static Dictionary <Coordinate, Tuple <int, List <Coordinate> > > FindReachableTiles( Unit unit, Tile[,] tiles) { int rows = Util.GetRows(tiles); int cols = Util.GetCols(tiles); int[,] dist = new int[rows, cols]; Coordinate[,] prev = new Coordinate[rows, cols]; var frontier = new MinHeap <TileCoordinateNode>(); TileCoordinate startTileCoordinate = new TileCoordinate( tiles[unit.location.r, unit.location.c], unit.location); TileCoordinateNode startNode = new TileCoordinateNode(startTileCoordinate, 0); frontier.Insert(startNode); for (int r = 0; r < rows; ++r) { for (int c = 0; c < cols; ++c) { Coordinate coord = new Coordinate(r, c); if (!coord.Equals(unit.location)) { dist[coord.r, coord.c] = int.MaxValue; prev[coord.r, coord.c] = null; } else { dist[coord.r, coord.c] = 0; prev[coord.r, coord.c] = null; // doesn't matter for start coord } } } while (frontier.HeapSize() > 0) { TileCoordinateNode node = frontier.Pop(); TileCoordinate tileCoordinate = node.tileCoordinate; Coordinate coordinate = tileCoordinate.coordinate; foreach (TileCoordinate adjacentTileCoord in GetAllowableAdjacentTiles(tiles, coordinate, unit.board, unit.team)) { Coordinate adjacentCoord = adjacentTileCoord.coordinate; // watch for overflow here int calculatedDist = dist[coordinate.r, coordinate.c] + adjacentTileCoord.tile.movementCost; bool calculatedDistPreferrable = dist[adjacentCoord.r, adjacentCoord.c] == int.MaxValue || calculatedDist < dist[adjacentCoord.r, adjacentCoord.c]; if (calculatedDistPreferrable && calculatedDist <= unit.movementPoints) { dist[adjacentCoord.r, adjacentCoord.c] = calculatedDist; prev[adjacentCoord.r, adjacentCoord.c] = coordinate; TileCoordinateNode adjacentNode = new TileCoordinateNode(adjacentTileCoord, calculatedDist); if (!frontier.Contains(adjacentNode)) { frontier.Insert(adjacentNode); } else { frontier.DecreaseKey(adjacentNode, adjacentNode); } } } } // djikstra finished // now processing and adding to the return dict var answer = new Dictionary <Coordinate, Tuple <int, List <Coordinate> > >(); for (int r = 0; r < rows; ++r) { for (int c = 0; c < cols; ++c) { Coordinate targetCoord = new Coordinate(r, c); int distanceToTarget = dist[r, c]; // cell must also be empty, unless it is the starting coord if (distanceToTarget != int.MaxValue && (targetCoord.Equals(unit.location) || unit.board[targetCoord.r, targetCoord.c] is null)) { /* * Console.WriteLine($"Distance from {unit.currentLocation}" + * $" to {targetCoordinate}" + * $" is: {distanceToTarget}"); */ //string ans = targetCoordinate.ToString(); List <Coordinate> pathToTarget = new List <Coordinate>(); Coordinate currentCoord = targetCoord; // all paths lead to the starting coordinate // and the starting coordinate's prev is null while (prev[currentCoord.r, currentCoord.c] != null) { // ans = $"{prev[targetCoordinate.r, targetCoordinate.c]}, {ans}"; pathToTarget.Insert(0, prev[currentCoord.r, currentCoord.c]); currentCoord = prev[currentCoord.r, currentCoord.c]; } pathToTarget.Add(targetCoord); // Console.WriteLine($"path to {targetCoord}: {String.Join(", ", pathToTarget)}"); answer.Add( targetCoord, new Tuple <int, List <Coordinate> >( distanceToTarget, pathToTarget) ); } } } return(answer); }
/// <summary> /// Given an array of tiles and a starting coordinate, /// find all reachable tiles (and paths to get to those tiles). /// This is implemented using a modified form of Djikstra's algorithm. /// </summary> /// <param name="tiles"></param> /// <param name="startCoord"></param> public static void FindReachableTiles( int rows, int cols, int[,] dist, Coordinate[,] prev, Tile[,] tiles, Coordinate startCoord, int movementPoints) { var frontier = new MinHeap <TileCoordinateNode>(); TileCoordinate startTileCoordinate = new TileCoordinate( tiles[startCoord.r, startCoord.c], startCoord); TileCoordinateNode startNode = new TileCoordinateNode(startTileCoordinate, 0); frontier.Insert(startNode); for (int r = 0; r < rows; ++r) { for (int c = 0; c < cols; ++c) { Coordinate coord = new Coordinate(r, c); if (!coord.Equals(startCoord)) { dist[coord.r, coord.c] = int.MaxValue; prev[coord.r, coord.c] = null; } else { dist[coord.r, coord.c] = 0; prev[coord.r, coord.c] = null; // doesn't matter for start coord } } } while (frontier.HeapSize() > 0) { TileCoordinateNode node = frontier.Pop(); TileCoordinate tileCoordinate = node.tileCoordinate; Coordinate coordinate = tileCoordinate.coordinate; foreach (TileCoordinate adjacentTileCoord in GetAdjacentTiles(tiles, coordinate)) { Coordinate adjacentCoord = adjacentTileCoord.coordinate; // watch for overflow here int calculatedDist = dist[coordinate.r, coordinate.c] + adjacentTileCoord.tile.movementCost; bool calculatedDistPreferrable = dist[adjacentCoord.r, adjacentCoord.c] == int.MaxValue || calculatedDist < dist[adjacentCoord.r, adjacentCoord.c]; if (calculatedDistPreferrable && calculatedDist <= movementPoints) { dist[adjacentCoord.r, adjacentCoord.c] = calculatedDist; prev[adjacentCoord.r, adjacentCoord.c] = coordinate; TileCoordinateNode adjacentNode = new TileCoordinateNode(adjacentTileCoord, calculatedDist); if (!frontier.Contains(adjacentNode)) { frontier.Insert(adjacentNode); } else { frontier.DecreaseKey(adjacentNode, adjacentNode); } } } } }