/// <summary> /// Override default ZapDarlElixirDrills with SmartZap if user select that. /// </summary> /// <returns></returns> public override IEnumerable <int> ZapDarkElixirDrills() { if (!isZapped) { if (GetCurrentSetting("Smart Zap Drills") == 1) { // Call Smart zap method var minDEAmount = GetCurrentSetting("Min Dark Elixir per Zap"); var minDEDrillLevel = UserSettings.MinDarkElixirDrillLevel; var spells = Deploy.GetTroops().Extract(u => u.ElementType == DeployElementType.Spell); foreach (var t in SmartZapping.SmartZap(minDEAmount, minDEDrillLevel, spells, GetCurrentSetting("Use EarthQuake spell on drills"))) { yield return(t); } } else { // Call the default Zap drills method. foreach (var t in base.ZapDarkElixirDrills()) { yield return(t); } } } isZapped = true; }
public override IEnumerable <int> AttackRoutine() { Log.Info("[16 Fingers] Attack start"); // Get all the units available Log.Debug("Scanning troops"); var unitDeployElements = Deploy.GetTroops(); // remove spells unitDeployElements.Extract(DeployElementType.Spell); var heroesAndClanCastle = unitDeployElements.Extract(u => u.IsHero || u.Id == DeployId.ClanCastle); var unitGroups = new Dictionary <string, DeployElement[]> { { "tank units", unitDeployElements.Extract(AttackType.Tank).ToArray() }, { "attack units", unitDeployElements.Extract(AttackType.Damage).ToArray() }, { "heal units", unitDeployElements.Extract(AttackType.Heal).ToArray() }, { "wallbreak units", unitDeployElements.Extract(AttackType.Wallbreak).ToArray() }, }; PointFT left = new PointFT(PointFT.MinRedZoneX, PointFT.MaxRedZoneY); PointFT top = new PointFT(PointFT.MaxRedZoneX, PointFT.MaxRedZoneY); PointFT right = new PointFT(PointFT.MaxRedZoneX, PointFT.MinRedZoneY); PointFT bottom = new PointFT(PointFT.MinRedZoneX, PointFT.MinRedZoneY); Tuple <PointFT, PointFT>[] lines = { new Tuple <PointFT, PointFT>(left, top), new Tuple <PointFT, PointFT>(right, top), new Tuple <PointFT, PointFT>(left, bottom), new Tuple <PointFT, PointFT>(right, bottom), }; foreach (var unitGroup in unitGroups) { Logger.Info("[16 Fingers] Deploying " + unitGroup.Key); foreach (var y in Deploy.AlongLines(unitGroup.Value, lines, 4)) { yield return(y); } } Logger.Info("[16 Fingers] Deploying heroes"); var heroPoint = new Container <PointFT> { Item = new PointFT((lines[0].Item1.X + lines[0].Item2.X) / 2, (lines[0].Item1.Y + lines[0].Item2.Y) / 2) }; foreach (var delay in Deploy.AtPoint(heroesAndClanCastle.Where(d => d?.Count > 0).ToArray(), heroPoint, 1, 0, (int)(UserSettings.WaveDelay * 1000))) { yield return(delay); } // Remove clan castle before watching heroes heroesAndClanCastle.ExtractOne(x => x.ElementType == DeployElementType.ClanTroops); Deploy.WatchHeroes(heroesAndClanCastle); Logger.Info("[16 Fingers] Deploy done"); }
public override IEnumerable <int> AttackRoutine() { // Bottom right side var rightBottom = new PointFT((float)GameGrid.MaxX - 2, (float)GameGrid.DeployExtents.MinY); var bottomRight = new PointFT((float)GameGrid.MinX + 8, (float)GameGrid.DeployExtents.MinY); var center = new PointFT(bottomRight.X + 0.5f * (rightBottom.X - bottomRight.X), bottomRight.Y + 0.5f * (rightBottom.Y - bottomRight.Y)); // Screenshot for 3 points for the deployment line using (var bmp = Screenshot.Capture()) { using (var g = Graphics.FromImage(bmp)) { Visualize.RectangleT(bmp, new RectangleT((int)rightBottom.X, (int)rightBottom.Y, 1, 1), new Pen(Color.Blue)); Visualize.RectangleT(bmp, new RectangleT((int)bottomRight.X, (int)bottomRight.Y, 1, 1), new Pen(Color.Blue)); Visualize.RectangleT(bmp, new RectangleT((int)center.X, (int)center.Y, 1, 1), new Pen(Color.White)); } var d = DateTime.UtcNow; Screenshot.Save(bmp, "Bottom Right {d.Year}-{d.Month}-{d.Day} {d.Hour}-{d.Minute}-{d.Second}-{d.Millisecond}"); } // Deploy troops var units = Deploy.GetTroops(); units.OrderForDeploy(); for (var i = 3; i >= 1; i--) { switch (i) { case 3: point = rightBottom; break; case 2: point = bottomRight; break; case 1: point = center; break; } foreach (var unit in units) { if (unit?.Count > 0) { foreach (var t in Deploy.AtPoint(unit, point, unit.Count / i)) { yield return(t); } } } } }
/// <summary> /// Read troops to check if it's air attack or not /// </summary> public static void IsAirAttack() { var deployElements = Deploy.GetTroops(); var dragon = deployElements.ExtractOne(DeployId.Dragon); var babyDragon = deployElements.ExtractOne(DeployId.BabyDragon); var balloon = deployElements.ExtractOne(DeployId.Balloon); var Lava = deployElements.ExtractOne(DeployId.LavaHound); var minion = deployElements.ExtractOne(DeployId.Minion); AllInOnePushDeploy.IsAirAttack = (Lava?.Count > 0 || balloon?.Count > 5 || dragon?.Count > 5 || babyDragon?.Count > 5 || minion?.Count >= 10) ? true : false; }
public static IEnumerable <int> DeployUnusedTroops() { // Check unit bar for unused troops Log.Warning($"{AllInOnePushDeploy.AttackName} search for unused troops !!"); var unusedTroops = Deploy.GetTroops(); var spell = unusedTroops.Extract(DeployElementType.Spell); if (unusedTroops.Sum(u => u.Count) > 0) { unusedTroops.OrderForDeploy(); foreach (var u in unusedTroops) { if (u?.Count > 0) { Log.Warning($"we found {u.Count}x {u.PrettyName}"); } } Log.Info($"[{AllInOnePushDeploy.AttackName}] deploy unused troops"); foreach (var unit in unusedTroops) { if (unit?.Count > 0) { if (unit.IsRanged) { foreach (var t in Deploy.AlongLine(unit, AllInOnePushDeploy.FirstFunnellingPoint, AllInOnePushDeploy.SecondFunnellingPoint, unit.Count, 4)) { yield return(t); } } else { foreach (var t in Deploy.AtPoint(unit, AllInOnePushDeploy.Origin, unit.Count)) { yield return(t); } } } } } else { Log.Info($"[{AllInOnePushDeploy.AttackName}] all Troops have been deployed"); } }
public override IEnumerable <int> AttackRoutine() { Log.Info("[Random] Attack start"); // Get all units, regardless of type List <DeployElement> units = Deploy.GetTroops(); // Scatter them across the map Random rng = new Random(); foreach (DeployElement unit in units) { Log.Info("[Random] Deploying " + unit.PrettyName); for (int i = 0; i < 2; i++) { Input.Click(unit.Rect.GetCenter()); } while (unit.Count > 0) { Log.Debug($"Trying to deploy {unit.Count:N0} {unit.PrettyName}s"); for (int i = 0; i < unit.Count; i++) { Point p = new PointFT( (float)rng.Range(PointFT.MinRedZoneX, PointFT.MaxRedZoneX), (float)rng.Range(PointFT.MinRedZoneY, PointFT.MaxRedZoneY)) .ToScreenAbsolute(); Input.Click(p); } int deployed = unit.Recount(); Log.Debug($"Deployed {deployed:N0} {unit.PrettyName}s"); } yield return(1000); } // Watch for hero health etc. Deploy.WatchHeroes(units.Where(u => u.IsHero).ToList()); Log.Info("[Random] Deploy done"); }
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() { Log.Info($"[{AttackName}] V{Version} Deploy start"); // Get user settings ClanCastleSettings = GetCurrentSetting("use Clan Castle troops as"); QWSettings = GetCurrentSetting("Use Queen Walk"); HealerOnQWSettings = GetCurrentSetting("Number of healers to use on Queen"); RageOnQWSettings = GetCurrentSetting("Drop 1 rage in the first of the QW"); ShiftSpells = GetCurrentSetting("Shift Spells In(+) and Out(-)"); int customOrder = GetCurrentSetting("use custom deploy order"); Debug = GetCurrentSetting("Debug Mode") == 1 ? true : false; // Set core point AllInOnePushHelper.SetCore(); // Set the target var targetFromUserSettings = GetCurrentSetting("Select Your Target"); foreach (var t in AllInOnePushHelper.SeTarget(targetFromUserSettings)) { yield return(t); } // Set Origin var originPoints = new[] { new PointFT(GameGrid.DeployExtents.MaxX, Core.Y), new PointFT(GameGrid.DeployExtents.MinX, Core.Y), new PointFT(Core.X, GameGrid.DeployExtents.MaxY), new PointFT(Core.X, GameGrid.DeployExtents.MinY) }; Origin = originPoints.OrderBy(point => point.DistanceSq(Target)).First(); // Set deploy elements deployElements = Deploy.GetTroops(useCache: false); Log.Info($"[{AttackName}] Deployable Troops: " + ToUnitString(deployElements)); // Check attack type (air or ground attack) AllInOnePushHelper.IsAirAttack(); // Set deployment points AllInOnePushHelper.SetDeployPoints(); // Set custom order list if (customOrder == 1) { CustomOrderList = new List <int> { GetCurrentSetting("#1"), GetCurrentSetting("#2"), GetCurrentSetting("#3"), GetCurrentSetting("#4"), GetCurrentSetting("#5"), GetCurrentSetting("#6") }; } // Deploy troops if (!IsAirAttack) { Log.Info($"[{AttackName}] Ground attack start"); foreach (var t in TroopsDeployment.GroundAttack(customOrder)) { yield return(t); } } else { Log.Info($"[{AttackName}] Air attack start"); foreach (var t in TroopsDeployment.AirAttack(customOrder)) { yield return(t); } } yield break; }
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); } }
public override IEnumerable <int> AttackRoutine() { deployPoints = DeployHelper.GetRectPoints(15); OrderPointsClockwise(deployPoints); if (deployPoints == null) { throw new ArgumentNullException("deployPoints"); } if (deployPoints.Count == 0) { throw new ArgumentException("deployPoints must contain at least one point"); } // Get outer border to move ranges towards there if possible List <Point> outerBorderPoints = DeployHelper.GetRectPoints(15); if (_surrenderOnFirstStar) { Logger.Info("[Deploy] Bot will surrender as soon as the first star is reached to save troops (Trophy Push Mode)"); } var allDeployElements = Deploy.GetTroops(); var heroes = allDeployElements .Where(u => (UserSettings.UseKing && u.ElementType == DeployElementType.HeroKing) || (UserSettings.UseQueen && u.ElementType == DeployElementType.HeroQueen) || (UserSettings.UseWarden && u.ElementType == DeployElementType.HeroWarden)) .ToList(); int deployPointCounter = 0; int waveLimit = UserSettings.WaveSize; double waitTimeSeconds = UserSettings.WaveDelay; Random rng = new Random(); while (true) { Logger.Debug("Scan Troops"); // Scan available troops var units = Deploy.GetTroops() .Where(u => u.ElementType == DeployElementType.NormalUnit || (UserSettings.UseKing && u.ElementType == DeployElementType.HeroKing) || (UserSettings.UseQueen && u.ElementType == DeployElementType.HeroQueen) || (UserSettings.UseWarden && u.ElementType == DeployElementType.HeroWarden) || (UserSettings.UseClanTroops && u.ElementType == DeployElementType.ClanTroops)) .ToList(); // Remove king/queen ExtractHeroes(units, heroes); Logger.DebugDev("Deployable Troops: " + ToUnitString(units)); // Break if we don't have any left if (units.Count == 0 || units.All(u => u.Count == 0)) { break; } // Order units by priority // Tank > Wallbreaker > Heal > Damage > Heroes Logger.Debug("OrderTroops"); units.OrderForDeploy(); int waveCounter = 0; foreach (var u in units) { if (u == null) { Logger.Info("Wave #{0} complete, short deploy delay now...", waveCounter); yield return(rng.Next(900, 2000)); continue; } if (u.Count == 0) { continue; } // Select unit Logger.Debug("[Deploy] Deploying '{0}'", u); u.Select(); // Deploy them while (true) { int unitCount = u.Count; int totalDeployedThisWave = 0; while (unitCount > 0) { //var line = deployLines[lineCounter++ % deployLines.Length]; // //DeployHelper.ClickAlongLine(line.Item1, line.Item2, deployCount, 10); var deployCount = unitCount; Logger.Debug("Deploy Start"); for (int i = 0; i < deployCount; i++) { if (_surrenderOnFirstStar) { if (SurrenderIfWeHaveAStar()) { yield return(500); yield break; } } if (deployPointCounter >= deployPoints.Count) { deployPointCounter = 0; } Logger.Debug("deploy at point index {0} of {1}", deployPointCounter, deployPoints.Count); // We want to deploy our units evenly around those points. // Lets say we have 10 units, 100 points. // Then we deploy a unit at 0, 10, 20, ... double fraction = 1.618034; int index = (int)((deployPointCounter * fraction * deployPoints.Count) % deployPoints.Count); deployPointCounter++; var point = deployPoints[index]; // If this unit is ranged, we deploy further back if (u.IsRanged) { var borderPoint = outerBorderPoints.OrderBy(p => p.DistanceSq(point)).First(); var distance = (int)Math.Sqrt(borderPoint.DistanceSq(point)); if (distance > 10) { var maxMove = Math.Min(u.UnitData.Range * 16, distance); var dir = borderPoint.Normalize(); // Clamp the distance to the max move distance so we dont deploy too far behind borderPoint = new Point((int)(dir.Item1 * maxMove) + point.X, (int)(dir.Item2 * maxMove) + point.Y); var t = (float)rng.Range(0.85, 1.05); point = point.Lerp(borderPoint, t); } } // Modify this point a bit so its not too ovbious point.X += rng.Next(-12, 12); point.Y += rng.Next(-12, 12); Input.Click(point); totalDeployedThisWave++; if (totalDeployedThisWave >= waveLimit) { Logger.Info("WaveLimit {0} reached. Wait {1:0.0} sec.", waveLimit, waitTimeSeconds); yield return((int)(waitTimeSeconds * 1000)); totalDeployedThisWave = 0; } Thread.Sleep(10); Thread.Sleep(5); } Logger.Debug("Deploy End"); unitCount -= deployCount; } // Refresh unit count, if its really 0, break. Logger.Debug("RecountA"); int countA = u.Count; u.Recount(); int countB = u.Count; Logger.Debug("RecountB"); if (countA != countB) { Logger.Info("Recount of '{0}'. {1}->{2}", u.PrettyName, countA, countB); } if (u.Count <= 0) { Logger.Info("Unit '{0}' depleted. Break.", u.PrettyName); yield return(500); break; } } waveCounter++; } yield return(50); } if (heroes.Count > 0) { foreach (var y in DeployHeroes(heroes, deployPoints)) { if (_surrenderOnFirstStar) { if (SurrenderIfWeHaveAStar()) { break; } } yield return(y); } } Logger.Info("[Deploy] Deploy done."); }
public override IEnumerable <int> AttackRoutine() { Log.Info("[Breakthrough] Deploy start"); var funnelIds = new[] { DeployId.Archer, DeployId.Barbarian, DeployId.Minion, DeployId.Wizard }; var byLineIds = new[] { DeployId.Archer, DeployId.Barbarian, DeployId.Minion, DeployId.Wizard, DeployId.Balloon, DeployId.Dragon, DeployId.BabyDragon, DeployId.Miner }; var byPointIds = new[] { DeployId.Valkyrie, DeployId.Pekka, DeployId.Witch, DeployId.Goblin, DeployId.Bowler }; // get a list of all deployable units var deployElements = Deploy.GetTroops(); // extract spells into their own list var spells = deployElements.Extract(DeployElementType.Spell); // extract heores into their own list var heroes = deployElements.Extract(u => u.IsHero); // extract clanCastle into its own list var clanCastle = deployElements.ExtractOne(u => u.ElementType == DeployElementType.ClanTroops); // get tanks var tanks = deployElements.Extract(AttackType.Tank).ToArray(); // get wallbreakers var wallBreakers = deployElements.ExtractOne(DeployId.WallBreaker); // get healers var healers = deployElements.ExtractOne(DeployId.Healer); // get funnel troops var funnel = funnelIds.Select(id => deployElements.FirstOrDefault(u => u.Id == id)).Where(u => u != null).ToArray(); // get deploy all in a line var byLine = deployElements.Extract(byLineIds).ToArray(); // get deploy all by point var byPoint = deployElements.Extract(byPointIds).ToArray(); // get hogs var hogs = deployElements.ExtractOne(u => u.Id == DeployId.HogRider); // get heal spells var healSpells = spells.ExtractOne(u => u.Id == DeployId.Heal); // get rage spells var rageSpells = spells.ExtractOne(u => u.Id == DeployId.Rage); // user's wave delay setting var waveDelay = (int)(UserSettings.WaveDelay * 1000); // check if queen walk is an option if (heroes.Any(u => u.Id == DeployId.Queen) && healers?.Count >= 4) { var queen = heroes.ExtractOne(u => u.Id == DeployId.Queen); // get deploy points with queen walk CreateDeployPoints(true); // deploy queen walk Log.Info("[Breakthrough] Queen walk available."); Log.Info($"[Breakthrough] Deploying {queen.PrettyName}"); foreach (var t in Deploy.AtPoint(queen, _qwPoint, waveDelay: waveDelay)) { yield return(t); } var healerCount = Math.Min(healers.Count, 4); Log.Info($"[Breakthrough] Deploying {healers.PrettyName} x{healerCount}"); foreach (var t in Deploy.AtPoint(healers, _healerPoint, healerCount, waveDelay: waveDelay)) { yield return(t); } // watch queen Deploy.WatchHeroes(new List <DeployElement> { queen }); if (rageSpells?.Count > 1) { Log.Info($"[Breakthrough] Deploying {rageSpells.PrettyName} x1"); foreach (var t in Deploy.AtPoint(rageSpells, _queenRagePoint, waveDelay: waveDelay)) { yield return(t); } } // wait 15 seconds yield return(15000); } else { // get deploy points without queen walk CreateDeployPoints(false); } var funnelTank = tanks.FirstOrDefault(u => u.Id == DeployId.Giant) ?? tanks.FirstOrDefault(); // deploy four tanks if available if (funnelTank != null) { var deployCount = Math.Min(funnelTank.Count, 4); Log.Info($"[Breakthrough] Deploying {funnelTank.PrettyName} x{deployCount}"); foreach (var t in Deploy.AlongLine(funnelTank, _attackLine.Item1, _attackLine.Item2, deployCount, deployCount, waveDelay: waveDelay)) { yield return(t); } } // deploy funnel foreach (var unit in funnel.Where(u => u.Count > 0)) { var deployElementCount = Math.Min(unit.Count, UserSettings.WaveSize / unit.UnitData.HousingSpace); Log.Info($"[Breakthrough] Deploying {unit.PrettyName} x{deployElementCount}"); foreach ( var t in Deploy.AlongLine(unit, _attackLine.Item1, _attackLine.Item2, deployElementCount, 4, waveDelay: waveDelay)) { yield return(t); } } // deploy Wallbreakers while (wallBreakers?.Count > 0) { var count = wallBreakers.Count; Log.Info($"[Breakthrough] Deploying {wallBreakers.PrettyName} x3"); foreach (var t in Deploy.AtPoint(wallBreakers, _orgin, 3)) { yield return(t); } // prevent infinite loop if deploy point is on red if (wallBreakers.Count != count) { continue; } Log.Warning($"[Breakthrough] Couldn't deploy {wallBreakers.PrettyName}"); break; } // deploy the rest of the tanks while (tanks.Any(u => u.Count > 0)) { var deployError = false; foreach (var unit in tanks.Where(u => u.Count > 0)) { var count = unit.Count; Log.Info($"[Breakthrough] Deploying {unit.PrettyName} x{unit.Count}"); foreach (var t in Deploy.AtPoint(unit, _orgin, unit.Count, waveDelay: waveDelay)) { yield return(t); } // prevent infinite loop if deploy point is on red if (unit.Count != count) { continue; } Log.Warning($"[Breakthrough] Couldn't deploy {unit.PrettyName}"); deployError = true; break; } if (deployError) { break; } } if (rageSpells?.Count > 0) { Log.Info($"[Breakthrough] Deploying {rageSpells.PrettyName} x1"); foreach (var t in Deploy.AtPoint(rageSpells, _ragePoint, waveDelay: waveDelay)) { yield return(t); } } if (healSpells?.Count > 0) { Log.Info($"[Breakthrough] Deploying {healSpells.PrettyName} x1"); foreach (var t in Deploy.AtPoint(healSpells, _healPoint, waveDelay: waveDelay)) { yield return(t); } } while (byLine.Any(u => u.Count > 0)) { foreach (var unit in byLine.Where(u => u.Count > 0)) { Log.Info($"[Breakthrough] Deploying {unit.PrettyName} x{unit.Count}"); foreach ( var t in Deploy.AlongLine(unit, _attackLine.Item1, _attackLine.Item2, unit.Count, 4, waveDelay: waveDelay)) { yield return(t); } } } while (byPoint.Any(u => u.Count > 0)) { var deployError = false; foreach (var unit in byPoint.Where(u => u.Count > 0)) { var count = unit.Count; Log.Info($"[Breakthrough] Deploying {unit.PrettyName} x{unit.Count}"); foreach (var t in Deploy.AtPoint(unit, _orgin, unit.Count, waveDelay: waveDelay)) { yield return(t); } // prevent infinite loop if deploy point is on red if (unit.Count != count) { continue; } Log.Warning($"[Breakthrough] Couldn't deploy {unit.PrettyName}"); deployError = true; break; } if (deployError) { break; } } if (clanCastle?.Count > 0) { Log.Info($"[Breakthrough] Deploying {clanCastle.PrettyName}"); foreach (var t in Deploy.AtPoint(clanCastle, _orgin, waveDelay: waveDelay)) { yield return(t); } } if (heroes.Any()) { foreach (var hero in heroes.Where(u => u.Count > 0)) { Log.Info($"[Breakthrough] Deploying {hero.PrettyName}"); foreach (var t in Deploy.AtPoint(hero, _orgin, waveDelay: waveDelay)) { yield return(t); } } } if (healers?.Count > 0) { Log.Info($"[Breakthrough] Deploying {healers.PrettyName} x{healers.Count}"); foreach (var t in Deploy.AtPoint(healers, _healerPoint, healers.Count, waveDelay: waveDelay)) { yield return(t); } } if (hogs?.Count > 0) { Log.Info($"[Breakthrough] Deploying {hogs.PrettyName} x{hogs.Count}"); foreach (var t in Deploy.AtPoint(hogs, _orgin, hogs.Count, waveDelay: waveDelay)) { yield return(t); } } Deploy.WatchHeroes(heroes); // get freeze spells _freezeSpell = spells.ExtractOne(u => u.Id == DeployId.Freeze); // no freeze spells so end deployment if (!(_freezeSpell?.Count > 0)) { yield break; } // find and watch inferno towers var infernos = InfernoTower.Find(); foreach (var inferno in infernos) { inferno.FirstActivated += DropFreeze; inferno.StartWatching(); } }
public override IEnumerable <int> AttackRoutine() { int waveLimit = UserSettings.WaveSize; int waveDelay = (int)(UserSettings.WaveDelay * 1000); int heroesIndex = -1; var core = new PointFT(-0.01f, 0.01f); // Points to draw lines in deploy extends area. var topLeft = new PointFT((float)GameGrid.MaxX - 2, (float)GameGrid.DeployExtents.MaxY); var topRight = new PointFT((float)GameGrid.DeployExtents.MaxX, (float)GameGrid.MaxY - 2); var rightTop = new PointFT((float)GameGrid.DeployExtents.MaxX, (float)GameGrid.MinY + 2); var rightBottom = new PointFT((float)GameGrid.MaxX - 2, (float)GameGrid.DeployExtents.MinY); // Move 8 tiles from bottom corner due to unitsbar. var bottomLeft = new PointFT((float)GameGrid.DeployExtents.MinX, (float)GameGrid.MinY + 8); var bottomRight = new PointFT((float)GameGrid.MinX + 8, (float)GameGrid.DeployExtents.MinY); var leftTop = new PointFT((float)GameGrid.MinX + 2, (float)GameGrid.DeployExtents.MaxY); var leftBottom = new PointFT((float)GameGrid.DeployExtents.MinX, (float)GameGrid.MaxY - 2); var linesPointsList = new List <PointFT> { topLeft, topRight, rightTop, rightBottom, bottomLeft, bottomRight, leftBottom, leftTop }; // Main four lines of attack. var topRightLine = new Tuple <PointFT, PointFT>(topRight, rightTop); var bottomRightLine = new Tuple <PointFT, PointFT>(bottomRight, rightBottom); var bottomLeftLine = new Tuple <PointFT, PointFT>(bottomLeft, leftBottom); var topLeftLine = new Tuple <PointFT, PointFT>(topLeft, leftTop); // List of the four attack lines in clocwise order var attackLines = new List <Tuple <PointFT, PointFT> > { topLeftLine, topRightLine, bottomRightLine, bottomLeftLine }; var deployHeroesAt = GetCurrentSetting("Deploy Heroes At"); var target = SmartFourFingersHelper.GetHeroesTarget(deployHeroesAt); var nearestRedPointToTarget = GameGrid.RedPoints.OrderBy(p => p.DistanceSq(target)).FirstOrDefault(); var nearestLinePoint = linesPointsList.OrderBy(p => p.DistanceSq(nearestRedPointToTarget)).FirstOrDefault(); heroesIndex = attackLines.FindIndex(u => (u.Item1.X == nearestLinePoint.X && u.Item1.Y == nearestLinePoint.Y) || (u.Item2.X == nearestLinePoint.X && u.Item2.Y == nearestLinePoint.Y)); var units = Deploy.GetTroops(); var heroes = units.Extract(x => x.IsHero); var cc = units.ExtractOne(u => u.ElementType == DeployElementType.ClanTroops); var spells = units.Extract(u => u.ElementType == DeployElementType.Spell); units.OrderForDeploy(); // Set first attack line // Start from the next line to user defined to end with user defined line var line = attackLines.NextOf(attackLines[heroesIndex]); var index = attackLines.FindIndex(u => u.Item1.X == line.Item1.X && u.Item1.Y == line.Item1.Y); Log.Info($"{AttackName} {Version} starts"); // Start troops deployment on four sides. for (var i = 4; i >= 1; i--) { foreach (var unit in units) { if (unit?.Count > 0) { var count = unit.Count / i; var fingers = count % 4 <= 1 ? count : 4; foreach (var t in Deploy.AlongLine(unit, line.Item1, line.Item2, count, fingers, 0, waveDelay)) { yield return(t); } } } if (i != 1) { line = attackLines.NextOf(attackLines[index]); index = attackLines.FindIndex(u => u.Item1.X == line.Item1.X && u.Item1.Y == line.Item1.Y); } } if (cc?.Count > 0) { Log.Info($"{AttackName} Deploy Clan Castle troops"); foreach (var t in Deploy.AlongLine(cc, line.Item1, line.Item2, 1, 1, 0, waveDelay)) { yield return(t); } } if (heroes.Any()) { Log.Info($"{AttackName} Deploy Heroes"); foreach (var hero in heroes.Where(u => u.Count > 0)) { foreach (var t in Deploy.AlongLine(hero, line.Item1, line.Item2, 1, 1, 0, waveDelay)) { yield return(t); } } Deploy.WatchHeroes(heroes, 5000); } var minDEDrillLevel = GetCurrentSetting("Min Drill Level"); // start smart zap if (GetCurrentSetting("Smart Zap Drills") == 1) { var waitBeforeSmartZap = GetCurrentSetting("Start Zap Drills After ?(sec)") * 1000; var minDEAmount = GetCurrentSetting("Min Dark Elixir per Zap"); yield return(waitBeforeSmartZap); foreach (var t in SmartZapping.SmartZap(minDEAmount, minDEDrillLevel, spells)) { yield return(t); } } // start Use EarthQuake spell on drills if (GetCurrentSetting("Use EarthQuake spell on drills") == 1) { foreach (var t in SmartZapping.UseEQOnDrills(minDEDrillLevel, spells)) { yield return(t); } } // end battle var endBattleTime = GetCurrentSetting("End Battle after zap ?(sec)"); foreach (var t in SmartZapping.EndBattle(endBattleTime)) { yield return(t); } }
public override double ShouldAccept() { if (!PassesBasicAcceptRequirements()) { return(0); } //TODO - Check which kind of army we have trained. Calculate an Air Offense Score, and Ground Offense Score. //TODO - Find all Base Defenses, and calculate an AIR and Ground Defensive Score. //TODO - From Collector/Storage fill levels, determine if loot is in Collectors, or Storages... (Will help to decide which alg to use.) //Verify that the Attacking Army contains at least 6 Dragons. deployElements = Deploy.GetTroops(); var dragons = deployElements.FirstOrDefault(u => u.Id == DeployId.Dragon); if (dragons == null || dragons?.Count < 6) { Log.Error($"{Tag} Army not correct! - Dark Dragon Deploy Requires at least 6 Dragons to function Properly. (You have {dragons?.Count ?? 0} dragons)"); return(0); } //Verify that there are enough spells to take out at least ONE air defense. 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(); var lightningCount = lightningSpells?.Count ?? 0; var earthquakeCount = 0; //Get a count of all earthquake spells... donated, or brewed... foreach (var spell in earthquakeSpells.Where(s => s.Count > 0)) { earthquakeCount += spell.Count; } if (lightningCount < 2 || lightningCount < 3 && earthquakeCount < 1) { //We dont have the Spells to take out the Closest Air Defense... Surrender before we drop any Dragons! Log.Error($"{Tag} We don't have enough spells to take out at least 1 air defense... Lightning Spells:{lightningCount}, Earthquake Spells:{earthquakeCount}"); return(0); } if (deployElements.Count >= 11) { //Possibly Too Many Deployment Elements! Bot Doesnt Scroll - Change Army Composition to have less than 12 unit types! Log.Warning($"{Tag} Warning! Full Army! - The Bot does not scroll through choices when deploying units... If your army has more than 11 unit types, The bot will not see them all, and cannot deploy everything!)"); } //Write out all the unit pretty names we found... Log.Debug($"{Tag} Deployable Troops: {ToUnitString(deployElements)}"); Log.Info($"{Tag} Base meets minimum Requirements... Checking DE Storage/Air Defense Locations..."); //Grab the Locations of the DE Storage darkElixirStorage = HumanLikeAlgorithms.TargetDarkElixirStorage(); if (!darkElixirStorage.ValidTarget) { Log.Warning($"{Tag} No Dark Elixir Storage Found - Skipping"); return(0); } //Get the locaiton of all Air Defenses var airDefensesTest = AirDefense.Find(); if (airDefensesTest.Length == 0) { Log.Warning($"{Tag} Could not find ANY air defenses - Skipping"); return(0); } Log.Info($"{Tag} Found {airDefensesTest.Length} Air Defense Buildings.. Continuing Attack.."); if (airDefensesTest.Length > 1) { //Now that we found all Air Defenses, order them in the array with closest AD to Target first. Array.Sort(airDefensesTest, delegate(AirDefense ad1, AirDefense ad2) { return(HumanLikeAlgorithms.DistanceFromPoint(ad1, darkElixirStorage.DeployGrunts) .CompareTo(HumanLikeAlgorithms.DistanceFromPoint(ad2, darkElixirStorage.DeployGrunts))); }); } //Create the Funnel Points deFunnelPoints = darkElixirStorage.GetFunnelingPoints(30); balloonFunnelPoints = darkElixirStorage.GetFunnelingPoints(20); #if DEBUG //During Debug, Create an Image of the base including what we found. CreateDebugImages(); #endif //We are Good to attack! return(1); }
public override IEnumerable <int> AttackRoutine() { var allDeployElements = Deploy.GetTroops(); List <Point> redPoints = new List <Point>(); foreach (var s in AttackHelper.FindRedPoints(redPoints)) { yield return(s); } var leftCorner = DeployHelper.DeployPointALeft; var topCorner = DeployHelper.DeployPointATop; double tankPaddingFraction = 0.35; var tankDeployA = leftCorner.Lerp(topCorner, tankPaddingFraction); var tankDeployB = leftCorner.Lerp(topCorner, 1 - tankPaddingFraction); double wallBreakerFraction = 0.43; var wallBreakDeployA = leftCorner.Lerp(topCorner, wallBreakerFraction); var wallBreakDeployB = leftCorner.Lerp(topCorner, 1 - wallBreakerFraction); double damageFraction = 0.15; var damageDeployPointA = leftCorner.Lerp(topCorner, damageFraction); var damageDeployPointB = leftCorner.Lerp(topCorner, 1 - damageFraction); // We want to drag our deploy points to the closest redline point // For that we get the vector from left to top, then we get its orthogonal vector (the one of both that points more towards the center) // then we can order the redline points. we want the one whos direction is closest to the orthogonal vector. var vecLeftTop = topCorner.Subtract(leftCorner).Normalize(); var ortho = GetOrthogonalDirection(tankDeployA, new Point((int)(vecLeftTop.Item1 * 30), (int)(vecLeftTop.Item2 * 30)), new PointF(0.5f, 0.5f).ToAbsolute()); tankDeployA = GetBestRedlinePoint(redPoints, tankDeployA, ortho); tankDeployB = GetBestRedlinePoint(redPoints, tankDeployB, ortho); wallBreakDeployA = GetBestRedlinePoint(redPoints, wallBreakDeployA, ortho); wallBreakDeployB = GetBestRedlinePoint(redPoints, wallBreakDeployB, ortho); var attackLine = DeployHelper.GetPointsForLine(damageDeployPointA, damageDeployPointB, 30).ToArray(); for (int i = 0; i < attackLine.Length; i++) { attackLine[i] = GetBestRedlinePoint(redPoints, attackLine[i], ortho); } attackLine = attackLine.Distinct().ToArray(); var validUnits = allDeployElements.Where(u => u.UnitData != null).ToArray(); var clanTroops = allDeployElements.FirstOrDefault(u => u.ElementType == DeployElementType.ClanTroops); //Func<DeployElement, bool> isAir = e => e.UnitData.UnitType == UnitType.Air; Func <DeployElement, bool> isBalloon = e => e.UnitData.NameSimple.Contains("balloon"); Func <DeployElement, bool> isMinion = e => e.UnitData.NameSimple.Contains("minion"); var tankUnits = validUnits.Where(u => u.UnitData.AttackType == AttackType.Tank).ToArray(); var wallBreakerUnits = validUnits.Where(u => u.UnitData.AttackType == AttackType.Wallbreak).ToArray(); var attackUnits = validUnits.Where(u => u.UnitData.AttackType == AttackType.Damage && !isMinion(u)).ToArray(); if (clanTroops != null && UserSettings.UseClanTroops) { attackUnits = attackUnits.Concat(new[] { clanTroops }).ToArray(); } var healUnits = validUnits.Where(u => u.UnitData.AttackType == AttackType.Heal).ToArray(); var balloonUnits = validUnits.Where(isBalloon).ToArray(); var minionUnits = validUnits.Where(isMinion).ToArray(); // Deploy tank units if (tankUnits.Any()) { Logger.Debug($"{tankUnits.Length} tanking element{(tankUnits.Length > 1 ? "s" : "")} available to deploy."); foreach (var s in DeployUnits(tankUnits, new[] { tankDeployA, tankDeployB }, 20)) { yield return(s); } yield return(2000); } // Deploy wallbreakers if (tankUnits.Any()) { Logger.Debug("Wallbreakers available to deploy."); foreach (var s in DeployUnits(wallBreakerUnits, new[] { wallBreakDeployA, wallBreakDeployB }, 40)) { yield return(s); } yield return(1000); } // Check whether we got an air troopset and decide to perform an air attack or not var balloonCount = balloonUnits.Sum(i => i.Count); if (balloonCount > 10) { // Ok, we have an air troopset, so we will deploy the air units first according to different deploy rules. attackUnits = attackUnits.Where(u => !isBalloon(u)).ToArray(); int spotCount = (int)Math.Ceiling(balloonCount / 4.0); // We want to make x spots where balloons are deployed var airPoints = DeployHelper.GetPointsForLine(damageDeployPointA, damageDeployPointB, spotCount); for (int i = 0; i < airPoints.Count; i++) { airPoints[i] = GetBestRedlinePoint(redPoints, airPoints[i], ortho); } airPoints = airPoints.Distinct().ToList(); // Deploy those air units foreach (var s in DeployUnits(balloonUnits, airPoints.ToArray(), firstCycleYield: 1000)) { yield return(s); } } // Deploy atackers if (attackUnits.Any()) { Logger.Debug($"{attackUnits.Length} attacking element{(attackUnits.Length > 1 ? "s" : "")} available to deploy."); foreach (var s in DeployUnits(attackUnits, attackLine, 0, 5, 2500)) { yield return(s); } yield return(500); } // Minions if (minionUnits.Any()) { foreach (var s in DeployUnits(minionUnits, attackLine)) { yield return(s); } } // Deploy healers foreach (var s in DeployUnits(healUnits, attackLine)) { yield return(s); } // Deploy heroes var heroes = allDeployElements .Where(u => (UserSettings.UseKing && u.ElementType == DeployElementType.HeroKing) || (UserSettings.UseQueen && u.ElementType == DeployElementType.HeroQueen) || (UserSettings.UseWarden && u.ElementType == DeployElementType.HeroWarden)) .ToList(); if (heroes.Count > 0) { foreach (var y in DeployHeroes(heroes, attackLine)) { yield return(y); } } }
public override IEnumerable <int> AttackRoutine() { List <Point> deployPoints = new List <Point>(); Logger.Info("[Attack-DeadBase] Now deploying main attack wave."); var analysisPoints = new List <Point>(); var mineRects = new List <Rectangle>(); var fiber = new Fiber <int>(DeployHelper.GenerateDeployPointsFromMines(analysisPoints, RedPoints, mineRects)); while (fiber.Run()) { yield return(1); } deployPoints.AddRange(analysisPoints); // Debug: show deploy points using (var frame = Screenshot.Capture(true)) { using (var g = Graphics.FromImage(frame)) { foreach (var p in RedPoints) { g.RectWithOutline(p.ToRectangle(2, 2), Color.Red); } foreach (var m in mineRects) { g.RectWithOutline(m, Color.DarkOrange); } foreach (var p in deployPoints) { g.RectWithOutline(p.ToRectangle(4, 4), Color.White); } foreach (var m in mineRects) { var closure = m; foreach (var closest in deployPoints.OrderBy(p => p.DistanceSq(closure.GetCenter())).Take(2)) { g.DrawLine(Pens.Red, m.GetCenter(), closest); } } } if (UserSettings.SaveAttackAnalysisImage) { Screenshot.Save(frame, $"AttackAnalysis_Dead {deployPoints.Count} points {RedPoints.Count} red"); } if (UserSettings.DisplayAttackAnalysisImage) { Screenshot.Show(frame); } } _deployPoints = deployPoints.ToArray(); if (_deployPoints == null) { throw new ArgumentNullException("deployPoints"); } if (_deployPoints.Length == 0) { throw new ArgumentException("deployPoints must contain at least one point"); } // Get outer border to move ranges towards there if possible List <Point> outerBorderPoints = DeployHelper.GetRectPoints(15); if (surrenderOnFirstStar) { Logger.Info("[Deploy] Bot will surrender as soon as the first star is reached to save troops (Trophy Push Mode)"); } var allDeployElements = Deploy.GetTroops(); var heroes = allDeployElements .Where(u => (UserSettings.UseKing && u.ElementType == DeployElementType.HeroKing) || (UserSettings.UseQueen && u.ElementType == DeployElementType.HeroQueen) || (UserSettings.UseWarden && u.ElementType == DeployElementType.HeroWarden)) .ToList(); int deployPointCounter = 0; int waveLimit = UserSettings.WaveSize; double waitTimeSeconds = UserSettings.WaveDelay; Random rng = new Random(); while (true) { Logger.Debug("Scan Troops"); // Scan available troops var units = Deploy.GetTroops() .Where(u => u.ElementType == DeployElementType.NormalUnit || (UserSettings.UseKing && u.ElementType == DeployElementType.HeroKing) || (UserSettings.UseQueen && u.ElementType == DeployElementType.HeroQueen) || (UserSettings.UseWarden && u.ElementType == DeployElementType.HeroWarden) || (UserSettings.UseClanTroops && u.ElementType == DeployElementType.ClanTroops)) .ToList(); // Remove king/queen ExtractHeroes(units, heroes); Logger.DebugDev("Deployable Troops: " + ToUnitString(units)); // Break if we don't have any left if (units.Count == 0 || units.All(u => u.Count == 0)) { break; } // Order units by priority // Tank > Wallbreaker > Heal > Damage > Heroes Logger.Debug("OrderTroops"); units.OrderForDeploy(); int waveCounter = 0; foreach (var u in units) { if (u == null) { Logger.Info("Wave #{0} complete, short deploy delay now...", waveCounter); yield return(rng.Next(900, 2000)); continue; } if (u.Count == 0) { continue; } // Select unit Logger.Debug("[Deploy] Deploying '{0}'", u); u.Select(); // Deploy them while (true) { int unitCount = u.Count; int totalDeployedThisWave = 0; while (unitCount > 0) { //var line = deployLines[lineCounter++ % deployLines.Length]; // //DeployHelper.ClickAlongLine(line.Item1, line.Item2, deployCount, 10); var deployCount = Math.Min(u.Count, 4); Logger.Debug("Deploy Start"); for (int i = 0; i < deployCount; i++) { if (surrenderOnFirstStar) { if (SurrenderIfWeHaveAStar()) { yield return(500); yield break; } } if (deployPointCounter >= _deployPoints.Length) { deployPointCounter = 0; } Logger.Debug("deploy at point index {0} of {1}", deployPointCounter, _deployPoints.Length); var point = _deployPoints[deployPointCounter++]; // If this unit is ranged, we deploy further back if (u.IsRanged) { var borderPoint = outerBorderPoints.OrderBy(p => p.DistanceSq(point)).First(); var distance = (int)Math.Sqrt(borderPoint.DistanceSq(point)); if (distance > 10) { var maxMove = Math.Min(u.UnitData.Range * 16, distance); var dir = borderPoint.Normalize(); // Clamp the distance to the max move distance so we dont deploy too far behind borderPoint = new Point((int)(dir.Item1 * maxMove) + point.X, (int)(dir.Item2 * maxMove) + point.Y); var t = (float)rng.Range(0.85, 1.05); point = point.Lerp(borderPoint, t); } } // Modify this point a bit so its not too ovbious point.X += rng.Next(-12, 12); point.Y += rng.Next(-12, 12); Input.Click(point); totalDeployedThisWave++; if (totalDeployedThisWave >= waveLimit) { Logger.Info("WaveLimit {0} reached. Wait {1:0.0} sec.", waveLimit, waitTimeSeconds); yield return((int)(waitTimeSeconds * 1000)); totalDeployedThisWave = 0; } Thread.Sleep(10); Thread.Sleep(5); } Logger.Debug("Deploy End"); unitCount -= deployCount; } // Refresh unit count, if its really 0, break. Logger.Debug("RecountA"); int countA = u.Count; u.Recount(); int countB = u.Count; Logger.Debug("RecountB"); if (countA != countB) { Logger.Info("Recount of '{0}'. {1}->{2}", u.PrettyName, countA, countB); } if (u.Count <= 0) { Logger.Info("Unit '{0}' depleted. Break.", u.PrettyName); yield return(500); break; } } waveCounter++; } yield return(50); } if (heroes.Count > 0) { foreach (var y in DeployHeroes(heroes, _deployPoints)) { if (surrenderOnFirstStar) { if (SurrenderIfWeHaveAStar()) { break; } } yield return(y); } } Logger.Info("[Deploy] Deploy done."); }
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"); }
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); } }
public override double ShouldAccept() { if (!PassesBasicAcceptRequirements()) { return(0); } //TODO - Check which kind of army we have trained. Calculate an Air Offense Score, and Ground Offense Score. //TODO - Find all Base Defenses, and calculate an AIR and Ground Defensive Score. //TODO - From Collector/Storage fill levels, determine if loot is in Collectors, or Storages... (Will help to decide which alg to use.) //Verify that the Attacking Army contains at least 6 Dragons. deployElements = Deploy.GetTroops(); var dragons = deployElements.FirstOrDefault(u => u.Id == DeployId.Dragon); if (dragons == null || dragons?.Count < 6) { Log.Error($"{Tag} Army not correct! - Dark Dragon Deploy Requires at least 6 Dragons to function Properly. (You have {dragons?.Count ?? 0} dragons)"); return(0); } //Verify that there are enough spells to take out at least ONE air defense. 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(); var lightningCount = lightningSpells?.Count ?? 0; var earthquakeCount = 0; //Get a count of all earthquake spells... donated, or brewed... foreach (var spell in earthquakeSpells.Where(s => s.Count > 0)) { earthquakeCount += spell.Count; } if (lightningCount < 2 || lightningCount < 3 && earthquakeCount < 1) { //We dont have the Spells to take out the Closest Air Defense... Surrender before we drop any Dragons! Log.Error($"{Tag} We don't have enough spells to take out at least 1 air defense... Lightning Spells:{lightningCount}, Earthquake Spells:{earthquakeCount}"); return(0); } if (deployElements.Count >= 11) { //Possibly Too Many Deployment Elements! Bot Doesnt Scroll - Change Army Composition to have less than 12 unit types! Log.Warning($"{Tag} Warning! Full Army! - The Bot does not scroll through choices when deploying units... If your army has more than 11 unit types, The bot will not see them all, and cannot deploy everything!)"); } //Write out all the unit pretty names we found... Log.Debug($"{Tag} Deployable Troops: {ToUnitString(deployElements)}"); Log.Info($"{Tag} Base meets minimum Requirements... Checking DE Storage/Air Defense Locations..."); //Check to see if we can find ANY air Defenses... (Could Skip here if not all are found.) var airDefensesTest = AirDefense.Find(); if (airDefensesTest.Length == 0) { Log.Warning($"{Tag} Could not find ANY air defenses - Skipping"); return(0); } //For now just log the Trophy counts... //TODO - Later we will want to use these to see if the base is worth attacking when we are in Trophy mode... try { int trophiesWin, trophiesDefeat = -1; if (Opponent.GetLootableTrophies(out trophiesWin, out trophiesDefeat)) { Log.Info($"Trophies if we Win: {trophiesWin}, Trophies if we lose: {trophiesDefeat}"); } } catch (Exception ex) { Log.Error($"Error getting trophy values... - {ex.Message} - {ex.StackTrace}"); } Log.Info($"{Tag} Found {airDefensesTest.Length} Air Defense Buildings.. Continuing Attack.."); //We are Good to attack! return(1); }
public override IEnumerable <int> AttackRoutine() { var DE = DarkElixirStorage.Find()?.FirstOrDefault()?.Location.GetCenter(); //set the target if (DE == null) { for (var i = 1; i <= 3; i++) { Log.Warning($"bot didn't found the DE Storage .. we will attemp search NO. {i + 1}"); yield return(1000); DE = DarkElixirStorage.Find()?.FirstOrDefault()?.Location.GetCenter(); if (DE != null) { Log.Warning($"DE Storage found after {i + 1} retries"); break; } } } if (DE != null) { _target = (PointFT)DE; } else { Log.Debug("[Goblin Knife] coundn't locate the target after aligning the base"); Log.Error("Couldn't find DE Storage we will return home"); Surrender(); yield break; } CreateDeployPoints(); Log.Info($"[Goblin Knife] V{Version} Deploy start"); //get troops var deployElements = Deploy.GetTroops(); var clanCastle = deployElements.ExtractOne(u => u.ElementType == DeployElementType.ClanTroops && UserSettings.UseClanTroops); var earthQuakeSpell = deployElements.Extract(u => u.Id == DeployId.Earthquake); var jumpSpell = deployElements.ExtractOne(DeployId.Jump); var giant = deployElements.ExtractOne(DeployId.Giant); var goblin = deployElements.ExtractOne(DeployId.Goblin); var wizard = deployElements.ExtractOne(DeployId.Wizard); var barbarian = deployElements.ExtractOne(DeployId.Barbarian); var archer = deployElements.ExtractOne(DeployId.Archer); var wallbreaker = deployElements.ExtractOne(DeployId.WallBreaker); var ragespell = deployElements.ExtractOne(DeployId.Rage); var healspell = deployElements.ExtractOne(DeployId.Heal); _freezeSpell = deployElements.ExtractOne(DeployId.Freeze); var heroes = deployElements .Extract(u => (UserSettings.UseKing && u.ElementType == DeployElementType.HeroKing) || (UserSettings.UseQueen && u.ElementType == DeployElementType.HeroQueen) || (UserSettings.UseWarden && u.ElementType == DeployElementType.HeroWarden)) .ToList(); //open near to dark elixer with 4 earthquakes if (earthQuakeSpell?.Sum(u => u.Count) >= 4) { foreach (var unit in earthQuakeSpell) { foreach (int t in Deploy.AtPoint(unit, _earthQuakePoint, unit.Count)) { yield return(t); } } } else { _useJump = true; } yield return(1000); if (giant?.Count > 0) { foreach (int t in Deploy.AlongLine(giant, _attackLine.Item1, _attackLine.Item2, 6, 6)) { yield return(t); } } yield return(1000); if (wizard?.Count > 0) { foreach (int t in Deploy.AlongLine(wizard, _attackLine.Item1, _attackLine.Item2, 8, 4)) { yield return(t); } } if (barbarian?.Count > 0) { while (barbarian.Count > 0) { int count = barbarian.Count; Log.Info($"[Goblin Knife] Deploying {barbarian.PrettyName}"); foreach (int t in Deploy.AlongLine(barbarian, _attackLine.Item1, _attackLine.Item2, count, 4)) { yield return(t); } // prevent infinite loop if deploy point is on red if (barbarian.Count != count) { continue; } Log.Warning($"[Goblin Knife] Couldn't deploy {barbarian.PrettyName}"); break; } } if (archer?.Count > 0) { int archerCount = (int)(archer.Count / 2); Log.Info($"[Goblin Knife] Deploying {archer.PrettyName} "); foreach (int t in Deploy.AlongLine(archer, _attackLine.Item1, _attackLine.Item2, archerCount, 4)) { yield return(t); } } yield return(3000); if (ragespell?.Count >= 2) { foreach (int t in Deploy.AtPoint(ragespell, _ragePoint)) { yield return(t); } } if (wallbreaker?.Count > 0) { Log.Info($"[Goblin Knife] send test {wallbreaker.PrettyName} to check for bombs"); foreach (int t in Deploy.AtPoint(wallbreaker, _orgin, 1)) { yield return(t); } } yield return(1000); while (wallbreaker?.Count > 0) { int count = wallbreaker.Count; Log.Info("[Goblin Knife] send wall breakers in groups"); foreach (int t in Deploy.AtPoint(wallbreaker, _orgin, 3)) { yield return(t); } // prevent infinite loop if deploy point is on red if (wallbreaker.Count != count) { continue; } Log.Warning($"[Goblin Knife] Couldn't deploy {wallbreaker.PrettyName}"); break; } while (giant?.Count > 0) { int count = giant.Count; Log.Info($"[Goblin Knife] Deploying {giant.PrettyName} x{count}"); foreach (int t in Deploy.AtPoint(giant, _orgin, count)) { yield return(t); } // prevent infinite loop if deploy point is on red if (giant.Count != count) { continue; } Log.Warning($"[Goblin Knife] Couldn't deploy {giant.PrettyName}"); break; } yield return(1000); while (wizard?.Count > 0) { int count = wizard.Count; Log.Info($"[Goblin Knife] Deploying {wizard}"); foreach (int t in Deploy.AlongLine(wizard, _attackLine.Item1, _attackLine.Item2, 4, 2)) { yield return(t); } // prevent infinite loop if deploy point is on red if (wizard.Count != count) { continue; } Log.Warning($"[Goblin Knife] Couldn't deploy {wizard.PrettyName}"); break; } if (archer?.Count > 0) { Log.Info($"[Goblin Knife] Deploying {archer.PrettyName} "); foreach (int t in Deploy.AlongLine(archer, _attackLine.Item1, _attackLine.Item2, archer.Count, 4)) { yield return(t); } } yield return(1500); if (_useJump == true && jumpSpell?.Count > 0) { foreach (int t in Deploy.AtPoint(jumpSpell, _jumpPoint)) { yield return(t); } } if (healspell?.Count > 0) { foreach (int t in Deploy.AtPoint(healspell, _healPoint)) { yield return(t); } } if (clanCastle?.Count > 0) { Log.Info($"[Goblin Knife] Deploying {clanCastle.PrettyName}"); foreach (int t in Deploy.AtPoint(clanCastle, _orgin)) { yield return(t); } } if (heroes.Any()) { _delay = 1000; foreach (DeployElement hero in heroes.Where(u => u.Count > 0)) { foreach (int t in Deploy.AtPoint(hero, _orgin)) { yield return(t); } } watchHeroes = true; } yield return(_delay); while (goblin?.Count > 0) { int testGoblins = (int)(goblin.Count / 3); Log.Info($"[Goblin Knife] Deploying {goblin.PrettyName} x{testGoblins}"); foreach (int t in Deploy.AtPoint(goblin, _orgin, testGoblins)) { yield return(t); } yield return(2000); int count = goblin.Count; Log.Info($"[Goblin Knife] Deploying {goblin.PrettyName} x{count}"); foreach (int t in Deploy.AtPoint(goblin, _orgin, count)) { yield return(t); } // prevent infinite loop if deploy point is on red if (goblin.Count != count) { continue; } Log.Warning($"[Goblin Knife] Couldn't deploy {goblin.PrettyName}"); break; } //use freeze if inferno is found if (_freezeSpell?.Count > 0) { var infernos = InfernoTower.Find(); // find and watch inferno towers if (infernos != null) { foreach (var inferno in infernos) { inferno.FirstActivated += DropFreeze; inferno.StartWatching(); } } } yield return(200); foreach (int t in Deploy.AtPoint(healspell, _target)) { yield return(t); } foreach (int t in Deploy.AtPoint(ragespell, _target)) { yield return(t); } if (watchHeroes == true) { Deploy.WatchHeroes(heroes, 7000); } }
public override IEnumerable <int> AttackRoutine() { // Set start battle time. startTime = DateTime.Now; int waveLimit = UserSettings.WaveSize; int waveDelay = (int)(UserSettings.WaveDelay * 1000); int heroesIndex = -1; // Set the core point of the base SmartFourFingersHelper.SetCore(); if (!IsTargetsCalculated) { SmartFourFingersHelper.CalculateTargets(); } // Points to draw lines in deploy extends area. var topLeft = new PointFT((float)GameGrid.MaxX - 2, (float)GameGrid.DeployExtents.MaxY); var topRight = new PointFT((float)GameGrid.DeployExtents.MaxX, (float)GameGrid.MaxY - 2); var rightTop = new PointFT((float)GameGrid.DeployExtents.MaxX, (float)GameGrid.MinY + 2); var rightBottom = new PointFT((float)GameGrid.MaxX - 2, (float)GameGrid.DeployExtents.MinY); // Move 8 tiles from bottom corner due to unitsbar. var bottomLeft = new PointFT((float)GameGrid.DeployExtents.MinX, (float)GameGrid.MinY + 8); var bottomRight = new PointFT((float)GameGrid.MinX + 8, (float)GameGrid.DeployExtents.MinY); var leftTop = new PointFT((float)GameGrid.MinX + 2, (float)GameGrid.DeployExtents.MaxY); var leftBottom = new PointFT((float)GameGrid.DeployExtents.MinX, (float)GameGrid.MaxY - 2); var linesPointsList = new List <PointFT> { topLeft, topRight, rightTop, rightBottom, bottomLeft, bottomRight, leftBottom, leftTop }; // Main four lines of attack. var topRightLine = new Tuple <PointFT, PointFT>(topRight, rightTop); var bottomRightLine = new Tuple <PointFT, PointFT>(bottomRight, rightBottom); var bottomLeftLine = new Tuple <PointFT, PointFT>(bottomLeft, leftBottom); var topLeftLine = new Tuple <PointFT, PointFT>(topLeft, leftTop); // List of the four attack lines in clocwise order AttackLines = new List <Tuple <PointFT, PointFT> > { topRightLine, bottomRightLine, bottomLeftLine, topLeftLine }; var deployHeroesAt = GetCurrentSetting("Deploy Heroes At"); var target = SmartFourFingersHelper.GetHeroesTarget(deployHeroesAt); // Search for target if not found for 3 more times if (target.X == 0f && target.Y == 0f) { for (var i = 1; i <= 3; i++) { yield return(1000); target = SmartFourFingersHelper.GetHeroesTarget(deployHeroesAt); if (target.X != 0f || target.Y != 0f) { break; } } } var nearestRedPointToTarget = GameGrid.RedPoints.OrderBy(p => p.DistanceSq(target)).FirstOrDefault(); var nearestLinePoint = linesPointsList.OrderBy(p => p.DistanceSq(nearestRedPointToTarget)).FirstOrDefault(); heroesIndex = AttackLines.FindIndex(u => (u.Item1.X == nearestLinePoint.X && u.Item1.Y == nearestLinePoint.Y) || (u.Item2.X == nearestLinePoint.X && u.Item2.Y == nearestLinePoint.Y)); var units = Deploy.GetTroops(); var heroes = units.Extract(x => x.IsHero); var cc = units.ExtractOne(u => u.ElementType == DeployElementType.ClanTroops); var spells = units.Extract(u => u.ElementType == DeployElementType.Spell); units.OrderForDeploy(); // Set first attack line // Start from the next line to user defined to end with user defined line var line = AttackLines.NextOf(AttackLines[heroesIndex]); var index = AttackLines.FindIndex(u => u.Item1.X == line.Item1.X && u.Item1.Y == line.Item1.Y); var deploymentMode = GetCurrentSetting("Set Troops Deployment"); if (deploymentMode == 2) { AddFakeTargets(); } var targetsCount = TargetsAtLine[index]; Log.Info($"{AttackName} {Version} starts"); var deployCC = GetCurrentSetting("CC Deployment"); if (deployCC == 0 && cc?.Count > 0) { Log.Info($"{AttackName} Deploy Clan Castle troops first"); foreach (var t in Deploy.AlongLine(cc, line.Item1, line.Item2, 1, 1, 0, waveDelay)) { yield return(t); } } // Start troops deployment on four sides. for (var i = 4; i >= 1; i--) { Log.Info($"Targets at this side = {targetsCount} targets from total {TotalTargetsCount} targets"); foreach (var unit in units) { if (unit?.Count > 0) { var count = deploymentMode == 0 ? unit.Count / i : (TotalTargetsCount > 0 ? unit.Count * targetsCount / TotalTargetsCount : 0); var housing = unit.UnitData.HousingSpace; var fingers = housing < 4 ? (count < 8 ? count : 4) : 1; Log.Info($"{AttackName} Deploy {count}x {unit.PrettyName}"); foreach (var t in Deploy.AlongLine(unit, line.Item1, line.Item2, count, fingers, 0, waveDelay)) { yield return(t); } } } if (i != 1) { line = AttackLines.NextOf(AttackLines[index]); index = AttackLines.FindIndex(u => u.Item1.X == line.Item1.X && u.Item1.Y == line.Item1.Y); TotalTargetsCount -= targetsCount; targetsCount = TargetsAtLine[index]; } } if (deployCC == 1 && cc?.Count > 0) { Log.Info($"{AttackName} Deploy Clan Castle troops"); foreach (var t in Deploy.AlongLine(cc, line.Item1, line.Item2, 1, 1, 0, waveDelay)) { yield return(t); } } if (heroes.Any()) { Log.Info($"{AttackName} Deploy Heroes"); foreach (var hero in heroes.Where(u => u.Count > 0)) { foreach (var t in Deploy.AlongLine(hero, line.Item1, line.Item2, 1, 1, 0, waveDelay)) { yield return(t); } } Deploy.WatchHeroes(heroes, 5000); } // Call FinalizeAttack and ForceZap at the same time var zapSpells = Deploy.GetTroops().Extract(u => u.ElementType == DeployElementType.Spell); if (spells.Extract(u => u.Id == DeployId.Lightning).Sum(u => u.Count) > 0 || spells.Extract(u => u.Id == DeployId.Earthquake).Sum(u => u.Count) > 0) { var finalize = this.FinalizeAttack(units).GetEnumerator(); var force = ForceZap().GetEnumerator(); var firstEnumMoreItems = finalize.MoveNext(); var secondEnumMoreItems = force.MoveNext(); // Start both FinalizeAttack and ForceZap while (firstEnumMoreItems && secondEnumMoreItems) { firstEnumMoreItems = finalize.MoveNext(); secondEnumMoreItems = force.MoveNext(); yield return(200); } // Complete ForceZap if FinalizeAttack finished while (!firstEnumMoreItems && secondEnumMoreItems) { secondEnumMoreItems = force.MoveNext(); yield return(200); } // Complete FinalizeAttack if ForceZap finished while (!secondEnumMoreItems && firstEnumMoreItems) { firstEnumMoreItems = finalize.MoveNext(); yield return(200); } } else { yield break; } }
/// <summary> /// Set deploy points for troops and spells /// </summary> public static void SetDeployPoints() { var GreenPoints = AllInOnePushHelper.GenerateGreenPoints(); 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)) .OrderBy(point => Math.Atan2(point.X, point.Y)).ToArray(); AllPoints = GreenPoints.Concat(redPoints).ToArray(); // Top right side var topRight = AllPoints.OrderBy(p => p.DistanceSq(new PointFT((float)GameGrid.DeployExtents.MaxX, (float)GameGrid.MaxY - 2))).First(); var rightTop = AllPoints.OrderBy(p => p.DistanceSq(new PointFT((float)GameGrid.DeployExtents.MaxX, (float)GameGrid.MinY + 2))).First(); // Bottom right side var rightBottom = AllPoints.OrderBy(p => p.DistanceSq(new PointFT((float)GameGrid.MaxX - 5, (float)GameGrid.DeployExtents.MinY - 2))).First(); var bottomRight = AllPoints.OrderBy(p => p.DistanceSq(new PointFT((float)GameGrid.MinX + 10, (float)GameGrid.DeployExtents.MinY - 2))).First(); // Bottom left side // Move 8 tiles from bottom corner due to unitsbar. var bottomLeft = AllPoints.OrderBy(p => p.DistanceSq(new PointFT((float)GameGrid.DeployExtents.MinX, (float)GameGrid.MinY + 8))).First(); var leftBottom = AllPoints.OrderBy(p => p.DistanceSq(new PointFT((float)GameGrid.DeployExtents.MinX, (float)GameGrid.MaxY - 2))).First(); // Top Left side var leftTop = AllPoints.OrderBy(p => p.DistanceSq(new PointFT((float)GameGrid.MinX + 2, (float)GameGrid.DeployExtents.MaxY))).First(); var topLeft = AllPoints.OrderBy(p => p.DistanceSq(new PointFT((float)GameGrid.MaxX - 2, (float)GameGrid.DeployExtents.MaxY))).First(); var isJumpSpell = Deploy.GetTroops().ExtractOne(DeployId.Jump)?.Count > 0 ? true : false; if (AllInOnePushDeploy.Origin.X > AllInOnePushDeploy.Core.X) { Log.Info($"[{AllInOnePushDeploy.AttackName}] Attacking from the top right"); AllInOnePushDeploy.AttackLine = new Tuple <PointFT, PointFT>(topRight, rightTop); var distance = Math.Abs(AllInOnePushDeploy.Origin.X) - Math.Abs(AllInOnePushDeploy.Target.X); var target = distance >= AllInOnePushDeploy.MinDistace ? AllInOnePushDeploy.Target : AllInOnePushDeploy.Core; var firstWall = GetFirstWallForJump(AllInOnePushDeploy.Origin.Y, "Y"); AllInOnePushDeploy.FirstJumpPoint = new PointFT(firstWall.X - 2.75f, AllInOnePushDeploy.Core.Y); var maxX = isJumpSpell ? AllInOnePushDeploy.FirstJumpPoint.X - 5f : firstWall.X - 1.5f; var start = target.X + 4f; var earthQuakePoints = new List <PointFT> { new PointFT(AllInOnePushDeploy.Target.X + 6f, AllInOnePushDeploy.Core.Y) }; var jumpPoints = new List <PointFT> { new PointFT(AllInOnePushDeploy.Target.X + 5.5f, AllInOnePushDeploy.Core.Y) }; if (GetWallsInsideSpell(earthQuakePoints[0], 4f) < 8) { while (maxX > start) { earthQuakePoints.Add(new PointFT(start, AllInOnePushDeploy.Core.Y)); jumpPoints.Add(new PointFT(start - 0.5f, AllInOnePushDeploy.Core.Y)); start += 0.25f; } } AllInOnePushDeploy.EqPoint = earthQuakePoints.OrderByDescending(e => GetWallsInsideSpell(e)).FirstOrDefault(); // Prevent overlaping EQ with jump if (isJumpSpell && AllInOnePushDeploy.FirstJumpPoint.X - AllInOnePushDeploy.EqPoint.X < 7f) { AllInOnePushDeploy.EqPoint = new PointFT(AllInOnePushDeploy.FirstJumpPoint.X - 7f, AllInOnePushDeploy.FirstJumpPoint.Y); } AllInOnePushDeploy.SecondJumpPoint = new PointFT(AllInOnePushDeploy.EqPoint.X - 0.5f, AllInOnePushDeploy.EqPoint.Y); var shiftSpells = AllInOnePushDeploy.ShiftSpells; AllInOnePushDeploy.FirstRagePoint = new PointFT(AllInOnePushDeploy.Origin.X - 11f - shiftSpells, AllInOnePushDeploy.Core.Y); AllInOnePushDeploy.FirstHealPoint = new PointFT(AllInOnePushDeploy.Origin.X - 17f - shiftSpells, AllInOnePushDeploy.Core.Y); AllInOnePushDeploy.SecondRagePoint = new PointFT(AllInOnePushDeploy.Origin.X - 22f - shiftSpells, AllInOnePushDeploy.Core.Y); AllInOnePushDeploy.FirstHastePoint = new PointFT(AllInOnePushDeploy.Origin.X - 26f - shiftSpells, AllInOnePushDeploy.Core.Y); //try to find better funneling points var frac = 0.75f; AllInOnePushDeploy.FirstFunnellingPoint = new PointFT(AllInOnePushDeploy.Origin.X + frac * (AllInOnePushDeploy.AttackLine.Item1.X - AllInOnePushDeploy.Origin.X), AllInOnePushDeploy.Origin.Y + frac * (AllInOnePushDeploy.AttackLine.Item1.Y - AllInOnePushDeploy.Origin.Y)); AllInOnePushDeploy.SecondFunnellingPoint = new PointFT(AllInOnePushDeploy.Origin.X + frac * (AllInOnePushDeploy.AttackLine.Item2.X - AllInOnePushDeploy.Origin.X), AllInOnePushDeploy.Origin.Y + frac * (AllInOnePushDeploy.AttackLine.Item2.Y - AllInOnePushDeploy.Origin.Y)); AllInOnePushDeploy.FirstHasteLine = new Tuple <PointFT, PointFT> ( new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X - 11f - shiftSpells, AllInOnePushDeploy.FirstFunnellingPoint.Y), new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X - 11f - shiftSpells, AllInOnePushDeploy.SecondFunnellingPoint.Y) ); AllInOnePushDeploy.FirstRageLine = new Tuple <PointFT, PointFT> ( new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X - 19f - shiftSpells, AllInOnePushDeploy.FirstFunnellingPoint.Y), new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X - 19f - shiftSpells, AllInOnePushDeploy.SecondFunnellingPoint.Y) ); AllInOnePushDeploy.SecondHasteLine = new Tuple <PointFT, PointFT> ( new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X - 24f - shiftSpells, AllInOnePushDeploy.FirstFunnellingPoint.Y), new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X - 24f - shiftSpells, AllInOnePushDeploy.SecondFunnellingPoint.Y) ); AllInOnePushDeploy.SecondRageLine = new Tuple <PointFT, PointFT> ( new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X - 24f - shiftSpells, AllInOnePushDeploy.FirstFunnellingPoint.Y), new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X - 24f - shiftSpells, AllInOnePushDeploy.SecondFunnellingPoint.Y) ); AllInOnePushDeploy.QWHealer = new PointFT(GameGrid.DeployExtents.MaxX, AllInOnePushDeploy.FirstFunnellingPoint.Y); AllInOnePushDeploy.QWRagePoint = new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X - 3, AllInOnePushDeploy.FirstFunnellingPoint.Y - 1); AllInOnePushDeploy.SecondFunnellingRagePoint = new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X - 3, AllInOnePushDeploy.SecondFunnellingPoint.Y + 1); } else if (AllInOnePushDeploy.Origin.X < AllInOnePushDeploy.Core.X) { Log.Info($"[{AllInOnePushDeploy.AttackName}] Attacking from the bottom left"); AllInOnePushDeploy.AttackLine = new Tuple <PointFT, PointFT>(leftBottom, bottomLeft); var distance = Math.Abs(AllInOnePushDeploy.Origin.X) - Math.Abs(AllInOnePushDeploy.Target.X); var target = distance >= AllInOnePushDeploy.MinDistace ? AllInOnePushDeploy.Target : AllInOnePushDeploy.Core; var firstWall = GetFirstWallForJump(AllInOnePushDeploy.Origin.Y, "Y"); AllInOnePushDeploy.FirstJumpPoint = new PointFT(firstWall.X + 2.75f, AllInOnePushDeploy.Core.Y); var maxX = isJumpSpell ? AllInOnePushDeploy.FirstJumpPoint.X + 5f : firstWall.X + 1.5f; var start = target.X - 4f; var earthQuakePoints = new List <PointFT> { new PointFT(AllInOnePushDeploy.Target.X - 6f, AllInOnePushDeploy.Core.Y) }; var jumpPoints = new List <PointFT> { new PointFT(AllInOnePushDeploy.Target.X - 5.5f, AllInOnePushDeploy.Core.Y) }; if (GetWallsInsideSpell(earthQuakePoints[0], 4f) < 8) { while (maxX < start) { earthQuakePoints.Add(new PointFT(start, AllInOnePushDeploy.Core.Y)); jumpPoints.Add(new PointFT(start + 0.5f, AllInOnePushDeploy.Core.Y)); start -= 0.25f; } } AllInOnePushDeploy.EqPoint = earthQuakePoints.OrderByDescending(e => GetWallsInsideSpell(e)).FirstOrDefault(); // Prevent overlaping EQ with jump if (isJumpSpell && Math.Abs(AllInOnePushDeploy.FirstJumpPoint.X - AllInOnePushDeploy.EqPoint.X) < 7f) { AllInOnePushDeploy.EqPoint = new PointFT(AllInOnePushDeploy.FirstJumpPoint.X + 7f, AllInOnePushDeploy.FirstJumpPoint.Y); } AllInOnePushDeploy.SecondJumpPoint = new PointFT(AllInOnePushDeploy.EqPoint.X + 0.5f, AllInOnePushDeploy.EqPoint.Y); var shiftSpells = AllInOnePushDeploy.ShiftSpells; AllInOnePushDeploy.FirstRagePoint = new PointFT(AllInOnePushDeploy.Origin.X + 11f + shiftSpells, AllInOnePushDeploy.Core.Y); AllInOnePushDeploy.FirstHealPoint = new PointFT(AllInOnePushDeploy.Origin.X + 17f + shiftSpells, AllInOnePushDeploy.Core.Y); AllInOnePushDeploy.SecondRagePoint = new PointFT(AllInOnePushDeploy.Origin.X + 22f + shiftSpells, AllInOnePushDeploy.Core.Y); AllInOnePushDeploy.FirstHastePoint = new PointFT(AllInOnePushDeploy.Origin.X + 26f + shiftSpells, AllInOnePushDeploy.Core.Y); //try to find better funneling points var frac = 0.75f; AllInOnePushDeploy.FirstFunnellingPoint = new PointFT(AllInOnePushDeploy.Origin.X + frac * (AllInOnePushDeploy.AttackLine.Item1.X - AllInOnePushDeploy.Origin.X), AllInOnePushDeploy.Origin.Y + frac * (AllInOnePushDeploy.AttackLine.Item1.Y - AllInOnePushDeploy.Origin.Y)); AllInOnePushDeploy.SecondFunnellingPoint = bottomLeft; AllInOnePushDeploy.FirstHasteLine = new Tuple <PointFT, PointFT> ( new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X + 11f + shiftSpells, AllInOnePushDeploy.FirstFunnellingPoint.Y), new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X + 11f + shiftSpells, AllInOnePushDeploy.SecondFunnellingPoint.Y) ); AllInOnePushDeploy.FirstRageLine = new Tuple <PointFT, PointFT> ( new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X + 19f + shiftSpells, AllInOnePushDeploy.FirstFunnellingPoint.Y), new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X + 19f + shiftSpells, AllInOnePushDeploy.SecondFunnellingPoint.Y) ); AllInOnePushDeploy.SecondHasteLine = new Tuple <PointFT, PointFT> ( new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X + 24f + shiftSpells, AllInOnePushDeploy.FirstFunnellingPoint.Y), new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X + 24f + shiftSpells, AllInOnePushDeploy.SecondFunnellingPoint.Y) ); AllInOnePushDeploy.SecondRageLine = new Tuple <PointFT, PointFT> ( new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X + 24f + shiftSpells, AllInOnePushDeploy.FirstFunnellingPoint.Y), new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X + 24f + shiftSpells, AllInOnePushDeploy.SecondFunnellingPoint.Y) ); AllInOnePushDeploy.QWHealer = new PointFT(GameGrid.DeployExtents.MinX, AllInOnePushDeploy.FirstFunnellingPoint.Y); AllInOnePushDeploy.QWRagePoint = new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X + 3, AllInOnePushDeploy.FirstFunnellingPoint.Y - 1); AllInOnePushDeploy.SecondFunnellingRagePoint = new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X + 3, AllInOnePushDeploy.SecondFunnellingPoint.Y + 1); } else if (AllInOnePushDeploy.Origin.Y > AllInOnePushDeploy.Core.Y) { Log.Info($"[{AllInOnePushDeploy.AttackName}] Attacking from the top left"); AllInOnePushDeploy.AttackLine = new Tuple <PointFT, PointFT>(leftTop, topLeft); var distance = Math.Abs(AllInOnePushDeploy.Origin.Y) - Math.Abs(AllInOnePushDeploy.Target.Y); var target = distance >= AllInOnePushDeploy.MinDistace ? AllInOnePushDeploy.Target : AllInOnePushDeploy.Core; var firstWall = GetFirstWallForJump(AllInOnePushDeploy.Origin.X, "X"); AllInOnePushDeploy.FirstJumpPoint = new PointFT(AllInOnePushDeploy.Core.X, firstWall.Y - 2.75f); var maxX = isJumpSpell ? AllInOnePushDeploy.FirstJumpPoint.Y - 5f : firstWall.Y - 1.5f; var start = target.Y + 4f; var earthQuakePoints = new List <PointFT> { new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.Target.Y + 6f) }; var jumpPoints = new List <PointFT> { new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.Target.Y + 5.5f) }; if (GetWallsInsideSpell(earthQuakePoints[0], 4f) < 8) { while (maxX > start) { earthQuakePoints.Add(new PointFT(AllInOnePushDeploy.Core.X, start)); jumpPoints.Add(new PointFT(AllInOnePushDeploy.Core.X, start - 0.5f)); start += 0.25f; } } AllInOnePushDeploy.EqPoint = earthQuakePoints.OrderByDescending(e => GetWallsInsideSpell(e)).FirstOrDefault(); // Prevent overlaping EQ with jump if (isJumpSpell && AllInOnePushDeploy.FirstJumpPoint.Y - AllInOnePushDeploy.EqPoint.Y < 7f) { AllInOnePushDeploy.EqPoint = new PointFT(AllInOnePushDeploy.FirstJumpPoint.X, AllInOnePushDeploy.FirstJumpPoint.Y - 7f); } AllInOnePushDeploy.SecondJumpPoint = new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.EqPoint.Y - 0.5f); var shiftSpells = AllInOnePushDeploy.ShiftSpells; AllInOnePushDeploy.FirstRagePoint = new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.Origin.Y - 11f - shiftSpells); AllInOnePushDeploy.FirstHealPoint = new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.Origin.Y - 17f - shiftSpells); AllInOnePushDeploy.SecondRagePoint = new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.Origin.Y - 22f - shiftSpells); AllInOnePushDeploy.FirstHastePoint = new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.Origin.Y - 26f - shiftSpells); //try to find better funneling points var frac = 0.75f; AllInOnePushDeploy.FirstFunnellingPoint = new PointFT(AllInOnePushDeploy.Origin.X + frac * (AllInOnePushDeploy.AttackLine.Item1.X - AllInOnePushDeploy.Origin.X), AllInOnePushDeploy.Origin.Y + frac * (AllInOnePushDeploy.AttackLine.Item1.Y - AllInOnePushDeploy.Origin.Y)); AllInOnePushDeploy.SecondFunnellingPoint = new PointFT(AllInOnePushDeploy.Origin.X + frac * (AllInOnePushDeploy.AttackLine.Item2.X - AllInOnePushDeploy.Origin.X), AllInOnePushDeploy.Origin.Y + frac * (AllInOnePushDeploy.AttackLine.Item2.Y - AllInOnePushDeploy.Origin.Y)); AllInOnePushDeploy.FirstHasteLine = new Tuple <PointFT, PointFT> ( new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X, AllInOnePushDeploy.FirstFunnellingPoint.Y - 11f - shiftSpells), new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X, AllInOnePushDeploy.SecondFunnellingPoint.Y - 11f - shiftSpells) ); AllInOnePushDeploy.FirstRageLine = new Tuple <PointFT, PointFT> ( new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X, AllInOnePushDeploy.FirstFunnellingPoint.Y - 19f - shiftSpells), new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X, AllInOnePushDeploy.SecondFunnellingPoint.Y - 19f - shiftSpells) ); AllInOnePushDeploy.SecondHasteLine = new Tuple <PointFT, PointFT> ( new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X, AllInOnePushDeploy.FirstFunnellingPoint.Y - 24f - shiftSpells), new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X, AllInOnePushDeploy.SecondFunnellingPoint.Y - 24f - shiftSpells) ); AllInOnePushDeploy.SecondRageLine = new Tuple <PointFT, PointFT> ( new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X, AllInOnePushDeploy.FirstFunnellingPoint.Y - 24f - shiftSpells), new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X, AllInOnePushDeploy.SecondFunnellingPoint.Y - 24f - shiftSpells) ); AllInOnePushDeploy.QWHealer = new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X, GameGrid.DeployExtents.MaxY); AllInOnePushDeploy.QWRagePoint = new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X + 1, AllInOnePushDeploy.FirstFunnellingPoint.Y - 3); AllInOnePushDeploy.SecondFunnellingRagePoint = new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X - 1, AllInOnePushDeploy.SecondFunnellingPoint.Y - 3); } else // (orgin.Y < core.Y) { Log.Info($"[{AllInOnePushDeploy.AttackName}] Attacking from the bottom right"); // Avoid bottom right side until fix zoom out on attack progress issue var avoidBottomRight = AllInOnePushDeploy.AvoidBottomRight; if (avoidBottomRight == 1) { var originPoints = new[] { new PointFT(GameGrid.DeployExtents.MaxX, AllInOnePushDeploy.Core.Y), new PointFT(GameGrid.DeployExtents.MinX, AllInOnePushDeploy.Core.Y), new PointFT(AllInOnePushDeploy.Core.X, GameGrid.DeployExtents.MaxY), new PointFT(AllInOnePushDeploy.Core.X, GameGrid.DeployExtents.MinY) }; AllInOnePushDeploy.Origin = originPoints.OrderBy(point => point.DistanceSq(AllInOnePushDeploy.Target)).ElementAt(1); Log.Warning($"Avoid bottom right side set to true, We will attack from next closest side to the target"); SetDeployPoints(); } else { AllInOnePushDeploy.AttackLine = new Tuple <PointFT, PointFT>(rightBottom, bottomRight); var distance = Math.Abs(AllInOnePushDeploy.Origin.Y) - Math.Abs(AllInOnePushDeploy.Target.Y); var target = distance >= AllInOnePushDeploy.MinDistace ? AllInOnePushDeploy.Target : AllInOnePushDeploy.Core; var firstWall = GetFirstWallForJump(AllInOnePushDeploy.Origin.X, "X"); AllInOnePushDeploy.FirstJumpPoint = new PointFT(AllInOnePushDeploy.Core.X, firstWall.Y + 2.75f); var maxX = isJumpSpell ? AllInOnePushDeploy.FirstJumpPoint.Y + 5f : firstWall.Y + 1.5f; var start = target.Y - 4f; var earthQuakePoints = new List <PointFT> { new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.Target.Y - 6f) }; var jumpPoints = new List <PointFT> { new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.Target.Y - 5.5f) }; if (GetWallsInsideSpell(earthQuakePoints[0], 4f) < 8) { while (maxX < start) { earthQuakePoints.Add(new PointFT(AllInOnePushDeploy.Core.X, start)); jumpPoints.Add(new PointFT(AllInOnePushDeploy.Core.X, start + 0.5f)); start -= 0.25f; } } AllInOnePushDeploy.EqPoint = earthQuakePoints.OrderByDescending(e => GetWallsInsideSpell(e)).FirstOrDefault(); // Prevent overlaping EQ with jump if (isJumpSpell && Math.Abs(AllInOnePushDeploy.FirstJumpPoint.Y - AllInOnePushDeploy.EqPoint.Y) < 7f) { AllInOnePushDeploy.EqPoint = new PointFT(AllInOnePushDeploy.FirstJumpPoint.X, AllInOnePushDeploy.FirstJumpPoint.Y + 7f); } AllInOnePushDeploy.SecondJumpPoint = new PointFT(AllInOnePushDeploy.EqPoint.X, AllInOnePushDeploy.EqPoint.Y + 0.5f); var shiftSpells = AllInOnePushDeploy.ShiftSpells; AllInOnePushDeploy.FirstRagePoint = new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.Origin.Y + 11f + shiftSpells); AllInOnePushDeploy.FirstHealPoint = new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.Origin.Y + 17f + shiftSpells); AllInOnePushDeploy.SecondRagePoint = new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.Origin.Y + 22f + shiftSpells); AllInOnePushDeploy.FirstHastePoint = new PointFT(AllInOnePushDeploy.Core.X, AllInOnePushDeploy.Origin.Y + 26f + shiftSpells); // Set Funneling point AllInOnePushDeploy.FirstFunnellingPoint = rightBottom; AllInOnePushDeploy.SecondFunnellingPoint = bottomRight; AllInOnePushDeploy.FirstHasteLine = new Tuple <PointFT, PointFT> ( new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X, AllInOnePushDeploy.FirstFunnellingPoint.Y + 11f + shiftSpells), new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X, AllInOnePushDeploy.SecondFunnellingPoint.Y + 11f + shiftSpells) ); AllInOnePushDeploy.FirstRageLine = new Tuple <PointFT, PointFT> ( new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X, AllInOnePushDeploy.FirstFunnellingPoint.Y + 19f + shiftSpells), new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X, AllInOnePushDeploy.SecondFunnellingPoint.Y + 19f + shiftSpells) ); AllInOnePushDeploy.SecondHasteLine = new Tuple <PointFT, PointFT> ( new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X, AllInOnePushDeploy.FirstFunnellingPoint.Y + 24f + shiftSpells), new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X, AllInOnePushDeploy.SecondFunnellingPoint.Y + 24f + shiftSpells) ); AllInOnePushDeploy.SecondRageLine = new Tuple <PointFT, PointFT> ( new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X, AllInOnePushDeploy.FirstFunnellingPoint.Y + 24f + shiftSpells), new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X, AllInOnePushDeploy.SecondFunnellingPoint.Y + 24f + shiftSpells) ); AllInOnePushDeploy.QWHealer = new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X, GameGrid.DeployExtents.MinY); AllInOnePushDeploy.QWRagePoint = new PointFT(AllInOnePushDeploy.FirstFunnellingPoint.X - 1, AllInOnePushDeploy.FirstFunnellingPoint.Y + 3); AllInOnePushDeploy.SecondFunnellingRagePoint = new PointFT(AllInOnePushDeploy.SecondFunnellingPoint.X + 1, AllInOnePushDeploy.SecondFunnellingPoint.Y + 3); } } AllInOnePushDeploy.SecondJumpPoint = AllInOnePushDeploy.EqPoint; AllInOnePushDeploy.FirstFunnellingPoint = AllPoints.OrderBy(p => p.DistanceSq(AllInOnePushDeploy.FirstFunnellingPoint)).First(); AllInOnePushDeploy.SecondFunnellingPoint = AllPoints.OrderBy(p => p.DistanceSq(AllInOnePushDeploy.SecondFunnellingPoint)).First(); AllInOnePushDeploy.Origin = AllPoints.OrderBy(p => p.DistanceSq(AllInOnePushDeploy.Origin)).First(); DebugBottomRightSidePoints(); }
public override IEnumerable <int> AttackRoutine() { Log.Debug("[Snipe Deploy] Getting the best townhall deploy point."); var snipePoint = new Container <PointFT> { Item = GetTownHallDeployPoint() }; Log.Debug($"[Snipe Deploy] Deploy point is ({snipePoint.Item})"); var troops = Deploy.GetTroops(); var hero = troops.GetQueen() ?? troops.GetKing() ?? troops.GetWarden(); if (hero != null) { Log.Info("[Snipe Deploy] Deploying the " + hero.PrettyName + " to snipe."); foreach (var t in Deploy.AtPoint(hero, snipePoint)) { yield return(t); } hero.Recount(); if (hero.Count > 0) { var th = TownHall.Find(); using (var bmp = Screenshot.Capture(true)) { Visualize.RectangleT(bmp, th.Location); Visualize.RectangleT(bmp, new RectangleT((int)snipePoint.Item.X, (int)snipePoint.Item.Y, 1, 1)); var d = DateTime.UtcNow; Screenshot.Save(bmp, $"Snipe Deploy_{d.Year}-{d.Month}-{d.Day}_{d.Hour}-{d.Minute}-{d.Second}"); } } yield return(5000); Deploy.WatchHeroes(new List <DeployElement> { hero }); var countdown = new Countdown(15.0); if (hero.Count < 1) { while (countdown.IsRunning) { if (Attack.SurrenderIfWeHaveAStar()) { yield break; } else { yield return(200); } } } else { Log.Info("[Snipe Deploy] Hero failed to deploy; trying to use troops"); } } var snipeTroops = troops.GetByAttackType(AttackType.Damage) .GetByType(DeployElementType.NormalUnit) .Where(u => u.UnitData?.TargetType == TargetType.Loot || u.UnitData?.TargetType == TargetType.None) .ToArray(); if (snipeTroops.Length > 0) { Log.Info("[Snipe Deploy] Deploying troops to snipe."); var countdown = new Countdown(15.0, true); var pt = new Container <PointFT> { Item = GetTownHallDeployPoint() }; while (true) { if (countdown.IsFinished) { foreach (var t in Deploy.AtPoint(snipeTroops, snipePoint)) { yield return(t); } snipeTroops.Recount(); if (snipeTroops.All(u => u.Count < 1)) { yield break; } countdown.Restart(); } if (Attack.SurrenderIfWeHaveAStar()) { yield break; } } } }
public override IEnumerable <int> AttackRoutine() { #if DEBUG debugMode = true; #endif var visuals = new List <VisualObject>(); // get a list of all deployable units var deployElements = Deploy.GetTroops(); Log.Debug("[Debug] 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 heores into their own list var heroes = deployElements.Extract(u => u.IsHero); // extract clanCastle into its own list var clanCastle = deployElements.ExtractOne(u => u.ElementType == DeployElementType.ClanTroops); // 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); #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 (!debugMode) { if (zapQuakeTargets?.Count > 0) { foreach (var t in ZapQuakeAirDefenses(zapQuakeTargets, earthQuakeSpells, lightningSpells)) { yield return(t); } } else { Log.Warning("[Smart Air] Couldn't find AirDefense targets for ZapQuaking!"); } } } else { Log.Info("[Smart Air] 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("[Smart Air] Rescanning AirDefenses to check for remaining AirDefenses"); AirDefense.Find(CacheBehavior.CheckForDestroyed); } #endregion var airDefenses = AirDefense.Find(); var lavaLoonDeployPoints = CreateLavaHoundDeployPoints(airDefenses, visuals); if (debugMode) { VisualizeDeployment(visuals); } }