private ShortestPathNode simplifyPath(ShortestPathNode head) { var start = head; var node = head.Next; while (node != null) { if (node.IsGoal || node.Next != null && (!areInLineOfSight(start.Position, node.Next.Position) || start.TargetWayPoint != node.TargetWayPoint)) { start.Next = node; start.CostToNext = TimeSpan.FromSeconds(Distance.Between(start.Position, node.Position) / maxSpeed); start = node; } node = node.Next; } return head; }
public GridShortestPathHeuristic( Vector startPosition, IReadOnlyList<IGoal> wayPoints, ITrack raceTrack, BoundingSphereCollisionDetector collisionDetector, double stepSize, double maxSpeed) { this.raceTrack = raceTrack; this.maxSpeed = maxSpeed; this.collisionDetector = collisionDetector; var path = findShortestPath(startPosition, wayPoints, stepSize); if (path == null) { throw new Exception($"Cannot find path from [{startPosition.X}, {startPosition.Y}] to goal."); } shortestPathStart = simplifyPath(path); }
private ShortestPathNode? findShortestPath(Vector start, IReadOnlyList<IGoal> wayPoints, double stepSize) { var open = new BinaryHeapOpenSet<GridKey, GridSearchNode>(); var closed = new ClosedSet<GridKey>(); open.Add( new GridSearchNode( key: new GridKey(start, wayPoints.Count), start, previous: null, distanceFromStart: 0.0, estimatedCost: 0.0, wayPoints, targetWayPoint: 0)); while (!open.IsEmpty) { var nodeToExpand = open.DequeueMostPromissing(); if (nodeToExpand.RemainingWayPoints.Count == 0) { var head = new ShortestPathNode( wayPoints.Last().Position, // use the goal position directly targetWayPoint: wayPoints.Count - 1); // the goal way point should be kept here var backtrackingNode = nodeToExpand.Previous; while (backtrackingNode != null) { var node = new ShortestPathNode(backtrackingNode.Position, backtrackingNode.TargetWayPoint); node.CostToNext = TimeSpan.FromSeconds(Distance.Between(node.Position, head.Position) / maxSpeed); node.Next = head; head = node; backtrackingNode = backtrackingNode.Previous; } return head; } closed.Add(nodeToExpand.Key); for (var dx = -1; dx <= 1; dx++) { for (var dy = -1; dy <= 1; dy++) { if (dx == 0 && dy == 0) continue; var nextPoint = new Vector( nodeToExpand.Position.X + dx * stepSize, nodeToExpand.Position.Y + dy * stepSize); var reachedWayPoint = nodeToExpand.RemainingWayPoints[0].ReachedGoal(nextPoint); var remainingWayPoints = reachedWayPoint ? nodeToExpand.RemainingWayPoints.Skip(1).ToList().AsReadOnly() : nodeToExpand.RemainingWayPoints; var targetWayPoint = wayPoints.Count - nodeToExpand.RemainingWayPoints.Count; // I want to keep the ID of the waypoint in the node which reaches the waypoint and only increase it for its childer var key = new GridKey(nextPoint, remainingWayPoints.Count); if (closed.Contains(key)) { continue; } if (collisionDetector.IsCollision(nextPoint)) { closed.Add(key); continue; } var distance = nodeToExpand.DistanceFromStart + (nextPoint - nodeToExpand.Position).CalculateLength(); var node = new GridSearchNode(key, nextPoint, nodeToExpand, distance, distance + 0, remainingWayPoints, targetWayPoint); if (open.Contains(node.Key)) { if (node.DistanceFromStart < open.Get(node.Key).DistanceFromStart) { open.ReplaceExistingWithTheSameKey(node); } } else { open.Add(node); } } } } return null; }