private void TryExpand(ref int armiesLeft, int maxDistanceArg, float armyMult, Dictionary <BonusIDType, float> bonusWeights) { var maxDistance = maxDistanceArg; foreach (var borderTerritory in MultiAttackStanding.Territories.Values.Where(o => Bot.IsBorderTerritory(MultiAttackStanding, o.ID)).OrderByDescending(o => o.NumArmies.NumArmies).ToList()) { if (Bot.PastTime(10)) { return; } var stackSize = Math.Max(0, MultiAttackStanding.Territories[borderTerritory.ID].NumArmies.NumArmies - Bot.Settings.OneArmyMustStandGuardOneOrZero); var canDeployOnBorderTerritory = Bot.Standing.Territories[borderTerritory.ID].OwnerPlayerID == Bot.PlayerID; if (stackSize == 0 && !canDeployOnBorderTerritory) { continue; } var bonusPaths = Bot.Map.Bonuses.Keys .Where(o => Bot.BonusValue(o) > 0) .Select(o => { if (maxDistance > 1 && Bot.PastTime(7)) { AILog.Log("MultiAttackExpand", "Due to slow speed, reducing bonus search distance from " + maxDistance + " to 1"); maxDistance = 1; //if we're taking too long, give up on far away bonuses. Otherwise this algorithm can take forever on large maps } if (Bot.PastTime(10)) { return(null); } return(MultiAttackPathToBonus.TryCreate(Bot, borderTerritory.ID, o, MultiAttackStanding, maxDistance)); }) .Where(o => o != null) .ToDictionary(o => o.BonusID, o => o); var adjustedWeights = bonusPaths.Values.ToDictionary(o => o.BonusID, o => bonusWeights[o.BonusID] - o.ArmiesNeedToKillToGetThere * armyMult); //AILog.Log("ExpandMultiAttack", "Found " + bonusPaths.Count + " bonuses in range of " + Bot.TerrString(borderTerritory.ID) + ": " + bonusPaths.Values.OrderByDescending(o => adjustedWeights[o.BonusID]).Select(o => Bot.BonusString(o.BonusID)).JoinStrings(", ")); foreach (var bonus in bonusPaths.Values.OrderByDescending(o => adjustedWeights[o.BonusID])) { var estimatedArmiesNeedToDeploy = Math.Max(0, bonus.EstArmiesNeededToCapture - stackSize); if (estimatedArmiesNeedToDeploy > armiesLeft) { continue; } if (estimatedArmiesNeedToDeploy > 0 && !canDeployOnBorderTerritory) { continue; } AILog.Log("ExpandMultiAttack", "Considering expansion to bonus " + Bot.BonusString(bonus.BonusID) + " from " + Bot.TerrString(borderTerritory.ID) + ". stackSize=" + stackSize + " estimatedArmiesNeedToDeploy=" + estimatedArmiesNeedToDeploy + " weight=" + adjustedWeights[bonus.BonusID] + " ArmiesNeedToKillToGetThere=" + bonus.ArmiesNeedToKillToGetThere + " EstArmiesNeededToCapture=" + bonus.EstArmiesNeededToCapture + " armiesLeft=" + armiesLeft + " PathToGetThere=" + bonus.PathToGetThere.Select(o => Bot.TerrString(o)).JoinStrings(" -> ")); var plan = MultiAttackPlan.TryCreate(Bot, bonus, MultiAttackStanding, borderTerritory.ID); if (plan == null) { AILog.Log("ExpandMultiAttack", " - Could not find a plan"); continue; } var actualArmiesNeedToCapture = Bot.ArmiesToTakeMultiAttack(plan.Select(o => ExpansionHelper.GuessNumberOfArmies(Bot, o.To, MultiAttackStanding, GuessOpponentNumberOfArmiesInFog))); var actualArmiesNeedToDeploy = Math.Max(0, actualArmiesNeedToCapture - stackSize); if (actualArmiesNeedToDeploy > armiesLeft) { AILog.Log("ExpandMultiAttack", " - actualArmiesNeedToDeploy=" + actualArmiesNeedToDeploy + " is not enough, have " + armiesLeft); continue; } var stackStartedFrom = this.StackStartedFrom.ContainsKey(borderTerritory.ID) ? this.StackStartedFrom[borderTerritory.ID] : borderTerritory.ID; if (!Bot.Orders.TryDeploy(stackStartedFrom, actualArmiesNeedToDeploy)) { AILog.Log("ExpandMultiAttack", " - Could not deploy armies"); continue; } armiesLeft -= actualArmiesNeedToDeploy; AILog.Log("ExpandMultiAttack", " - Attempting to capture. actualArmiesNeedToDeploy=" + actualArmiesNeedToDeploy + " plan=" + plan.Select(o => o.ToString()).JoinStrings(" -> ")); var terr = borderTerritory.ID; foreach (var planStep in plan) { Assert.Fatal(Bot.Map.Territories[terr].ConnectedTo.ContainsKey(planStep.To), terr + " does not connect to " + planStep.To); var defendersKill = SharedUtility.Round(ExpansionHelper.GuessNumberOfArmies(Bot, planStep.To).DefensePower *Bot.Settings.DefenseKillRate); if (planStep.Type == MultiAttackPlanType.MainStack) { Bot.Orders.AddAttack(terr, planStep.To, AttackTransferEnum.AttackTransfer, 100, false, true); MultiAttackStanding.Territories[planStep.To] = TerritoryStanding.Create(planStep.To, Bot.PlayerID, MultiAttackStanding.Territories[terr].NumArmies.Subtract(new Armies(defendersKill))); MultiAttackStanding.Territories[terr].NumArmies = new Armies(Bot.Settings.OneArmyMustStandGuardOneOrZero); terr = planStep.To; } else if (planStep.Type == MultiAttackPlanType.OneTerritoryOffshoot) { var attackWith = Bot.ArmiesToTake(ExpansionHelper.GuessNumberOfArmies(Bot, planStep.To)); Bot.Orders.AddAttack(terr, planStep.To, AttackTransferEnum.AttackTransfer, attackWith, false, false); MultiAttackStanding.Territories[planStep.To] = TerritoryStanding.Create(planStep.To, Bot.PlayerID, new Armies(attackWith - defendersKill)); MultiAttackStanding.Territories[terr].NumArmies = MultiAttackStanding.Territories[terr].NumArmies.Subtract(new Armies(attackWith)); } this.StackStartedFrom.Add(planStep.To, stackStartedFrom); } //After taking a bonus, we changed MultiAttackStanding. Therefore, we need to re-calc everything. TryExpand(ref armiesLeft, maxDistance, armyMult, bonusWeights); return; } } }