Skip to content

Commit

Permalink
implemented support for weighted paths
Browse files Browse the repository at this point in the history
  • Loading branch information
val antonini committed Apr 25, 2024
1 parent c47d2b6 commit 5968008
Show file tree
Hide file tree
Showing 10 changed files with 96 additions and 33 deletions.
4 changes: 2 additions & 2 deletions AStar.Tests/LongerPathingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public void TestPathingOptions()

var pathfinder = new PathFinder(_world, pathfinderOptions);
var path = pathfinder.FindPath(new Position(1, 1), new Position(30, 30));
Helper.Print(_world, path);

}

[Test]
Expand All @@ -65,7 +65,7 @@ public void ShouldPathEnvironment()

var path = pathfinder.FindPath(new Position(1, 1), new Position(30, 30));

Helper.Print(_world, path);


path.ShouldBe(new[] {
new Position(1, 1),
Expand Down
2 changes: 0 additions & 2 deletions AStar.Tests/OptionsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ public void ShouldEnforceSearchLimit()

var path = pathfinder.FindPath(new Position(1, 1), new Position(1, 5));

Helper.Print(_world, path);

path.ShouldBeEmpty();
}
}
Expand Down
11 changes: 0 additions & 11 deletions AStar.Tests/PathfinderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ public void ShouldPathRectangleGrid()

var path = pathfinder.FindPath(new Position(0, 0), new Position(2, 4));

Helper.Print(_world, path);

path.ShouldBe(new[] {
new Position(0, 0),
new Position(1, 1),
Expand All @@ -52,8 +50,6 @@ public void ShouldPathToAdjacent()
{
var path = _pathFinder.FindPath(new Position(1, 1), new Position(2, 1));

Helper.Print(_world, path);

path.ShouldBe(new[] {
new Position(1, 1),
new Position(2, 1),
Expand All @@ -65,8 +61,6 @@ public void ShouldDoSimplePath()
{
var path = _pathFinder.FindPath(new Position(1, 1), new Position(4, 2));

Helper.Print(_world, path);

path.ShouldBe(new[] {
new Position(1, 1),
new Position(2, 2),
Expand All @@ -83,8 +77,6 @@ public void ShouldDoSimplePathWithNoDiagonal()

var path = _pathFinder.FindPath(new Position(1, 1), new Position(4, 2));

Helper.Print(_world, path);

path.ShouldBe(new[] {
new Position(1, 1),
new Position(2, 1),
Expand All @@ -105,7 +97,6 @@ public void ShouldDoSimplePathWithNoDiagonalAroundObstacle()
_world[2, 2] = 0;

var path = _pathFinder.FindPath(new Position(1, 1), new Position(4, 2));
Helper.Print(_world, path);

path.ShouldBe(new[] {
new Position(1, 1),
Expand All @@ -127,8 +118,6 @@ public void ShouldPathAroundObstacle()

var path = _pathFinder.FindPath(new Position(1, 1), new Position(4, 2));

Helper.Print(_world, path);

path.ShouldBe(new[] {
new Position(1, 1),
new Position(1, 2),
Expand Down
6 changes: 0 additions & 6 deletions AStar.Tests/PathingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ public void ShouldPathPredictably()

var path = pathfinder.FindPath(new Position(1, 1), new Position(2, 3));

Helper.Print(_world, path);

path.ShouldBe(new[] {
new Position(1, 1),
new Position(2, 2),
Expand All @@ -43,8 +41,6 @@ public void ShouldPathPredictably2()

var path = pathfinder.FindPath(new Position(1, 1), new Position(1, 5));

Helper.Print(_world, path);

path.ShouldBe(new[] {
new Position(1, 1),
new Position(1, 2),
Expand All @@ -62,8 +58,6 @@ public void ShouldPathPredictably3()

var path = pathfinder.FindPath(new Position(1, 1), new Position(1, 5));

Helper.Print(_world, path);

path.ShouldBe(new[] {
new Position(1, 1),
new Position(1, 2),
Expand Down
4 changes: 0 additions & 4 deletions AStar.Tests/PunishChangeDirectionIssueTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ public void ShouldPunishChangingDirectionsIssue()

var path = pathfinder.FindPath(new Position(7, 2), new Position(1, 17));

Helper.Print(_world, path);

path.ShouldBe(new[] {
new Position(7, 2),
new Position(7, 3),
Expand Down Expand Up @@ -70,8 +68,6 @@ public void ShouldCorrectIssue()

var path = pathfinder.FindPath(new Position(1, 2), new Position(8, 14));

Helper.Print(_world, path);

path.ShouldBe(new[] {
new Position(1, 2),
new Position(2, 3),
Expand Down
5 changes: 0 additions & 5 deletions AStar.Tests/PunishChangeDirectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ public void ShouldPunishChangingDirections()

var path = pathfinder.FindPath(new Position(2, 9), new Position(15, 3));


Helper.Print(world, path);

path.ShouldBe(new[]
{
new Position(2, 9),
Expand Down Expand Up @@ -92,9 +89,7 @@ public void ShouldCalculateAdjacentCorrectly()
};

Console.WriteLine("actual");
Helper.Print(world, path);
Console.WriteLine("expected");
Helper.Print(world, expected);

path.ShouldBe(expected);
}
Expand Down
55 changes: 55 additions & 0 deletions AStar.Tests/WeightedPathingTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using AStar.Options;
using NUnit.Framework;
using Shouldly;

namespace AStar.Tests
{
[TestFixture]
public class WeightedPathingTests
{
[Test]
public void ShouldPathWithWeight()
{
var level = @"1111115
1511151
1155511
1111111";
var world = Helper.ConvertStringToPathfinderGrid(level);
var opts = new PathFinderOptions { Weighting = Weighting.Positive };
var pathfinder = new PathFinder(world, opts);

var path = pathfinder.FindPath(new Position(1, 1), new Position(1, 5));

path.ShouldBe(new[] {
new Position(1, 1),
new Position(2, 2),
new Position(2, 3),
new Position(2, 4),
new Position(1, 5),
});
}

[Test]
public void ShouldPathWithInvertedWeight()
{
var level = @"9999995
9599959
9955599
9999999";

var world = Helper.ConvertStringToPathfinderGrid(level);
var opts = new PathFinderOptions { Weighting = Weighting.Negative };
var pathfinder = new PathFinder(world, opts);

var path = pathfinder.FindPath(new Position(1, 1), new Position(1, 5));

path.ShouldBe(new[] {
new Position(1, 1),
new Position(2, 2),
new Position(2, 3),
new Position(2, 4),
new Position(1, 5),
});
}
}
}
11 changes: 11 additions & 0 deletions AStar/Options/PathFinderOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,22 @@ public class PathFinderOptions

public int SearchLimit { get; set; }

public Weighting Weighting {get;set;}

public PathFinderOptions()
{
HeuristicFormula = HeuristicFormula.Manhattan;
UseDiagonals = true;
SearchLimit = 2000;
}
}

public enum Weighting {
// The number in the grid will not influence the path.
None,
// Higher open values will be favoured and applied to the new h value.
Positive,
// Lower open values will be favoured and applied to the new h value.
Negative
}
}
18 changes: 16 additions & 2 deletions AStar/PathFinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,24 @@ public Position[] FindPath(Position start, Position end)
newG += CalculateModifierToG(q, successor, end);
}

var newH = _heuristic.Calculate(successor.Position, end);
switch (_options.Weighting)
{
case Weighting.Positive:
newH -= _world[successor.Position];
break;
case Weighting.Negative:
newH += _world[successor.Position];
break;
case Weighting.None:
default:
break;
}

var updatedSuccessor = new PathFinderNode(
position: successor.Position,
g: newG,
h:_heuristic.Calculate(successor.Position, end),
h: newH,
parentNodePosition: q.Position);

if (BetterPathToSuccessorFound(updatedSuccessor, successor))
Expand Down Expand Up @@ -163,4 +177,4 @@ private static Position[] OrderClosedNodesAsArray(IModelAGraph<PathFinderNode> g
return path.ToArray();
}
}
}
}
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,18 @@ q. why doesn't this algorithm always find the shortest path?
a. A* optimises speed over accuracy. Because the algorithm relies on a
heuristic to determine the distances from start and finish, it won't necessarily
produce the shortest path to the target.

## Changes from 1.1.0 to 1.3.0
- Introduced path weighting to favour or penalize cells. This is off by default and
can be opted into using the new options. See [this blog post for more info](https://valantonini.com/posts/20210401/)
```csharp
var level = @"1111115
1511151
1155511
1111111";
var world = Helper.ConvertStringToPathfinderGrid(level);
var opts = new PathFinderOptions { Weighting = Weighting.Positive };
var pathfinder = new PathFinder(world, opts);
```
## Changes from 1.0.0 to 1.1.0
- Reimplemented the punish change direction to perform more consistently

Expand Down

0 comments on commit 5968008

Please sign in to comment.