/// <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 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); }
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 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(); } }
public float BonusFuzz(BonusIDType bonusID) { if (!UseRandomness) { return(0); } if (_bonusFuzz == null) { _bonusFuzz = new Dictionary <BonusIDType, float>(); } if (!_bonusFuzz.ContainsKey(bonusID)) { _bonusFuzz.Add(bonusID, (float)RandomUtility.BellRandom(-2, 2)); } return(_bonusFuzz[bonusID]); }
public override void Go(int useArmies, bool highlyValuedOnly) { //In FFA, focus on expansion even moreso than in headsup float minWeight = !highlyValuedOnly ? float.MinValue : ExpansionHelper.BaseBonusWeight + (Bot.IsFFA ? -10 : 0) + (Bot.UseRandomness ? (float)RandomUtility.BellRandom(-9, 9) : 0); AILog.Log("Expand", "Expand called with useArmies=" + useArmies + ", minWeight=" + minWeight); Assert.Fatal(useArmies >= 0, "useArmies negative"); var meetsFilter = AttackableNeutrals.Where(o => o.Value.Weight > minWeight).ToDictionary(o => o.Key, o => o.Value); //Don't bother with anything less than the min weight AILog.Log("Expand", meetsFilter.Count + " items over weight " + minWeight + " (" + AttackableNeutrals.Count + " total), top:"); foreach (var spot in meetsFilter.OrderByDescending(o => o.Value.Weight).Take(10)) { AILog.Log("Expand", " - " + spot.Value); } int armiesToExpandWithRemaining = useArmies; while (meetsFilter.Count > 0) { var expandTo = meetsFilter.OrderByDescending(o => o.Value.Weight).First().Key; meetsFilter.Remove(expandTo); if (Bot.Orders.Orders.OfType <GameOrderAttackTransfer>().Any(o => o.To == expandTo)) { continue; //we've already attacked it } int attackWith = Bot.ArmiesToTake(Bot.Standing.Territories[expandTo].NumArmies.Fogged ? ExpansionHelper.GuessNumberOfArmies(Bot, expandTo) : Bot.Standing.Territories[expandTo].NumArmies); var attackFromList = Bot.Map.Territories[expandTo].ConnectedTo.Keys .Select(o => Bot.Standing.Territories[o]) .Where(o => o.OwnerPlayerID == Bot.PlayerID && !Bot.AvoidTerritories.Contains(o.ID)) .ToDictionary(o => o.ID, o => Bot.MakeOrders.GetArmiesAvailable(o.ID)) .OrderByDescending(o => o.Value).ToList(); if (attackFromList.Count == 0) { continue; //nowhere to attack from } var attackFrom = attackFromList[0]; int armiesNeedToDeploy = Math.Max(0, attackWith - attackFrom.Value); if (armiesToExpandWithRemaining >= armiesNeedToDeploy) { //Deploy if needed if (armiesNeedToDeploy > 0) { armiesToExpandWithRemaining -= armiesNeedToDeploy; if (!Bot.Orders.TryDeploy(attackFrom.Key, armiesNeedToDeploy)) { continue; } else { //Remember that we deployed armies towards the capture of this bonus foreach (var bonusID in AttackableNeutrals[expandTo].Bonuses.Keys) { foreach (var an in AttackableNeutrals.Values) { if (an.Bonuses.ContainsKey(bonusID)) { an.Bonuses[bonusID].DeployedTowardsCapturing += armiesNeedToDeploy; } } } } } AILog.Log("Expand", "Expanding into " + Bot.TerrString(expandTo) + " from " + Bot.TerrString(attackFrom.Key) + " with " + attackWith + " by deploying " + armiesNeedToDeploy + ", already had " + attackFrom.Value + " available"); //Attack Bot.Orders.AddAttack(attackFrom.Key, expandTo, AttackTransferEnum.AttackTransfer, attackWith, false); } } }
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); }
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); }