/// <summary> /// Runs the commander away from opponents /// </summary> private static void DoCommander(BotMain bot, TerritoryStanding cmdrTerritory, Commander cmdr) { var directive = CommanderDirective(bot, cmdrTerritory.ID); if (directive.HasValue) { AILog.Log("SpecialUnits", "Directive directs us to move the commander from " + bot.TerrString(cmdrTerritory.ID) + " to " + bot.TerrString(directive.Value)); if (directive.Value != cmdrTerritory.ID) { bot.Orders.AddAttack(cmdrTerritory.ID, directive.Value, AttackTransferEnum.AttackTransfer, cmdrTerritory.NumArmies.NumArmies, false, commanders: true); } bot.AvoidTerritories.Add(cmdrTerritory.ID); //add this so we don't deploy there, we want the commander to stay alone return; } var powerDiff = bot.Map.Territories[cmdrTerritory.ID].ConnectedTo.Keys .Select(o => bot.Standing.Territories[o]) .Where(o => bot.IsOpponent(o.OwnerPlayerID) && o.NumArmies.Fogged == false) .Sum(o => o.NumArmies.AttackPower) - cmdrTerritory.NumArmies.DefensePower; var toDeploy = Math.Max(0, powerDiff); if (powerDiff > 0) { if (bot.UseRandomness) { toDeploy = SharedUtility.Round(toDeploy * RandomUtility.BellRandom(0.5, 1.5)); } if (toDeploy > bot.MakeOrders.IncomeTracker.RemainingUndeployed) { toDeploy = bot.MakeOrders.IncomeTracker.RemainingUndeployed; } if (toDeploy > 0 && bot.Orders.TryDeploy(cmdrTerritory.ID, toDeploy)) { AILog.Log("SpecialUnits", "Deployed " + toDeploy + " to defend commander"); } } //Consider this territory and all adjacent territories. Which is the furthest from any enemy? var terrDistances = bot.Map.Territories[cmdrTerritory.ID].ConnectedTo.Keys.ConcatOne(cmdrTerritory.ID) .Where(o => bot.Standing.Territories[o].OwnerPlayerID == bot.PlayerID || bot.Standing.Territories[o].NumArmies.DefensePower <= 4) //don't go somewhere that's defended heavily .ToDictionary(o => o, o => bot.DistanceFromEnemy(o)); AILog.Log("SpecialUnits", "Commander run options: " + terrDistances.Select(o => bot.TerrString(o.Key) + " dist=" + o.Value).JoinStrings(", ")); var sorted = terrDistances.OrderByDescending(o => o.Value).ToList(); sorted.RemoveWhere(o => o.Value < sorted[0].Value); var runTo = bot.UseRandomness ? sorted.Random().Key : sorted[0].Key; if (runTo == cmdrTerritory.ID) { return; //already there } AILog.Log("SpecialUnits", "Moving commander from " + bot.TerrString(cmdrTerritory.ID) + " to " + bot.TerrString(runTo)); bot.Orders.AddAttack(cmdrTerritory.ID, runTo, AttackTransferEnum.AttackTransfer, cmdrTerritory.NumArmies.NumArmies + toDeploy, false, commanders: true); }
public MultiMoves CalculateMoveArmiesToBorderMoves(MultiMoves presentMoves) { MultiMoves result = presentMoves.Clone(); var nonOwnedTerritories = MapInformer.GetNonOwnedTerritories(result.GetTerritoryStandingsAfterAllMoves().Values.ToList(), GameState.MyPlayerId); var keys = new List <TerritoryIDType>(); nonOwnedTerritories.ForEach(k => keys.Add(k.ID)); var distances = MapInformer.GetDistancesFromTerritories(keys); foreach (TerritoryIDType territoryId in distances.Keys) { int distance = distances[territoryId]; if (distance <= 1) { continue; } var neighbors = MapInformer.GetNeighborTerritories(territoryId); var lowestDistanceNeighborValue = neighbors.Min(c => distances[c]); var lowestDistanceNeighbor = neighbors.Where(n => distances[n] == lowestDistanceNeighborValue).First(); TerritoryStanding theTerritory = result.GetTerritoryStandingsAfterAllMoves()[territoryId]; int armiesAvailable = theTerritory.NumArmies.ArmiesOrZero - theTerritory.ArmiesMarkedAsUsed.ArmiesOrZero - 1; if (armiesAvailable == 0) { continue; } GameOrderAttackTransfer attackMove = GameOrderAttackTransfer.Create (GameState.MyPlayerId, territoryId, lowestDistanceNeighbor, AttackTransferEnum.AttackTransfer, new Armies(armiesAvailable), REASON); result.AddAttackOrder(attackMove); } return(result); }
public static List <TerritoryIDType> RemoveMarkedAsUsedTerritories(TerritoryStanding territory, List <TerritoryIDType> testTerritories) { List <TerritoryIDType> territoriesMarkedAsUsed = territory.TerritoriesMarkedAsUsed; List <TerritoryIDType> result = new List <TerritoryIDType>(); testTerritories.Where(o => !territoriesMarkedAsUsed.Contains(o)).ForEach(x => result.Add(x)); return(result); }
private void AddBorderTerritoryDeployment(MultiMoves movesSoFar, int availableDeployment) { TerritoryStanding territoryToDeployTo = MapInformer.GetOwnedBorderTerritories(movesSoFar.GetTerritoryStandingsAfterAllMoves(), GameState.MyPlayerId).FirstOrDefault(); if (territoryToDeployTo == null) { territoryToDeployTo = MapInformer.GetOwnedTerritories(GameState.CurrentTurn().LatestTurnStanding.Territories.Values.ToList(), GameState.MyPlayerId).First(); } movesSoFar.AddDeployOrder(GameOrderDeploy.Create(GameState.MyPlayerId, availableDeployment, territoryToDeployTo.ID, REASON)); }
public Dictionary <TerritoryIDType, TerritoryStanding> GetTerritoryStandingsAfterAllMoves() { if (standingCache != null) { return(standingCache); } Dictionary <TerritoryIDType, TerritoryStanding> beginStandings = GameState.CurrentTurn().LatestTurnStanding.Territories; Dictionary <TerritoryIDType, TerritoryStanding> endStandings = new Dictionary <TerritoryIDType, TerritoryStanding>(); // init foreach (TerritoryIDType territoryId in beginStandings.Keys) { endStandings.Add(territoryId, beginStandings[territoryId].Clone()); } // handle deployments foreach (GameOrderDeploy deployMove in DeployMoves) { endStandings[deployMove.DeployOn].NumArmies = new Armies(endStandings[deployMove.DeployOn].NumArmies.ArmiesOrZero + deployMove.NumArmies); } // handle attack and transfer moves foreach (GameOrderAttackTransfer attackTransferMove in AttackMoves) { int attackingArmies = attackTransferMove.NumArmies.ArmiesOrZero; if (endStandings[attackTransferMove.To].OwnerPlayerID == GameState.MyPlayerId) { // if transfer remove armies from start and add armies to too endStandings[attackTransferMove.From].NumArmies = new Armies(endStandings[attackTransferMove.From].NumArmies.ArmiesOrZero - attackingArmies); endStandings[attackTransferMove.To].NumArmies = new Armies(endStandings[attackTransferMove.To].NumArmies.ArmiesOrZero + attackingArmies); endStandings[attackTransferMove.To].ArmiesMarkedAsUsed = new Armies(endStandings[attackTransferMove.To].ArmiesMarkedAsUsed.ArmiesOrZero + attackingArmies); } else { // if attack the result needs to get calculated TerritoryStanding from = endStandings[attackTransferMove.From]; TerritoryStanding to = endStandings[attackTransferMove.To]; AttackOutcome attackOutcome = new AttackOutcome(from.NumArmies.ArmiesOrZero, attackingArmies, to.NumArmies.ArmiesOrZero); from.NumArmies = new Armies(attackOutcome.RemainingArmiesAttackingTerritory); if (attackOutcome.IsTerritoryTaken) { to.OwnerPlayerID = GameState.MyPlayerId; to.NumArmies = new Armies(attackOutcome.NewArmiesDefendingTerritoryAttacker); } else { to.NumArmies = new Armies(attackOutcome.RemainingArmiesDefendingTerritoryDefender); } } } standingCache = endStandings; return(endStandings); }
public static Armies GuessOpponentNumberOfArmiesInFog(BotMain bot, TerritoryStanding ts) { //most enemy territories will have the minimum, but we'll add a few more to account for deployments and unexpected surprises if (!bot.UseRandomness) { return(new Armies(bot.Settings.OneArmyMustStandGuardOneOrZero + 1)); } var r = RandomUtility.RandomNumber(6) + RandomUtility.RandomNumber(6); if (r < 1) { return(new Armies(bot.Settings.OneArmyMustStandGuardOneOrZero)); } else { return(new Armies(bot.Settings.OneArmyMustStandGuardOneOrZero + Math.Max(1, r - 6))); } }
private static void DoBoss(BotMain bot, TerritoryStanding terr, SpecialUnit su) { AILog.Log("SpecialUnits", "Considering boss " + su.ID + " on " + bot.TerrString(terr.ID)); var routes = bot.Directives.Where(o => o.StartsWith("BossRoute ")).Select(o => new BossRoute(o, bot)).ToList(); var routeNexts = routes.Select(o => new { Route = o, Terr = o.NextTerr(terr.ID) }).Where(o => o.Terr.HasValue).ToList(); if (routeNexts.Count > 0) { var routeNext = routeNexts.WeightedRandom(o => o.Route.Weight); AILog.Log("SpecialUnits", routeNexts.Count + " matching routes: " + routeNexts.Select(o => o.Route.Name).JoinStrings(", ") + ", selected " + routeNext.Route); if (RandomUtility.RandomPercentage() > routeNext.Route.Chance) { AILog.Log("SpecialUnits", "Skipping boss route to " + routeNext.Terr.Value + " due to failed random chance. "); } else { AILog.Log("SpecialUnits", "Moving boss along route to " + bot.TerrString(routeNext.Terr.Value) + ". "); bot.Orders.AddAttack(terr.ID, routeNext.Terr.Value, AttackTransferEnum.AttackTransfer, 0, true, bosses: true); bot.AvoidTerritories.Add(routeNext.Terr.Value); return; } } else if (routes.Count > 0) { //Move towards the nearest route territory. If there's a tie, take the one that's furthest along in that route var terrRoutes = routes.SelectMany(r => r.Route.Select((t, i) => new { Route = r, Terr = t, Index = i })) .GroupBy(o => o.Terr) .Select(o => o.MaxSelectorOrDefault(r => r.Index)) .ToDictionary(o => o.Terr, o => o); var visited = new HashSet <TerritoryIDType>(); visited.Add(terr.ID); while (true) { var visit = visited.SelectMany(o => bot.Map.Territories[o].ConnectedTo.Keys).ToHashSet(false); if (visit.Count == 0) { throw new Exception("Never found route territory"); } var visitOnRoute = visit.Where(o => terrRoutes.ContainsKey(o)).ToList(); if (visitOnRoute.Count > 0) { var final = visitOnRoute.Select(o => terrRoutes[o]).MaxSelectorOrDefault(o => o.Index); if (RandomUtility.RandomPercentage() > final.Route.Chance) { AILog.Log("SpecialUnits", "Skipping moving boss to route due to failed random check: " + final.Route); break; } else { var move = FindPath.TryFindShortestPath(bot, terr.ID, t => t == final.Terr); AILog.Log("SpecialUnits", "Moving boss to get back to route. Moving to " + bot.TerrString(move[0]) + " to get to " + bot.TerrString(final.Terr) + " index=" + final.Index + " " + final.Route); bot.Orders.AddAttack(terr.ID, move[0], AttackTransferEnum.AttackTransfer, 0, true, bosses: true); bot.AvoidTerritories.Add(final.Terr); return; } } visited.AddRange(visit); } } var attackCandidates = bot.Map.Territories[terr.ID].ConnectedTo.Keys.Select(o => bot.Standing.Territories[o]) .Where(o => !bot.IsTeammateOrUs(o.OwnerPlayerID) && o.NumArmies.DefensePower < 300 && !bot.AvoidTerritories.Contains(o.ID)) .ToList(); if (attackCandidates.Count > 0) { var ranks = attackCandidates.ToDictionary(o => o.ID, ts => { if (bot.IsOpponent(ts.OwnerPlayerID)) { return(bot.Players[ts.OwnerPlayerID].IsAI ? 3 : 2); //prefer human player } else if (ts.OwnerPlayerID == TerritoryStanding.NeutralPlayerID) { return(1); } else { throw new Exception("Unexpected owner " + ts.OwnerPlayerID); } }); var max = ranks.Values.Max(); var to = ranks.Where(o => o.Value == max).Random().Key; AILog.Log("SpecialUnits", "Normal boss move to " + bot.TerrString(to)); bot.Orders.AddAttack(terr.ID, to, AttackTransferEnum.AttackTransfer, 0, false, bosses: true); bot.AvoidTerritories.Add(to); } else { //Surrounded by ourself or teammates. Move towards enemy var move = bot.MoveTowardsNearestBorderNonNeutralThenNeutral(terr.ID); if (move.HasValue) { AILog.Log("SpecialUnits", "Landlocked boss move to " + bot.TerrString(move.Value)); bot.Orders.AddAttack(terr.ID, move.Value, AttackTransferEnum.AttackTransfer, 0, false, bosses: true); } } }
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; } } }