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); } } }