示例#1
0
        private WoWUnit FindTarget(WoWPoint location)
        {
            WoWUnit target = null;

            if (_targetGuid.IsValid)
            {
                target = ObjectManager.GetObjectByGuid <WoWUnit>(_targetGuid);
            }

            if (target != null && target.IsAlive
                // if rescuing NPCs then we don't want to include those rescued by other players.
                && (DropPassengerButton == 0 || !IsRescuedByOtherPlayer(target)) &&
                !Blacklist.Contains(target, BlacklistFlags.Combat | BlacklistFlags.Interact))
            {
                return(target);
            }

            target = ObjectManager.GetObjectsOfType <WoWUnit>()
                     .Where(u => !u.IsDead && NpcList.Contains((int)u.Entry) &&
                            !Blacklist.Contains(u, BlacklistFlags.Combat | BlacklistFlags.Interact)
                            // if rescuing NPCs then we don't want to include those rescued by other players.
                            && (DropPassengerButton == 0 || !IsRescuedByOtherPlayer(target)) &&
                            u.Location.Distance2D(location) < NpcScanRange)
                     .OrderBy(u => location.Distance2DSqr(u.Location))
                     .FirstOrDefault();

            if (target != null)
            {
                _targetGuid = target.Guid;
            }

            return(target);
        }
示例#2
0
        /// <summary>Determines whether <paramref name="myLoc" /> is within a tolerable proximity of <paramref name="location" />.</summary>
        /// <param name="myLoc">My loc.</param>
        /// <param name="location">The location.</param>
        /// <param name="tolerance">The tolerance. Default: <see cref="Navigator.PathPrecision"/></param>
        /// <returns></returns>
        public static bool AtLocation(WoWPoint myLoc, WoWPoint location, float?tolerance)
        {
            var tol = tolerance ?? Navigator.PathPrecision;

            // We are checking if point is in an upright cylinder whose center is positioned at 'location',
            // radius set to 'tolerance' and height set to max(4.5, 'tolerance') x 2.
            // This is the most suitable method when using mesh navigation because the mesh is often above or below the
            // actual ingame terrain so there's a need to clamp the tolerance along the Z axis to an amount that
            // is greater then the maximum terrain/mesh Z coord difference
            return(myLoc.Distance2DSqr(location) <= tol * tol && Math.Abs(myLoc.Z - location.Z) <= Math.Max(4.5f, tol));
        }
示例#3
0
        /// <summary>Determines if <paramref name="myPos"/> is at <paramref name="otherPos"/></summary>
        private bool AtLocation(WoWPoint myPos, WoWPoint otherPos)
        {
            // We are using cylinder distance comparison because often times we want faily high precision
            // but need an increased tolerance in the z coord due to 'otherPos' sometimes being below terrain.
            if (myPos.Distance2DSqr(otherPos) > RoughDestination.ArrivalTolerance * RoughDestination.ArrivalTolerance)
            {
                return(false);
            }

            var zTolerance = Math.Max(4.5f, RoughDestination.ArrivalTolerance);

            return(Math.Abs(otherPos.Z - myPos.Z) < zTolerance);
        }
示例#4
0
        /// <summary>
        /// locates safe point away from enemies
        /// </summary>
        /// <param name="ptOrigin">start point for search</param>
        /// <param name="minSafeDist">min distance to be safe</param>
        /// <returns></returns>
        public WoWPoint FindLocationOriginal(WoWPoint ptOrigin)
        {
            WoWPoint destinationLocation = new WoWPoint();
            List<WoWPoint> mobLocations = new List<WoWPoint>();
            int arcIncrement = 360 / RaysToCheck;

            mobLocations = AllEnemyMobLocations;

            double minSafeDistSqr = MinSafeDistance * MinSafeDistance;

            double degreesFacing = (Me.RenderFacing * 180f) / Math.PI;
            // Logger.WriteDebug( Color.Cyan, "Facing {0:F0}d {1:F2}r Searching for {2:F1} yard mob free area", degreesFacing, Me.RenderFacing, MinSafeDistance);
            for (int arcIndex = 0; arcIndex < RaysToCheck ; arcIndex++)
            {
                float degreesFrom = 180;
                if ((arcIndex & 1) == 0)
                    degreesFrom += (arcIndex >> 1) * arcIncrement;
                else
                    degreesFrom -= (arcIndex >> 1) * arcIncrement;

                for (float distFromOrigin = MinScanDistance; distFromOrigin <= MaxScanDistance ; distFromOrigin += IncrementScanDistance )
                {
                    float heading = (float)(degreesFrom * Math.PI / 180f);
                    heading -= Me.RenderFacing;
                    destinationLocation = ptOrigin.RayCast((float)(degreesFrom * Math.PI / 180f), distFromOrigin);
                    double mobDistSqr = destinationLocation.Distance2DSqr(NearestMobLoc(destinationLocation, mobLocations));

                    if (mobDistSqr <= minSafeDistSqr)
                        continue;

                    //if (Navigator.CanNavigateFully(Me.Location, destinationLocation))
                    if (Navigator.GeneratePath(Me.Location, destinationLocation).Length <= 0)
                    {
                        // Logger.WriteDebug( Color.Cyan, "Mob-free location failed path check for degrees={0:F1} dist={1:F1}", degreesFrom, distFromOrigin);
                        continue;
                    }

                    if (!Styx.WoWInternals.World.GameWorld.IsInLineOfSight(Me.Location, destinationLocation))
                    {
                        // Logger.WriteDebug( Color.Cyan, "Mob-free location failed line of sight check for degrees={0:F1} dist={1:F1}", degreesFrom, distFromOrigin);
                        continue;
                    }

                    if (MobToRunFrom != null)
                    {
                        if (!Styx.WoWInternals.World.GameWorld.IsInLineOfSpellSight(destinationLocation, MobToRunFrom.Location))
                        {
                            // Logger.WriteDebug( Color.Cyan, "Mob-free location failed line of sight check for degrees={0:F1} dist={1:F1}", degreesFrom, distFromOrigin);
                            continue;
                        }
                    }

                    Logger.WriteDebug(Color.Cyan, "Found mob-free location ({0:F1} yd radius) at degrees={1:F1} dist={2:F1}", MinSafeDistance, degreesFrom, distFromOrigin);
                    return destinationLocation;
                }
            }

            Logger.WriteDebug(Color.Cyan, "No mob-free location ({0:F1} yd radius) found within {1:F1} yds", MinSafeDistance, MaxScanDistance );
            return WoWPoint.Empty;
        }
示例#5
0
        public WoWPoint FindLocation(WoWPoint ptOrigin)
        {
            DateTime startFind = DateTime.Now;
            int countPointsChecked = 0;
            int countFailToPointNav = 0;
            int countFailRange = 0;
            int countFailSafe = 0;
            int countFailToPointLoS = 0;
            int countFailToMobLoS = 0;
            double furthestNearMobDistSqr = 0f;
            WoWPoint ptFurthest = WoWPoint.Empty;
            bool reallyCheckRangeToLineOfSightMob = CheckRangeToLineOfSightMob && Me.GotTarget;
            WoWPoint ptAdjOrigin = ptOrigin;
            ptAdjOrigin.Z += 1f;

            WoWPoint ptDestination = new WoWPoint();
            List<WoWPoint> mobLocations = new List<WoWPoint>();
            float arcIncrement = ((float)Math.PI * 2) / RaysToCheck;

            mobLocations = AllEnemyMobLocationsToCheck;
            double minSafeDistSqr = MinSafeDistance * MinSafeDistance;

            float baseDestinationFacing = MobToRunFrom == null ?
                                            Me.RenderFacing + (float)Math.PI
                                            : Styx.Helpers.WoWMathHelper.CalculateNeededFacing(MobToRunFrom.Location, Me.Location);

            // Logger.WriteDebug( Color.Cyan, "SafeArea: search near {0:F0}d @ {1:F1} yds for mob free area", RadiansToDegrees(baseDestinationFacing), MinSafeDistance);

            for (int arcIndex = 0; arcIndex < RaysToCheck; arcIndex++)
            {
                // rather than tracing around the circle, toggle between clockwise and counter clockwise for each test
                // .. so we favor a position furthest away from mob
                float checkFacing = baseDestinationFacing;
                if ((arcIndex & 1) == 0)
                    checkFacing += arcIncrement * (arcIndex >> 1);
                else
                    checkFacing -= arcIncrement * ((arcIndex >> 1) + 1);

                for (float distFromOrigin = MinScanDistance; distFromOrigin <= MaxScanDistance; distFromOrigin += IncrementScanDistance)
                {
                    countPointsChecked++;

                    ptDestination = ptOrigin.RayCast(checkFacing, distFromOrigin);
                    if (!Navigator.CanNavigateFully(Me.Location, ptDestination))
                    {
                        // Logger.WriteDebug( Color.Cyan, "Safe Location failed navigation check for degrees={0:F1} dist={1:F1}", RadiansToDegrees(checkFacing), distFromOrigin);
                        countFailToPointNav++;
                        continue;
                    }

                    WoWPoint ptNearest = NearestMobLoc(ptDestination, mobLocations);
                    if (ptNearest == WoWPoint.Empty)
                    {
                        if (furthestNearMobDistSqr < minSafeDistSqr)
                        {
                            furthestNearMobDistSqr = minSafeDistSqr;
                            ptFurthest = ptDestination;     // set best available if others fail
                        }
                    }
                    else
                    {
                        double mobDistSqr = ptDestination.Distance2DSqr(ptNearest);
                        if (furthestNearMobDistSqr < mobDistSqr)
                        {
                            furthestNearMobDistSqr = mobDistSqr;
                            ptFurthest = ptDestination;     // set best available if others fail
                        }
                        if (mobDistSqr <= minSafeDistSqr)
                        {
                            countFailSafe++;
                            continue;
                        }
                    }

                    if (reallyCheckRangeToLineOfSightMob && RangeToLineOfSightMob < ptDestination.Distance(LineOfSightMob.Location) - LineOfSightMob.MeleeDistance())
                    {
                        countFailRange++;
                        continue;
                    }

                    if (CheckLineOfSightToSafeLocation)
                    {
                        WoWPoint ptAdjDest = ptDestination;
                        ptAdjDest.Z += 1f;
                        if (!Styx.WoWInternals.World.GameWorld.IsInLineOfSight(ptAdjOrigin, ptAdjDest))
                        {
                            // Logger.WriteDebug( Color.Cyan, "Mob-free location failed line of sight check for degrees={0:F1} dist={1:F1}", degreesFrom, distFromOrigin);
                            countFailToPointLoS++;
                            continue;
                        }
                    }

                    if (CheckSpellLineOfSightToMob && LineOfSightMob != null)
                    {
                        if (!Styx.WoWInternals.World.GameWorld.IsInLineOfSpellSight(ptDestination, LineOfSightMob.Location))
                        {
                            if (!Styx.WoWInternals.World.GameWorld.IsInLineOfSight(ptDestination, LineOfSightMob.Location))
                            {
                                // Logger.WriteDebug( Color.Cyan, "Mob-free location failed line of sight check for degrees={0:F1} dist={1:F1}", degreesFrom, distFromOrigin);
                                countFailToMobLoS++;
                                continue;
                            }
                        }
                    }

                    Logger.WriteDebug(Color.Cyan, "SafeArea: Found mob-free location ({0:F1} yd radius) at degrees={1:F1} dist={2:F1} on point check# {3}", MinSafeDistance, WoWMathHelper.RadiansToDegrees(checkFacing), distFromOrigin, countPointsChecked);
                    Logger.WriteDebug(Color.Cyan, "SafeArea: processing took {0:F0} ms", (DateTime.Now - startFind).TotalMilliseconds);
                    return ptDestination;
                }
            }

            Logger.WriteDebug(Color.Cyan, "SafeArea: No mob-free location ({0:F1} yd radius) found within {1:F1} yds ({2} checked, {3} nav, {4} not safe, {5} range, {6} pt los, {7} mob los)", MinSafeDistance, MaxScanDistance, countPointsChecked, countFailToPointNav, countFailSafe, countFailRange, countFailToPointLoS, countFailToMobLoS);
            if (ChooseSafestAvailable && ptFurthest != WoWPoint.Empty)
            {
                Logger.WriteDebug(Color.Cyan, "SafeArea: choosing best available spot in {0:F1} yd radius where closest mob is {1:F1} yds", MinSafeDistance, Math.Sqrt(furthestNearMobDistSqr));
                Logger.WriteDebug(Color.Cyan, "SafeArea: processing took {0:F0} ms", (DateTime.Now - startFind).TotalMilliseconds);
                return ChooseSafestAvailable ? ptFurthest : WoWPoint.Empty;
            }

            Logger.WriteDebug(Color.Cyan, "SafeArea: processing took {0:F0} ms", (DateTime.Now - startFind).TotalMilliseconds);
            return WoWPoint.Empty;
        }
示例#6
0
文件: Kite.cs 项目: aash/Singular
        public WoWPoint FindLocation(WoWPoint ptOrigin)
        {
            DateTime startFind = DateTime.UtcNow;
            int countPointsChecked = 0;
            int countFailDiff = 0;
            int countFailTrace = 0;
            int countFailToPointNav = 0;
            int countFailRange = 0;
            int countFailSafe = 0;
            int countFailToPointLoS = 0;
            int countFailToMobLoS = 0;
            TimeSpan spanTrace = TimeSpan.Zero;
            TimeSpan spanNav = TimeSpan.Zero;
            double furthestNearMobDistSqr = 0f;
            WoWPoint ptFurthest = WoWPoint.Empty;
            float facingFurthest = 0f;

            bool reallyCheckRangeToLineOfSightMob = CheckRangeToLineOfSightMob && Me.GotTarget();
            WoWPoint ptAdjOrigin = ptOrigin;
            // ptAdjOrigin.Z += 1f;   // comment out origin adjustment since using GetTraceLinePos()

            WoWPoint ptDestination = new WoWPoint();
            List<WoWPoint> mobLocations = new List<WoWPoint>();
            float arcIncrement = ((float)Math.PI * 2) / RaysToCheck;

            mobLocations = AllEnemyMobLocationsToCheck;
            double minSafeDistSqr = MinSafeDistance * MinSafeDistance;

            #if OLD_WAY
            float baseDestinationFacing = MobToRunFrom == null ?
                                            Me.RenderFacing + (float)Math.PI
                                            : Styx.Helpers.WoWMathHelper.CalculateNeededFacing(MobToRunFrom.Location, Me.Location);
            #else
            float baseDestinationFacing;
            if (PreferredDirection == Disengage.Direction.None && MobToRunFrom != null)
                baseDestinationFacing = Styx.Helpers.WoWMathHelper.CalculateNeededFacing(MobToRunFrom.Location, Me.Location);
            else if (PreferredDirection == Disengage.Direction.Frontwards)
                baseDestinationFacing = Me.RenderFacing;
            else // if (PreferredDirection == Disengage.Direction.Backwards)
                baseDestinationFacing = Me.RenderFacing + (float)Math.PI;
            #endif
            Logger.WriteDebug( Color.Cyan, "SafeArea: facing {0:F0} degrees, looking for safespot towards {1:F0} degrees",
                WoWMathHelper.RadiansToDegrees(Me.RenderFacing),
                WoWMathHelper.RadiansToDegrees(baseDestinationFacing)
                );

            for (int arcIndex = 0; arcIndex < RaysToCheck; arcIndex++)
            {
                // rather than tracing around the circle, toggle between clockwise and counter clockwise for each test
                // .. so we favor a position furthest away from mob
                float checkFacing = baseDestinationFacing;
                if ((arcIndex & 1) == 0)
                    checkFacing += arcIncrement * (arcIndex >> 1);
                else
                    checkFacing -= arcIncrement * ((arcIndex >> 1) + 1);

                checkFacing = WoWMathHelper.NormalizeRadian(checkFacing);
                for (float distFromOrigin = MinScanDistance; distFromOrigin <= MaxScanDistance; distFromOrigin += IncrementScanDistance)
                {
                    countPointsChecked++;

                    ptDestination = ptOrigin.RayCast(checkFacing, distFromOrigin);

                    Logger.WriteDebug("SafeArea: checking {0:F1} degrees at {1:F1} yds", WoWMathHelper.RadiansToDegrees(checkFacing), distFromOrigin);

                    DateTime start = DateTime.UtcNow;
                    bool failTrace = Movement.MeshTraceline(Me.Location, ptDestination);
                    spanTrace += DateTime.UtcNow - start;

                    bool failNav;
                    if (DirectPathOnly)
                    {
                        failNav = failTrace;
                        spanNav = spanTrace;
                    }
                    else
                    {
                        start = DateTime.UtcNow;
                        failNav = !Navigator.CanNavigateFully(Me.Location, ptDestination);
                        spanNav += DateTime.UtcNow - start;
                    }

                    if (failTrace)
                        countFailTrace++;

                    if (failTrace != failNav)
                        countFailDiff++;

                    if (failNav)
                    {
                        // Logger.WriteDebug( Color.Cyan, "Safe Location failed navigation check for degrees={0:F1} dist={1:F1}", RadiansToDegrees(checkFacing), distFromOrigin);
                        countFailToPointNav++;
                        continue;
                    }

                    WoWPoint ptNearest = NearestMobLoc(ptDestination, mobLocations);
                    if (ptNearest == WoWPoint.Empty)
                    {
                        if (furthestNearMobDistSqr < minSafeDistSqr)
                        {
                            furthestNearMobDistSqr = minSafeDistSqr;
                            ptFurthest = ptDestination;     // set best available if others fail
                            facingFurthest = checkFacing;
                        }
                    }
                    else
                    {
                        double mobDistSqr = ptDestination.Distance2DSqr(ptNearest);
                        if (furthestNearMobDistSqr < mobDistSqr)
                        {
                            furthestNearMobDistSqr = mobDistSqr;
                            ptFurthest = ptDestination;     // set best available if others fail
                            facingFurthest = checkFacing;
                        }
                        if (mobDistSqr <= minSafeDistSqr)
                        {
                            countFailSafe++;
                            continue;
                        }
                    }

                    if (reallyCheckRangeToLineOfSightMob && RangeToLineOfSightMob < ptDestination.Distance(LineOfSightMob.Location) - LineOfSightMob.MeleeDistance())
                    {
                        countFailRange++;
                        continue;
                    }

                    if (CheckLineOfSightToSafeLocation)
                    {
                        WoWPoint ptAdjDest = ptDestination;
                        ptAdjDest.Z += 1f;
                        if (!Styx.WoWInternals.World.GameWorld.IsInLineOfSight(ptAdjOrigin, ptAdjDest))
                        {
                            // Logger.WriteDebug( Color.Cyan, "Mob-free location failed line of sight check for degrees={0:F1} dist={1:F1}", degreesFrom, distFromOrigin);
                            countFailToPointLoS++;
                            continue;
                        }
                    }

                    if (CheckSpellLineOfSightToMob && LineOfSightMob != null)
                    {
                        if (!Styx.WoWInternals.World.GameWorld.IsInLineOfSpellSight(ptDestination, LineOfSightMob.GetTraceLinePos()))
                        {
                            if (!Styx.WoWInternals.World.GameWorld.IsInLineOfSight(ptDestination, LineOfSightMob.GetTraceLinePos()))
                            {
                                // Logger.WriteDebug( Color.Cyan, "Mob-free location failed line of sight check for degrees={0:F1} dist={1:F1}", degreesFrom, distFromOrigin);
                                countFailToMobLoS++;
                                continue;
                            }
                        }
                    }

                    Logger.WriteDebug(Color.Cyan, "SafeArea: Found mob-free location ({0:F1} yd radius) at degrees={1:F1} dist={2:F1} on point check# {3} at {4}, {5}, {6}", MinSafeDistance, WoWMathHelper.RadiansToDegrees(checkFacing), distFromOrigin, countPointsChecked, ptDestination.X, ptDestination.Y, ptDestination.Z);
                    Logger.WriteDebug(Color.Cyan, "SafeArea: processing took {0:F0} ms", (DateTime.UtcNow - startFind).TotalMilliseconds);
                    Logger.WriteDebug(Color.Cyan, "SafeArea: meshtrace took {0:F0} ms / fullynav took {1:F0} ms", spanTrace.TotalMilliseconds, spanNav.TotalMilliseconds);
                    Logger.WriteDebug(Color.Cyan, "SafeArea: stats for ({0:F1} yd radius) found within {1:F1} yds ({2} checked, {3} nav, {4} not safe, {5} range, {6} pt los, {7} mob los, {8} mesh trace)", MinSafeDistance, MaxScanDistance, countPointsChecked, countFailToPointNav, countFailSafe, countFailRange, countFailToPointLoS, countFailToMobLoS, countFailTrace);
                    return ptDestination;
                }
            }

            Logger.WriteDebug(Color.Cyan, "SafeArea: No mob-free location ({0:F1} yd radius) found within {1:F1} yds ({2} checked, {3} nav, {4} not safe, {5} range, {6} pt los, {7} mob los, {8} mesh trace)", MinSafeDistance, MaxScanDistance, countPointsChecked, countFailToPointNav, countFailSafe, countFailRange, countFailToPointLoS, countFailToMobLoS, countFailTrace);
            if (ChooseSafestAvailable && ptFurthest != WoWPoint.Empty)
            {
                Logger.WriteDebug(Color.Cyan, "SafeArea: choosing best available spot in {0:F1} yd radius where closest mob is {1:F1} yds", MinSafeDistance, Math.Sqrt(furthestNearMobDistSqr));
                Logger.WriteDebug(Color.Cyan, "SafeArea: processing took {0:F0} ms", (DateTime.UtcNow - startFind).TotalMilliseconds);
                Logger.WriteDebug(Color.Cyan, "SafeArea: meshtrace took {0:F0} ms / fullynav took {1:F0} ms", spanTrace.TotalMilliseconds, spanNav.TotalMilliseconds);
                return ChooseSafestAvailable ? ptFurthest : WoWPoint.Empty;
            }

            Logger.WriteDebug(Color.Cyan, "SafeArea: processing took {0:F0} ms", (DateTime.UtcNow - startFind).TotalMilliseconds);
            Logger.WriteDebug(Color.Cyan, "SafeArea: meshtrace took {0:F0} ms / fullynav took {1:F0} ms", spanTrace.TotalMilliseconds, spanNav.TotalMilliseconds);
            return WoWPoint.Empty;
        }
示例#7
0
        /// <summary>Finds clustered targets</summary>
        /// <param name="radius">radius</param>
        /// <param name="minDistance">minimum distance</param>
        /// <param name="maxDistance">maximum distance</param>
        /// <param name="minTargets">minimum targets to qualify</param>
        /// <param name="playersOnly">true for players only</param>
        /// <returns>The find cluster targets.</returns>
        public static WoWPoint FindClusterTargets(double radius, double minDistance, double maxDistance, int minTargets, bool playersOnly)
        {
            List<WoWUnit> hostile = ObjectManager.GetObjectsOfType<WoWUnit>(true, false);
            var avoid = new List<WoWUnit>();
            var maxDistance2 = (maxDistance + radius) * (maxDistance + radius);

            if (playersOnly)
            {
                hostile = hostile.Where(x =>
                                        x.IsPlayer &&
                                        IsAttackable(x) && x.Distance2DSqr < maxDistance2).ToList();
            }
            else
            {
                hostile = hostile.Where(x =>
                                        !x.IsPlayer &&
                                        IsAttackable(x) && x.Distance2DSqr < maxDistance2).ToList();
                avoid = hostile.Where(
                            x => // check for controlled units, like sheep etc
                            UnitIsControlled(x, true)).ToList();
            }

            if (hostile.Count < minTargets)
            {
                return WoWPoint.Empty;
            }

            var score = minTargets - 1;
            var best = WoWPoint.Empty;

            for (var x = Me.Location.X - maxDistance; x <= Me.Location.X + maxDistance; x++)
            {
                for (var y = Me.Location.Y - maxDistance; y <= Me.Location.Y + maxDistance; y++)
                {
                    var spot = new WoWPoint(x, y, Me.Location.Z);
                    var dSquare = spot.Distance2DSqr(Me.Location);
                    if (dSquare > maxDistance * maxDistance || dSquare < minDistance * minDistance)
                    {
                        continue;
                    }

                    if (avoid.Any(t => t.Location.Distance2DSqr(spot) <= radius * radius))
                    {
                        continue;
                    }

                    var hits = hostile.Count(t => t.Location.DistanceSqr(spot) < radius * radius);
                    if (hits > score)
                    {
                        best = spot;
                        score = hits;
                        CLULogger.DiagnosticLog("ClusteredTargets(range=" + minDistance + "-" + maxDistance + ", radius=" + radius + ") => SCORE=" + score + " at " + spot);
                        foreach (var u in hostile.Where(t => t.Location.DistanceSqr(spot) < radius * radius))
                            CLULogger.DiagnosticLog(" -> " + CLULogger.SafeName(u) + " " + u.Level);
                        CLULogger.DiagnosticLog("---------------------");
                    }
                }
            }

            return best;
        }