private void ScanInternal() { while (frontier.Count > 0) { Point current = frontier.Dequeue(); foreach (Dir8 dir in EightDirections.Enumerate(true, false, false)) { Point neighbor = current.PointInDir(dir); if (!neighbor.ExistsOnMap()) { continue; } int neighborCost = GetCellCost(neighbor); if (neighborCost < 0) { if (this[neighbor] == Unexplored) { this[neighbor] = Blocked; } } else { int totalCost = this[current] + neighborCost; if (this[neighbor] > totalCost) { this[neighbor] = totalCost; frontier.Enqueue(neighbor, totalCost); } } } } }
public static IEnumerable <Point> GetNeighbors(this Point source) { foreach (Dir8 dir in EightDirections.Enumerate(true, false, false)) { yield return(source.PointInDir(dir)); } }
public static IEnumerable <Point> Scan(Point source, Func <Point, bool> condition, SourceOption sourceOption = SourceOption.AlwaysIncludeSource) { List <Point> frontier = new List <Point>(); HashSet <Point> visited = new HashSet <Point> { source }; switch (sourceOption) { case SourceOption.AlwaysIncludeSource: yield return(source); break; case SourceOption.ConditionallyIncludeSource: case SourceOption.StopScanOnSourceFailure: if (condition.Invoke(source)) { yield return(source); } else if (sourceOption == SourceOption.StopScanOnSourceFailure) { yield break; } break; } frontier.Add(source); while (frontier.Count > 0) { Point current = frontier[frontier.Count - 1]; frontier.RemoveAt(frontier.Count - 1); foreach (Dir8 dir in EightDirections.Enumerate(true, false, false)) { Point neighbor = current.PointInDir(dir); if (!neighbor.ExistsBetweenMapEdges()) { continue; } if (visited.Contains(neighbor)) { continue; } if (condition.Invoke(neighbor)) { yield return(neighbor); frontier.Add(neighbor); visited.Add(neighbor); } } } }
/* Might add these at some point -- these are the ones that would allow partial rescans by resetting values starting at the changed cell(s). * 1) Search outward from the given changed cell(s) as long as you can keep jumping to a cell with HIGHER total cost. This set of cells represents * all the cells which might have been influenced by the changed cell(s). * 2) Note where this search stopped: Each of the cells adjacent to this set (but not part of it) will be the frontier for the rescan. * 3) Reset the value of each cell in this set to Unexplored. * 3) Rescan the map from the frontier. * (If any of the changed cells could be sources, that needs to be handled too.) * public void Rescan(Point changedCell){ } * public void Rescan(IEnumerable<Point> changedCells){ }*/ private void RescanInternal() // Differs from ScanInternal in how it uses the frontier { while (frontier.Count > 0) { Point current = frontier.Dequeue(); foreach (Dir8 dir in EightDirections.Enumerate(true, false, false)) { Point neighbor = current.PointInDir(dir); if (!neighbor.ExistsOnMap()) { continue; } if (this[neighbor] == Blocked) { continue; } int neighborCost = GetCellCost(neighbor); if (neighborCost < 0) { if (this[neighbor] == Unexplored) { frontier.TryRemove(neighbor); // (Not sure this can happen on a rescan if done correctly) this[neighbor] = Blocked; } } else { int totalCost = this[current] + neighborCost; if (this[neighbor] > totalCost) { // Remove them before changing sort values, so the sort doesn't break: if (this[neighbor] != Unexplored) { frontier.TryRemove(neighbor); } this[neighbor] = totalCost; frontier.Enqueue(neighbor, totalCost); } } } } }