public static IEnumerable <int> DeployLava()
        {
            if (lava?.Count >= 2)
            {
                var count = lava.Count / 2;

                foreach (var t in Deploy.AtPoints(lava, new PointFT[] { AllInOnePushDeploy.FirstFunnellingPoint, AllInOnePushDeploy.SecondFunnellingPoint }, count, 0, 200, 5))
                {
                    yield return(t);
                }

                if (lava?.Count > 0)
                {
                    foreach (var t in Deploy.AtPoint(lava, AllInOnePushDeploy.SecondFunnellingPoint, lava.Count))
                    {
                        yield return(t);
                    }
                }

                if (clanCastle?.Count > 0 && AllInOnePushDeploy.ClanCastleSettings > 0)
                {
                    foreach (var t in Deploy.AtPoint(clanCastle, AllInOnePushDeploy.FirstFunnellingPoint))
                    {
                        yield return(t);
                    }
                }
            }
            else if (lava?.Count == 1)
            {
                if (clanCastle?.Count > 0 && AllInOnePushDeploy.ClanCastleSettings > 0)
                {
                    foreach (var t in Deploy.AtPoint(clanCastle, AllInOnePushDeploy.FirstFunnellingPoint))
                    {
                        yield return(t);
                    }

                    foreach (var t in Deploy.AtPoint(lava, AllInOnePushDeploy.SecondFunnellingPoint))
                    {
                        yield return(t);
                    }
                }
                else
                {
                    foreach (var t in Deploy.AtPoint(lava, AllInOnePushDeploy.Origin))
                    {
                        yield return(t);
                    }
                }
            }
        }
        public override IEnumerable <int> AttackRoutine()
        {
            Log.Info("[Deploy] Deploy start");

            // Get all the units available
            Log.Debug("Scanning troops");
            var deployElements = Deploy.GetTroops();
            var spells         = deployElements.Extract(DeployElementType.Spell);
            var tankUnits      = deployElements.Extract(AttackType.Tank).ToArray();
            var attackUnits    = deployElements.Extract(AttackType.Damage).ToArray();
            var healUnits      = deployElements.Extract(AttackType.Heal).ToArray();

            var waveCounter = 1;

            // Get starting resources
            LootResources preLoot = Opponent.GetAvailableLoot();

            if (preLoot == null)
            {
                Log.Error("[Deploy] Milking deploy could not read available starting loot");
                Attack.Surrender();
                yield break;
            }

            Log.Debug($"[Deploy] Pre-attack resources - G: {preLoot.Gold}, E: {preLoot.Elixir}, DE: {preLoot.DarkElixir}");

            // Make sure we wait at least 15 seconds in this attack, in case we snipe TH

            // Loop until surrender conditions are met
            while (true)
            {
                // Get deploy points for each mine that is on the outside of the base
                //resourcesFull = GetResourcesState();
                GenerateDeployPointsFromMinesToMilk(CacheBehavior.CheckForDestroyed);
                if (deployPoints == null || deployPoints.Length < 1)
                {
                    Log.Debug("Surrendering because deployPoints = " + deployPoints?.Length);
                    break;
                }

                if (tankUnits.Any())
                {
                    foreach (var t in Deploy.AtPoints(tankUnits, deployPoints))
                    {
                        yield return(t);
                    }
                    yield return(1000);
                }

                if (attackUnits.Any())
                {
                    foreach (var t in Deploy.AtPoints(attackUnits, deployPoints, 6))
                    {
                        yield return(t);
                    }
                    yield return(1000);
                }

                if (healUnits.Any())
                {
                    foreach (var t in Deploy.AtPoints(healUnits, deployPoints))
                    {
                        yield return(t);
                    }
                    yield return(1000);
                }

                // Wait for the wave to finish
                Log.Info($"[Deploy] Wave {waveCounter} Deployed. Waiting to finish...");
                foreach (var t in Attack.WaitForNoResourceChange())
                {
                    yield return(t);
                }

                // Get starting resources, cache needs to be false to force a new check
                LootResources postLoot = Opponent.GetAvailableLoot(false);

                if (postLoot == null)
                {
                    Log.Warning("[Deploy] Milking Deploy could not read available loot this wave");
                }
                else
                {
                    Log.Debug($"[Deploy] Wave {waveCounter} resources - G: {postLoot.Gold}, E: {postLoot.Elixir}, DE: {postLoot.DarkElixir}");
                    int newGold   = preLoot.Gold - postLoot.Gold;
                    int newElixir = preLoot.Elixir - postLoot.Elixir;
                    int newDark   = preLoot.DarkElixir - postLoot.DarkElixir;
                    Log.Debug($"[Deploy] Wave {waveCounter} resource diff - G: {newGold}, E: {newElixir}, DE: {newDark}, points: {deployPoints.Length}");
                    if (newGold + newElixir < 5000 * deployPoints.Length)
                    {
                        Log.Debug("Surrendering because gained resources isn't enough");
                        break;
                    }
                    preLoot = postLoot;
                }

                waveCounter++;
            }

            // Wait for the wave to finish
            Log.Info("[Deploy] Deploy done. Waiting to finish...");
            foreach (var t in Attack.WaitForNoResourceChange(10))
            {
                yield return(t);
            }

            if (spells.Any(u => u.Id == DeployId.Lightning) && DarkElixirDrill.Find(CacheBehavior.ForceScan, 4).Length > 0)
            {
                Log.Debug("Level 4 or greater drills found, waiting for attack to finish.");
                foreach (var t in Attack.WaitForNoResourceChange(5))
                {
                    yield return(t);
                }

                foreach (var t in ZapDarkElixirDrills())
                {
                    yield return(t);
                }
            }

            Attack.Surrender();
        }
        public override IEnumerable <int> AttackRoutine()
        {
#if DEBUG
            debugMode = true;
#endif
            var visuals = new List <VisualObject>();
            // Call it once in order to cache the RedPoints in the beginning
            // If this will be called after spell deployment the redline will disappear!! - Do not remove this!
            var redPoints = GameGrid.RedPoints;

            // get a list of all deployable units
            var deployElements = Deploy.GetTroops();
            Log.Info("[BabyLoon] Deployable Troops: " + ToUnitString(deployElements));
            if (!HasNeededTroops(deployElements))
            {
                Log.Error("[Smart Air] Couldn't find a known troop composition. Consider using one of the known troop compositions. Check our forums to learn more about " +
                          "the Smart Air Deploy in order to achieve the best possible results.");
                Surrender();
                yield break;
            }

            // extract special units into their own list under respect of usersettings
            var heroes = deployElements
                         .Extract(u => (UserSettings.UseKing && u.ElementType == DeployElementType.HeroKing) ||
                                  (UserSettings.UseQueen && u.ElementType == DeployElementType.HeroQueen) ||
                                  (UserSettings.UseWarden && u.ElementType == DeployElementType.HeroWarden))
                         .ToList();

            var clanCastle = deployElements.ExtractOne(u => u.ElementType == DeployElementType.ClanTroops && UserSettings.UseClanTroops);

            // remove left special units so we won't use them accidentially
            deployElements.
            RemoveAll(u => u.ElementType == DeployElementType.ClanTroops ||
                      u.ElementType == DeployElementType.HeroKing ||
                      u.ElementType == DeployElementType.HeroQueen ||
                      u.ElementType == DeployElementType.HeroWarden);

            // extract units into their own lists in order to use them for more specific behaviours
            var lightningSpell  = deployElements.ExtractOne(x => x.Id == DeployId.Lightning);
            var earthQuakeSpell = deployElements.ExtractOne(x => x.Id == DeployId.Earthquake);
            var rageSpell       = deployElements.ExtractOne(x => x.Id == DeployId.Rage);
            var freezeSpels     = deployElements.ExtractOne(x => x.Id == DeployId.Freeze);
            var tanks           = deployElements.Extract(AttackType.Tank).ToArray();
            var balloon         = deployElements.ExtractOne(x => x.Id == DeployId.Balloon);
            var babyDragon      = deployElements.ExtractOne(x => x.Id == DeployId.BabyDragon);
            var wallBreakers    = deployElements.ExtractOne(x => x.Id == DeployId.WallBreaker);
            var damageDealers   = deployElements.Extract(AttackType.Damage).OrderByDescending(x => x.UnitData.HP).ToArray();

            // 1. Find the redpoint which is the closest to the TownHall
            var townHall          = TownHall.Find();
            var townHallPosition  = townHall == null ? new PointFT(0, 0) : townHall.Location.GetCenter();
            var closestThRedPoint = GameGrid.RedPoints.OrderBy(x => x.DistanceSq(townHallPosition)).First();
            visuals.Add(new PointsObject("Closest TH Point", Color.FromArgb(200, Color.Cyan), new PointFT[] { closestThRedPoint }));

            // 2. Prepare DeployPoint Calculation
            var    babyDragonSpread = babyDragon?.Count * 0.031 ?? 0;
            var    spreadFactor     = 0.7 + babyDragonSpread;
            double minAngle         = closestThRedPoint.Angle - spreadFactor;
            double maxAngle         = closestThRedPoint.Angle + spreadFactor;
            double twoPi            = 2 * Math.PI;
            double circleStart      = -Math.PI;
            double circleEnd        = Math.PI;
            if (minAngle < -Math.PI)
            {
                circleEnd = minAngle + twoPi;
                minAngle  = -Math.PI;
            }
            else if (maxAngle > Math.PI)
            {
                circleStart = maxAngle - twoPi;
                maxAngle    = Math.PI;
            }

            // 1. Condition: All Points between minAngle and MaxAngle
            // OR
            // 2. Condition: All Points between 0 and circleStart
            // OR
            // 3. Condition: All Points between circleEnd and twoPi
            var potentialDeployPoints = GameGrid.RedPoints.Where(x => x.Angle >= minAngle && x.Angle <= maxAngle ||
                                                                 x.Angle >= -Math.PI && x.Angle <circleStart ||
                                                                                                 x.Angle> circleEnd && x.Angle <= Math.PI).ToArray();
            potentialDeployPoints = OrderDeployPoints(potentialDeployPoints);
            visuals.Add(new PointsObject("Potential DeployPoints", Color.FromArgb(100, Color.Red), potentialDeployPoints, 1));

            // 3. If possible - zapquake 2nd closest AirDefense
            if (earthQuakeSpell?.Count >= 1 && lightningSpell?.Count >= 2)
            {
                var target = FindZapQuakeTarget(closestThRedPoint, visuals);
                if (target != null)
                {
                    Log.Info("[BabyLoon] Going to ZapQuake second closest AirDefense from deployside");
                    foreach (var t in ZapQuakeTarget(target.Location.GetCenter(), earthQuakeSpell, lightningSpell))
                    {
                        yield return(t);
                    }
                }
                else
                {
                    Log.Info("[BabyLoon] Couldn't find more than 1 AirDefense - hence skipping ZapQuake");
                }
            }
            else
            {
                Log.Debug("[BabyLoon] Not enough spells for ZapQuake available");
            }


            // 4. Calculate all deployPoints
            var babyDragonDeployPoints = CalculateBabyDragonDeployPoints(babyDragon, potentialDeployPoints, visuals);
            var balloonDeployPoints    = CalculateBalloonDeployPoints(balloon, potentialDeployPoints, visuals);
            var rageDeployPoints       = CalculateRageDeployPoints(rageSpell, potentialDeployPoints, visuals);
            var tankDeployPoints       = CalculateTankDeployPoints(tanks, potentialDeployPoints, visuals);
            // Deploy Clancastle 2 tiles away from the closestThRedPoint
            var clanCastleDeployPoint = closestThRedPoint.TransformPositionAlongAxis(2).Constrain();

            // 5. Deploy Tanks and ClanCastle in the center of the DeploySide
            if (tanks.Length > 0)
            {
                foreach (var tank in tanks)
                {
                    Log.Info($"[BabyLoon] Deploying {tank.PrettyName} x{tank.Count}");
                }

                // If there is just one tank use closestThRedPoint as deploypoint
                if (tanks.Sum(x => x.Count) == 1)
                {
                    while (tanks.Sum(x => x.Count) > 0)
                    {
                        var initialTroopCount = tanks.Sum(x => x.Count);
                        foreach (var t in Deploy.AtPoint(tanks, closestThRedPoint))
                        {
                            yield return(t);
                        }

                        // Prevent an infinite loop if deploy point is inside of the redzone
                        if (tanks.Sum(x => x.Count) != initialTroopCount)
                        {
                            continue;
                        }

                        foreach (var tank in tanks)
                        {
                            Log.Warning($"[BabyLoon] Couldn't deploy x{tank.Count} {tank.PrettyName}");
                        }
                        break;
                    }
                }

                // If there is more than one tank to deploy, deploy them along the attacking side
                while (tanks.Sum(x => x.Count) > 0)
                {
                    var initialTroopCount = tanks.Sum(x => x.Count);
                    foreach (var t in Deploy.AtPoints(tanks, tankDeployPoints))
                    {
                        yield return(t);
                    }

                    // Prevent an infinite loop if deploy point is inside of the redzone
                    if (tanks.Sum(x => x.Count) != initialTroopCount)
                    {
                        continue;
                    }

                    foreach (var tank in tanks)
                    {
                        Log.Warning($"[BabyLoon] Couldn't deploy x{tank.Count} {tank.PrettyName}");
                    }
                    break;
                }
            }

            // 6. Deploy BabyDragons
            if (babyDragon?.Count > 0)
            {
                Log.Info($"[BabyLoon] Deploying {babyDragon.PrettyName} x{babyDragon.Count}");

                while (babyDragon.Count > 0)
                {
                    var initialBabyDragonCount = babyDragon.Count;
                    foreach (var t in Deploy.AtPoints(babyDragon, babyDragonDeployPoints, 1, 50))
                    {
                        yield return(t);
                    }

                    // Prevent an infinite loop if deploy points are inside of the redzone
                    if (babyDragon.Count != initialBabyDragonCount)
                    {
                        continue;
                    }

                    Log.Warning($"[BabyLoon] Couldn't deploy {babyDragon.PrettyName}");
                    break;
                }

                // Humanizing pause
                yield return(500);
            }

            // 7. Deploy balloons
            if (balloon?.Count > 0)
            {
                Log.Debug($"[BabyLoon] Wait 2 seconds before deploying {balloon.PrettyName} x{balloon.Count}!");
                yield return(2000);

                Log.Info($"[BabyLoon] Deploying {balloon.PrettyName} x{balloon.Count}");

                while (balloon.Count > 0)
                {
                    var initialBalloonCount = balloon.Count;
                    foreach (var t in Deploy.AtPoints(balloon, balloonDeployPoints, 1, 50))
                    {
                        yield return(t);
                    }

                    // Prevent an infinite loop if deploy points are inside of the redzone
                    if (balloon.Count != initialBalloonCount)
                    {
                        continue;
                    }

                    Log.Warning($"[BabyLoon] Couldn't deploy {balloon.PrettyName}");
                    break;
                }
            }

            // 8. Deploy Ragespells
            if (rageSpell?.Count > 0)
            {
                Log.Info($"[BabyLoon] Deploying {rageSpell.PrettyName} x{rageSpell.Count}");

                while (rageSpell.Count > 0)
                {
                    var initialRageSpellCount = rageSpell.Count;
                    foreach (var t in Deploy.AtPoints(rageSpell, rageDeployPoints, 1, 50))
                    {
                        yield return(t);
                    }

                    // Prevent an infinite loop if deploy points are inside of the redzone
                    if (balloon.Count != initialRageSpellCount)
                    {
                        continue;
                    }

                    Log.Warning($"[BabyLoon] Couldn't deploy {rageSpell.PrettyName}");
                    break;
                }
            }

            // 9. Deploy all other troops
            if (damageDealers.Length > 0)
            {
                foreach (var damagedealer in damageDealers)
                {
                    Log.Info($"[BabyLoon] Deploying {damagedealer.PrettyName} x{damagedealer.Count}");
                }
                while (damageDealers.Sum(x => x.Count) > 0)
                {
                    var initialTroopCount = damageDealers.Sum(x => x.Count);
                    foreach (var t in Deploy.AtPoint(damageDealers, closestThRedPoint))
                    {
                        yield return(t);
                    }

                    // Prevent an infinite loop if deploy point is inside of the redzone
                    if (tanks.Sum(x => x.Count) != initialTroopCount)
                    {
                        continue;
                    }

                    foreach (var damagedealer in damageDealers)
                    {
                        Log.Warning($"[BabyLoon] Couldn't deploy x{damagedealer.Count} {damagedealer.PrettyName}");
                    }
                    break;
                }
            }

            // 10. Deploy Heroes and watch them to activate abilities
            if (heroes.Any())
            {
                var heroDeployPoints = GameGrid.RedPoints.OrderBy(x => x.DistanceSq(closestThRedPoint)).Take(10).ToArray();
                heroDeployPoints = heroDeployPoints.Select(x => x.TransformPositionAlongAxis(4).Constrain()).ToArray();

                foreach (var hero in heroes.Where(u => u.Count > 0))
                {
                    Log.Info($"[BabyLoon] Deploying {hero.PrettyName}");
                    foreach (var t in Deploy.AtPoints(hero, heroDeployPoints))
                    {
                        yield return(t);
                    }
                }

                Deploy.WatchHeroes(heroes, 7000);
            }

            // 11. Deploy Wallbreakers in 3 unit stacks near heroes
            while (wallBreakers?.Count > 0)
            {
                // Shift Heroes' deploypoint by 1 tile in both directions to prevent deployment on the same spot (wallbreakers may die due to splashdamage against heroes)
                var wallbreakerDeployPoints = GameGrid.RedPoints.OrderBy(x => x.DistanceSq(closestThRedPoint)).Take(15).ToArray();
                var count = wallBreakers.Count;

                Log.Info($"[BabyLoon] Deploying {wallBreakers.PrettyName} x3");
                foreach (var t in Deploy.AtPoints(wallBreakers, wallbreakerDeployPoints, 3))
                {
                    yield return(t);
                }

                // prevent infinite loop if deploy point is on red
                if (wallBreakers.Count != count)
                {
                    continue;
                }

                Log.Warning($"[BabyLoon] Couldn't deploy {wallBreakers.PrettyName}");
                break;
            }

            if (clanCastle?.Count > 0)
            {
                Log.Info($"[BabyLoon] Deploying {clanCastle.PrettyName}");
                foreach (var t in Deploy.AtPoint(clanCastle, clanCastleDeployPoint))
                {
                    yield return(t);
                }
            }


            if (debugMode)
            {
                VisualizeDeployment(visuals);
            }
        }
예제 #4
0
        public override IEnumerable <int> AttackRoutine()
        {
            Log.Info($"{Tag}Deploy start - V.{Assembly.GetExecutingAssembly().GetName().Version.ToString()}");
            Log.Debug($"{Tag}Attack Identifier: [{AttackId}] - (Links Debug Images to Log File)");

            var attackStartTime = DateTime.UtcNow;

            //Write out all the Current Algorithm Settings...
            var settings = $"{Tag}Current Custom Settings Values:{Environment.NewLine}";

            foreach (var setting in AllCurrentSettings)
            {
                settings += $"{setting.InstanceType} Setting - '{setting.Name}' Value: {setting.Value}{Environment.NewLine}";
            }
            Log.Debug(settings);

            var waveCounter = 1;

            //Check if we can snipe the town hall, and if so, what are the Deployment points for Gruns/Ranged.
            TownHall townHall = TownHall.Find(CacheBehavior.Default);

            Target townHallTarget = townHall.GetSnipeDeployPoints();

            // Get starting resources
            LootResources preLoot = Opponent.GetAvailableLoot();

            if (preLoot == null)
            {
                Log.Error($"{Tag}Could not read available starting loot");
                Attack.Surrender();
                yield break;
            }
            Log.Info($"{Tag}Pre-attack resources - G: {preLoot.Gold}, E: {preLoot.Elixir}, DE: {preLoot.DarkElixir}");

            var collectorCacheBehavior = CacheBehavior.ForceScan;
            var collectorCount         = 0;
            var acceptableTargetRange  = CurrentSetting("Acceptable Target Range");

            acceptableTargetRange = acceptableTargetRange * acceptableTargetRange;
            var activeBase         = !Opponent.IsDead();
            var clanCastleDeployed = false;

            bool watchHeroes    = false;
            bool kingDeployed   = false;
            bool queenDeployed  = false;
            bool wardenDeployed = false;

            // Loop until surrender conditions are met
            while (true)
            {
                // Get all the units available
                Log.Info($"{Tag}Scanning troops for wave {waveCounter}");

                var allElements    = Attack.GetAvailableDeployElements();
                var deployElements = allElements.Where(x => x.UnitData != null).ToArray();
                var rangedUnits    = deployElements.Where(x => x.IsRanged == true && x.ElementType == DeployElementType.NormalUnit && x.UnitData.AttackType == AttackType.Damage);

                IEnumerable <DeployElement> gruntUnits = null;
                IEnumerable <DeployElement> tankUnits  = null;

                if (CurrentSetting("Use Valkyries as Tanks") == 0)
                {
                    gruntUnits = deployElements.Where(x => x.IsRanged == false && x.ElementType == DeployElementType.NormalUnit && x.UnitData.AttackType == AttackType.Damage);
                    tankUnits  = deployElements.Where(x => x.IsRanged == false && x.ElementType == DeployElementType.NormalUnit && x.UnitData.AttackType == AttackType.Tank);
                }
                else
                {
                    //Reclassify Valks as Tanks!
                    gruntUnits = deployElements.Where(x => x.IsRanged == false && x.ElementType == DeployElementType.NormalUnit && x.UnitData.AttackType == AttackType.Damage && x.Id != DeployId.Valkyrie);
                    tankUnits  = deployElements.Where(x => (x.IsRanged == false && x.ElementType == DeployElementType.NormalUnit && x.UnitData.AttackType == AttackType.Tank) || x.Id == DeployId.Valkyrie);
                }

                List <DeployElement> king      = allElements.Where(x => x.IsHero && x.Name.ToLower().Contains("king")).ToList();
                List <DeployElement> queen     = allElements.Where(x => x.IsHero && x.Name.ToLower().Contains("queen")).ToList();
                List <DeployElement> warden    = allElements.Where(x => x.IsHero && x.Name.ToLower().Contains("warden")).ToList();
                List <DeployElement> allHeroes = new List <DeployElement>();
                allHeroes.AddRange(king);
                allHeroes.AddRange(queen);
                allHeroes.AddRange(warden);

                //Write out all the unit pretty names we found...
                Log.Debug($"{Tag}Deployable Troops (wave {waveCounter}): {ToUnitString(allElements)}");

                var    outputDebugImage = (CurrentSetting("Debug Mode") == 1);
                double avgFillState     = 0;
                double avgCollectorLvl  = 0;

                //First time through force a Scan... after the first wave always recheck for Destroyed ones...
                Target[] targets = HumanLikeAlgorithms.GenerateTargets(algorithmName, acceptableTargetRange, IgnoreGold, IgnoreElixir, AttackId, out avgFillState, out avgCollectorLvl, collectorCacheBehavior, outputDebugImage, activeBase);

                collectorCount = targets.Length;

                Target reminderTarget = null;
                if (collectorCount > 0)
                {
                    reminderTarget = targets[0];
                }

                //Reorder the Deploy points so they look more human like when attacking.
                var groupedTargets = targets.ReorderToClosestNeighbor().GroupCloseTargets();

                collectorCacheBehavior = CacheBehavior.CheckForDestroyed;

                if (collectorCount < 1)
                {
                    Log.Info($"{Tag}Collectors Remaining = {collectorCount} - Deployment Done. Exiting Attack Loop.");
                    break;
                }

                int meleCount   = 0;
                int rangedCount = 0;
                int tankCount   = 0;

                if (CurrentSetting("Deploy All Troops Mode") == 0)
                {
                    //Determine Counts of each type of unit to use...
                    meleCount   = CurrentSetting("Ground Units Per Target");
                    rangedCount = CurrentSetting("Ranged Units Per Target");
                    tankCount   = CurrentSetting("Tank Units Per Target");
                }
                else
                {
                    //Get the total count of Valid Targets. (Including Town Hall if there is one.)
                    int totalTargetCount = targets.Length;

                    if (townHallTarget.ValidTarget)
                    {
                        totalTargetCount++;
                    }

                    //Will be the largest int without remainder.
                    meleCount   = gruntUnits.TotalUnitCount() / totalTargetCount;
                    rangedCount = rangedUnits.TotalUnitCount() / totalTargetCount;
                    tankCount   = tankUnits.TotalUnitCount() / totalTargetCount;

                    //Make sure if there are less than 1 per target, but still more than zero. set to 1.
                    if (tankCount <= 0 && tankUnits.TotalUnitCount() > 0)
                    {
                        tankCount = 1;
                    }
                    if (rangedCount <= 0 && rangedUnits.TotalUnitCount() > 0)
                    {
                        rangedCount = 1;
                    }
                    if (meleCount <= 0 && gruntUnits.TotalUnitCount() > 0)
                    {
                        meleCount = 1;
                    }
                }

                if (townHallTarget.ValidTarget)
                {
                    //Drop some Grunt and Ranged troups on the TH as well as collectors.
                    //If there are Teslas around it, oh well. we only spent 9-12 units  of each type trying.
                    if (gruntUnits.Any())
                    {
                        Log.Info($"{Tag}TH Snipe Dead {meleCount} Grunts Near: X:{townHallTarget.DeployGrunts.X} Y:{townHallTarget.DeployGrunts.Y}");
                        foreach (var t in Deploy.AtPoints(gruntUnits.FilterTypesByCount(), townHallTarget.DeployGrunts.RandomPointsInArea(_thDeployRadius, meleCount), 1))
                        {
                            yield return(t);
                        }
                        yield return(Rand.Int(300, 500)); //Wait
                    }

                    if (rangedUnits.Any())
                    {
                        Log.Info($"{Tag}TH Snipe Dead {rangedCount} Ranged Near: X:{townHallTarget.DeployRanged.X} Y:{townHallTarget.DeployRanged.Y}");
                        foreach (var t in Deploy.AtPoints(rangedUnits.FilterTypesByCount(), townHallTarget.DeployRanged.RandomPointsInArea(_thDeployRadius, rangedCount), 1))
                        {
                            yield return(t);
                        }
                        yield return(Rand.Int(300, 500)); //Wait
                    }

                    if (UserSettings.UseClanTroops)
                    {
                        var clanCastle = allElements.FirstOrDefault(u => u.ElementType == DeployElementType.ClanTroops);

                        clanCastleDeployed = true;

                        if (clanCastle?.Count > 0)
                        {
                            Log.Info($"{Tag}Deploying Clan Castle Near Town Hall");
                            foreach (var t in Deploy.AtPoint(clanCastle, townHallTarget.DeployRanged, clanCastle.Count))
                            {
                                yield return(t);
                            }
                        }
                        else
                        {
                            Log.Info($"{Tag}No Clan Castle Troops found to Deploy on Town Hall...");
                        }
                    }

                    //Only do this once.
                    townHallTarget.ValidTarget = false;
                }

                //Determine the index of the 1st and 2nd largest set of targets all in a row.
                var largestSetIndex       = -1;
                int largestSetCount       = 0;
                var secondLargestSetIndex = -1;
                int secondLargestSetCount = 0;

                for (int i = 0; i < groupedTargets.Count; i++)
                {
                    if (groupedTargets[i].Length > largestSetCount)
                    {
                        secondLargestSetCount = largestSetCount;
                        secondLargestSetIndex = largestSetIndex;
                        largestSetCount       = groupedTargets[i].Length;
                        largestSetIndex       = i;
                    }
                    else if (groupedTargets[i].Length > secondLargestSetCount)
                    {
                        secondLargestSetCount = groupedTargets[i].Length;
                        secondLargestSetIndex = i;
                    }
                }

                Log.Info($"{Tag}{groupedTargets.Count} Target Groups, Largest has {largestSetCount} targets, Second Largest {secondLargestSetCount} targets.");

                if (largestSetCount <= 1)
                {
                    Log.Info($"{Tag}No group of two or more targets found - Skipping deploy of Heros & Clan Castle.");
                }

                //Deploy Barch Units - In Groups on Sets of collectors that are close together.
                for (int p = 0; p < groupedTargets.Count; p++)
                {
                    //Deploy Tanks on the Set of Targets. (If any exist)
                    for (int i = 0; i < groupedTargets[p].Length; i++)
                    {
                        var gruntDeployPoint = groupedTargets[p][i].DeployGrunts;

                        //First Deploy tanks
                        if (tankUnits.Any())
                        {
                            Log.Debug($"{Tag}Deploying {tankCount} Tank Units on {groupedTargets[p][i].Name} {p + 1}-{i}");
                            foreach (var t in Deploy.AtPoints(tankUnits.FilterTypesByCount(), gruntDeployPoint.RandomPointsInArea(_collectorDeployRadius, tankCount), 1))
                            {
                                yield return(t);
                            }
                            yield return(Rand.Int(10, 40)); //Wait
                        }
                    }

                    if (gruntUnits.Any())
                    {
                        //Pause inbetween switching units.
                        yield return(Rand.Int(90, 100)); //Wait
                    }

                    //Deploy Grunts on the Set of Targets.
                    for (int i = 0; i < groupedTargets[p].Length; i++)
                    {
                        var gruntDeployPoint = groupedTargets[p][i].DeployGrunts;

                        //Next Deploy Ground troops
                        if (gruntUnits.Any())
                        {
                            Log.Debug($"{Tag}Deploying {meleCount} Ground Units on {groupedTargets[p][i].Name} {p + 1}-{i}");
                            foreach (var t in Deploy.AtPoints(gruntUnits.FilterTypesByCount(), gruntDeployPoint.RandomPointsInArea(_collectorDeployRadius, meleCount), 1))
                            {
                                yield return(t);
                            }
                            yield return(Rand.Int(10, 40)); //Wait
                        }
                    }

                    if (rangedUnits.Any())
                    {
                        //Pause inbetween switching units.
                        yield return(Rand.Int(90, 100)); //Wait
                    }

                    //Deploy Ranged units on same set of Targets.
                    for (int i = 0; i < groupedTargets[p].Length; i++)
                    {
                        var rangedDeployPoint = groupedTargets[p][i].DeployRanged;

                        if (rangedUnits.Any())
                        {
                            Log.Debug($"{Tag}Deploying {rangedCount} Ranged Units on {groupedTargets[p][i].Name} {p + 1}-{i}");
                            foreach (var t in Deploy.AtPoints(rangedUnits.FilterTypesByCount(), rangedDeployPoint.RandomPointsInArea(_collectorDeployRadius, rangedCount), 1))
                            {
                                yield return(t);
                            }
                            yield return(Rand.Int(40, 50)); //Wait
                        }
                    }

                    if (largestSetIndex == p && largestSetCount >= 2 && waveCounter == 1)
                    {
                        //We are currently deploying to the largest set of Targets - AND its a set of 2 or more.
                        //Preferrably Drop All Heros on this set (2nd Target in the set.)
                        reminderTarget = groupedTargets[p][1];

                        if (!clanCastleDeployed && UserSettings.UseClanTroops)
                        {
                            var clanCastle = allElements.FirstOrDefault(u => u.ElementType == DeployElementType.ClanTroops);

                            if (clanCastle?.Count > 0)
                            {
                                Log.Info($"{Tag}Deploying Clan Castle on Largest set of Targets: {largestSetCount} targets.");
                                foreach (var t in Deploy.AtPoint(clanCastle, groupedTargets[p][1].DeployRanged, clanCastle.Count))
                                {
                                    yield return(t);
                                }
                            }
                            else
                            {
                                Log.Info($"{Tag}No Clan Castle Troops found to Deploy...");
                            }
                            clanCastleDeployed = true;
                        }

                        if (UserSettings.UseKing && king.Any() && !kingDeployed)
                        {
                            yield return(Rand.Int(90, 100)); //Wait before dropping King

                            Log.Info($"{Tag}Deploying King on largest set of targets: {largestSetCount} targets.");
                            foreach (var t in Deploy.AtPoint(king[0], groupedTargets[p][1].DeployGrunts))
                            {
                                yield return(t);
                            }
                            yield return(Rand.Int(200, 500)); //Wait

                            kingDeployed = true;
                            watchHeroes  = true;
                        }

                        if (UserSettings.UseQueen && queen.Any() && !queenDeployed)
                        {
                            yield return(Rand.Int(90, 100)); //Wait before dropping Queen

                            Log.Info($"{Tag}Deploying Queen on largest set of targets: {largestSetCount} targets.");
                            foreach (var t in Deploy.AtPoint(queen[0], groupedTargets[p][1].DeployRanged))
                            {
                                yield return(t);
                            }
                            yield return(Rand.Int(200, 500)); //Wait

                            queenDeployed = true;
                            watchHeroes   = true;
                        }

                        if (UserSettings.UseWarden && warden.Any() && !wardenDeployed)
                        {
                            Log.Info($"{Tag}Deploying Warden on largest set of targets: {largestSetCount} targets.");
                            foreach (var t in Deploy.AtPoint(warden[0], groupedTargets[p][1].DeployRanged))
                            {
                                yield return(t);
                            }
                            yield return(Rand.Int(200, 500)); //Wait

                            wardenDeployed = true;
                            watchHeroes    = true;
                        }

                        //Now that the first round of deploying is done, watch any heros if necessary.
                        if (watchHeroes)
                        {
                            //Watch Heros and Hit ability when they get low.
                            Log.Info($"{Tag}Watching heros to activate abilities when health gets Low.");
                            Deploy.WatchHeroes(allHeroes);
                            watchHeroes = false; //Only do this once through the loop.
                        }
                    }

                    yield return(Rand.Int(90, 100)); //Wait before switching units back to Grutns and deploying on next set of targets.
                }

                //If Deploy ALL Troops is turned on,
                if (CurrentSetting("Deploy All Troops Mode") == 1 && reminderTarget != null)
                {
                    //Deploy the Reminder of troops on the LARGEST Group of targets.

                    //First Deploy tanks
                    foreach (var units in tankUnits)
                    {
                        if (units?.Count > 0)
                        {
                            Log.Debug($"{Tag}Deploying Reminder of {units.PrettyName} Tank Units ({units.Count}) on {reminderTarget.Name}");
                            foreach (var t in Deploy.AtPoint(units, reminderTarget.DeployGrunts, units.Count))
                            {
                                yield return(t);
                            }
                            yield return(Rand.Int(2000, 3000)); //Wait
                        }
                    }

                    //Next Deploy Grunts
                    foreach (var units in gruntUnits)
                    {
                        if (units?.Count > 0)
                        {
                            Log.Debug($"{Tag}Deploying Reminder of {units.PrettyName} Mele Units ({units.Count}) on {reminderTarget.Name}");
                            foreach (var t in Deploy.AtPoint(units, reminderTarget.DeployGrunts, units.Count))
                            {
                                yield return(t);
                            }
                            yield return(Rand.Int(100, 200)); //Wait
                        }
                    }

                    //Next Deploy Ranged
                    foreach (var units in rangedUnits)
                    {
                        if (units?.Count > 0)
                        {
                            Log.Debug($"{Tag}Deploying Reminder of {units.PrettyName} Ranged Units ({units.Count}) on {reminderTarget.Name}");
                            foreach (var t in Deploy.AtPoint(units, reminderTarget.DeployRanged, units.Count))
                            {
                                yield return(t);
                            }
                            yield return(Rand.Int(100, 200)); //Wait
                        }
                    }

                    //There is only ONE wave in Deploy all troops mode...
                    break;
                }

                //wait a random number of seconds before the next round on all Targets...
                yield return(Rand.Int(5000, 7000));

                // Get starting resources, cache needs to be false to force a new check
                LootResources postLoot = Opponent.GetAvailableLoot(false);
                if (postLoot == null)
                {
                    Log.Warning($"{Tag}could not read available loot this wave");
                    postLoot = new LootResources()
                    {
                        Gold = -1, Elixir = -1, DarkElixir = -1
                    };
                }

                Log.Info($"{Tag}Wave {waveCounter} resources - G: {postLoot.Gold}, E: {postLoot.Elixir}, DE: {postLoot.DarkElixir}");
                int newGold   = preLoot.Gold - postLoot.Gold;
                int newElixir = preLoot.Elixir - postLoot.Elixir;
                int newDark   = preLoot.DarkElixir - postLoot.DarkElixir;
                Log.Info($"{Tag}Wave {waveCounter} resource diff - G: {newGold}, E: {newElixir}, DE: {newDark}, Collectors: {collectorCount}");

                //Check to see if we are getting enough Resources...
                if (postLoot.Gold + postLoot.Elixir + postLoot.DarkElixir >= 0)
                {
                    if (newGold + newElixir < 3000 * collectorCount)
                    {
                        Log.Info($"{Tag}Stopping Troop Deployment because gained resources isn't enough");
                        break;
                    }
                    preLoot = postLoot;
                }

                waveCounter++;
            }

            if (CurrentSetting("Debug Mode") == 1)
            {
                Log.Debug($"{Tag}Deployment End. Taking last debug Screenshot.");
                HumanLikeAlgorithms.SaveBasicDebugScreenShot(algorithmName, AttackId, "Battle End");
            }

            //We broke out of the attack loop - allow attack to end how specified in the General Bot Settings...
            Log.Info($"{Tag} <<<<<< End of Human Barch Algorithm >>>>>>");
        }
        public override IEnumerable <int> AttackRoutine()
        {
            Log.Info($"[Human Barch] Deploy start - V.{Assembly.GetExecutingAssembly().GetName().Version.ToString()}");

            var waveCounter = 1;

            //Check if we can snipe the town hall, and if so, what are the Deployment points for Gruns/Ranged.
            TownHall townHall = TownHall.Find(CacheBehavior.Default);

            Target townHallTarget = townHall.GetSnipeDeployPoints();

            // Get starting resources
            LootResources preLoot = Opponent.GetAvailableLoot();

            if (preLoot == null)
            {
                Log.Error("[Human Barch] Could not read available starting loot");
                Attack.Surrender();
                yield break;
            }
            Log.Info($"[Human Barch] Pre-attack resources - G: {preLoot.Gold}, E: {preLoot.Elixir}, DE: {preLoot.DarkElixir}");

            var collectorCacheBehavior = CacheBehavior.Default;
            var collectorCount         = 0;
            var isDead = Opponent.IsDead(true);

            // Loop until surrender conditions are met
            while (true)
            {
                // Get all the units available
                Log.Info($"[Human Barch] Scanning troops for wave {waveCounter}");

                var allElements                = Attack.GetAvailableDeployElements();
                var deployElements             = allElements.Where(x => x.UnitData != null).ToArray();
                var rangedUnits                = deployElements.Where(x => x.IsRanged == true && x.ElementType == DeployElementType.NormalUnit && x.UnitData.AttackType == AttackType.Damage);
                var gruntUnits                 = deployElements.Where(x => x.IsRanged == false && x.ElementType == DeployElementType.NormalUnit && x.UnitData.AttackType == AttackType.Damage);
                List <DeployElement> king      = allElements.Where(x => x.IsHero && x.Name.ToLower().Contains("king")).ToList();
                List <DeployElement> queen     = allElements.Where(x => x.IsHero && x.Name.ToLower().Contains("queen")).ToList();
                List <DeployElement> allHeroes = new List <DeployElement>();
                allHeroes.AddRange(king);
                allHeroes.AddRange(queen);

                bool watchHeroes = false;

                //Dont Deploy any Tank Units... even if we have them.

                if (!isDead)
                {
                    if (townHallTarget.ValidTarget)
                    {
                        //Before we enter the main attack routine... If there is an exposed TH, Snipe it.
                        //If there are Teslas around it, oh well. we only spent 9-12 units  of each type trying.
                        if (gruntUnits.Any())
                        {
                            var gruntsToDeploy = Rand.Int(5, 15);
                            Log.Info($"[Human Barch] Sniping Town Hall {gruntsToDeploy} Grunts Near: X:{townHallTarget.DeployGrunts.X} Y:{townHallTarget.DeployGrunts.Y}");
                            foreach (var t in Deploy.AtPoints(gruntUnits.FilterTypesByCount(), townHallTarget.DeployGrunts.RandomPointsInArea(_thDeployRadius, gruntsToDeploy), 1, Rand.Int(10, 40), Rand.Int(10, 40)))
                            {
                                yield return(t);
                            }
                            //Wait almost a second
                            yield return(Rand.Int(300, 500)); //Wait
                        }

                        if (rangedUnits.Any())
                        {
                            var rangedToDeploy = Rand.Int(5, 15);
                            Log.Info($"[Human Barch] Sniping Town Hall {rangedToDeploy} Ranged Near: X:{townHallTarget.DeployRanged.X} Y:{townHallTarget.DeployRanged.Y}");
                            foreach (var t in Deploy.AtPoints(rangedUnits.FilterTypesByCount(), townHallTarget.DeployRanged.RandomPointsInArea(_thDeployRadius, rangedToDeploy), 1, Rand.Int(10, 40), Rand.Int(10, 40)))
                            {
                                yield return(t);
                            }
                            //Wait almost a second
                            yield return(Rand.Int(300, 500)); //Wait
                        }

                        //If we dont have a star yet, Drop the King...
                        if (!Attack.HaveAStar())
                        {
                            if (UserSettings.UseKing && king.Any())
                            {
                                Log.Info($"[Human Barch] Deploying King at: X:{townHallTarget.DeployGrunts.X} Y:{townHallTarget.DeployGrunts.Y}");
                                foreach (var t in Deploy.AtPoint(king[0], townHallTarget.DeployGrunts))
                                {
                                    yield return(t);
                                }
                                yield return(Rand.Int(900, 1000)); //Wait

                                watchHeroes = true;
                            }

                            //Deploy the Queen
                            if (UserSettings.UseQueen && queen.Any())
                            {
                                Log.Info($"[Human Barch] Deploying Queen at: X:{townHallTarget.DeployRanged.X} Y:{townHallTarget.DeployRanged.Y}");
                                foreach (var t in Deploy.AtPoint(queen[0], townHallTarget.DeployRanged))
                                {
                                    yield return(t);
                                }
                                yield return(Rand.Int(900, 1000)); //Wait

                                watchHeroes = true;
                            }

                            if (watchHeroes)
                            {
                                //Watch Heros and Hit ability when they get low.
                                Deploy.WatchHeroes(allHeroes);
                                watchHeroes = false; //Only do this once through the loop.
                            }
                        }

                        //Only try once to snipe the town hall when deploying waves.
                        townHallTarget.ValidTarget = false;
                    }
                }
                else
                {
                    //First time through use cached... after the first wave always recheck for Destroyed ones...
                    Target[] targets = HumanLikeAlgorithms.GenerateTargets(_minimumAttackDistanceToCollectors, IgnoreGold, IgnoreElixir, collectorCacheBehavior);
                    collectorCount = targets.Length;

                    //Reorder the Deploy points so they look more human like when attacking.
                    var groupedTargets = targets.ReorderToClosestNeighbor().GroupCloseTargets();

                    collectorCacheBehavior = CacheBehavior.CheckForDestroyed;

                    if (collectorCount < 1)
                    {
                        Log.Info($"[Human Barch] Surrendering - Collectors Remaining = {collectorCount}");

                        // Wait for the wave to finish
                        Log.Info("[Human Barch] Deploy done. Waiting to finish...");
                        var x = Attack.WatchResources(10d).Result;

                        break;
                    }

                    if (townHallTarget.ValidTarget)
                    {
                        //Drop some Grunt and Ranged troups on the TH as well as collectors.
                        //If there are Teslas around it, oh well. we only spent 9-12 units  of each type trying.
                        if (gruntUnits.Any())
                        {
                            var gruntsToDeploy = Rand.Int(4, 6);
                            Log.Info($"[Human Barch] + TH Snipe Dead {gruntsToDeploy} Grunts Near: X:{townHallTarget.DeployGrunts.X} Y:{townHallTarget.DeployGrunts.Y}");
                            foreach (var t in Deploy.AtPoints(gruntUnits.FilterTypesByCount(), townHallTarget.DeployGrunts.RandomPointsInArea(_thDeployRadius, gruntsToDeploy), 1, Rand.Int(10, 40), Rand.Int(10, 40)))
                            {
                                yield return(t);
                            }
                            yield return(Rand.Int(300, 500)); //Wait
                        }

                        if (rangedUnits.Any())
                        {
                            var rangedToDeploy = Rand.Int(4, 6);
                            Log.Info($"[Human Barch] + TH Snipe Dead {rangedToDeploy} Ranged Near: X:{townHallTarget.DeployRanged.X} Y:{townHallTarget.DeployRanged.Y}");
                            foreach (var t in Deploy.AtPoints(rangedUnits.FilterTypesByCount(), townHallTarget.DeployRanged.RandomPointsInArea(_thDeployRadius, rangedToDeploy), 1, Rand.Int(10, 40), Rand.Int(10, 40)))
                            {
                                yield return(t);
                            }
                            yield return(Rand.Int(300, 500)); //Wait
                        }

                        //Only do this once.
                        townHallTarget.ValidTarget = false;
                    }

                    //Determine the index of the 1st and 2nd largest set of targets all in a row.
                    var largestSetIndex       = -1;
                    int largestSetCount       = 0;
                    var secondLargestSetIndex = -1;
                    int secondLargestSetCount = 0;

                    for (int i = 0; i < groupedTargets.Count; i++)
                    {
                        if (groupedTargets[i].Length > largestSetIndex)
                        {
                            secondLargestSetCount = largestSetCount;
                            secondLargestSetIndex = largestSetIndex;
                            largestSetCount       = groupedTargets[i].Length;
                            largestSetIndex       = i;
                        }
                        else if (groupedTargets[i].Length > secondLargestSetIndex)
                        {
                            secondLargestSetCount = groupedTargets[i].Length;
                            secondLargestSetIndex = i;
                        }
                    }

                    Log.Info($"[Human Barch] {groupedTargets.Count} Target Groups, Largest has {largestSetCount} targets, Second Largest {secondLargestSetCount} targets.");

                    //Deploy Barch Units - In Groups on Sets of collectors that are close together.
                    for (int p = 0; p < groupedTargets.Count; p++)
                    {
                        //Deploy Grunts on the Set of Targets.
                        for (int i = 0; i < groupedTargets[p].Length; i++)
                        {
                            var gruntDeployPoint = groupedTargets[p][i].DeployGrunts;

                            if (gruntUnits.Any())
                            {
                                int decreaseFactor = 0;
                                if (i > 0)
                                {
                                    decreaseFactor = (int)Math.Ceiling(i / 2d);
                                }

                                var gruntsAtCollector = (Rand.Int(6, 8) - decreaseFactor);
                                Log.Info($"[Human Barch] {gruntsAtCollector} Grunts Around Point: X:{gruntDeployPoint.X} Y:{gruntDeployPoint.Y}");
                                foreach (var t in Deploy.AtPoints(gruntUnits.FilterTypesByCount(), gruntDeployPoint.RandomPointsInArea(_collectorDeployRadius, gruntsAtCollector), 1, Rand.Int(10, 40)))
                                {
                                    yield return(t);
                                }
                                yield return(Rand.Int(10, 40)); //Wait
                            }
                        }

                        //Pause inbetween switching units.
                        yield return(Rand.Int(90, 100)); //Wait

                        if (secondLargestSetIndex == p && secondLargestSetCount >= 3)
                        {
                            //We are currently deploying to the 2nd largest set of Targets - AND its a set of 3 or more.
                            //Drop the King on the 2nd Target in the set.

                            if (UserSettings.UseKing && king.Any())
                            {
                                Log.Info($"[Human Barch] Deploying King at: X:{groupedTargets[p][1].DeployGrunts.X} Y:{groupedTargets[p][1].DeployGrunts.Y}");
                                foreach (var t in Deploy.AtPoint(king[0], groupedTargets[p][1].DeployGrunts))
                                {
                                    yield return(t);
                                }
                                yield return(Rand.Int(900, 1000)); //Wait

                                watchHeroes = true;
                            }
                        }

                        if (largestSetIndex == p && largestSetCount >= 3)
                        {
                            //We are currently deploying to the largest set of Targets - AND its a set of 3 or more.
                            //Drop the Queen on the 2nd Target in the set.

                            if (UserSettings.UseQueen && queen.Any())
                            {
                                yield return(Rand.Int(90, 100)); //Wait before dropping Queen

                                Log.Info($"[Human Barch] Deploying Queen at: X:{groupedTargets[p][1].DeployRanged.X} Y:{groupedTargets[p][1].DeployRanged.Y}");
                                foreach (var t in Deploy.AtPoint(queen[0], groupedTargets[p][1].DeployRanged))
                                {
                                    yield return(t);
                                }
                                yield return(Rand.Int(900, 1000)); //Wait

                                watchHeroes = true;
                            }
                        }

                        if (watchHeroes)
                        {
                            //Watch Heros and Hit ability when they get low.
                            Deploy.WatchHeroes(allHeroes);
                            watchHeroes = false; //Only do this once through the loop.
                        }


                        //Deploy Ranged units on same set of Targets.
                        for (int i = 0; i < groupedTargets[p].Length; i++)
                        {
                            var rangedDeployPoint = groupedTargets[p][i].DeployRanged;

                            if (rangedUnits.Any())
                            {
                                int decreaseFactor = 0;
                                if (i > 0)
                                {
                                    decreaseFactor = (int)Math.Ceiling(i / 2d);
                                }

                                var rangedAtCollector = (Rand.Int(5, 7) - decreaseFactor);
                                Log.Info($"[Human Barch] {rangedAtCollector} Ranged Around Point: X:{rangedDeployPoint.X} Y:{rangedDeployPoint.Y}");
                                foreach (var t in Deploy.AtPoints(rangedUnits.FilterTypesByCount(), rangedDeployPoint.RandomPointsInArea(_collectorDeployRadius, rangedAtCollector), 1, Rand.Int(10, 40)))
                                {
                                    yield return(t);
                                }
                                yield return(Rand.Int(40, 50)); //Wait
                            }
                        }

                        yield return(Rand.Int(90, 100)); //Wait before switching units back to Grutns and deploying on next set of targets.
                    }
                }

                //Never deploy any Healing type Units.


                //wait a random number of seconds before the next round on all Targets...
                yield return(Rand.Int(2000, 5000));

                // Get starting resources, cache needs to be false to force a new check
                LootResources postLoot = Opponent.GetAvailableLoot(false);
                if (postLoot == null)
                {
                    Log.Warning($"[Human Barch] Human Barch Deploy could not read available loot this wave");
                    postLoot = new LootResources()
                    {
                        Gold = -1, Elixir = -1, DarkElixir = -1
                    };
                }

                Log.Info($"[Human Barch] Wave {waveCounter} resources - G: {postLoot.Gold}, E: {postLoot.Elixir}, DE: {postLoot.DarkElixir}");
                int newGold   = preLoot.Gold - postLoot.Gold;
                int newElixir = preLoot.Elixir - postLoot.Elixir;
                int newDark   = preLoot.DarkElixir - postLoot.DarkElixir;
                Log.Info($"[Human Barch] Wave {waveCounter} resource diff - G: {newGold}, E: {newElixir}, DE: {newDark}, Collectors: {collectorCount}");

                if (isDead)
                {
                    if (postLoot.Gold + postLoot.Elixir + postLoot.DarkElixir >= 0)
                    {
                        if (newGold + newElixir < 3000 * collectorCount)
                        {
                            Log.Info("[Human Barch] Surrendering because gained resources isn't enough");
                            break;
                        }
                        preLoot = postLoot;
                    }
                }
                else
                {
                    if (Attack.HaveAStar())
                    {
                        Log.Info("[Human Barch] We have a star! TH Sniped!");

                        //Check the Delta in Resources.
                        if (newGold + newElixir < (preLoot.Gold + preLoot.Elixir) * .05f) //Less than 5% of what is available.
                        {
                            //Switch the attack mode to Dead - so we get some of the collectors.
                            Log.Info($"[Human Barch] Not much loot gained from Snipe(G:{newGold} E:{newElixir} out of G:{preLoot.Gold} E:{preLoot.Elixir}) - Try to Loot Collectors also...");
                            isDead = true;
                        }
                        else
                        {
                            //Halt the Attack.
                            break;
                        }
                    }

                    if (waveCounter > 10)
                    {
                        Log.Info("[Human Barch] Fail! TH Not Sniped! our troops died - Surrendering...");
                        break;
                    }
                }

                waveCounter++;
            }

            //TODO - Can we destroy some trash buildings to get a star if we dont already have one?

            //Last thing Call ZapDarkElixterDrills... This uses the Clashbot settings for when to zap, and what level drills to zap.
            Log.Info("[Human Barch] Checking to see if we can Zap DE Drills...");
            foreach (var t in ZapDarkElixirDrills())
            {
                yield return(t);
            }

            //We broke out of the attack loop...
            Attack.Surrender();
        }
        public override IEnumerable <int> AttackRoutine()
        {
            Log.Info("[Red Line] Attack start");

            var deployElements = Deploy.GetTroops();

            deployElements.Extract(DeployElementType.Spell);
            var heroes = deployElements.Extract(u => u.IsHero);
            var cc     = deployElements.ExtractOne(DeployId.ClanCastle);
            var ranged = deployElements.Extract(u => u.IsRanged);
            var units  = deployElements.OrderBy(u => u.UnitData?.AttackType != AttackType.Tank).ToList();

            var clockwisePoints = GameGrid.RedPoints
                                  .Where(
                point =>
                !(point.X > 18 && point.Y > 18 || point.X > 18 && point.Y < -18 || point.X < -18 && point.Y > 18 ||
                  point.X < -18 && point.Y < -18))
                                  .OrderBy(point => Math.Atan2(point.X, point.Y)).ToList();

            var inflatedPoints = clockwisePoints
                                 .Select(
                point =>
                new PointFT((float)(point.X + (point.X / Math.Sqrt(point.DistanceSq(new PointFT(0, 0)))) * 4),
                            (float)(point.Y + (point.Y / Math.Sqrt(point.DistanceSq(new PointFT(0, 0)))) * 4)))
                                 .ToList();

            while (units.Count > 0)
            {
                Log.Info("[Red Line] Deploying melee and tanking troops");
                foreach (var element in units)
                {
                    foreach (
                        var t in
                        Deploy.AtPoints(element,
                                        clockwisePoints.GetNth(GameGrid.RedPoints.Length / (double)element.Count).ToArray()))
                    {
                        yield return(t);
                    }
                }
                units.Recount();
                units.RemoveAll(unit => unit.Count < 1);
            }

            while (ranged.Count > 0)
            {
                Log.Info("[Red Line] Deploying ranged troops");
                foreach (var element in ranged)
                {
                    foreach (
                        var t in
                        Deploy.AtPoints(element,
                                        inflatedPoints.GetNth(GameGrid.RedPoints.Length / (double)element.Count).ToArray()))
                    {
                        yield return(t);
                    }
                }
                ranged.Recount();
                ranged.RemoveAll(unit => unit.Count < 1);
            }

            var pt = new Container <PointFT> {
                Item = new PointFT((float)GameGrid.MinX, GameGrid.MinY)
            };

            if (cc?.Count > 0 && UserSettings.UseClanTroops)
            {
                Log.Info($"[Red Line] Deploying {cc.PrettyName}");
                foreach (var y in Deploy.AtPoint(cc, pt))
                {
                    yield return(y);
                }
            }

            if (heroes.Any())
            {
                foreach (var hero in heroes.Where(u => u.Count > 0))
                {
                    Log.Info($"[Red Line] Deploying {hero.PrettyName}");
                    foreach (var t in Deploy.AtPoint(hero, pt))
                    {
                        yield return(t);
                    }
                }
            }

            Deploy.WatchHeroes(heroes);

            Log.Info("[Red Line] Deploy done");
        }
예제 #7
0
        public override IEnumerable <int> AttackRoutine()
        {
#if DEBUG
            debugMode = true;
#endif
            var visuals = new List <VisualObject>();
            // user's wave delay setting
            var waveDelay = (int)(UserSettings.WaveDelay * 1000);

            // Call it once in order to cache the RedPoints in the beginning
            // If this will be called after spell deployment the redline will disappear!! - Do not remove this!
            var redPoints = GameGrid.RedPoints;

            // get a list of all deployable units
            var deployElements = Deploy.GetTroops();
            Log.Debug("[Debug] Deployable Troops: " + ToUnitString(deployElements));
            if (!HasNeededTroops(deployElements))
            {
                Log.Error("[LavaLoon] Couldn't find a known troop composition. Consider using one of the known troop compositions. Check our forums to learn more about " +
                          "the LavaLoon Deploy in order to achieve the best possible results.");
                Surrender();
                yield break;
            }

            // extract heores into their own list
            var heroes = deployElements
                         .Extract(u => (UserSettings.UseKing && u.ElementType == DeployElementType.HeroKing) ||
                                  (UserSettings.UseQueen && u.ElementType == DeployElementType.HeroQueen) ||
                                  (UserSettings.UseWarden && u.ElementType == DeployElementType.HeroWarden))
                         .ToList();

            // extract clanCastle into its own list
            var clanCastle = deployElements.ExtractOne(u => u.ElementType == DeployElementType.ClanTroops && UserSettings.UseClanTroops);

            // extract spells into their own list
            var lightningSpells  = deployElements.ExtractOne(x => x.Id == DeployId.Lightning);
            var earthQuakeSpells = deployElements.ExtractOne(x => x.Id == DeployId.Earthquake);
            var rageSpells       = deployElements.ExtractOne(x => x.Id == DeployId.Rage);
            var freezeSpells     = deployElements.ExtractOne(x => x.Id == DeployId.Freeze);

            // extract tank units into their own list
            var tanks = deployElements.Extract(AttackType.Tank).ToArray();

            // extract balloons into their own list
            var balloon = deployElements.ExtractOne(x => x.Id == DeployId.Balloon);

            // extract the attack units into their own list
            var damageDealer = deployElements.Extract(AttackType.Damage).OrderByDescending(x => x.UnitData.HP).ToArray();

            // extract wallbreakers into their own list
            var wallBreakers = deployElements.ExtractOne(x => x.Id == DeployId.WallBreaker);

            #region ZapQuake AirDefenses if possible and rescan with a CheckForDestroyed
            // ZapQuake AirDefenses if required spells exist
            List <AirDefense> zapQuakeTargets = null;
            if (earthQuakeSpells?.Count > 0 && lightningSpells?.Count >= 2)
            {
                zapQuakeTargets = FindAirDefenseTargets(earthQuakeSpells, lightningSpells, visuals);

                if (zapQuakeTargets?.Count > 0)
                {
                    foreach (var t in ZapQuakeAirDefenses(zapQuakeTargets, earthQuakeSpells, lightningSpells))
                    {
                        yield return(t);
                    }
                }
                else
                {
                    Log.Warning("[LavaLoon] Couldn't find AirDefense targets for ZapQuaking!");
                }
            }
            else
            {
                Log.Info("[LavaLoon] Could not find enough spells (at least 1 Earthquake and 2 Lightning spells) to zapquake AirDefenses");
            }

            // If we have zapquaked something we should rescan to check for the remaining AirDefenses
            if (zapQuakeTargets != null)
            {
                Log.Info("[LavaLoon] Rescanning AirDefenses to check for remaining AirDefenses");
                AirDefense.Find(CacheBehavior.CheckForDestroyed);
            }
            #endregion

            var airDefenses               = AirDefense.Find();
            var deployInfo                = PrepareDeployCalculation(airDefenses, visuals);
            var tankDeployPoints          = CreateLavaHoundDeployPoints(deployInfo, visuals).ToArray();
            var balloonDeployPoints       = CreateBalloonDeployPoints(deployInfo, balloon, visuals).ToArray();
            var attackWaveDeployPoints    = CreateAttackWaveDeployPoints(deployInfo, visuals).ToArray();
            var funnelCreatorDeployPoints = CreateFunnelCreatorDeployPoints(deployInfo, visuals).ToArray();
            var intersectionDeployPoint   = CreateIntersectionDeployPoint(deployInfo, visuals).First();

            // deploy all tanks if available
            if (tanks != null)
            {
                foreach (var tank in tanks)
                {
                    var deployCount = tank.Count;
                    Log.Info($"[Breakthrough] Deploying {tank.PrettyName} x{deployCount}");

                    // Deploy all Tanks alternating to each tank deploypoint (e. g.: left - right, left - right, left)
                    while (tank?.Count > 0)
                    {
                        var initialTankCount = tank.Count;

                        foreach (var deployPoint in tankDeployPoints)
                        {
                            foreach (var t in Deploy.AtPoint(tank, deployPoint, 1))
                            {
                                yield return(t);
                            }
                        }

                        // Prevent an infinite loop if deploy point is inside of the redzone
                        if (tank.Count != initialTankCount)
                        {
                            continue;
                        }

                        Log.Warning($"[LavaLoon] Couldn't deploy {tank.PrettyName}");
                        break;
                    }
                }
            }

            if (balloon != null)
            {
                Log.Info($"[LavaLoon] Deploying {balloon.PrettyName} x{balloon.Count}");

                while (balloon?.Count > 0)
                {
                    var initialWallBreakersCount = balloon?.Count;
                    foreach (var t in Deploy.AtPoints(balloon, balloonDeployPoints, 3, 50))
                    {
                        yield return(t);
                    }

                    // Prevent an infinite loop if deploy point is inside of the redzone
                    if (balloon.Count != initialWallBreakersCount)
                    {
                        yield return(1200);

                        continue;
                    }

                    Log.Warning($"[LavaLoon] Couldn't deploy {wallBreakers.PrettyName}");
                    break;
                }
            }

            if (clanCastle?.Count > 0)
            {
                Log.Info($"[LavaLoon] Deploying {clanCastle.PrettyName}");
                foreach (var t in Deploy.AtPoint(clanCastle, intersectionDeployPoint, waveDelay: waveDelay))
                {
                    yield return(t);
                }
            }

            if (damageDealer.Any())
            {
                Log.Debug("[LavaLoon] 1500ms Delay before deploying the attack wave");
                yield return(1500);

                foreach (var troop in damageDealer)
                {
                    Log.Info($"[LavaLoon] Deploying {troop.PrettyName} x{troop.Count}");
                }
                while (damageDealer.Sum(x => x.Count) > 0)
                {
                    var initialTroopCount = damageDealer.Sum(x => x.Count);
                    foreach (var t in Deploy.AtPoints(damageDealer, funnelCreatorDeployPoints))
                    {
                        yield return(t);
                    }

                    // Prevent an infinite loop if deploy point is inside of the redzone
                    if (damageDealer.Sum(x => x.Count) != initialTroopCount)
                    {
                        continue;
                    }

                    var remainingTroops = damageDealer.Where(x => x.Count > 0).ToList();
                    foreach (var troop in remainingTroops)
                    {
                        Log.Warning($"[LavaLoon] Couldn't deploy x{troop.Count} {troop.PrettyName}");
                    }
                    break;
                }
            }

            if (heroes.Any())
            {
                Log.Debug("[LavaLoon] 1500ms Delay before deploying heroes");
                yield return(1500);

                var heroDeployPoint = intersectionDeployPoint.TransformPositionAlongAxis(4).Constrain();

                foreach (var hero in heroes.Where(u => u.Count > 0))
                {
                    Log.Info($"[LavaLoon] Deploying {hero.PrettyName}");
                    foreach (var t in Deploy.AtPoint(hero, heroDeployPoint))
                    {
                        yield return(t);
                    }
                }

                Deploy.WatchHeroes(heroes, 7000);
            }

            if (wallBreakers?.Count > 0)
            {
                var wallBreakersDeployPoint = intersectionDeployPoint.TransformPositionAlongAxis(4).Constrain();

                Log.Info($"[LavaLoon] Deploying {wallBreakers.PrettyName} x{wallBreakers.Count}");

                while (wallBreakers?.Count > 0)
                {
                    var initialWallBreakersCount = wallBreakers?.Count;

                    // Deploy Wallbreakers in 3 unit stacks (which is enough to crush walls) and deploy further stacks with 1.2s delay
                    // in order to avoid that all of them get destroyed by splash damages
                    foreach (var t in Deploy.AtPoint(wallBreakers, wallBreakersDeployPoint, 3, waveDelay: waveDelay))
                    {
                        yield return(t);
                    }

                    // Prevent an infinite loop if deploy point is inside of the redzone
                    if (wallBreakers.Count != initialWallBreakersCount)
                    {
                        yield return(1200);

                        continue;
                    }

                    Log.Warning($"[LavaLoon] Couldn't deploy {wallBreakers.PrettyName}");
                    break;
                }
            }

            if (debugMode)
            {
                VisualizeDeployment(visuals);
            }
        }