コード例 #1
0
 public void SetChronoLog(ChronoInfos _Infos)
 {
     if (!m_ChronoLog)
     {
         return;
     }
     m_ChronoLog.text = _Infos.ToLogMessage();
 }
コード例 #2
0
    private static ChronoInfos StopChrono(Tile _Start, Tile _End, bool _PathFound)
    {
        chrono.Stop();
        ChronoInfos infos = new ChronoInfos();

        infos.PathFound               = _PathFound;
        infos.ElapsedTime             = chrono.ElapsedMilliseconds;
        infos.ForeachNeighborsChrono  = foreachNeighborsChrono;
        infos.ClonePathChrono         = clonePathChrono;
        infos.ExtendPathChrono        = extendPathChrono;
        infos.SearchInCloseListChrono = searchInCloseListChrono;
        infos.SearchInsertionChrono   = searchInsertionChrono;
        infos.InsertToOpenListChrono  = insertToOpenListChrono;
        infos.CreateSolutionChrono    = createSolutionChrono;
        return(infos);
    }
コード例 #3
0
    private void SearchPath(Pathfinder.PathfindingMode _SearchMode)
    {
        Debug.Log(_SearchMode);

        /**
         * if (m_StartTile == null)
         * {
         *  m_StartTile = m_GameGrid.GetTileAt(m_RowStart, m_ColumnStart);
         * }
         *
         * if (m_EndTile == null)
         * {
         *  m_EndTile = m_GameGrid.GetTileAt(m_RowEnd, m_ColumnEnd);
         * }
         * /**/

        if (m_PathVisualizer)
        {
            m_PathVisualizer.gameObject.SetActive(false);
        }

        Path        path      = null;
        ChronoInfos infos     = new ChronoInfos();
        float       timeLimit = m_HasLimitSearchTime ? m_TimeLimit : -1;

        switch (_SearchMode)
        {
        case Pathfinder.PathfindingMode.PM_Dijkstra:
            path = Pathfinder.SearchDijkstraPathFromTo(m_StartTile, m_EndTile, out infos, timeLimit);
            break;

        case Pathfinder.PathfindingMode.PM_Dijkstra_LinkOpti:
            path = Pathfinder.SearchDisjktraPathFromTo_LinkOpti(m_StartTile, m_EndTile, out infos, timeLimit);
            break;

        case Pathfinder.PathfindingMode.PM_AStar:
            path = Pathfinder.AStarCustomBasic(m_StartTile, m_EndTile, out infos, timeLimit);
            break;

        case Pathfinder.PathfindingMode.PM_AStar_LinkOpti:
            path = Pathfinder.AStar_LinkOpti(m_StartTile, m_EndTile, out infos, timeLimit);
            break;

        case Pathfinder.PathfindingMode.PM_HPA:
            path = Pathfinder.SearchHPAFromTo(m_GridClustered, m_StartTile, m_EndTile, out infos, timeLimit);
            break;

        default:
            break;
        }

        Debug.Log(infos.ToLogMessage());

        m_Hud?.SetChronoLog(infos);

        if (m_PathVisualizer && path != null)
        {
            m_PathVisualizer.gameObject.SetActive(true);
            m_PathVisualizer.TracePath(path, m_GameGrid.TilesRows);
        }
    }
コード例 #4
0
    /// <summary>
    /// Search a path with HPA algorithm
    /// </summary>
    /// <param name="_Start">Tile to start from</param>
    /// <param name="_End">Tile to reach</param>
    /// <param name="_ChronoInfos">Struct with times value to evaluate performance</param>
    /// <param name="_MaxSeconds">Max time allowed to search a solution. If value is negative, no time limit applied</param>
    /// <returns>the shortest path from start tile to end tile if it exists, null otherwise</returns>
    public static Path SearchHPAFromTo(GridClustered _GridClustered, Tile _Start, Tile _End, out ChronoInfos _ChronoInfos, float _MaxSeconds)
    {
        StartChrono();

        if (_Start == null || !_Start.IsAccessible || _End == null || !_End.IsAccessible || _GridClustered == null)
        {
            _ChronoInfos = ChronoInfos.InfosNone;
            return(null);
        }

        Cluster startCluster    = _GridClustered.GetClusterOfTile(_Start);
        Cluster endCluster      = _GridClustered.GetClusterOfTile(_End);
        Path    instantSolution = null;

        if (startCluster == endCluster && (instantSolution = startCluster.ComputeInternalPathFromTo(_Start, _End)) != null)
        {
            _ChronoInfos = StopChrono(_Start, _End, true);
            return(instantSolution);
        }

        List <BridgeStep>           openList = new List <BridgeStep>();
        CustomComparer <BridgeStep> comparer = new CustomComparer <BridgeStep>(false);

        HashSet <Bridge> closeSet = new HashSet <Bridge>();

        // store all the possible bridges connected to the start tile with their path
        // populate the open list with these bridges as all possible first steps
        Dictionary <Tile, Path> allFirstParts = new Dictionary <Tile, Path>();

        for (int i = 0; i < startCluster.Bridges.Count; i++)
        {
            Path path = startCluster.ComputeInternalPathFromTo(_Start, startCluster.Bridges[i].Start);
            if (path != null)
            {
                allFirstParts.Add(startCluster.Bridges[i].Start, path);

                BridgeStep stepToAdd      = new BridgeStep(startCluster.Bridges[i], path.Weight);
                int        indexInsertion = openList.BinarySearch(stepToAdd, comparer);
                if (indexInsertion < 0)
                {
                    indexInsertion = ~indexInsertion;
                }
                openList.Insert(indexInsertion, stepToAdd);
            }
        }

        // store all the possible bridges connected to the end tile with their path
        Dictionary <Tile, Path> allLastParts = new Dictionary <Tile, Path>();

        for (int i = 0; i < endCluster.Bridges.Count; i++)
        {
            Path path = endCluster.ComputeInternalPathFromTo(endCluster.Bridges[i].Start, _End);
            if (path != null)
            {
                allLastParts.Add(endCluster.Bridges[i].Start, path);
            }
        }

        while (openList.Count > 0)
        {
            if (_MaxSeconds > 0 && chrono.Elapsed.TotalSeconds > _MaxSeconds)
            {
                _ChronoInfos = StopChrono(_Start, _End, false);
                return(null);
            }

            BridgeStep stepToExtend = openList.Last();

            removeFromOpenChrono -= chrono.ElapsedMilliseconds;
            openList.RemoveAt(openList.Count - 1);
            removeFromOpenChrono += chrono.ElapsedMilliseconds;

            // if the step is already marked as solution, then it's the shortest solution
            if (stepToExtend.ReachedGoal)
            {
                // recreate whole path
                createSolutionChrono -= chrono.ElapsedMilliseconds;
                Path solution = stepToExtend.CreateCompletePath(_GridClustered, allFirstParts, allLastParts);
                createSolutionChrono += chrono.ElapsedMilliseconds;

                _ChronoInfos = StopChrono(_Start, _End, true);
                return(solution);
            }

            Bridge bridgeToExtend = stepToExtend.Bridge;
            if (closeSet.Contains(bridgeToExtend))
            {
                continue;
            }

            closeSet.Add(bridgeToExtend);

            // when reach a bridge with an end connected to the goal (check in the dictionary allLastParts) for the first time, mark it as one possible solution
            // with the mark, add also the weight of the last part which connect the end bridge and the goal
            // then, re-insert it in the open list at the right index.
            // if we try to extend a bridge already marked, then it's the shortest solution

            // if the bridge to extend is a bridge with the end connected to the goal cluster (in dictionary allLastParts)
            if (endCluster.Bridges.Exists(bridge => bridge.Start == bridgeToExtend.End) && allLastParts.ContainsKey(bridgeToExtend.End))
            {
                // mark step as possible solution and add the weight of the right last part
                stepToExtend.MarkAsSolution(allLastParts[stepToExtend.Bridge.End].Weight);

                // find the right index to re-insert in open list
                // if we try to extend it again, it means that it's the shortest path to the goal
                int indexInsertion = openList.BinarySearch(stepToExtend, comparer);
                if (indexInsertion < 0)
                {
                    indexInsertion = ~indexInsertion;
                }
                openList.Insert(indexInsertion, stepToExtend);
            }
            else
            {
                // Get the cluster from the bridgeToExtend End
                Cluster cluster = _GridClustered.GetClusterOfTile(bridgeToExtend.End);

                // Get the right reversed bridge
                Bridge reverseBridge = cluster.Bridges.Find(b => b.Start == bridgeToExtend.End);

                // loop through all the reversed bridge neighbors
                foreachNeighborsChrono -= chrono.ElapsedMilliseconds;
                foreach (Bridge neighbor in reverseBridge.Neighbors.Keys)
                {
                    // if the bridge is already in close set, skip to next neighbor
                    searchInCloseListChrono -= chrono.ElapsedMilliseconds;
                    bool existInCloseList = closeSet.Contains(neighbor);
                    searchInCloseListChrono += chrono.ElapsedMilliseconds;
                    if (existInCloseList)
                    {
                        continue;
                    }

                    // create extension with neighbor
                    extendPathChrono -= chrono.ElapsedMilliseconds;
                    BridgeStep extension = stepToExtend.MakeExtensionWith(neighbor, reverseBridge.Neighbors[neighbor].Weight);
                    extendPathChrono += chrono.ElapsedMilliseconds;

                    searchInsertionChrono -= chrono.ElapsedMilliseconds;
                    int indexInsertion = openList.BinarySearch(extension, comparer);
                    if (indexInsertion < 0)
                    {
                        indexInsertion = ~indexInsertion;
                    }
                    searchInsertionChrono += chrono.ElapsedMilliseconds;

                    insertToOpenListChrono -= chrono.ElapsedMilliseconds;
                    openList.Insert(indexInsertion, extension);
                    insertToOpenListChrono += chrono.ElapsedMilliseconds;
                }
                foreachNeighborsChrono += chrono.ElapsedMilliseconds;
            }
        }

        // No path found in limited time
        _ChronoInfos = StopChrono(_Start, _End, false);
        return(null);
    }
コード例 #5
0
    /// <summary>
    /// Search a path with Dijkstra algorithm.
    /// </summary>
    /// <param name="_Start">Tile to start from</param>
    /// <param name="_End">Tile to reach</param>
    /// <param name="_ChronoInfos">Struct with times value to evaluate performance</param>
    /// <param name="_MaxSeconds">Max time allowed to search a solution. If value is negative, no time limit applied</param>
    /// <returns>the shortest path from start tile to end tile if it exists, null otherwise</returns>
    public static Path SearchDijkstraPathFromTo(Tile _Start, Tile _End, out ChronoInfos _ChronoInfos, float _TimeLimit)
    {
        StartChrono();

        if (_Start == null || !_Start.IsAccessible || _End == null || !_End.IsAccessible)
        {
            _ChronoInfos = ChronoInfos.InfosNone;
            return(null);
        }

        List <Path> openList = new List <Path> {
            new Path(_Start)
        };
        HashSet <Tile> closeHashSet = new HashSet <Tile>();
        // Use a comparer to sort the open list by descending order on path weight.
        // This way, the shortest path in open list is at end
        //PathComparer pComparer = new PathComparer(false);
        CustomComparer <Path> pComparer = new CustomComparer <Path>(false);

        while (openList.Count > 0)
        {
            // Stop search if the chrono reached the time limit.
            // It avoids to get infinite loop or too long to find a solution. Mainly usefull to debug
            if (_TimeLimit > 0 && chrono.Elapsed.TotalSeconds > _TimeLimit)
            {
                _ChronoInfos = StopChrono(_Start, _End, false);
                return(null);
            }

            // Take the last path (which is the shortest as we sort the open list by descending weight)
            Path pathToExtend = openList.Last();

            // If last tile is the end, then return this path
            if (pathToExtend.LastStep == _End)
            {
                _ChronoInfos = StopChrono(_Start, _End, true);
                return(pathToExtend);
            }

            // Remove the current path from open list
            removeFromOpenChrono -= chrono.ElapsedMilliseconds;
            openList.RemoveAt(openList.Count - 1);
            removeFromOpenChrono += chrono.ElapsedMilliseconds;

            // If the last tile was already reach, skip the whole process on the current path
            Tile tileToExtend = pathToExtend.LastStep;
            if (closeHashSet.Contains(tileToExtend))
            {
                continue;
            }

            // Add the last tile to the close list (HashSet)
            closeHashSet.Add(tileToExtend);

            foreachNeighborsChrono -= chrono.ElapsedMilliseconds;
            // loop through all neighbors from step to extend
            for (int i = 0; i < tileToExtend.Neighbors.Count; i++)
            {
                Tile neighbor = tileToExtend.Neighbors[i];

                // if neighbor is inaccessible (wall) , skip iteration
                if (!neighbor.IsAccessible)
                {
                    continue;
                }

                // search in the close list if the neighbor was already reached
                searchInCloseListChrono -= chrono.ElapsedMilliseconds;
                bool shortestExists = closeHashSet.Contains(neighbor);
                searchInCloseListChrono += chrono.ElapsedMilliseconds;

                if (shortestExists)
                {
                    continue;
                }

                // Clone the current path to extend
                clonePathChrono -= chrono.ElapsedMilliseconds;
                Path extendedPath = new Path(pathToExtend);
                clonePathChrono += chrono.ElapsedMilliseconds;

                // Extend path with the current valid neighbor
                extendPathChrono -= chrono.ElapsedMilliseconds;
                extendedPath.AddStep(neighbor);
                extendPathChrono += chrono.ElapsedMilliseconds;

                // Search in open list where to insert the extended path
                searchInsertionChrono -= chrono.ElapsedMilliseconds;
                int indexInsertion = openList.BinarySearch(extendedPath, pComparer);
                if (indexInsertion < 0)
                {
                    indexInsertion = ~indexInsertion;
                }
                searchInsertionChrono += chrono.ElapsedMilliseconds;

                // Insert the extended path in open list
                insertToOpenListChrono -= chrono.ElapsedMilliseconds;
                openList.Insert(indexInsertion, extendedPath);
                insertToOpenListChrono += chrono.ElapsedMilliseconds;
            }
            foreachNeighborsChrono += chrono.ElapsedMilliseconds;
        }
        _ChronoInfos = StopChrono(_Start, _End, false);
        return(null);
    }
コード例 #6
0
    /// <summary>
    /// Search a path with custom AStar algorithm
    /// (PathLink is use to explore the grid. this way, there's no duplicate of tile array when we extend open list)
    /// </summary>
    /// <param name="_Start">Tile to start from</param>
    /// <param name="_End">Tile to reach</param>
    /// <param name="_ChronoInfos">Struct with times value to evaluate performance</param>
    /// <param name="_MaxSeconds">Max time allowed to search a solution. If value is negative, no time limit applied</param>
    /// <returns>the shortest path from start tile to end tile if it exists, null otherwise</returns>
    public static Path AStar_LinkOpti(Tile _Start, Tile _End, out ChronoInfos _ChronoInfos, float _MaxSeconds)
    {
        StartChrono();

        if (_Start == null || !_Start.IsAccessible || _End == null || !_End.IsAccessible)
        {
            _ChronoInfos = ChronoInfos.InfosNone;
            return(null);
        }

        List <PathLinkHeuristic> openList = new List <PathLinkHeuristic>(100)
        {
            new PathLinkHeuristic(_Start, Tile.GetManhattanDistance(_Start, _End))
        };
        HashSet <Tile> closeHashSet = new HashSet <Tile>();
        CustomComparer <PathLinkHeuristic> customComparer = new CustomComparer <PathLinkHeuristic>(true);

        while (openList.Count > 0)
        {
            if (_MaxSeconds > 0 && chrono.Elapsed.TotalSeconds > _MaxSeconds)
            {
                _ChronoInfos = StopChrono(_Start, _End, false);
                return(null);
            }

            PathLinkHeuristic linkToExtend = openList.First();
            if (linkToExtend.Tile == _End)
            {
                _ChronoInfos          = StopChrono(_Start, _End, true);
                createSolutionChrono -= chrono.ElapsedMilliseconds;
                Path solution = Path.CreatePath(linkToExtend);
                createSolutionChrono += chrono.ElapsedMilliseconds;
                return(solution);
            }

            removeFromOpenChrono -= chrono.ElapsedMilliseconds;
            openList.RemoveAt(0);
            removeFromOpenChrono += chrono.ElapsedMilliseconds;

            if (closeHashSet.Contains(linkToExtend.Tile))
            {
                continue;
            }

            closeHashSet.Add(linkToExtend.Tile);
            Tile tileToExtend = linkToExtend.Tile;

            foreachNeighborsChrono -= chrono.ElapsedMilliseconds;
            for (int i = 0; i < tileToExtend.Neighbors.Count; i++)
            {
                Tile neighbor = tileToExtend.Neighbors[i];

                searchInCloseListChrono -= chrono.ElapsedMilliseconds;
                bool existInCloseList = closeHashSet.Contains(neighbor);
                searchInCloseListChrono += chrono.ElapsedMilliseconds;

                if (!neighbor.IsAccessible || existInCloseList)
                {
                    continue;
                }

                extendPathChrono -= chrono.ElapsedMilliseconds;
                //PathLinkHeuristic extendedLink = new PathLinkHeuristic(linkToExtend, neighbor, Tile.GetManhattanDistance(neighbor, _End));
                PathLinkHeuristic extendedLink = linkToExtend.MakeExtensionWith(neighbor, Tile.GetManhattanDistance(neighbor, _End));
                extendPathChrono += chrono.ElapsedMilliseconds;

                searchInsertionChrono -= chrono.ElapsedMilliseconds;
                int indexInsertion = openList.BinarySearch(extendedLink, customComparer);
                if (indexInsertion < 0)
                {
                    indexInsertion = ~indexInsertion;
                }
                searchInsertionChrono += chrono.ElapsedMilliseconds;

                insertToOpenListChrono -= chrono.ElapsedMilliseconds;
                openList.Insert(indexInsertion, extendedLink);
                insertToOpenListChrono += chrono.ElapsedMilliseconds;
            }
            foreachNeighborsChrono += chrono.ElapsedMilliseconds;
        }

        _ChronoInfos = StopChrono(_Start, _End, false);
        return(null);
    }
コード例 #7
0
    /// <summary>
    /// Search a path with custom AStar algorithm
    /// (Path class is used to explore the grid)
    /// </summary>
    /// <param name="_Start">Tile to start from</param>
    /// <param name="_End">Tile to reach</param>
    /// <param name="_ChronoInfos">Struct with times value to evaluate performance</param>
    /// <param name="_MaxSeconds">Max time allowed to search a solution. If value is negative, no time limit applied</param>
    /// <returns>the shortest path from start tile to end tile if it exists, null otherwise</returns>
    public static Path AStarCustomBasic(Tile _Start, Tile _End, out ChronoInfos _ChronoInfos, float _MaxSeconds)
    {
        StartChrono();

        if (_Start == null || !_Start.IsAccessible || _End == null || !_End.IsAccessible)
        {
            _ChronoInfos = ChronoInfos.InfosNone;
            return(null);
        }

        List <PathHeuristic> openList = new List <PathHeuristic>(50)
        {
            new PathHeuristic(_Start, Tile.GetManhattanDistance(_Start, _End))
        };
        HashSet <Tile> closeHashSet = new HashSet <Tile>();
        CustomComparer <PathHeuristic> phComparer = new CustomComparer <PathHeuristic>(false);

        while (openList.Count > 0)
        {
            if (_MaxSeconds > 0 && chrono.Elapsed.TotalSeconds > _MaxSeconds)
            {
                _ChronoInfos = StopChrono(_Start, _End, false);
                return(null);
            }

            PathHeuristic pathToExtend = openList.Last();

            removeFromOpenChrono -= chrono.ElapsedMilliseconds;
            openList.RemoveAt(openList.Count - 1);
            removeFromOpenChrono += chrono.ElapsedMilliseconds;

            Tile tileToExtend = pathToExtend.LastStep;
            if (tileToExtend == _End)
            {
                _ChronoInfos = StopChrono(_Start, _End, true);
                return(pathToExtend);
            }

            if (closeHashSet.Contains(tileToExtend))
            {
                continue;
            }

            closeHashSet.Add(tileToExtend);

            foreachNeighborsChrono -= chrono.ElapsedMilliseconds;
            for (int i = 0; i < tileToExtend.Neighbors.Count; i++)
            {
                Tile neighbor = tileToExtend.Neighbors[i];
                if (!neighbor.IsAccessible)
                {
                    continue;
                }

                searchInCloseListChrono -= chrono.ElapsedMilliseconds;
                if (closeHashSet.Contains(neighbor))
                {
                    continue;
                }
                searchInCloseListChrono += chrono.ElapsedMilliseconds;

                clonePathChrono -= chrono.ElapsedMilliseconds;
                PathHeuristic extendedPath = new PathHeuristic(pathToExtend);
                clonePathChrono += chrono.ElapsedMilliseconds;

                extendPathChrono -= chrono.ElapsedMilliseconds;
                extendedPath.AddStep(neighbor, Tile.GetManhattanDistance(neighbor, _End));
                extendPathChrono += chrono.ElapsedMilliseconds;

                searchInsertionChrono -= chrono.ElapsedMilliseconds;
                int indexInsertion = openList.BinarySearch(extendedPath, phComparer);
                if (indexInsertion < 0)
                {
                    indexInsertion = ~indexInsertion;
                }
                searchInsertionChrono += chrono.ElapsedMilliseconds;

                insertToOpenListChrono -= chrono.ElapsedMilliseconds;
                openList.Insert(indexInsertion, extendedPath);
                insertToOpenListChrono += chrono.ElapsedMilliseconds;
            }
            foreachNeighborsChrono += chrono.ElapsedMilliseconds;
        }

        _ChronoInfos = StopChrono(_Start, _End, false);
        return(null);
    }
コード例 #8
0
    /// <summary>
    /// Search a path with Dijkstra algorithm.
    /// The open list is optimized with PathLink class and not Path class. This way, we do not duplicate a whole path when we extend it,
    /// we create PathLink insted and use it as LinkedList to simulate a path.
    /// </summary>
    /// <param name="_Start">Tile to start from</param>
    /// <param name="_End">Tile to reach</param>
    /// <param name="_ChronoInfos">Struct with times value to evaluate performance</param>
    /// <param name="_MaxSeconds">Max time allowed to search a solution. If value is negative, no time limit applied</param>
    /// <returns>the shortest path from start tile to end tile if it exists, null otherwise</returns>
    public static Path SearchDisjktraPathFromTo_LinkOpti(Tile _Start, Tile _End, out ChronoInfos _ChronoInfos, float _TimeLimit)
    {
        StartChrono();

        if (_Start == null || !_Start.IsAccessible || _End == null || !_End.IsAccessible)
        {
            _ChronoInfos = ChronoInfos.InfosNone;
            return(null);
        }

        List <PathLink> openList = new List <PathLink>(100)
        {
            new PathLink(_Start)
        };
        HashSet <Tile>            closeHashSet   = new HashSet <Tile>();
        CustomComparer <PathLink> customComparer = new CustomComparer <PathLink>(false);

        while (openList.Count > 0)
        {
            // Stop search if the chrono reached the time limit.
            // It avoids to get infinite loop or too long to find a solution. Mainly usefull to debug
            if (_TimeLimit > 0 && chrono.Elapsed.TotalSeconds > _TimeLimit)
            {
                _ChronoInfos = StopChrono(_Start, _End, false);
                return(null);
            }

            // Take the last (and so the shortest) path to extend
            PathLink pathToExtend = openList.Last();
            Tile     tileToExtend = pathToExtend.Tile;

            // If the end is reach, return solution
            if (tileToExtend == _End)
            {
                _ChronoInfos = StopChrono(_Start, _End, true);
                return(Path.CreatePath(pathToExtend));
            }

            // Remove the current path from open list
            removeFromOpenChrono -= chrono.ElapsedMilliseconds;
            openList.RemoveAt(openList.Count - 1);
            removeFromOpenChrono += chrono.ElapsedMilliseconds;

            // If the last tile was already reached, skip process to next path to extend
            if (closeHashSet.Contains(tileToExtend))
            {
                continue;
            }
            closeHashSet.Add(tileToExtend);

            foreachNeighborsChrono -= chrono.ElapsedMilliseconds;
            // Loop on  neighbors of last tile
            for (int i = 0; i < tileToExtend.Neighbors.Count; i++)
            {
                Tile neighbor = tileToExtend.Neighbors[i];
                // If neighbor is wall or already reached, skip process to next neighbor

                searchInCloseListChrono -= chrono.ElapsedMilliseconds;
                bool existInCloseList = closeHashSet.Contains(neighbor);
                searchInCloseListChrono += chrono.ElapsedMilliseconds;

                if (!neighbor.IsAccessible || existInCloseList)
                {
                    continue;
                }


                // Extend pathlink with the path to extend and the current neighbor
                extendPathChrono -= chrono.ElapsedMilliseconds;
                //PathLink extendedPath = new PathLink(pathToExtend, neighbor);
                PathLink extendedPath = pathToExtend.MakeExtensionWith(neighbor);
                extendPathChrono += chrono.ElapsedMilliseconds;

                // Search index to insert extended path in open list
                searchInsertionChrono -= chrono.ElapsedMilliseconds;
                int indexInsertion = openList.BinarySearch(extendedPath, customComparer);
                if (indexInsertion < 0)
                {
                    indexInsertion = ~indexInsertion;
                }
                searchInsertionChrono += chrono.ElapsedMilliseconds;

                // Insert the extended path in open list
                insertToOpenListChrono -= chrono.ElapsedMilliseconds;
                openList.Insert(indexInsertion, extendedPath);
                insertToOpenListChrono += chrono.ElapsedMilliseconds;
            }
            foreachNeighborsChrono += chrono.ElapsedMilliseconds;
        }

        _ChronoInfos = StopChrono(_Start, _End, false);
        return(null);
    }