public void ClearAdjacencyGraph_InvalidatesOldNodes() { var solver = new PathSolver <Point, PathTestOption>(_testMap); // Build a graph based on all three points. solver.BuildAdjacencyGraph(new Point[] { _startPt, _reachableEndPt, _unreachableEndPt }); // Clear the graph, and then rebuild it with just one point. solver.ClearAdjacencyGraph(); solver.BuildAdjacencyGraph(_startPt); // Try to find a path to _unreachableEndPt. PathSolver should complain that it doesn't know about that point. PathResult <Point> pathResult = null; Assert.ThrowsException <PathfindingException>(() => pathResult = solver.FindPath(_startPt, _unreachableEndPt, PathTestOption.Normal)); }
static void Main(string[] args) { // Create the map we'll use for this program. Since StarMap implements IPathGraph, it can answer // questions PathSolver asks about connections and distances between stars. var maxJumpDist = _shipTypes.Select((ship) => ship.MaxJumpDistance).Max(); var starMap = CreateMap(maxJumpDist); // Create a PathSolver instance that we'll use. The first type parameter, string, is what // we'll use to identify nodes in the graph. The second, ShipCharacteristics, is the type of // caller data that we will pass to FindPath, and it will subsequently pass to Cost and EstimatedCost. // In this example, what type of spaceship we're plotting a path for. var solver = new PathSolver <string, ShipCharacteristics>(starMap); // Pre-build PathSolver's internal data. We have to give it one or more seed points - nodes that // we know exist. One is sufficient as long as all of the others are reachable from it. solver.BuildAdjacencyGraph("A"); // Show the user the star map. foreach (var row in _asciiMap) { Console.WriteLine(row); } // Prompt for input about what paths to plot. string startStar; string endStar; Console.Write("Enter starting star: "); var startStarInput = Console.ReadLine(); if (startStarInput.Length > 0) { startStar = startStarInput.ToUpper().Substring(0, 1); } else { return; } Console.Write("Enter destination star: "); var endStarInput = Console.ReadLine(); if (endStarInput.Length > 0) { endStar = endStarInput.ToUpper().Substring(0, 1); } else { return; } // Find a path for each type of spaceship, and display it to the user. Console.WriteLine($"--{startStar} to {endStar}--"); foreach (var ship in _shipTypes) { WritePath(starMap, solver, startStar, endStar, ship); } }
public void FindPath_ThrowsIfEmptyDestinationList() { var solver = new PathSolver <Point, PathTestOption>(_testMap); solver.BuildAdjacencyGraph(_startPt); PathResult <Point> pathResult = null; var destList = new List <Point>(); Assert.ThrowsException <PathfindingException>(() => pathResult = solver.FindPath(_startPt, destList, PathTestOption.Normal)); }
public void FindPath_ThrowsIfDestNodeNotInGraph() { var solver = new PathSolver <Point, PathTestOption>(_testMap); solver.BuildAdjacencyGraph(_startPt); PathResult <Point> pathResult = null; var ptOutsideOfGraph = new Point(-1, -1); Assert.ThrowsException <PathfindingException>(() => pathResult = solver.FindPath(_startPt, ptOutsideOfGraph, PathTestOption.Normal)); }
public void FindPath_NoPathToUnreachable() { // In this case, _startPt and _unreachableEndPt are disconnected in the adjacency graph. var solver = new PathSolver <Point, PathTestOption>(_testMap); solver.BuildAdjacencyGraph(new Point[] { _startPt, _reachableEndPt, _unreachableEndPt }); var solution = solver.FindPath(_startPt, _unreachableEndPt, PathTestOption.Normal); Assert.IsTrue(solution.PathCost == double.PositiveInfinity); Assert.IsNull(solution.Path); StringAssert.Contains(solution.PerformanceSummary(), "Path not found"); }
public void FindPath_ZeroLengthPathToSelf() { var solver = new PathSolver <Point, PathTestOption>(_testMap); solver.BuildAdjacencyGraph(new Point[] { _startPt, _reachableEndPt, _unreachableEndPt }); var solution = solver.FindPath(_startPt, _startPt, PathTestOption.Normal); Assert.IsTrue(solution.PathCost == 0); Assert.IsNotNull(solution.Path); Assert.IsTrue(solution.Path.Count == 0); StringAssert.Contains(solution.PerformanceSummary(), "Zero-length path"); }
public void PerformanceSummary_HasInfo() { var solver = new PathSolver <Point, PathTestOption>(_testMap); solver.BuildAdjacencyGraph(new Point[] { _startPt, _reachableEndPt, _unreachableEndPt }); var solution = solver.FindPath(_startPt, _reachableEndPt, PathTestOption.Normal); var summary = solver.PerformanceSummary(); Assert.IsNotNull(summary); StringAssert.Contains(summary, "pathCount=1;"); }
public void FindPath_NoPathIfAllInfiniteCosts() { // In this case, the start and end points are connected in the adjacency graph, but all possible paths // have an infinite cost. PathSolver treats that as unreachable. var solver = new PathSolver <Point, PathTestOption>(_testMap); solver.BuildAdjacencyGraph(new Point[] { _startPt, _reachableEndPt, _unreachableEndPt }); var solution = solver.FindPath(_startPt, _reachableEndPt, PathTestOption.InfiniteCost); Assert.IsTrue(solution.PathCost == double.PositiveInfinity); Assert.IsNull(solution.Path); StringAssert.Contains(solution.PerformanceSummary(), "Path not found"); }
public void FindPath_GoodPathToReachable() { var solver = new PathSolver <Point, PathTestOption>(_testMap); solver.BuildAdjacencyGraph(new Point[] { _startPt, _reachableEndPt, _unreachableEndPt }); var solution = solver.FindPath(_startPt, _reachableEndPt, PathTestOption.Normal); Assert.IsTrue(solution.PathCost >= _testMap.EstimatedCost(_startPt, _reachableEndPt, PathTestOption.Normal)); Assert.IsNotNull(solution.Path); Assert.IsTrue(solution.Path.Count > 0); Assert.IsTrue(solution.Path[solution.Path.Count - 1] == _reachableEndPt); Assert.IsFalse(solution.Path.Contains(_startPt)); Assert.IsTrue(solution.NodesReprocessedCount == 0); StringAssert.Contains(solution.PerformanceSummary(), $"Path from {_startPt}"); }
public void FindPath_GoodPathEvenWithOverestimate() { // If we give PathSolver a bad estimate, we should still get a path, but it won't always be optimal. // NodesReprocessedCount can only be > 0 in this case. var solver = new PathSolver <Point, PathTestOption>(_testMap); solver.BuildAdjacencyGraph(new Point[] { _startPt, _reachableEndPt, _unreachableEndPt }); var solution = solver.FindPath(_startPt, _reachableEndPt, PathTestOption.OverEstimateRemainingDistance); Assert.IsNotNull(solution.Path); Assert.IsTrue(solution.Path.Count > 0); Assert.IsTrue(solution.Path[solution.Path.Count - 1] == _reachableEndPt); Assert.IsFalse(solution.Path.Contains(_startPt)); Assert.IsTrue(solution.NodesReprocessedCount > 0); StringAssert.Contains(solution.PerformanceSummary(), $"Path from {_startPt}"); }
private static void Demo() { // Create a tile map. In this case we're doing it from hard-coded strings, but it // would be trivial to get it from a file instead. var map = new Map(_demoMapStrings); // Create an instace of an IPathGraph. This is a class that the caller provides // that answers which tiles are adjacent to which, and what the cost is to move from // one to the next. var tileGraph = new TileGraph(map); // Create a PathSolver instance. The first type parameter here is Point2D. It's whatever // we're using to identify nodes (tile in this case). The second one - int here - is a dummy // type since we don't care about callerData/passthrough values in this example. var solver = new PathSolver <Point2D, int>(tileGraph); var allImportantPoints = _demoGoalPts.Concat(_demoStartPts); // Pre-build the PathSolver's data structures. We're telling it to build a graph of all of the points // reachable from the ones we give it here. solver.BuildAdjacencyGraph(allImportantPoints); // Create a path and draw the map for each point in _demoStartPts. foreach (var startPt in _demoStartPts) { // Find the shortest path from startPt to any of points listed in _interestingGoals. Of course, // you could just give it one ending point. The third parameter, 0 here, is unused in this example. var pathResult = solver.FindPath(startPt, _demoGoalPts, 0); // Write the map out as a grid characters, with a couple extra bits: // O is the starting point for our path. // X is one of the ending points for our path. // . is a tile we moved through on the path. var startPtsArray = new Point2D[] { startPt }; var mapStrings = map.RowStrings(pathResult.Path, startPtsArray, _demoGoalPts); Console.WriteLine(); foreach (var mapRow in mapStrings) { Console.WriteLine(mapRow); } // Write performance info. Console.WriteLine(pathResult.PerformanceSummary()); } }
/// <summary> /// Search for a whole lot of paths using a large map. /// </summary> private static void Benchmark() { // Build the necessary pieces (as above in Demo()). var map = new Map(_benchmarkMapStrings); var tileGraph = new TileGraph(map); var solver = new PathSolver <Point2D, int>(tileGraph); // Init the solver's graph. (This step is included in solver.LifetimeSolutionTimeMS, by the way.) solver.BuildAdjacencyGraph(_benchmarkPts); // Solve a path for every combination of 1 starting point and 2 destination points. (So if P is the number of // points, then we're solving P*(P-1)*(P-2)/2 paths.) for (var startIdx = 0; startIdx < _benchmarkPts.Length; ++startIdx) { var startPt = _benchmarkPts[startIdx]; for (var endIdx1 = 0; endIdx1 < _benchmarkPts.Length; ++endIdx1) { if (endIdx1 == startIdx) { continue; } for (var endIdx2 = endIdx1 + 1; endIdx2 < _benchmarkPts.Length; ++endIdx2) { if (endIdx2 == startIdx) { continue; } var endPts = new[] { _benchmarkPts[endIdx1], _benchmarkPts[endIdx2] }; var pathResult = solver.FindPath(startPt, endPts, 0); } } } // Write out a summary of the PathSolver's lifetime statistics. Console.WriteLine(solver.PerformanceSummary()); }