roy-t / AStar

A fast 2D path finding library based on the A* algorithm. Works with both grids and graphs. Supports any .NET variant that supports .NETStandard 2.0 or higher. This library has no external dependencies. The library is licensed under the MIT license.
http://roy-t.nl
MIT License
338 stars 60 forks source link

Index was outside the bounds of the array error #23

Closed dankcellar closed 5 years ago

dankcellar commented 5 years ago

Hello, this library is awesome! I'm having some weird issue with getting consistent paths with the error being IndexOutOfRangeException: Index was outside the bounds of the array. from Unity.

Grid grid = new Grid(gridWidth, gridLength, 1.0f);
for (int row = 0; row < gridLength; row++)
{
  for (int column = 0; column < gridWidth; column++)
  {  
    if (IsWall(column, row))
    {
      grid.BlockCell(new Position(column, row));
    }
  }
}

Parallel.ForEach(positions, position =>
{
  Position[] path = CreatePath(grid, position, endPosition);
});

This will throw an error about every 10 runs and I'm almost certain it has to do with the threading. I know https://github.com/roy-t/AStar/issues/4 was an issue when it comes to threading a single find path call but I was wondering it is possible to do it the above way.

Here is the full Unity output if that helps. Thanks again for this library!

IndexOutOfRangeException: Index was outside the bounds of the array.
(wrapper stelemref) System.Object.virt_stelemref_class_small_idepth(intptr,object)
System.Collections.Generic.List`1[T].Add (T item) (at <d7ac571ca2d04b2f981d0d886fa067cf>:0)
RoyT.AStar.PathFinder.MessageCurrent (RoyT.AStar.Position position, System.Collections.Generic.IReadOnlyList`1[T] path) (at Assets/Scripts/Roy-T.AStar/PathFinder.Debug.cs:17)
RoyT.AStar.PathFinder.FindPath (RoyT.AStar.Grid grid, RoyT.AStar.Position start, RoyT.AStar.Position end, RoyT.AStar.Offset[] movementPattern, System.Int32 iterationLimit) (at Assets/Scripts/Roy-T.AStar/PathFinder.cs:68)
RoyT.AStar.Grid.GetPath (RoyT.AStar.Position start, RoyT.AStar.Position end, RoyT.AStar.Offset[] movementPattern, System.Int32 iterationLimit) (at Assets/Scripts/Roy-T.AStar/Grid.cs:152)
BetterDungeons.CreatePath (RoyT.AStar.Grid grid, BetterDungeons+Cell cell, BetterDungeons+Cell mainCell) (at Assets/Scripts/BetterDungeons.cs:265)
BetterDungeons+<>c__DisplayClass14_0.<CreateDungeon>b__0 (BetterDungeons+Room room) (at Assets/Scripts/BetterDungeons.cs:104)
System.Threading.Tasks.Parallel+<>c__DisplayClass31_0`2[TSource,TLocal].<ForEachWorker>b__0 (System.Int32 i) (at <d7ac571ca2d04b2f981d0d886fa067cf>:0)
System.Threading.Tasks.Parallel+<>c__DisplayClass17_0`1[TLocal].<ForWorker>b__1 () (at <d7ac571ca2d04b2f981d0d886fa067cf>:0)
System.Threading.Tasks.Task.InnerInvoke () (at <d7ac571ca2d04b2f981d0d886fa067cf>:0)
System.Threading.Tasks.Task.InnerInvokeWithArg (System.Threading.Tasks.Task childTask) (at <d7ac571ca2d04b2f981d0d886fa067cf>:0)
System.Threading.Tasks.Task+<>c__DisplayClass178_0.<ExecuteSelfReplicating>b__0 (System.Object <p0>) (at <d7ac571ca2d04b2f981d0d886fa067cf>:0)
Rethrow as AggregateException: One or more errors occurred.
System.Threading.Tasks.Task.ThrowIfExceptional (System.Boolean includeTaskCanceledExceptions) (at <d7ac571ca2d04b2f981d0d886fa067cf>:0)
System.Threading.Tasks.Task.Wait (System.Int32 millisecondsTimeout, System.Threading.CancellationToken cancellationToken) (at <d7ac571ca2d04b2f981d0d886fa067cf>:0)
System.Threading.Tasks.Task.Wait () (at <d7ac571ca2d04b2f981d0d886fa067cf>:0)
System.Threading.Tasks.Parallel.ForWorker[TLocal] (System.Int32 fromInclusive, System.Int32 toExclusive, System.Threading.Tasks.ParallelOptions parallelOptions, System.Action`1[T] body, System.Action`2[T1,T2] bodyWithState, System.Func`4[T1,T2,T3,TResult] bodyWithLocal, System.Func`1[TResult] localInit, System.Action`1[T] localFinally) (at <d7ac571ca2d04b2f981d0d886fa067cf>:0)
System.Threading.Tasks.Parallel.ForEachWorker[TSource,TLocal] (System.Collections.Generic.IList`1[T] list, System.Threading.Tasks.ParallelOptions parallelOptions, System.Action`1[T] body, System.Action`2[T1,T2] bodyWithState, System.Action`3[T1,T2,T3] bodyWithStateAndIndex, System.Func`4[T1,T2,T3,TResult] bodyWithStateAndLocal, System.Func`5[T1,T2,T3,T4,TResult] bodyWithEverything, System.Func`1[TResult] localInit, System.Action`1[T] localFinally) (at <d7ac571ca2d04b2f981d0d886fa067cf>:0)
System.Threading.Tasks.Parallel.ForEachWorker[TSource,TLocal] (System.Collections.Generic.IEnumerable`1[T] source, System.Threading.Tasks.ParallelOptions parallelOptions, System.Action`1[T] body, System.Action`2[T1,T2] bodyWithState, System.Action`3[T1,T2,T3] bodyWithStateAndIndex, System.Func`4[T1,T2,T3,TResult] bodyWithStateAndLocal, System.Func`5[T1,T2,T3,T4,TResult] bodyWithEverything, System.Func`1[TResult] localInit, System.Action`1[T] localFinally) (at <d7ac571ca2d04b2f981d0d886fa067cf>:0)
System.Threading.Tasks.Parallel.ForEach[TSource] (System.Collections.Generic.IEnumerable`1[T] source, System.Action`1[T] body) (at <d7ac571ca2d04b2f981d0d886fa067cf>:0)
BetterDungeons.CreateDungeon () (at Assets/Scripts/BetterDungeons.cs:101)
BetterDungeons..ctor (System.Int32 gridLength, System.Int32 gridWidth, System.Int32 minRoomLength, System.Int32 minRoomWidth, System.Double percentWalls, System.Int32 passes, System.Int32 seed) (at Assets/Scripts/BetterDungeons.cs:40)
DrawDungeon.Start () (at Assets/Scripts/DrawDungeon.cs:25)
roy-t commented 5 years ago

Hey edowyney29, you should definitely be able to achieve this kind of parallelism. The reason the code breaks is because of the code in PathFinder.Debug.cs. Which updates the built-in UI. This should only happen when running your code in DEBUG mode (maybe that is something you can check in Unity). In RELEASE mode you should not have this problem.

Alternatively, if Unity doesn't care about the '[Conditional("DEBUG")]' attribute you can just throw away everything in the PathFinder.Debug.cs file and remove all reference to methods in that file. It shouldn't affect you :).

Best of luck, and let me know if this works :).

bradzoob commented 5 years ago

Woah, I feel a fool for not having even tried Parallel.For* with this. lol.