diff --git a/src/NosCore.PathFinder/NosCore.PathFinder.csproj b/src/NosCore.PathFinder/NosCore.PathFinder.csproj
index 76edcc2..3c75461 100644
--- a/src/NosCore.PathFinder/NosCore.PathFinder.csproj
+++ b/src/NosCore.PathFinder/NosCore.PathFinder.csproj
@@ -12,7 +12,7 @@
https://github.com/NosCoreIO/NosCore.PathFinder.git
nostale, noscore, nostale private server source, nostale emulator
- 2.1.0
+ 2.1.1
false
NosCore's PathFinder
diff --git a/src/NosCore.PathFinder/Pathfinder/GoalBasedPathfinder.cs b/src/NosCore.PathFinder/Pathfinder/GoalBasedPathfinder.cs
index 1204b70..7877993 100644
--- a/src/NosCore.PathFinder/Pathfinder/GoalBasedPathfinder.cs
+++ b/src/NosCore.PathFinder/Pathfinder/GoalBasedPathfinder.cs
@@ -27,7 +27,7 @@ public GoalBasedPathfinder(IMapGrid mapGrid, IHeuristic heuristic)
public GoalBasedPathfinder(IMapGrid mapGrid, IHeuristic heuristic, BrushFire brushfire) : this(mapGrid, heuristic)
{
- BrushFirecache.Set(brushfire.Origin, brushfire, DateTimeOffset.Now.AddSeconds(10));
+ BrushFirecache.Set((mapGrid, brushfire.Origin), brushfire, DateTimeOffset.Now.AddSeconds(10));
}
public IEnumerable<(short X, short Y)> FindPath((short X, short Y) start, (short X, short Y) end)
@@ -39,10 +39,10 @@ public GoalBasedPathfinder(IMapGrid mapGrid, IHeuristic heuristic, BrushFire bru
return list;
}
- if (!BrushFirecache.TryGetValue(end, out BrushFire brushFire))
+ if (!BrushFirecache.TryGetValue((_mapGrid, end), out BrushFire brushFire))
{
brushFire = _mapGrid.LoadBrushFire(end, _heuristic);
- BrushFirecache.Set(end, brushFire, DateTimeOffset.Now.AddSeconds(10));
+ BrushFirecache.Set((_mapGrid, end), brushFire, DateTimeOffset.Now.AddSeconds(10));
}
if (!brushFire.Distances.ContainsKey(start))
diff --git a/src/NosCore.PathFinder/Pathfinder/JumpPointSearchPathfinder.cs b/src/NosCore.PathFinder/Pathfinder/JumpPointSearchPathfinder.cs
index db7810a..f3c5e77 100644
--- a/src/NosCore.PathFinder/Pathfinder/JumpPointSearchPathfinder.cs
+++ b/src/NosCore.PathFinder/Pathfinder/JumpPointSearchPathfinder.cs
@@ -59,7 +59,8 @@ public JumpPointSearchPathfinder(IMapGrid mapGrid, IHeuristic heuristic)
public IEnumerable<(short X, short Y)> FindPath((short X, short Y) start, (short X, short Y) end)
{
- if (Cache.TryGetValue((start, end), out IEnumerable<(short X, short Y)>? cachedList))
+ // Keyed by grid too: the cache is shared by every pathfinder instance, one per map.
+ if (Cache.TryGetValue((_mapGrid, start, end), out IEnumerable<(short X, short Y)>? cachedList))
{
return cachedList ?? Array.Empty<(short X, short Y)>();
}
@@ -67,7 +68,7 @@ public JumpPointSearchPathfinder(IMapGrid mapGrid, IHeuristic heuristic)
var path = BuildFullPath(GetJumpList(start, end).ToList());
for (var i = 0; i < path.Count; i++)
{
- Cache.Set((path[i], end), path.Skip(i), DateTimeOffset.Now.AddSeconds(10));
+ Cache.Set((_mapGrid, path[i], end), path.Skip(i + 1), DateTimeOffset.Now.AddSeconds(10));
}
return path;
}
diff --git a/test/NosCore.PathFinder.Tests/JumpPointSearchPathfinderTests.cs b/test/NosCore.PathFinder.Tests/JumpPointSearchPathfinderTests.cs
index 087c2dc..884cbc5 100644
--- a/test/NosCore.PathFinder.Tests/JumpPointSearchPathfinderTests.cs
+++ b/test/NosCore.PathFinder.Tests/JumpPointSearchPathfinderTests.cs
@@ -53,5 +53,51 @@ public void Test_JumpPointSearchPathfinder()
TestHelper.VerifyFile("jump-point-search-pathfinder.png", image, listPixel, "Jump Point Search Pathfinder (break at walls)");
}
+
+ [TestMethod]
+ public void ACachedPathStartsOnTheFirstStepLikeAFreshOne()
+ {
+ var pathfinder = new JumpPointSearchPathfinder(Walled(new[]
+ {
+ new byte[] { 0, 0, 0, 0, 0, 0 },
+ }), new OctileDistanceHeuristic());
+ var fresh = pathfinder.FindPath((1, 1), (6, 1)).ToList();
+
+ var cached = pathfinder.FindPath(fresh[0], (6, 1)).ToList();
+
+ CollectionAssert.AreEqual(fresh.Skip(1).ToList(), cached);
+ }
+
+ [TestMethod]
+ public void TwoMapsDoNotShareCachedPaths()
+ {
+ var open = new JumpPointSearchPathfinder(Walled(new[]
+ {
+ new byte[] { 0, 0, 0, 0, 0 },
+ new byte[] { 0, 0, 0, 0, 0 },
+ }), new OctileDistanceHeuristic());
+ var blocked = new JumpPointSearchPathfinder(Walled(new[]
+ {
+ new byte[] { 0, 0, 1, 0, 0 },
+ new byte[] { 0, 0, 0, 0, 0 },
+ }), new OctileDistanceHeuristic());
+
+ var straight = open.FindPath((1, 1), (5, 1)).ToList();
+ var around = blocked.FindPath(straight[0], (5, 1)).ToList();
+
+ Assert.Contains(((short)3, (short)1), straight);
+ Assert.DoesNotContain(((short)3, (short)1), around, "the blocked map must not reuse the open map's path through its wall");
+ Assert.AreEqual(((short)5, (short)1), around.Last());
+ }
+
+ private static TestMap Walled(byte[][] inside)
+ {
+ var width = inside[0].Length + 2;
+ var wall = Enumerable.Repeat((byte)1, width).ToArray();
+ return new TestMap(new[] { wall }
+ .Concat(inside.Select(row => new byte[] { 1 }.Concat(row).Append((byte)1).ToArray()))
+ .Append(wall)
+ .ToArray());
+ }
}
}