private void ExpandHex(IDirectedPath path, Hexside hexside) { var here = path.PathStep.Hex; var there = Board[here.Coords.GetNeighbour(hexside)]; if (there != null && !ClosedSet.Contains(there.Coords)) { var cost = StepCost(here, hexside, there); if ((cost > 0) && (path.TotalCost + cost < BestSoFar || !OpenSet.ContainsKey(there.Coords)) ) { var key = path.TotalCost + cost + Heuristic(there.Coords); var newPath = path.AddStep(there, HexsideDirection(hexside), cost); TraceFindPathEnqueue(there.Coords, key, 0); IDirectedPath oldPath; if (!OpenSet.TryGetValue(there.Coords, out oldPath)) { OpenSet.Add(there.Coords, newPath); Queue.Enqueue(key, newPath); } else if (newPath.TotalCost < oldPath.TotalCost) { OpenSet.Remove(there.Coords); OpenSet.Add(there.Coords, newPath); Queue.Enqueue(key, newPath); } SetBestSoFar(newPath, GetPartnerPath(there.Coords)); } } }
/// <summary>Paint the direction arrow for each hex of the current shortest path.</summary> /// <param name="g">Graphics object for the canvas being painted.</param> /// <param name="path"></param> protected virtual void PaintPathArrow(Graphics g, IDirectedPath path) { if (g == null) { throw new ArgumentNullException("g"); } if (path == null) { throw new ArgumentNullException("path"); } g.TranslateTransform(GridSize.Width * 2 / 3, GridSize.Height / 2); var unit = GridSize.Height / 8.0F; if (path.PathSoFar == null) { g.DrawLine(Pens.Black, -unit * 2, -unit * 2, unit * 2, unit * 2); g.DrawLine(Pens.Black, -unit * 2, unit * 2, unit * 2, -unit * 2); } else { g.RotateTransform(60 * (int)path.PathStep.HexsideEntry); g.DrawLine(Pens.Black, 0, unit * 4, 0, -unit); g.DrawLine(Pens.Black, 0, unit * 4, -unit * 3 / 2, unit * 2); g.DrawLine(Pens.Black, 0, unit * 4, unit * 3 / 2, unit * 2); } }
private void ExpandHex(IDirectedPath path, Hexside hexside) { var here = path.PathStep.Coords; var there = here.GetNeighbour(hexside); if (!ClosedSet.Contains(there)) { TryStepCost(here, hexside).IfHasValueDo(cost => { if (path.TotalCost + cost < BestSoFar || !OpenSet.ContainsKey(there)) { Heuristic(there).IfHasValueDo(heuristic => { var key = path.TotalCost + cost + heuristic; var newPath = path.AddStep(there, HexsideDirection(hexside), cost); PathfinderExtensions.TraceFindPathEnqueue(there, key, 0); if (!OpenSet.TryGetValue(there, out var oldPath)) { OpenSet.Add(there, newPath); Queue.Enqueue(key, newPath); } else if (newPath.TotalCost < oldPath.TotalCost) { OpenSet.Remove(there); OpenSet.Add(there, newPath); Queue.Enqueue(key, newPath); } SetBestSoFar(newPath, PartnerPath(there)); }); } }); } }
/// <summary>Returns a DirectedPath composed by extending this DirectedPath by one hex.</summary> public DirectedPathCollection(IDirectedPath pathSoFar, DirectedPathStepHex pathStep, int stepCost) { PathStep = pathStep; PathSoFar = pathSoFar; TotalCost = (pathSoFar?.TotalCost ?? 0) + stepCost; TotalSteps = (pathSoFar?.TotalSteps ?? -1) + 1; }
/// <inheritdoc/> public void SetBestSoFar(IDirectedPath pathRev, IDirectedPath pathFwd) { if (pathFwd.TotalCost + pathRev.TotalCost < BestSoFar) { _pathRev = pathRev; _pathFwd = pathFwd; BestSoFar = _pathRev.TotalCost + _pathFwd.TotalCost; PathfinderExtensions.TraceFindPathDetailBestSoFar(pathFwd, pathRev, BestSoFar); } }
/// <summary>TODO</summary> private static IDirectedPath MergePaths(IDirectedPath targetPath, IDirectedPath sourcePath) { if (sourcePath != null) { while (sourcePath.PathSoFar != null) { var hexside = sourcePath.PathStep.HexsideExit; var cost = sourcePath.TotalCost - (sourcePath = sourcePath.PathSoFar).TotalCost; targetPath = targetPath.AddStep(sourcePath.PathStep.Hex, hexside, cost); } } return(targetPath); }
/// <summary>Returns the result of stacking <paramref name="mergePath"/> onto <paramref name="this"/></summary> public static Maybe <IDirectedPath> MergePaths(this IDirectedPath @this, IDirectedPath mergePath) { if (@this == null || mergePath == null) { return(null); } while (mergePath.PathSoFar != null) { var hexside = mergePath.PathStep.HexsideExit; var cost = mergePath.TotalCost - (mergePath = mergePath.PathSoFar).TotalCost; @this = @this.AddStep(mergePath.PathStep.Coords, hexside, cost); } return(@this.ToMaybe()); }
/// <summary>Returns the result of stacking <paramref name="mergePath"/> onto <paramref name="targetPath"/></summary> public static Maybe <IDirectedPath> MergePaths <THex>(this IDirectedPath targetPath, IDirectedPath mergePath) where THex : class, IHex { if (mergePath != null) { while (mergePath.PathSoFar != null) { var hexside = mergePath.PathStep.HexsideExit; var cost = mergePath.TotalCost - (mergePath = mergePath.PathSoFar).TotalCost; targetPath = targetPath.AddStep(mergePath.PathStep.Hex, hexside, cost); } } return(targetPath.ToMaybe()); }
/// <summary>Updates the record of the shortest path found so far.</summary> /// <param name="pathFwd">The half-path obtained by searching backward from the target (so stacked forwards).</param> /// <param name="pathRev">The half-path obtained by searching forward from the source (so stacked backwards).</param> public void SetBestSoFar(IDirectedPath pathRev, IDirectedPath pathFwd) { if (pathFwd == null || pathRev == null) { return; } if (pathFwd.TotalCost + pathRev.TotalCost < BestSoFar) { _pathRev = pathRev; _pathFwd = pathFwd; BestSoFar = _pathRev.TotalCost + _pathFwd.TotalCost; pathFwd.TraceFindPathDetailBestSoFar(pathRev, BestSoFar); } }
/// <summary>Updates the record of the shortest path found so far.</summary> /// <param name="pathFwd">The half-path obtained by searching backward from the target (so stacked forwards).</param> /// <param name="pathRev">The half-path obtained by searching forward from the source (so stacked backwards).</param> void IPathHalves.SetBestSoFar(IDirectedPath pathRev, IDirectedPath pathFwd) { if (pathFwd == null || pathRev == null) { return; } if (pathFwd.TotalCost + pathRev.TotalCost < _bestSoFar) { _pathRev = pathRev; _pathFwd = pathFwd; _bestSoFar = _pathRev.TotalCost + _pathFwd.TotalCost; Pathfinder.TraceFindPathDetailBestSoFar(pathFwd.PathStep.Hex.Coords, pathRev.PathStep.Hex.Coords, _bestSoFar); } }
void ExpandNeighbour(IDirectedPath path, NeighbourHex neighbour) { if (!OpenSet.Contains(neighbour.Hex.Coords)) { var cost = StepCost(neighbour.Hex, neighbour.HexsideExit); if (cost > 0) { var newPath = path.AddStep(neighbour, cost); var key = Estimate(Heuristic, VectorGoal, Source.Coords, neighbour.Hex.Coords, newPath.TotalCost); TraceFindPathEnqueue(neighbour.Hex.Coords, key >> 16, (int)(key & 0xFFFFu)); Queue.Enqueue(key, newPath); } } }
void PaintPathArrow(Graphics g, IDirectedPath path) { g.TranslateTransform(GridSize.Width * 2 / 3, GridSize.Height / 2); var unit = GridSize.Height / 8.0F; if (path.PathSoFar == null) { g.DrawLine(Pens.Black, -unit * 2, -unit * 2, unit * 2, unit * 2); g.DrawLine(Pens.Black, -unit * 2, unit * 2, unit * 2, -unit * 2); } else { g.RotateTransform(60 * (int)path.PathStep.HexsideEntry); g.DrawLine(Pens.Black, 0, unit * 4, 0, -unit); g.DrawLine(Pens.Black, 0, unit * 4, -unit * 3 / 2, unit * 2); g.DrawLine(Pens.Black, 0, unit * 4, unit * 3 / 2, unit * 2); } }
void PaintPath(Graphics g, IDirectedPath Path) { var state = g.Save(); using (var brush = new SolidBrush(Color.FromArgb(78, Color.PaleGoldenrod))) { var path = Path; while (path != null) { g.Restore(state); state = g.Save(); var coords = path.PathStep.Hex.Coords; g.TranslateTransform( MapMargin.Width + coords.User.X * GridSize.Width, MapMargin.Height + coords.User.Y * GridSize.Height + (coords.User.X + 1) % 2 * GridSize.Height / 2 ); g.FillPath(brush, HexgridPath); PaintPathArrow(g, path); path = path.PathSoFar; } } }
/// <summary>Paint the direction and destination indicators for each hex of the current shortest path.</summary> /// <param name="g">Type: Graphics - Object representing the canvas being painted.</param> /// <param name="path">Type: <see cref="IDirectedPath"/> - /// A directed path (ie linked-list> of hexes to be highlighted with a direction arrow.</param> protected virtual void PaintPathArrow(Graphics g, IDirectedPath path) { if (g == null) { throw new ArgumentNullException("g"); } if (path == null) { throw new ArgumentNullException("path"); } g.TranslateTransform(CentreOfHexOffset.Width, CentreOfHexOffset.Height); if (path.PathSoFar == null) { PaintPathDestination(g); } else { PaintPathArrow(g, path.PathStep.HexsideEntry); } }
/// <summary>Paint the current shortese path.</summary> /// <param name="g">Graphics object for the canvas being painted.</param> /// <param name="path"></param> protected virtual void PaintPath(Graphics g, IDirectedPath path) { if (g == null) { throw new ArgumentNullException("g"); } using (var brush = new SolidBrush(Color.FromArgb(78, Color.PaleGoldenrod))) { while (path != null) { var coords = path.PathStep.Hex.Coords; TranslateGraphicsToHex(g, coords); g.FillPath(brush, HexgridPath); if (ShowPathArrow) { PaintPathArrow(g, path); } path = path.PathSoFar; } } }
public static void TraceFindPathDequeue(this HexCoords coords, string searchDirection, IDirectedPath path, int priority, int preference) => Tracing.FindPathDequeue.Trace( $"{searchDirection} Dequeue Path at {coords} w/ cost={path.TotalCost,4} at {path.HexsideExit,-9}; estimate={priority,4}:{preference,4}.");
public static void TraceFindPathDequeue(this IHex hex, string searchDirection, IDirectedPath path, int priority, int preference) => Tracing.FindPathDequeue.Trace( "{0} Dequeue Path at {1} w/ cost={2,4} at {3,-9}; estimate={4,4}:{5,4}.", searchDirection, hex.Coords, path.TotalCost, path.HexsideExit, priority, preference);
public static void TraceFindPathDetailBestSoFar(this IDirectedPath pathFwd, IDirectedPath pathRev, int bestSoFar) => Tracing.FindPathDetail.Trace( $" SetBestSoFar: pathFwd at {pathFwd.PathStep.Coords}; pathRev at {pathRev.PathStep.Coords}; Cost = {bestSoFar}");
/// <summary>Returns a new instance composed by extending this DirectedPath by one hex.</summary> /// <param name="this"></param> /// <param name="stepHex"></param> /// <param name="stepCost"></param> public static IDirectedPath AddStep(this IDirectedPath @this, DirectedPathStepHex stepHex, int stepCost) => new DirectedPathCollection(@this, stepHex, stepCost);
/// <summary>Returns a new instance composed by extending this DirectedPath by one hex.</summary> /// <param name="this"></param> /// <param name="hex"></param> /// <param name="hexsideExit"></param> /// <param name="stepCost"></param> public static IDirectedPath AddStep(this IDirectedPath @this, IHex hex, Hexside hexsideExit, int stepCost) => @this.AddStep(new DirectedPathStepHex(hex, hexsideExit), stepCost);
protected override void SetBestSoFar(IDirectedPath selfPath, IDirectedPath partnerPath) { PathHalves.SetBestSoFar(selfPath, partnerPath); }
protected abstract void SetBestSoFar(IDirectedPath fwdPath, IDirectedPath revPath);