public void Go(int incomeToUse) { if (WeightedMoves.None()) { AILog.Log("DefendAttack", "No attacks or defenses to do, skipping DefendAttack"); return; //No attacks possible } //Divide between offense and defense. Defense armies could still be used for offense if we happen to attack there var baseOffenseRatio = (Bot.IsFFA ? 0.3 : 0.6); if (Bot.Settings.MultiAttack) { baseOffenseRatio = 0; //in MA, our expansion routine is actually our primary attack weapon. Therefore, set offense ratio to 0 so that we skip the routine that tries to attack one territory at a time. } var offenseRatio = baseOffenseRatio + (Bot.UseRandomness ? RandomUtility.BellRandom(-.15, .15) : 0); int armiesToOffense = SharedUtility.Round(incomeToUse * offenseRatio); int armiesToDefense = incomeToUse - armiesToOffense; AILog.Log("DefendAttack", "offenseRatio=" + offenseRatio + ": " + armiesToOffense + " armies go to offense, " + armiesToDefense + " armies go to defense"); //Find defensive opportunities. var orderedDefenses = WeightedMoves.OrderByDescending(o => o.DefenseImportance).ToList(); var orderedAttacks = WeightedMoves.OrderByDescending(o => o.OffenseImportance).ToList(); DoDefense(armiesToDefense, orderedDefenses); DoOffense(armiesToOffense, orderedAttacks); }
private static bool PlayBombCard(BotMain bot, CardInstance card) { var allBombableEnemyTerritories = bot.Standing.Territories.Values .Where(o => o.OwnerPlayerID == bot.PlayerID) .SelectMany(o => bot.Map.Territories[o.ID].ConnectedTo.Keys) .Distinct() .Select(o => bot.Standing.Territories[o]) .Where(o => bot.IsOpponent(o.OwnerPlayerID) && o.NumArmies.Fogged == false) .ToList(); var minArmies = !bot.UseRandomness ? bot.BaseIncome.Total * 2 : SharedUtility.Round(bot.BaseIncome.Total * RandomUtility.BellRandom(1, 3)); var weights = allBombableEnemyTerritories.Where(o => o.NumArmies.NumArmies > minArmies).ToDictionary(o => o.ID, o => o.NumArmies.NumArmies - minArmies); if (weights.Count == 0) { return(false); } var bomb = bot.UseRandomness ? RandomUtility.WeightedRandom(weights.Keys, o => weights[o]) : weights.OrderByDescending(o => o.Value).First().Key; AILog.Log("PlayCards", "Bombing " + bot.TerrString(bomb)); bot.Orders.AddOrder(GameOrderPlayCardBomb.Create(card.ID, bot.PlayerID, bomb)); return(true); }
public int ArmiesToTake(Armies defenseArmies) { Assert.Fatal(!defenseArmies.Fogged, "ArmiesToTake called on fog"); var ret = SharedUtility.Round((defenseArmies.DefensePower / Settings.OffenseKillRate) - 0.5); if (ret == SharedUtility.Round(defenseArmies.DefensePower * Settings.DefenseKillRate)) { ret++; } if (Settings.RoundingMode == RoundingModeEnum.WeightedRandom && (!UseRandomness || RandomUtility.RandomNumber(3) != 0)) { ret++; } if (Settings.LuckModifier > 0) { //Add up some armies to account for luck var factor = UseRandomness ? RandomUtility.BellRandom(2.5, 17.5) : 10.0; ret += SharedUtility.Round(Settings.LuckModifier / factor * ret); } return(ret); }
/// <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); }
private void TryDoAttack(PossibleAttack attack, ref int armiesToOffense) { bool commanders = true; var toTS = Bot.Standing.Territories[attack.To]; int attackWith = Bot.ArmiesToTake(toTS.NumArmies.Fogged == false ? toTS.NumArmies : ExpansionHelper.GuessNumberOfArmies(Bot, toTS.ID)); //Add a few more to what's required so we're not as predictable. if (Bot.UseRandomness) { attackWith += SharedUtility.Round(attackWith * (RandomUtility.RandomPercentage() * .2)); //Once in a while, be willing to do a stupid attack. Sometimes it will work out, sometimes it will fail catastrophically if (RandomUtility.RandomNumber(20) == 0) { var origAttackWith = attackWith; attackWith = SharedUtility.Round(attackWith * RandomUtility.RandomPercentage()); commanders = false; if (attackWith != origAttackWith) { AILog.Log("Offense", "Willing to do a \"stupid\" attack from " + Bot.TerrString(attack.From) + " to " + Bot.TerrString(attack.To) + ": attacking with " + attackWith + " instead of our planned " + origAttackWith); } } } else { attackWith += SharedUtility.Round(attackWith * 0.1); } int have = Bot.MakeOrders.GetArmiesAvailable(attack.From); int need = Math.Max(0, attackWith - have); if (need > armiesToOffense) { //We can't swing it. Just deploy the rest and quit. Will try again next turn. if (armiesToOffense > 0 && Bot.Orders.TryDeploy(attack.From, armiesToOffense)) { AILog.Log("Offense", "Could not attack from " + Bot.TerrString(attack.From) + " to " + Bot.TerrString(attack.To) + " with " + attackWith + ". Short by " + need + ". Just deploying " + armiesToOffense + " to the source."); armiesToOffense = 0; } } else { //We can attack. First deploy however many we needed if (need > 0) { if (!Bot.Orders.TryDeploy(attack.From, need)) { return; } armiesToOffense -= need; Assert.Fatal(armiesToOffense >= 0); } //Now issue the attack Bot.Orders.AddAttack(attack.From, attack.To, AttackTransferEnum.AttackTransfer, attackWith, false, commanders: commanders); AILog.Log("Offense", "Attacking from " + Bot.TerrString(attack.From) + " to " + Bot.TerrString(attack.To) + " with " + attackWith + " by deploying " + need); } }
private Moves CalculateDefendTerritoryMoves(BotTerritory territoryToDefend, int maxDeployment, bool useBackgroundArmies, int step, BotTerritory.DeploymentType lowerConservativeLevel, BotTerritory.DeploymentType upperConservativeLevel) { var outvar = new Moves(); var maxAttackingArmies = 0; var currentDeployment = 0; foreach (var opponentNeighbor in territoryToDefend.GetOpponentNeighbors()) { currentDeployment += opponentNeighbor.GetTotalDeployment(lowerConservativeLevel); var opponentArmies = opponentNeighbor.GetArmiesAfterDeployment(lowerConservativeLevel).AttackPower; var upperOpponentArmies = opponentNeighbor.GetArmiesAfterDeployment(upperConservativeLevel).AttackPower; var deploymentDifference = upperOpponentArmies - opponentArmies; for (var i = 0; i < step; i++) { if (deploymentDifference > 0) { deploymentDifference--; opponentArmies++; currentDeployment++; } } var idleArmies = opponentArmies - 1; maxAttackingArmies += idleArmies; } // Adjust stuff so opponent can't deploy eyerything to every territory var maxOpponentDeployment = territoryToDefend.GetOpponentNeighbors().Select(o => o.OwnerPlayerID).Distinct().Max(o => BotState.GetGuessedOpponentIncome(o, BotState.VisibleMap)); var deploymentDifference_1 = maxOpponentDeployment - currentDeployment; maxAttackingArmies -= deploymentDifference_1; var opponentKills = SharedUtility.Round(maxAttackingArmies * BotState.Settings.OffenseKillRate); var ownArmies = territoryToDefend.GetArmiesAfterDeploymentAndIncomingMoves().DefensePower; var missingArmies = Math.Max(0, opponentKills - ownArmies + 1); // First try to pull in more armies if (missingArmies > 0 && useBackgroundArmies) { var neighborsWithIdleArmies = GetNeighborsWithIdleArmies(territoryToDefend); foreach (var neighbor in neighborsWithIdleArmies) { var armiesToTransfer = Math.Min(missingArmies, neighbor.GetIdleArmies().NumArmies); if (armiesToTransfer > 0) { outvar.AddOrder(new BotOrderAttackTransfer(BotState.Me.ID, neighbor, territoryToDefend, new Armies(armiesToTransfer), "DefendTerritoryTask")); missingArmies -= armiesToTransfer; } } } // Then try to deploy if (missingArmies <= maxDeployment && missingArmies > 0) { outvar.AddOrder(new BotOrderDeploy(BotState.Me.ID, territoryToDefend, missingArmies)); } else if (missingArmies > maxDeployment) { return(null); } return(outvar); }
public static void Go(BotMain bot) { var terrs = bot.Standing.Territories.Values.Where(o => bot.IsTeammateOrUs(o.OwnerPlayerID) && o.NumArmies.NumArmies == bot.Settings.OneArmyMustStandGuardOneOrZero && o.NumArmies.SpecialUnits.Length == 0).Select(o => o.ID).ToHashSet(true); foreach (var bonus in bot.Map.Bonuses.Values) { if (bonus.Territories.All(o => terrs.Contains(o)) && bonus.ControlsBonus(bot.Standing).HasValue == false) { //bot bonus is entirely controlled by our team with 1s, but not by a single player. The player with the most territories should take it. var owners = bonus.Territories.GroupBy(o => bot.Standing.Territories[o].OwnerPlayerID).ToList(); owners.Sort((f, s) => SharedUtility.CompareInts(s.Count(), f.Count())); Assert.Fatal(owners.Count >= 2); var attacks = bonus.Territories .Where(o => bot.Standing.Territories[o].OwnerPlayerID != bot.PlayerID) //Territories in the bonus by our teammate .Where(o => bot.Map.Territories[o].ConnectedTo.Keys.Any(c => bot.Standing.Territories[c].OwnerPlayerID == bot.PlayerID)) //Where we control an adjacent .Select(o => new PossibleAttack(bot, bot.Map.Territories[o].ConnectedTo.Keys.First(c => bot.Standing.Territories[c].OwnerPlayerID == bot.PlayerID), o)); if (owners[0].Count() == owners[1].Count()) { //The top two players have the same number of terrs. 50% chance we should try taking one. if (attacks.Any() && RandomUtility.RandomNumber(2) == 0) { var doAttack1 = bot.UseRandomness ? attacks.Random() : attacks.First(); var numArmies = bot.ArmiesToTake(bot.Standing.Territories[doAttack1.To].NumArmies); if (bot.Orders.TryDeploy(doAttack1.From, numArmies)) { AILog.Log("ResolveTeamBonuses", "Detected a split bonus " + bot.BonusString(bonus) + ", and we're attempting to break the split by doing a small attack from " + bot.TerrString(doAttack1.From) + " to " + bot.TerrString(doAttack1.To) + " with " + numArmies); bot.Orders.AddAttack(doAttack1.From, doAttack1.To, AttackTransferEnum.Attack, numArmies, true); } } } else if (owners[0].Key == bot.PlayerID) { //We should take the bonus foreach (var doAttack2 in attacks) { var numArmies = bot.ArmiesToTake(bot.Standing.Territories[doAttack2.To].NumArmies); if (bot.Orders.TryDeploy(doAttack2.From, numArmies)) { AILog.Log("ResolveTeamBonuses", "Detected we should take bonus " + bot.BonusString(bonus) + ", so we're attacking from " + bot.TerrString(doAttack2.From) + " to " + bot.TerrString(doAttack2.To) + " with " + numArmies); bot.Orders.AddAttack(doAttack2.From, doAttack2.To, AttackTransferEnum.Attack, 2, true); } } } } } }
public static Moves CalculatePreventTerritoriesTask(BotMain state, List <BotTerritory> territoriesToPrevent, PlayerIDType opponentID, int maxDeployment, BotTerritory.DeploymentType conservativeLevel) { var outvar = new Moves(); if (territoriesToPrevent.Count == 0) { return(outvar); } var opponentAttacks = CalculateGuessedOpponentTakeOverMoves(state, territoriesToPrevent, opponentID, true, conservativeLevel); if (opponentAttacks == null) { return(outvar); } // Just try to prevent the territory with the highest defense territory value var highestDefenceTerritoryValue = 0d; BotTerritory highestDefenceValueTerritory = null; foreach (var territory in territoriesToPrevent) { if (territory.OwnerPlayerID == state.Me.ID && territory.DefenceTerritoryValue >= highestDefenceTerritoryValue) { highestDefenceValueTerritory = territory; highestDefenceTerritoryValue = territory.DefenceTerritoryValue; } } var currentArmies = highestDefenceValueTerritory.GetArmiesAfterDeploymentAndIncomingMoves().DefensePower; var attackingArmies = CalculateOpponentAttackingArmies(highestDefenceValueTerritory, opponentAttacks); var minimumNeededArmies = SharedUtility.Round(attackingArmies.AttackPower * state.Settings.OffensiveKillRate); //var minimumNeededArmies = SharedUtility.Round(attackingArmies.AttackPower * state.Settings.OffensiveKillRate); var maximumNeededArmies = minimumNeededArmies; var maximumMissingArmies = Math.Max(0, maximumNeededArmies - currentArmies); var minimumMissingArmies = Math.Max(0, minimumNeededArmies - currentArmies); if (maximumMissingArmies <= maxDeployment && maximumMissingArmies > 0) { outvar.AddOrder(new BotOrderDeploy(state.Me.ID, highestDefenceValueTerritory, maximumMissingArmies)); } else if (minimumMissingArmies <= maxDeployment && maxDeployment > 0) { outvar.AddOrder(new BotOrderDeploy(state.Me.ID, highestDefenceValueTerritory, maxDeployment)); } // If no solution then empty moves instead of null return(outvar); }
private void BuildCities() { if (Bot.Settings.CommerceGame == false || Bot.Settings.CommerceCityBaseCost.HasValue == false) { return; //can't build cities } var totalGold = Bot.Standing.NumResources(Bot.PlayerID, ResourceType.Gold); var spentGold = Bot.Settings.CostOfBuyingArmies(IncomeTracker.TotalArmiesDeployed); var maxPercent = !Bot.UseRandomness ? 0.5 : RandomUtility.BellRandom(0, 0.9); int goldLeftToSpendOnCities = Math.Min(totalGold - spentGold, SharedUtility.Round(totalGold * maxPercent)); //limit our cities to about half our gold to ensure we don't over-build AILog.Log("BuildCities", "totalGold=" + totalGold + " spentGold=" + spentGold + " goldToSpendOnCities=" + goldLeftToSpendOnCities + " maxPercent=" + maxPercent); if (goldLeftToSpendOnCities < Bot.Settings.CommerceCityBaseCost.Value) { return; //can't even afford one city } //randomize the safe range. This makes it int acceptableRangeFromOpponent = !Bot.UseRandomness ? 4 : SharedUtility.Round(RandomUtility.BellRandom(2, 6)); var eligibleTerritories = Bot.TerritoriesNotNearOpponent(acceptableRangeFromOpponent); eligibleTerritories.RemoveAll(Bot.AvoidTerritories); var numCitiesOn = eligibleTerritories.ToDictionary(o => o, o => Bot.Standing.Territories[o].NumStructures(StructureType.City)); //while we might be able to afford a city... while (goldLeftToSpendOnCities > Bot.Settings.CommerceCityBaseCost.Value) { var fewestCities = numCitiesOn.Values.Min(); var cheapestCityCost = fewestCities + Bot.Settings.CommerceCityBaseCost.Value; if (goldLeftToSpendOnCities < cheapestCityCost) { return; //can't afford any more, we must have one on every spot which increases the cost. } //We can afford it, let's build a city var buildCityOn = Bot.UseRandomness ? numCitiesOn.Where(o => o.Value == fewestCities).Random().Key : numCitiesOn.Where(o => o.Value == fewestCities).OrderBy(o => (int)o.Key).First().Key; goldLeftToSpendOnCities -= cheapestCityCost; //remember that we spent it for the loop above. AILog.Log("BuildCities", "Building a city on " + Bot.TerrString(buildCityOn) + " for " + cheapestCityCost + " unspentGold=" + goldLeftToSpendOnCities); Orders.PurchaseOrder.BuildCities.AddTo(buildCityOn, 1); numCitiesOn.AddTo(buildCityOn, 1); //Since we spent gold, adjust the remaining deployable armies so we don't overdeploy later this.UpdateEffectiveIncome(); } }
private void ResolveTeamBonuses() { HashSet <TerritoryIDType> terrs = Standing.Territories.Values.Where(o => this.IsTeammateOrUs(o.OwnerPlayerID) && o.NumArmies.NumArmies == 1).Select(o => o.ID).ToHashSet(true); foreach (BonusDetails bonus in Map.Bonuses.Values) { if (bonus.Territories.All(o => terrs.Contains(o)) && bonus.ControlsBonus(Standing).HasValue == false) { //This bonus is entirely controlled by our team with 1s, but not by a single player. The player with the most territories should take it. var owners = bonus.Territories.GroupBy(o => this.Standing.Territories[o].OwnerPlayerID).ToList(); owners.Sort((f, s) => SharedUtility.CompareInts(s.Count(), f.Count())); Assert.Fatal(owners.Count >= 2); var attacks = bonus.Territories .Where(o => this.Standing.Territories[o].OwnerPlayerID != this.PlayerID) //Territories in the bonus by our teammate .Where(o => this.Map.Territories[o].ConnectedTo.Any(c => this.Standing.Territories[c].OwnerPlayerID == this.PlayerID)) //Where we control an adjacent .Select(o => new PossibleAttack(this, this.Map.Territories[o].ConnectedTo.RandomWhere(c => this.Standing.Territories[c].OwnerPlayerID == this.PlayerID), o)); if (owners[0].Count() == owners[1].Count()) { //The top two players have the same number of terrs. 50% chance we should try taking one. if (attacks.Any() && RandomUtility.RandomNumber(2) == 0) { var doAttack1 = attacks.Random(); if (TryDeploy(doAttack1.From, 2)) { AddAttack(doAttack1.From, doAttack1.To, Settings.AllowAttackOnly ? AttackTransferEnum.Attack : AttackTransferEnum.AttackTransfer, 2, true); } } } else if (owners[0].Key == PlayerID) { //We should take the bonus foreach (var doAttack2 in attacks) { if (TryDeploy(doAttack2.From, 2)) { AddAttack(doAttack2.From, doAttack2.To, Settings.AllowAttackOnly ? AttackTransferEnum.Attack : AttackTransferEnum.AttackTransfer, 2, true); } } } } } }
public static CaptureTerritories TryFindTurnsToTake(BotMain bot, BonusPath path, int armiesWeHaveInOrAroundBonus, int armiesEarnPerTurn, HashSet <TerritoryIDType> terrsToTake, Func <TerritoryStanding, int> terrToTakeDefenders, int timeout = 30) { Assert.Fatal(armiesEarnPerTurn >= 0, "Negative armiesEarnPerTurn"); var armiesDefending = terrsToTake.Sum(o => terrToTakeDefenders(bot.Standing.Territories[o])); var avgAttackersNeededPerTerritory = bot.ArmiesToTake(new Armies(SharedUtility.Round((double)armiesDefending / (double)terrsToTake.Count))); var avgRemaindersPerTerritory = avgAttackersNeededPerTerritory - SharedUtility.Round(avgAttackersNeededPerTerritory * bot.Settings.DefenseKillRate); var armiesNeeded = bot.ArmiesToTake(new Armies(armiesDefending)); for (int turns = path.TurnsToTakeByDistance; ; turns++) { if (turns >= timeout) { return(null); //could not find a solution in time } var totalDeployed = armiesEarnPerTurn * turns; var totalArmies = armiesWeHaveInOrAroundBonus + totalDeployed; var territoriesGettingRemaindersFrom = Math.Min(terrsToTake.Count - SharedUtility.Round(terrsToTake.Count / (float)turns), terrsToTake.Count - 1); var remaindersCanUse = territoriesGettingRemaindersFrom * avgRemaindersPerTerritory; var armiesToStandGuard = territoriesGettingRemaindersFrom * bot.Settings.OneArmyMustStandGuardOneOrZero; var totalNeed = armiesNeeded + armiesToStandGuard; var totalHave = totalArmies + remaindersCanUse; if (totalHave >= totalNeed) { return(new CaptureTerritories(turns, totalDeployed - (totalHave - totalNeed))); } if (armiesEarnPerTurn == 0) { return(null); //we can't take it with what we have, and we are earning no armies. Just abort rather than infinite loop } } #if CSSCALA throw new Exception(); #endif }
/// <summary>Calculates the minimum opponent moves that he needs to make if we don't deploy. /// </summary> /// <param name="state"></param> /// <param name="ownedTerritories"></param> /// <returns></returns> private static Moves CalculateMinimumOpponentMoves(BotMain state, PlayerIDType opponentID, List <BotTerritory> ownedTerritories, BotTerritory.DeploymentType conservativeLevel) { var outvar = new Moves(); foreach (var ownedTerritory in ownedTerritories) { var attackingOpponentTerritory = GetOpponentNeighborMaxIdleArmies(state, opponentID, ownedTerritory, outvar); if (attackingOpponentTerritory == null) { continue; } var stilIdleArmies = CalculateStillOpponentIdleArmies(state, attackingOpponentTerritory, outvar); var attackingOpponentArmies = SharedUtility.Round(ownedTerritory.GetArmiesAfterDeploymentAndIncomingAttacks(conservativeLevel).DefensePower / state.Settings.OffensiveKillRate); var opponentDeployment = Math.Max(0, attackingOpponentArmies - stilIdleArmies.DefensePower); if (opponentDeployment > 0) { outvar.AddOrder(new BotOrderDeploy(opponentID, attackingOpponentTerritory, opponentDeployment)); } outvar.AddOrder(new BotOrderAttackTransfer(opponentID, attackingOpponentTerritory, ownedTerritory, new Armies(attackingOpponentArmies), "PreventTerritoriesTask")); } // Now let's assume that the opponent doesen't leave armies idle var hasSomethingChanged = true; while (hasSomethingChanged) { hasSomethingChanged = false; foreach (var attackTransferMove in outvar.Orders.OfType <BotOrderAttackTransfer>()) { var stillIdleArmies = CalculateStillOpponentIdleArmies(state, attackTransferMove.From, outvar); if (stillIdleArmies.IsEmpty == false) { hasSomethingChanged = true; attackTransferMove.Armies = attackTransferMove.Armies.Add(new Armies(1)); } } } return(outvar); }
public int ArmiesToTakeMultiAttack(IEnumerable <Armies> defenseArmiesOnManyTerritories) { var list = defenseArmiesOnManyTerritories.ToList(); if (list.Count == 0) { return(0); } list.Reverse(); var ret = ArmiesToTake(list[0]); foreach (var def in list.Skip(1)) { var toTake = ArmiesToTake(def); var mustOccupyTerritory = ret + Settings.OneArmyMustStandGuardOneOrZero; var willLoseInFight = SharedUtility.Round(def.DefensePower * Settings.DefenseKillRate); ret = Math.Max(toTake, mustOccupyTerritory + willLoseInFight); } return(ret); }
public BossRoute(string rawCmd, BotMain bot) { this.Bot = bot; this.Route = new List <TerritoryIDType>(); foreach (var split in rawCmd.RemoveFromStartOfString("BossRoute ").Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)) { if (split.StartsWith("Chance=")) { this.Chance = SharedUtility.ParseOrZero(split.RemoveFromStartOfString("Chance=")) / 100.0; } else if (split.StartsWith("Weight=")) { this.Weight = SharedUtility.ParseOrZero(split.RemoveFromStartOfString("Weight=")) / 100.0; } else if (split.StartsWith("Name=")) { this.Name = split.RemoveFromStartOfString("Name="); } else { this.Route.Add((TerritoryIDType)int.Parse(split)); } } }
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; } } }
/// <returns>A list of the territories leading from start to any of finish. Will not include the start territory in the list, but will include the finish territory.</returns> public static List <TerritoryIDType> TryFindShortestPath(BotMain bot, TerritoryIDType start, Func <TerritoryIDType, bool> finish, Func <TerritoryIDType, bool> visitOpt = null) { if (finish(start)) { var ret = new List <TerritoryIDType>(); ret.Add(start); return(ret); } var previous = new Dictionary <TerritoryIDType, TerritoryIDType>(); var distances = new Dictionary <TerritoryIDType, int>(); var nodes = new List <TerritoryIDType>(); foreach (var vertex in bot.Map.Territories.Keys.Where(o => visitOpt == null || visitOpt(o))) { if (vertex == start) { distances[vertex] = 0; } else { distances[vertex] = int.MaxValue; } nodes.Add(vertex); } while (true) { if (bot.PastTime(10)) { return(null); //if we're taking too long, just abort. This algorithm can take forever on big maps. } if (nodes.Count == 0) { return(null); } nodes.Sort((x, y) => SharedUtility.CompareInts(distances[x], distances[y])); var smallest = nodes[0]; nodes.Remove(smallest); if (finish(smallest)) { var ret = new List <TerritoryIDType>(); while (previous.ContainsKey(smallest)) { ret.Add(smallest); smallest = previous[smallest]; } if (ret.Count == 0) { return(null); } ret.Reverse(); return(ret); } if (distances[smallest] == int.MaxValue) { return(null); } foreach (var neighbor in bot.Map.Territories[smallest].ConnectedTo.Keys.Where(o => visitOpt == null || visitOpt(o))) { var alt = distances[smallest] + 1; if (alt < distances[neighbor]) { distances[neighbor] = alt; previous[neighbor] = smallest; } } } #if CSSCALA throw new Exception(); #endif }
/// <summary> /// Initializes a new instance of the <see cref="QueuesOptions"/> class. /// </summary> public QueuesOptions() { _newBatchThreshold = -1; _processorCount = SharedUtility.GetProcessorCount(); }
private Dictionary <TerritoryIDType, int> ConstructCaptureCosts(CowzowBot Bot) { var captureCosts = new Dictionary <TerritoryIDType, int>(); foreach (var r in BotMap.VisibleTerritories) { if (r.OwnerPlayerID == TerritoryStanding.NeutralPlayerID) { var est = ArmiesNeededToCapture(r.Armies); captureCosts[r.ID] = est; } else { if (IsOpponent(r.OwnerPlayerID)) { if (OpponentOrders.OfType <GameOrderDeploy>().None(o => o.DeployOn == r.ID) && r.Bonuses.Any(Analyzer.MightBeOwned)) { var maxThreat = 0; foreach (var n in r.Neighbors) { if (n.IsVisible && n.OwnerPlayerID == Me.ID) { var threat = OpponentVision[n.ID]; if (threat > maxThreat) { maxThreat = threat; } } } var visibleCount = r.Bonuses.SelectMany(o => o.Territories).Where(o => o.IsVisible).Select(o => o.ID).Distinct().Count(); var est = Math.Max(maxThreat - 1, ArmiesNeededToCapture(r.Armies + 2)); if (visibleCount == 1 && r.Bonuses.Any(b => b.ArmiesReward >= 3)) //TODO: Magic numbers { var defendEst = SharedUtility.Ceiling(Settings.DefenseKillRate * Math.Min(Analyzer.TroopEstimate, MyIncome.Total)); est = Math.Max(est, ArmiesNeededToCapture(defendEst)); } // est = Math.max(est, armiesNeededToCapture(r.getArmies())); // est = Math.max(est, 5); captureCosts[r.ID] = est; } else { if (OpponentOrders.OfType <GameOrderDeploy>().None(o => o.DeployOn == r.ID) && !r.Bonuses.Any(Analyzer.MightBeOwned)) { var count = 0; foreach (var tmp in BotMap.VisibleTerritories) { if (IsOpponent(tmp.OwnerPlayerID)) { count++; } } var est = ArmiesNeededToCapture(r.Armies + Math.Min(2, (int)(Analyzer.GetEffectiveTroops() / Math.Max(count, 1)))); captureCosts[r.ID] = est; } else { var oppDeployed = OpponentOrders.OfType <GameOrderDeploy>().Single(o => o.DeployOn == r.ID).NumArmies; var guess = r.Armies; guess += (int)((oppDeployed + 0.5) / 2); foreach (var move in Bot.PreviousTurn.OfType <GameOrderAttackTransfer>().Where(o => o.PlayerID == Bot.Me.ID)) { if (move.To == r.ID) { guess += (int)(oppDeployed / 2); break; } } if (r.Bonuses.Any(Analyzer.MightBeOwned)) { guess += 1; } var est = ArmiesNeededToCapture(guess); captureCosts[r.ID] = est; } } } else { var est = (int)Math.Max(ArmiesNeededToCapture(r.Armies), r.GetStrongestNearestEnemy() + 2); est = Math.Max(est, r.Armies + (int)(Analyzer.TroopEstimate / 2)); captureCosts[r.ID] = est; } } } return(captureCosts); }
private int ArmiesNeededToCapture(int troops) { return(SharedUtility.Ceiling(troops / Settings.OffenseKillRate)); }
public void UpdateExpansionMap() { BotState.ExpansionMap = BotState.VisibleMap.GetMapCopy(); var visibleMap = BotState.VisibleMap; var expansionMap = BotState.ExpansionMap; var vmNeutralTerritories = visibleMap.GetNeutralTerritories(); List <BotTerritory> vmNeutralTerritoriesThatWeTake = new List <BotTerritory>(); // find out which territories we are taking by expansion foreach (var vmNeutralTerritory in vmNeutralTerritories) { if (vmNeutralTerritory.IsVisible) { var attackingArmies = vmNeutralTerritory.GetIncomingArmies(); if (vmNeutralTerritory.getOwnKills(attackingArmies.AttackPower, vmNeutralTerritory.Armies.DefensePower) >= vmNeutralTerritory.Armies.DefensePower) { vmNeutralTerritoriesThatWeTake.Add(vmNeutralTerritory); } } } // update the expansionMap according to our expansion foreach (var vmTakenTerritory in vmNeutralTerritoriesThatWeTake) { var emTakenTerritory = expansionMap.Territories[vmTakenTerritory.ID]; emTakenTerritory.OwnerPlayerID = BotState.Me.ID; emTakenTerritory.Armies = vmTakenTerritory.GetIncomingArmies().Subtract(new Armies(SharedUtility.Round(vmTakenTerritory.Armies.DefensePower * BotState.Settings.DefenseKillRate))); } }
private static bool PlayBlockadeCard(BotMain bot, CardInstance card) { //Look for bonuses that we can't hold and should blockade foreach (var bonus in bot.Map.Bonuses.Values) { var grouped = bonus.Territories.GroupBy(o => bot.Standing.Territories[o].OwnerPlayerID).ToDictionary(o => o.Key, o => o); if (!grouped.ContainsKey(bot.PlayerID)) { continue; //we're not in it } if (grouped.ContainsKey(TerritoryStanding.NeutralPlayerID)) { continue; //only complete bonuses -- if it's never been taken, don't blockade } var opps = grouped.Keys.Where(o => bot.IsOpponent(o)).ToList(); if (opps.Count == 0) { continue; //no opponents in it } if (bonus.Territories.Any(t => bot.AvoidTerritories.Contains(t))) { continue; //already doing something here, perhaps already blockading it. } var oppTerrs = opps.SelectMany(o => grouped[o].ToList()).ToHashSet(false); var friendlyTerrs = grouped.Where(o => bot.IsTeammateOrUs(o.Key)).SelectMany(o => o.Value.ToList()).ToList(); var friendlyArmies = friendlyTerrs.Sum(o => bot.Standing.Territories[o].NumArmies.DefensePower); var enemyArmies = oppTerrs.Sum(o => ExpansionHelper.GuessNumberOfArmies(bot, o).AttackPower); var ratio = bot.UseRandomness ? RandomUtility.BellRandom(1, 3) : 2; if (friendlyArmies * ratio > enemyArmies) { continue; } var armies = SharedUtility.Round(bot.EffectiveIncome.FreeArmies * (bot.UseRandomness ? RandomUtility.BellRandom(.1, .4) : .25)); if (armies < 5) { armies = 5; } var canBlockade = friendlyTerrs.Where(o => bot.Standing.Territories[o].OwnerPlayerID == bot.PlayerID && bot.Map.Territories[o].ConnectedTo.Keys.None(t => oppTerrs.Contains(t)) && bot.Standing.Territories[o].NumArmies.SpecialUnits.Length == 0 && bot.Standing.Territories[o].NumArmies.NumArmies < armies * 2 ).ToList(); if (canBlockade.Count == 0) { continue; } var blockade = bot.UseRandomness ? canBlockade.Random() : canBlockade.First(); var deploy = Math.Max(0, armies - bot.Standing.Territories[blockade].NumArmies.NumArmies); if (!bot.Orders.TryDeploy(blockade, deploy)) { continue; } AILog.Log("PlayCards", "Blockading " + bot.TerrString(blockade) + " with " + armies + " (had to deploy " + deploy + ")"); bot.Orders.AddOrder(GameOrderPlayCardBlockade.Create(card.ID, bot.PlayerID, blockade)); bot.AvoidTerritories.Add(blockade); return(true); } return(false); }
// TODO public void UpdateMap(BotMap mapToUpdate) { BotState.WorkingMap = BotState.VisibleMap.GetMapCopy(); foreach (var wmTerritory in BotState.WorkingMap.Territories.Values) { var vmTerritory = BotState.VisibleMap.Territories[wmTerritory.ID]; if (vmTerritory.OwnerPlayerID != BotState.Me.ID && vmTerritory.IsVisible) { var toBeKilledArmies = vmTerritory.GetArmiesAfterDeployment(BotTerritory.DeploymentType.Normal); var attackingArmies = vmTerritory.GetIncomingArmies(); if (vmTerritory.getOwnKills(attackingArmies.AttackPower, toBeKilledArmies.DefensePower) >= vmTerritory.Armies.DefensePower) //if (Math.Round(attackingArmies.AttackPower * BotState.Settings.OffensiveKillRate) >= toBeKilledArmies.DefensePower) { wmTerritory.OwnerPlayerID = BotState.Me.ID; wmTerritory.Armies = vmTerritory.GetIncomingArmies().Subtract(new Armies(SharedUtility.Round(vmTerritory.GetArmiesAfterDeployment(BotTerritory.DeploymentType.Normal).DefensePower *BotState.Settings.DefensiveKillRate))); } else { // TODO wmTerritory.Armies = vmTerritory.GetArmiesAfterDeployment(BotTerritory.DeploymentType.Normal).Subtract(new Armies(SharedUtility.Round(BotState.Settings.OffensiveKillRate * vmTerritory.GetIncomingArmies().AttackPower))); } } } }
private void Expand(int useArmies, int minWeight) { AILog.Log("Expand called with useArmies=" + useArmies + ", minWeight=" + minWeight); if (useArmies == 0) { return; } int armiesToExpandWithRemaining = useArmies; AILog.Log("Finding attackable neutrals"); var attackableNeutrals = AttackableTerritories.Where(o => o.IsNeutral).ToDictionary(o => o.ID, o => 0); AssignExpansionWeights(attackableNeutrals); AILog.Log("Before filter, " + PlayerID + "'s " + useArmies + " armies got " + attackableNeutrals.Count + " attackable spots with weights " + string.Join(" ; ", attackableNeutrals.Select(o => o.ToString()).ToArray())); //Sort by weight var expandToList = attackableNeutrals .Where(o => o.Value > minWeight) //Don't bother with anything less than the min weight .OrderByDescending(o => o.Value) .Select(o => o.Key) .ToList(); AILog.Log(PlayerID + " Got " + expandToList.Count + " items over weight " + minWeight + ", top finals are " + string.Join(",", expandToList.Take(4).Select(o => o.ToString()).ToArray())); foreach (var expandTo in expandToList) { //If we've already attacked this, quit if (Orders.OfType <GameOrderAttackTransfer>().Any(o => o.To == expandTo)) { continue; } int attackWith = SharedUtility.MathMax(1, Standing.Territories[expandTo].NumArmies.DefensePower * 2); AILog.Log("Figuring out where to attack from"); var attackFrom = Map.Territories[expandTo].ConnectedTo .Select(o => this.Standing.Territories[o]) .Where(o => o.OwnerPlayerID == this.PlayerID) .ToDictionary(o => o.ID, o => this.GetArmiesAvailable(o.ID)) .OrderByDescending(o => o.Value).First(); AILog.Log("Attacking from " + attackFrom); int armiesNeeded = attackWith - attackFrom.Value + 1; //Add one since one army must remain if (armiesNeeded > armiesToExpandWithRemaining) { break; //Can't manage it, stop expanding. TODO: We should still continue looking at other opportunities, since we may already have enough armies on hand. } //Deploy if needed if (armiesNeeded > 0) { armiesToExpandWithRemaining -= armiesNeeded; if (!TryDeploy(attackFrom.Key, armiesNeeded)) { continue; } } //Attack AddAttack(attackFrom.Key, expandTo, AttackTransferEnum.AttackTransfer, attackWith, true); //TODO: Why is it attacking teammates here? It shouldn't. } }