/// <summary> /// Returns an ordered list of AirDefenses sorted by distance ascending /// </summary> /// <param name="start">Initial AirDefense, fixed point for finding the nearest AirDefenses</param> /// <param name="remainingAirDefenses">List of the remaining AirDefenses</param> /// <returns></returns> List <AirDefense> OrderByDistance(AirDefense start, List <AirDefense> remainingAirDefenses) { var current = start; var remaining = remainingAirDefenses.ToList(); var path = new List <AirDefense> { start }; while (remaining.Count != 0) { var next = Closest(current, remaining); path.Add(next); remaining.Remove(next); current = next; } return(path); }
IEnumerable <int> FindAirDefenses() { airDefenses = AirDefense.Find(); //After finding air Defenses, Sort them. if (airDefenses.Length > 1) { //Now that we found all Air Defenses, order them in the array with closest AD to Target first. Array.Sort(airDefenses, delegate(AirDefense ad1, AirDefense ad2) { return(HumanLikeAlgorithms.DistanceFromPoint(ad1, mainTarget.DeployGrunts) .CompareTo(HumanLikeAlgorithms.DistanceFromPoint(ad2, mainTarget.DeployGrunts))); }); } else { Log.Error($"{Tag} Somehow no air defenses were found in Attack Phase. - Surrender."); surrender = true; yield break; } }
public static bool IsAirDefenseExposed(int distance = 7) { 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)); var ADs = AirDefense.Find().Where(c => c.Location.GetCenter() .DistanceSq(redPoints.OrderBy(p => p.DistanceSq(c.Location.GetCenter())) .FirstOrDefault()) <= distance); if (ADs.Count() > 2) { using (Bitmap bmp = Screenshot.Capture()) { var d = DateTime.UtcNow; Screenshot.Save(bmp, "Exposed Air Defense {d.Year}-{d.Month}-{d.Day} {d.Hour}-{d.Minute}-{d.Second}-{d.Millisecond}"); } Log.Warning("This base hase exposed air defenses, we will skip that base."); return(true); } return(false); }
internal static void AddArt(object Item, string SType) { using (var db = new DatabaseContext()) { switch (SType) { case "AirDefense": AirDefense tempAD = new AirDefense(); foreach (PropertyInfo x in Item.GetType().GetProperties()) { x.SetValue(tempAD, x.GetValue(Item)); } db.Artillery.Add(tempAD); break; case "Howitzers": Howitzers tempH = new Howitzers(); foreach (PropertyInfo x in Item.GetType().GetProperties()) { x.SetValue(tempH, x.GetValue(Item)); } db.Artillery.Add(tempH); break; case "Mortars": Mortars tempM = new Mortars(); foreach (PropertyInfo x in Item.GetType().GetProperties()) { x.SetValue(tempM, x.GetValue(Item)); } db.Artillery.Add(tempM); break; } db.SaveChanges(); } }
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() { #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..."); //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() { Log.Info($"{Tag} Deploy start - V.{Assembly.GetExecutingAssembly().GetName().Version.ToString()}"); // Prepare: // Find the airDefenses again. // We cannot use the airDefenses from ShouldAttack, because the bot wil move // the camera after accepting an opponent. // Then the cache gets invalidated // So we have to find the air defenses again airDefenses = AirDefense.Find(); //STEP 1 ******* Destroy all air defenses using Lightling & Quake if needed. ******* foreach (var t in DestroyAirDefenses()) { yield return(t); } //Pause after killing Air Defenses (to make it look like a person is attacking) yield return(Rand.Int(1000, 2000)); //STEP 2 ******* Deploy Dragon funnel and Main Dragon Force. ******* foreach (var t in DeployDragons()) { yield return(t); } //Pause yield return(Rand.Int(2000, 3000)); //STEP 3 ******* Deploy Lava Hounds (if any Exist). ******* foreach (var t in DeployLavaHounds()) { yield return(t); } //Pause for a little while... - Long enough for main dragons to begin to enter the base. yield return(Rand.Int(3000, 4000)); //STEP 4 ******* Deploy Ballons And/Or Hogs ******* foreach (var t in DeployBalloonsAndHogs()) { yield return(t); } //Pause a while... - Then drop the Heros. - They should start going through walls towards the center. yield return(Rand.Int(7000, 9000)); //STEP 5 ******* Deploy King (He tanks for wallbreakers a little) ******* foreach (var t in DeployKing()) { yield return(t); } //Wait for king to be targeted... yield return(Rand.Int(1000, 1200)); //STEP 6 ******* Deploy All Wallbreakers (Get the heros going inside the base) ******* foreach (var t in DeployWallBreakers()) { yield return(t); } //STEP 7 ******* Next Drop the Warden, (if we have one) ******* foreach (var t in DeployWarden()) { yield return(t); } //Pause a while... - Then drop the Queen, so she starts following the King into the base. yield return(Rand.Int(2000, 4000)); //STEP 8 ******* Drop the queen so she will follow the king in. ******* foreach (var t in DeployQueen()) { yield return(t); } //STEP 9 ******* Now that all heros have been deployed begin watching them and activate ability etc. ******* WatchHeros(); //TODO Deploy Baby Drags on the Back End - on Air D's 3 & 4' //STEP 10 ******* Deploy the Clan Castle if user settings say to ******* foreach (var t in DeployClanCastle()) { yield return(t); } //STEP 11 ******* If there is a Rage Spell, Deploy it now - Right in front of the DE Storage! ******* foreach (var t in DeployRageSpell()) { yield return(t); } //STEP 12 ******* Drop healers on the Heros if healers exist. ******* foreach (var t in DeployHealers()) { yield return(t); } //Pause for a little while longer... waiting for things to develop yield return(Rand.Int(4000, 6000)); //STEP 13 ******* Deploy Minions & Others? ******* foreach (var t in DeployOthers()) { yield return(t); } //TODO If there is a Heal Spell... Deploy it here... (This one will be harder to predict where to drop...) Meh, skipping for now. //STEP 14 ******* Use ANY other Spells at this point... (So they are ALL GONE!) ******* foreach (var t in DeployLeftoverSpells()) { yield return(t); } //STEP 15 ******* Deploy ANY troops left... (So they are ALL GONE!) ******* foreach (var t in DeployLeftoverTroops()) { yield return(t); } //At this point the attack is fully deployed... just waiting for the timer to run out, or base to be 100% destroyed. }
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); } }