/// <summary>
        /// Drop EQ spells on drills to get DE beacuse max EQ will gain 320 DE from max drill
        /// </summary>
        /// <param name="drillLevel">minmum drill level</param>
        /// <param name="spells">available spells in unitsbar</param>
        /// <returns>Drop EQ spells on drills</returns>
        public static IEnumerable <int> UseEQOnDrills(int drillLevel, List <DeployElement> spells)
        {
            var EQSpell = spells.Extract(u => u.Id == DeployId.Earthquake);
            var EQCount = EQSpell.Sum(u => u.Count);

            if (EQCount > 0)
            {
                Log.Info($"{SmartFourFingersDeploy.AttackName} start use EQ on drills");
                var drills = DarkElixirDrill.Find(CacheBehavior.ForceScan, drillLevel);
                if (drills.Any())
                {
                    foreach (var d in drills)
                    {
                        var eq = EQSpell.FirstOrDefault()?.Count > 0 ? EQSpell.FirstOrDefault() : EQSpell.LastOrDefault();
                        if (eq.Count > 0)
                        {
                            foreach (var t in Deploy.AtPoint(eq, d.Location.GetCenter()))
                            {
                                yield return(t);
                            }
                        }
                    }
                }
                else
                {
                    Log.Warning($"{SmartFourFingersDeploy.AttackName} no Drills found to use EQ on !!");
                }
            }
            else
            {
                Log.Warning($"{SmartFourFingersDeploy.AttackName} no EarthQuake spells found to use on drills");
            }
        }
        public static void CalculateTargets()
        {
            collectors = ElixirCollector.Find();

            mines = GoldMine.Find();

            drills = DarkElixirDrill.Find();

            int collectorsCount = collectors != null?collectors.Count() : 0;

            int minesCount = mines != null?mines.Count() : 0;

            int drillsCount = drills != null?drills.Count() : 0;

            // Set total count of targets
            SmartFourFingersDeploy.TotalTargetsCount = collectorsCount + minesCount + drillsCount;

            // four corners
            var top    = new PointFT((float)GameGrid.DeployExtents.MaxX + 1, GameGrid.DeployExtents.MaxY + 4);
            var right  = new PointFT((float)GameGrid.DeployExtents.MaxX + 1, GameGrid.DeployExtents.MinY - 4);
            var bottom = new PointFT((float)GameGrid.DeployExtents.MinX - 1, GameGrid.DeployExtents.MinY - 4);
            var left   = new PointFT((float)GameGrid.DeployExtents.MinX - 1, GameGrid.DeployExtents.MaxY + 4);

            SetCore();

            var corners = new List <Tuple <PointFT, PointFT> >
            {
                new Tuple <PointFT, PointFT>(top, right),
                new Tuple <PointFT, PointFT>(bottom, right),
                new Tuple <PointFT, PointFT>(bottom, left),
                new Tuple <PointFT, PointFT>(top, left)
            };

            // loop throw the 4 sides and count targets on each side
            var targetsAtLine = new List <int>();

            foreach (var l in corners)
            {
                var colCount = collectors.Where(t => t.Location.GetCenter().
                                                IsInTri(SmartFourFingersDeploy.Core, l.Item1, l.Item2))?.Count() ?? 0;
                var minCount = mines.Where(t => t.Location.GetCenter().
                                           IsInTri(SmartFourFingersDeploy.Core, l.Item1, l.Item2))?.Count() ?? 0;
                var drillCount = drills.Where(t => t.Location.GetCenter().
                                              IsInTri(SmartFourFingersDeploy.Core, l.Item1, l.Item2))?.Count() ?? 0;
                var total = colCount + minCount + drillCount;

                targetsAtLine.Add(total);
            }

            SmartFourFingersDeploy.TargetsAtLine = targetsAtLine;
        }
        public static Target[] GenerateTargets(float minimumDistance, bool ignoreGold, bool ignoreElixir, CacheBehavior behavior = CacheBehavior.Default)
        {
            // Find all Collectors & storages just sitting around...
            List <Building> buildings = new List <Building>();

            if (!ignoreGold)
            {
                //User has Gold min set to ZERO - which means Dont include Gold Targets
                buildings.AddRange(GoldMine.Find(behavior));
                buildings.AddRange(GoldStorage.Find(behavior));
            }

            if (!ignoreElixir)
            {
                //User has Elixir min set to ZERO - which means Dont include Elixir Targets
                buildings.AddRange(ElixirCollector.Find(behavior));
                buildings.AddRange(ElixirStorage.Find(behavior));
            }

            //We always includ DarkElixir - Because who doesnt love dark Elixir?
            buildings.AddRange(DarkElixirDrill.Find(behavior));
            buildings.AddRange(DarkElixirStorage.Find(behavior));

            List <Target> targetList = new List <Target>();

            foreach (Building building in buildings)
            {
                Target current = new Target();

                current.TargetBuilding  = building;
                current.Center          = building.Location.GetCenter();
                current.NearestRedLine  = GameGrid.RedPoints.OrderBy(p => p.DistanceSq(current.Center)).First();
                current.CenterToRedline = current.Center.DistanceSq(current.NearestRedLine);
                Log.Debug($"[Berts Algorithms] DistanceSq from {current.Name} to red point: {current.CenterToRedline.ToString("F1")}");
                if (current.CenterToRedline < minimumDistance)                                                                              //Compare distance to Redline to the Minimum acceptable distance Passed in
                {
                    current.DeployGrunts = current.Center.PointOnLineAwayFromEnd(current.NearestRedLine, _gruntDeployDistanceFromRedline);  //Barbs & Goblins
                    current.DeployRanged = current.Center.PointOnLineAwayFromEnd(current.NearestRedLine, _rangedDeployDistanceFromRedline); //Archers & Minions

                    targetList.Add(current);
                }
            }

            Log.Debug($"[Berts Algorithms] Found {targetList.Count} deploy points");

            return(targetList.ToArray());
        }
Exemple #4
0
        /// <summary>
        /// Search for drills and store the heighest amount of drills
        /// </summary>
        /// <param name="drillLevel">minimum drill level</param>
        /// <returns></returns>
        static IEnumerable <int> GetDrills(int drillLevel)
        {
            Log.Info($"[Smart Zap] looking for dark elixir drills ...");
            availableDrills = DarkElixirDrill.Find(CacheBehavior.ForceScan, drillLevel);
            for (var i = 1; i <= 5; i++)
            {
                yield return(1000);

                var reCountDrills = DarkElixirDrill.Find(CacheBehavior.ForceScan, drillLevel);

                if (reCountDrills.Count() > availableDrills.Count())
                {
                    availableDrills = reCountDrills;
                }
            }
            Log.Info($"[Smart Zap] found {availableDrills.Count()} drills");
        }
        private void GenerateDeployPointsFromMinesToMilk(CacheBehavior behavior = CacheBehavior.Default)
        {
            // Find all mines
            List <Building> mines = new List <Building>();
            TownHall        th    = TownHall.Find();

            if (th != null)
            {
                mines.Add(th);
            }
            if (!resourcesFull.HasFlag(ResourcesFull.Gold))
            {
                mines.AddRange(GoldMine.Find(behavior));
            }
            if (!resourcesFull.HasFlag(ResourcesFull.Elixir))
            {
                mines.AddRange(ElixirCollector.Find(behavior));
            }
            if (!resourcesFull.HasFlag(ResourcesFull.Delixir))
            {
                mines.AddRange(DarkElixirDrill.Find(behavior));
            }

            List <PointFT> resultPoints = new List <PointFT>();

            foreach (Building mine in mines)
            {
                PointFT center     = mine.Location.GetCenter();
                PointFT closest    = GameGrid.RedPoints.OrderBy(p => p.DistanceSq(center)).First();
                float   distanceSq = center.DistanceSq(closest);
                Log.Debug("DistanceSq from " + mine.GetType().Name + " to red point: " + distanceSq.ToString("F1"));
                if (distanceSq < 9)  // 3 tiles (squared = 9) means there is no wall or building between us and the collector
                {
                    Log.Debug("Adding deploy point");
                    PointFT awayFromRedLine = closest.AwayFrom(center, 0.5f);
                    resultPoints.Add(awayFromRedLine);
                }
            }
            Log.Debug("Found " + resultPoints.Count + " deploy points");
            deployPoints = resultPoints.ToArray();
        }
Exemple #6
0
        static IEnumerable <int> GetDestroyedDrills(PointFT point)
        {
            isDestroyed = false;
            Log.Info($"[Smart Zap] Check if this drill is destroyed ...");
            var drills = DarkElixirDrill.Find(CacheBehavior.ForceScan).Where(d => d.Location.GetCenter().DistanceSq(point) <= 1);

            for (var i = 1; i <= 4; i++)
            {
                yield return(1000);

                var reCountDrills = DarkElixirDrill.Find(CacheBehavior.ForceScan).Where(d => d.Location.GetCenter().DistanceSq(point) <= 1);

                if (reCountDrills.Count() > drills.Count())
                {
                    drills = reCountDrills;
                }
            }
            if (drills.Count() == 0)
            {
                isDestroyed = true;
                Log.Info($"[Smart Zap] the drill was destroyed !!");
            }
        }
        /// <summary>
        /// Check to see how many collector and mine near to the redline by user defined distance
        /// </summary>
        /// <param name="userDistance">Minimum distance for exposed colloctors and mines</param>
        /// <param name="minCollectors">minimum exposed collectors</param>
        /// <param name="minMines">minimum exposed mines</param>
        /// <param name="AttackName">Attack name for logs and debugging</param>
        /// <param name="debug">debug mode in advanced settings</param>
        /// <returns>true if matches user defined min collectores and mines</returns>
        public static bool IsBaseMinCollectorsAndMinesOutside(int userDistance, int minCollectors, int minMines, string AttackName, int debug)
        {
            var distance = userDistance * userDistance;

            var redPoints = 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));

            collectors = ElixirCollector.Find().Where(c => c.Location.GetCenter()
                                                      .DistanceSq(redPoints.OrderBy(p => p.DistanceSq(c.Location.GetCenter()))
                                                                  .FirstOrDefault()) <= distance);

            mines = GoldMine.Find().Where(c => c.Location.GetCenter()
                                          .DistanceSq(redPoints.OrderBy(p => p.DistanceSq(c.Location.GetCenter()))
                                                      .FirstOrDefault()) <= distance);

            drills = DarkElixirDrill.Find().Where(c => c.Location.GetCenter()
                                                  .DistanceSq(redPoints.OrderBy(p => p.DistanceSq(c.Location.GetCenter()))
                                                              .FirstOrDefault()) <= distance);

            int collectorsCount = collectors != null?collectors.Count() : 0;

            int minesCount = mines != null?mines.Count() : 0;

            int drillsCount = drills != null?drills.Count() : 0;

            // Set total count of targets
            SmartFourFingersDeploy.TotalTargetsCount = collectorsCount + minesCount + drillsCount;

            // four corners
            var top    = new PointFT((float)GameGrid.DeployExtents.MaxX + 1, GameGrid.DeployExtents.MaxY + 4);
            var right  = new PointFT((float)GameGrid.DeployExtents.MaxX + 1, GameGrid.DeployExtents.MinY - 4);
            var bottom = new PointFT((float)GameGrid.DeployExtents.MinX - 1, GameGrid.DeployExtents.MinY - 4);
            var left   = new PointFT((float)GameGrid.DeployExtents.MinX - 1, GameGrid.DeployExtents.MaxY + 4);

            SetCore();

            var corners = new List <Tuple <PointFT, PointFT> >
            {
                new Tuple <PointFT, PointFT>(top, right),
                new Tuple <PointFT, PointFT>(bottom, right),
                new Tuple <PointFT, PointFT>(bottom, left),
                new Tuple <PointFT, PointFT>(top, left)
            };

            // loop throw the 4 sides and count targets on each side
            var targetsAtLine = new List <int>();

            foreach (var l in corners)
            {
                var colCount = collectors.Where(t => t.Location.GetCenter().
                                                IsInTri(SmartFourFingersDeploy.Core, l.Item1, l.Item2))?.Count() ?? 0;
                var minCount = mines.Where(t => t.Location.GetCenter().
                                           IsInTri(SmartFourFingersDeploy.Core, l.Item1, l.Item2))?.Count() ?? 0;
                var drillCount = drills.Where(t => t.Location.GetCenter().
                                              IsInTri(SmartFourFingersDeploy.Core, l.Item1, l.Item2))?.Count() ?? 0;
                var total = colCount + minCount + drillCount;

                targetsAtLine.Add(total);
            }

            SmartFourFingersDeploy.TargetsAtLine = targetsAtLine;

            var op = new Opponent(0);

            //if (!op.IsForcedAttack )
            {
                Log.Info($"{AttackName} NO. of Colloctors & mines near from red line:");
                Log.Info($"elixir colloctors is {collectorsCount}");
                Log.Info($"gold mines is {minesCount}");
                Log.Info($"----------------------------");
                Log.Info($"sum of all is {collectorsCount + minesCount}");

                if (debug == 1)
                {
                    using (Bitmap bmp = Screenshot.Capture())
                    {
                        using (Graphics g = Graphics.FromImage(bmp))
                        {
                            foreach (var c in collectors)
                            {
                                var point = c.Location.GetCenter();
                                Visualize.Target(bmp, point, 30, Color.Purple);
                            }

                            foreach (var c in mines)
                            {
                                var point = c.Location.GetCenter();
                                Visualize.Target(bmp, point, 30, Color.Gold);
                            }

                            foreach (var c in drills)
                            {
                                var point = c.Location.GetCenter();
                                Visualize.Target(bmp, point, 30, Color.Black);
                            }
                            DrawLine(bmp, Color.Red, SmartFourFingersDeploy.Core, top);
                            DrawLine(bmp, Color.Red, SmartFourFingersDeploy.Core, right);
                            DrawLine(bmp, Color.Red, SmartFourFingersDeploy.Core, bottom);
                            DrawLine(bmp, Color.Red, SmartFourFingersDeploy.Core, left);
                        }
                        var d = DateTime.UtcNow;
                        Screenshot.Save(bmp, "Collectors and Mines {d.Year}-{d.Month}-{d.Day} {d.Hour}-{d.Minute}-{d.Second}-{d.Millisecond}");
                    }
                }
            }
            if (collectorsCount >= minCollectors && minesCount >= minMines)
            {
                return(true);
            }
            else
            {
                Log.Warning($"{AttackName} this base doesn't meets Collocetors & Mines requirements");
                return(false);
            }
        }
        IEnumerable <int> DeployLeftoverSpells()
        {
            var lightningSpells = deployElements.FirstOrDefault(u => u.ElementType == DeployElementType.Spell && u.Id == DeployId.Lightning);
            List <DeployElement> earthquakeSpells = deployElements.Where(u => u.ElementType == DeployElementType.Spell && u.Id == DeployId.Earthquake).ToList();
            List <DeployElement> skeletonSpells   = deployElements.Where(u => u.ElementType == DeployElementType.Spell && u.Id == DeployId.Skeleton).ToList();

            List <DeployElement> leftoverSpells = new List <DeployElement>();

            //To Prevent errors, perform a recount on all these.
            leftoverSpells.RecountAndAddIfAny(skeletonSpells);
            leftoverSpells.RecountAndAddIfAny(earthquakeSpells);
            leftoverSpells.RecountAndAddIfAny(lightningSpells);

            //Now if any Skeleton Spells exist, drop them ALL on the last air-D to Distract/Destroy.
            if (leftoverSpells.Count > 0)
            {
                yield return(Rand.Int(4000, 6000)); // pause a while longer... Dragons should be getting close to this one now...

                var adToDistract = 0;
                if (zapped1)
                {
                    adToDistract = 1;
                }
                if (zapped2)
                {
                    adToDistract = 2;
                }

                PointFT dropPoint    = new PointFT();
                string  locationDesc = string.Empty;

                if (airDefenses.Length < adToDistract + 1)
                {
                    //Only two Air Defenses were found? No Third to use spells on... drop them on A DE Collector.

                    //Put them on any Elixir Collector still up.
                    var deDrill = DarkElixirDrill.Find(CacheBehavior.ForceScan);

                    if (deDrill.Any())
                    {
                        dropPoint    = deDrill[0].Location.GetCenter();
                        locationDesc = "Dark Elixir Drill";
                    }
                    else
                    {
                        //Give up and just drop them in the middle of the map to get rid of them.
                        dropPoint    = HumanLikeAlgorithms.Origin;
                        locationDesc = "Center of map - (no other air Defenses or DE Drills Could be found)";
                    }
                }
                else
                {
                    //There were aditional air defenses found... drop them on the next one.
                    dropPoint    = airDefenses[adToDistract].Location.GetCenter();
                    locationDesc = $"Air Defense #{adToDistract + 1}";
                }

                foreach (var spell in leftoverSpells)
                {
                    Log.Info($"{Tag} Deploying {spell.Count} left over {spell.PrettyName} Spell(s) to {locationDesc}...");
                    foreach (var t in Deploy.AtPoint(spell, dropPoint, spell.Count))
                    {
                        yield return(t);
                    }
                }
            }
            else
            {
                Log.Info($"{Tag} All Spells Successfully Deployed...");
            }
        }
Exemple #9
0
        public static Target[] GenerateTargets(string algorithmName, float minimumDistance, bool ignoreGold, bool ignoreElixir, string AttackId, out double avgFillstate, out double avgCollectorLvl, CacheBehavior behavior = CacheBehavior.Default, bool outputDebugImage = false, bool activeBase = false)
        {
            // Find all Collectors & storages just sitting around...
            List <Building> buildings = new List <Building>();

            //Get a list of Gold Mines.
            List <GoldMine> goldMines = new List <GoldMine>();

            goldMines.AddRange(GoldMine.Find(behavior));

            //Get a list of Elixir Collectors.
            List <ElixirCollector> elixirCollectors = new List <ElixirCollector>();

            elixirCollectors.AddRange(ElixirCollector.Find(behavior));
            avgFillstate = 0;

            //Get the Average Fill State of all the Elixir Collectors - From this we can tell what percentage of the loot is in Collectors.
            if (elixirCollectors.Count > 1)
            {
                avgFillstate = elixirCollectors.Average(c => c.FillState);
            }

            //Log the Average Fill State of aLL elixir Collectors...
            Log.Debug($"[Berts Algorithms] - Fill State Average of ALL Elixir Collectors: {(avgFillstate * 10).ToString("F1")}");

            if (!ignoreGold)
            {
                buildings.AddRange(goldMines);
                if (activeBase)
                {
                    buildings.AddRange(GoldStorage.Find(behavior));
                }
            }
            if (!ignoreElixir)
            {
                buildings.AddRange(elixirCollectors);
                if (activeBase)
                {
                    buildings.AddRange(ElixirStorage.Find(behavior));
                }
            }

            //Determine the Average Collector Level.
            avgCollectorLvl = 0;

            if (ignoreGold && !ignoreElixir)
            {
                if (elixirCollectors.Count(c => c.Level.HasValue) > 1)
                {
                    avgCollectorLvl = elixirCollectors.Where(c => c.Level.HasValue).Average(c => (int)c.Level);
                }
            }
            else if (ignoreElixir && !ignoreGold)
            {
                if (goldMines.Count(c => c.Level.HasValue) > 1)
                {
                    avgCollectorLvl = goldMines.Where(c => c.Level.HasValue).Average(c => (int)c.Level);
                }
            }
            else if (!ignoreElixir && !ignoreGold)
            {
                if (buildings.Count(c => c.Level.HasValue) > 1)
                {
                    avgCollectorLvl = buildings.Where(c => c.Level.HasValue).Average(c => (int)c.Level);
                }
            }

            //We always includ DarkElixir - Because who doesnt love dark Elixir?
            buildings.AddRange(DarkElixirDrill.Find(behavior));
            if (activeBase)
            {
                buildings.AddRange(DarkElixirStorage.Find(behavior));
            }

            List <Target> targetList = new List <Target>();

            foreach (Building building in buildings)
            {
                Target current = new Target();

                current.TargetBuilding  = building;
                current.Center          = building.Location.GetCenter();
                current.Edge            = Origin.PointOnLineAwayFromEnd(current.Center, 1.0f);
                current.NearestRedLine  = AllPoints.OrderBy(p => p.DistanceSq(current.Edge)).First();
                current.CenterToRedline = current.Center.DistanceSq(current.NearestRedLine);
                if (current.CenterToRedline < minimumDistance)  //Compare distance to Redline to the Minimum acceptable distance Passed in
                {
                    Log.Debug($"[Berts Algorithms] Distance from {current.Name} to red point: {Math.Sqrt(current.CenterToRedline).ToString("F1")}, Min Distance: {Math.Sqrt(minimumDistance).ToString("F1")} - GO!");
                    current.DeployGrunts = current.Center.PointOnLineAwayFromEnd(current.NearestRedLine, _gruntDeployDistanceFromRedline);  //Barbs & Goblins
                    current.DeployRanged = current.Center.PointOnLineAwayFromEnd(current.NearestRedLine, _rangedDeployDistanceFromRedline); //Archers & Minions

                    targetList.Add(current);
                }
                else
                {
                    Log.Debug($"[Berts Algorithms] Distance from {current.Name} to red point: {Math.Sqrt(current.CenterToRedline).ToString("F1")}, Min Distance: {Math.Sqrt(minimumDistance).ToString("F1")} - TOO FAR!");
                }
            }

            if (outputDebugImage)
            {
                OutputDebugImage(algorithmName, buildings, targetList, AttackId);
            }

            return(targetList.ToArray());
        }
        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();
        }
        /// <summary>
        /// use lighting on drill in smart way to save elixir usage
        /// </summary>
        /// <param name="deAmount">minimum dark elixir to gain per zap (user defined)</param>
        /// <param name="drillLevel">minimum drill level to zap (user defined)</param>
        /// <param name="spells">available spells in unitsbar</param>
        /// <returns>deploy lighting on drills</returns>
        public static IEnumerable <int> SmartZap(int deAmount, int drillLevel, List <DeployElement> spells)
        {
            Log.Info($"{SmartFourFingersDeploy.AttackName} Smart Zap Drills module");
            bool zapDrill = true;

            var zap      = spells.Extract(u => u.Id == DeployId.Lightning);
            var zapCount = zap?.Sum(u => u.Count);

            if (zapCount <= 0)
            {
                Log.Warning($"{SmartFourFingersDeploy.AttackName} Smart Zap Drills No lighting Spells found for Smart Zap");
                zapDrill = false;
            }

            var drills = DarkElixirDrill.Find(CacheBehavior.ForceScan, drillLevel);

            if (drills == null)
            {
                Log.Warning("{SmartFourFingersDeploy.AttackName} Smart Zap Drills didn't found Dark Drills matches the requirements");
                zapDrill = false;
            }

            var opponent            = new Opponent(0);
            var availableDE         = opponent.GetAvailableLoot(false).DarkElixir;
            var availableDEAfterZap = 0;

            if (availableDE < deAmount)
            {
                Log.Warning($"{SmartFourFingersDeploy.AttackName} Smart Zap Drills this base only has {availableDE} DE .. it doesn't match the requirements ({deAmount})");
                zapDrill = false;
            }

            if (zapDrill)
            {
                Log.Info($"{SmartFourFingersDeploy.AttackName} Smart Zap Drills found {zap.Sum(u => u.Count)} Lighting Spell(s)");
                Log.Info($"{SmartFourFingersDeploy.AttackName} Smart Zap Drills found {drills.Count()} Dark drill(s)");

                // Zap each drill only twice beacuse (level 4 lighting will got 90% DE from max drill level)
                for (var j = 1; j <= 2; j++)
                {
                    for (var i = 0; i < drills.Count(); i++)
                    {
                        if (drills[i] != null && zapCount > 0)
                        {
                            // Get location of each drill
                            var DP = drills[i].Location.GetCenter();

                            // If we have our own lighting we will drop it first .. if we don't, use CC "beacuse IsClanSpell not working if only CC spell"
                            var zp = zap.FirstOrDefault().Count > 0 ? zap.FirstOrDefault() : zap.LastOrDefault();

                            foreach (var t in Deploy.AtPoint(zp, DP, 1))
                            {
                                yield return(t);
                            }

                            yield return(7000);

                            zapCount--;
                            if (zapCount > 0)
                            {
                                availableDEAfterZap = opponent.GetAvailableLoot(false).DarkElixir;
                                if (availableDE - availableDEAfterZap < deAmount)
                                {
                                    Log.Warning($"{SmartFourFingersDeploy.AttackName} Smart Zap Drills only {availableDE - availableDEAfterZap} DE from this drill .. you set it to {deAmount} .. will not zap it again ");
                                    drills[i] = null;
                                }
                                else
                                {
                                    Log.Info($"{SmartFourFingersDeploy.AttackName} Smart Zap Drills gain {availableDE - availableDEAfterZap} DE from this drill");
                                    availableDE = availableDEAfterZap;
                                }
                            }
                            var remDrills = DarkElixirDrill.Find(CacheBehavior.ForceScan, drillLevel);
                            if (!remDrills.Any())
                            {
                                break;
                            }
                        }

                        if (zapCount <= 0)
                        {
                            break;
                        }
                    }
                    yield return(7000);

                    var standingDrills = DarkElixirDrill.Find(CacheBehavior.ForceScan, drillLevel);
                    if (!standingDrills.Any())
                    {
                        Log.Warning($"{SmartFourFingersDeploy.AttackName} no drills to zap");
                        break;
                    }
                }
            }
        }
        public static Target[] GenerateTargets(float minimumDistance, bool ignoreGold, bool ignoreElixir, CacheBehavior behavior = CacheBehavior.Default, bool outputDebugImage = false)
        {
            // Find all Collectors & storages just sitting around...
            List <Building> buildings = new List <Building>();

            if (!ignoreGold)
            {
                //User has Gold min set to ZERO - which means Dont include Gold Targets
                buildings.AddRange(GoldMine.Find(behavior));
                buildings.AddRange(GoldStorage.Find(behavior));
            }

            if (!ignoreElixir)
            {
                //User has Elixir min set to ZERO - which means Dont include Elixir Targets
                buildings.AddRange(ElixirCollector.Find(behavior));
                buildings.AddRange(ElixirStorage.Find(behavior));
            }

            //We always includ DarkElixir - Because who doesnt love dark Elixir?
            buildings.AddRange(DarkElixirDrill.Find(behavior));
            buildings.AddRange(DarkElixirStorage.Find(behavior));

            List <Target> targetList = new List <Target>();

            foreach (Building building in buildings)
            {
                Target current = new Target();

                current.TargetBuilding  = building;
                current.Center          = building.Location.GetCenter();
                current.NearestRedLine  = GameGrid.RedPoints.OrderBy(p => p.DistanceSq(current.Center)).First();
                current.CenterToRedline = current.Center.DistanceSq(current.NearestRedLine);
                Log.Debug($"[Berts Algorithms] DistanceSq from {current.Name} to red point: {current.CenterToRedline.ToString("F1")}");
                if (current.CenterToRedline < minimumDistance)                                                                              //Compare distance to Redline to the Minimum acceptable distance Passed in
                {
                    current.DeployGrunts = current.Center.PointOnLineAwayFromEnd(current.NearestRedLine, _gruntDeployDistanceFromRedline);  //Barbs & Goblins
                    current.DeployRanged = current.Center.PointOnLineAwayFromEnd(current.NearestRedLine, _rangedDeployDistanceFromRedline); //Archers & Minions

                    targetList.Add(current);
                }
            }

            if (outputDebugImage)
            {
                var d             = DateTime.UtcNow;
                var debugFileName = $"Human Barch {d.Year}-{d.Month}-{d.Day} {d.Hour}-{d.Minute}-{d.Second}-{d.Millisecond}";
                //Get a screen Capture of all targets we found...
                using (Bitmap canvas = Screenshot.Capture())
                {
                    Screenshot.Save(canvas, $"{debugFileName}_1");

                    foreach (var building in buildings)
                    {
                        var color = Color.White;
                        if (building.GetType() == typeof(ElixirCollector) || building.GetType() == typeof(ElixirStorage))
                        {
                            color = Color.Violet;
                        }
                        if (building.GetType() == typeof(GoldMine) || building.GetType() == typeof(GoldStorage))
                        {
                            color = Color.Gold;
                        }
                        if (building.GetType() == typeof(DarkElixirDrill) || building.GetType() == typeof(DarkElixirStorage))
                        {
                            color = Color.Brown;
                        }

                        //Draw a target on each building.
                        Visualize.Target(canvas, building.Location.GetCenter(), 40, color);
                    }
                    //Save the Image to the Debug Folder...
                    Screenshot.Save(canvas, $"{debugFileName}_2");
                }

                //Get a screen Capture of all targets we found...
                using (Bitmap canvas = Screenshot.Capture())
                {
                    foreach (var target in targetList)
                    {
                        var color = Color.White;
                        if (target.TargetBuilding.GetType() == typeof(ElixirCollector) || target.TargetBuilding.GetType() == typeof(ElixirStorage))
                        {
                            color = Color.Violet;
                        }
                        if (target.TargetBuilding.GetType() == typeof(GoldMine) || target.TargetBuilding.GetType() == typeof(GoldStorage))
                        {
                            color = Color.Gold;
                        }
                        if (target.TargetBuilding.GetType() == typeof(DarkElixirDrill) || target.TargetBuilding.GetType() == typeof(DarkElixirStorage))
                        {
                            color = Color.Brown;
                        }

                        //Draw a target on each building.
                        Visualize.Target(canvas, target.TargetBuilding.Location.GetCenter(), 40, color);
                        Visualize.Target(canvas, target.DeployGrunts, 20, color);
                        Visualize.Target(canvas, target.DeployRanged, 20, color);
                    }
                    //Save the Image to the Debug Folder...
                    Screenshot.Save(canvas, $"{debugFileName}_3");
                }

                Log.Debug("[Berts Algorithms] Collector/Storage & Target Debug Images Saved!");
            }

            Log.Debug($"[Berts Algorithms] Found {targetList.Count} deploy points");

            return(targetList.ToArray());
        }