Chris3606 / GoRogue

.NET Standard roguelike library in C#. Features many algorithms and data structures pertinent to roguelike/2D game developers, specifically designed to be minimally intrusive upon the developer's architecture.
MIT License
494 stars 31 forks source link

FleeMap exception when border coords of baseMap are clear #211

Closed jpadkins closed 2 years ago

jpadkins commented 3 years ago

Hey, I believe I've found some unintended behavior with FleeMap.

Here is a snippet to reproduce:

using GoRogue;
using GoRogue.MapViews;
using GoRogue.Pathing;

...

// Arbitrary map size.
var goalStateMap = new ArrayMap<GoalState>(32, 32);

// Fill the map up with clear state.
for (int x = 0; x < goalStateMap.Width; ++x)
{
    for (int y = 0; y < goalStateMap.Height; ++y)
    {
        goalStateMap[x, y] = GoalState.Clear;
    }
}
// Arbitrary goal position.
goalStateMap[5, 5] = GoalState.Goal;

// Arbitrary distance method.
var goalMap = new GoalMap(goalStateMap, Distance.EUCLIDEAN);
// Arbitrary magnitude value.
var fleeMap = new FleeMap(goalMap, 1.2);

goalMap.Update();

The .Update() call at the end will raise

GoRogue.Pathing.GoalState GoRogue.MapViews.ArrayMap`1[[GoRogue.Pathing.GoalState, GoRogue, Version=2.6.4.0, Culture=neutral, PublicKeyToken=null]].get_Item(GoRogue.Coord ): System.IndexOutOfRangeException: Index was outside the bounds of the array.

To fix this, the outer edges can be set to GoalState.Obstacle:

var goalStateMap = new ArrayMap<GoalState>(32, 32);
for (int x = 0; x < goalStateMap.Width; ++x)
{
    for (int y = 0; y < goalStateMap.Height; ++y)
    {
        if (x == 0 || x == goalStateMap.Width - 1 || y == 0 || y == goalStateMap.Height - 1)
        {
            goalStateMap[x, y] = GoalState.Obstacle;
        }
        else
        {
            goalStateMap[x, y] = GoalState.Clear;
        }
    }
}
goalStateMap[5, 5] = GoalState.Goal;

var goalMap = new GoalMap(goalStateMap, Distance.EUCLIDEAN);
var fleeMap = new FleeMap(goalMap, 1.2);

goalMap.Update();

The above snippet no longer raises the exception.

I think the issue would be fixed if one or both of these lines also checked if the Coord was in bounds of the map (similar to how GoalMap checks if the Coord is contained in the walkable set): https://github.com/Chris3606/GoRogue/blob/master/GoRogue/Pathing/FleeMap.cs#L160 https://github.com/Chris3606/GoRogue/blob/master/GoRogue/Pathing/FleeMap.cs#L170 But I haven't had time yet to test it.

Or perhaps is this intentional behavior for performance reasons?

Chris3606 commented 3 years ago

I'll look into this, as I recall it may be a bug as those performance checks generally are not performance-heavy. Most of my recent development time has been devoted to preparing for the GoRogue v3 release, but I'll try and get this back-ported to 2.x as well once I track it down since it's rather limiting behavior. Thank you for reporting this!