public override string ToString()
 {
     if (Type == MultiAttackPlanType.MainStack)
     {
         return(Bot.TerrString(To));
     }
     else
     {
         return(Bot.TerrString(To) + " " + Type);
     }
 }
Exemple #2
0
        /// <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 static float GetExpansionWeight(BotMain bot, TerritoryIDType terrID)
        {
            var td = bot.Map.Territories[terrID];

            var bonusPaths = td.PartOfBonuses
                             .Where(o => bot.BonusValue(o) > 0)
                             .Select(o => BonusPath.TryCreate(bot, o, ts => ts.ID == terrID))
                             .Where(o => o != null)
                             .ToDictionary(o => o.BonusID, o => o);

            var turnsToTake = bonusPaths.Keys.ToDictionary(o => o, o => TurnsToTake(bot, td.ID, o, bonusPaths[o]));

            foreach (var cannotTake in turnsToTake.Where(o => o.Value == null).ToList())
            {
                turnsToTake.Remove(cannotTake.Key);
                bonusPaths.Remove(cannotTake.Key);
            }

            var bonusWeights = bonusPaths.Keys.ToDictionary(o => o, o => ExpansionHelper.WeighBonus(bot, o, ts => ts.ID == terrID, turnsToTake[o].NumTurns));

            var weight = 0.0f;

            weight += ExpansionHelper.WeighMultipleBonuses(td.PartOfBonuses.Where(o => bonusWeights.ContainsKey(o)).ToDictionary(o => o, o => bonusWeights[o]));

            AILog.Log("PickTerritories", "Expansion weight for terr " + bot.TerrString(terrID) + " is " + weight + ". " + td.PartOfBonuses.Select(b => "Bonus " + bot.BonusString(b) + " Weight=" + (bonusWeights.ContainsKey(b) ? bonusWeights[b] : 0) + " TurnsToTake=" + (turnsToTake.ContainsKey(b) ? turnsToTake[b].ToString() : "") + " Path=" + (bonusPaths.ContainsKey(b) ? bonusPaths[b].ToString() : "")).JoinStrings(", "));

            return(weight);
        }
        public static List <TerritoryIDType> Go(BotMain bot, HashSet <TerritoryIDType> allAvailable, int maxPicks)
        {
            var expansionWeights = allAvailable.ToDictionary(o => o, o => GetExpansionWeight(bot, o));

            var ordered = expansionWeights.OrderByDescending(o => o.Value).ToList();

            AILog.Log("PickTerritories", "Came up with " + expansionWeights.Count + " expansion weights: ");
            foreach (var e in ordered.Take(30))
            {
                AILog.Log("PickTerritories", " - " + bot.TerrString(e.Key) + ": " + e.Value);
            }

            if (!bot.UseRandomness)
            {
                return(ordered.Select(o => o.Key).Take(maxPicks).ToList());
            }

            //Normalize weights
            var top        = ordered.Take(maxPicks * 2);
            var sub        = top.Min(o => o.Value) - 1;
            var normalized = top.ToDictionary(o => o.Key, o => o.Value - sub);

            var picks = new List <TerritoryIDType>();

            while (picks.Count < maxPicks && normalized.Count > 0)
            {
                var pick = RandomUtility.WeightedRandom(normalized.Keys, o => normalized[o]);
                picks.Add(pick);
                normalized.Remove(pick);
            }
            return(picks);
        }
Exemple #5
0
        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);
        }
Exemple #6
0
        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 DoDefense(int armies, List <PossibleAttack> orderedDefenses)
        {
            AILog.Log("Defense", orderedDefenses.Count + " defend ops:");
            foreach (var defend in orderedDefenses.Take(10))
            {
                AILog.Log("Defense", " - " + defend);
            }

            if (orderedDefenses.Count == 0)
            {
                AILog.Log("Defense", "No defenses");
                return;
            }

#if IOS
            //Work around the "attacking to JIT compile method" for below WeightedRandom call
            if (RandomUtility.RandomNumber(2) == -1)
            {
                new List <PossibleAttack>().Select(o => o.DefenseImportance).ToList();
            }
#endif

            var allDefenses = new Dictionary <TerritoryIDType, int>();

            if (Bot.UseRandomness)
            {
                for (int i = 0; i < armies; i++)
                {
                    var defend = orderedDefenses.WeightedRandom(o => o.DefenseImportance);
                    if (Bot.Orders.TryDeploy(defend.From, 1))
                    {
                        allDefenses.AddTo(defend.From, 1);
                    }
                }
            }
            else
            {
                var avg           = orderedDefenses.Select(o => o.DefenseImportance).Average();
                var betterThanAvg = orderedDefenses.Where(o => o.DefenseImportance >= avg).ToList();
                var armiesLeft    = armies;
                while (armiesLeft > 0)
                {
                    bool deployedAny = false;
                    foreach (var d in betterThanAvg)
                    {
                        if (Bot.Orders.TryDeploy(d.From, 1))
                        {
                            deployedAny = true;
                            allDefenses.AddTo(d.From, 1);
                            armiesLeft--;
                            if (armiesLeft <= 0)
                            {
                                break;
                            }
                        }
                    }

                    if (!deployedAny)
                    {
                        break; //We couldn't deploy any, possibly due to local deployments.
                    }
                }
            }

            AILog.Log("Defense", "Defended " + allDefenses.Count + " territories: " + allDefenses.OrderByDescending(o => o.Value).Select(o => Bot.TerrString(o.Key) + " with " + o.Value).JoinStrings(", "));
        }
Exemple #8
0
        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);
                }
            }
        }
        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);
                            }
                        }
                    }
                }
            }
        }
Exemple #10
0
 public override string ToString()
 {
     return(Bot.TerrString(ID) + " Weight=" + Weight + ", CriticalPath=" + WeightFromCriticalPath + ", Bonuses: " + Bonuses.Select(o => Bot.BonusString(o.Key) + " " + o.Value).JoinStrings(", "));
 }
 private static void Deploy(BotMain bot, string source, TerritoryIDType terrID, int armies)
 {
     AILog.Log("DeployRemaining", source + " deploying " + armies + " to " + bot.TerrString(terrID));
     bot.Orders.Deploy(terrID, armies, true);
 }
Exemple #12
0
 public override string ToString()
 {
     return("From " + Bot.TerrString(From) + " to " + Bot.TerrString(To) + ".  DefenseImportance=" + DefenseImportance + " OffensiveImportance=" + OffenseImportance);
 }
        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);
                }
            }
        }
Exemple #14
0
        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;
                }
            }
        }
Exemple #15
0
        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);
        }
        public static void Go(BotMain bot)
        {
            var attacks = bot.Orders.Orders.OfType <GameOrderAttackTransfer>();

            foreach (var orders in attacks
                     .Where(o => bot.Standing.Territories[o.From].OwnerPlayerID == bot.PlayerID)
                     .Select(o => new UtilizeSpareArmies(o, bot.MakeOrders.GetArmiesAvailable(o.From)))
                     .Where(o => o.Available > 0)
                     .GroupBy(o => o.Order.From))
            {
                var order = bot.UseRandomness ? orders.Random() : orders.First();

                AILog.Log("UtilizeSpareArmies", "Adding " + order.Available + " available armies into attack from " + bot.TerrString(order.Order.From) + " to " + bot.TerrString(order.Order.To) + ", originally had " + order.Order.NumArmies.NumArmies);
                order.Order.NumArmies = order.Order.NumArmies.Add(new Armies(order.Available));
            }
        }
        /// <summary>
        /// Any armies that are surrounded by our own territories (or our teammates) should move towards the nearest enemy.
        /// </summary>
        public static void Go(BotMain bot)
        {
            foreach (var landlocked in bot.Standing.Territories.Values.Where(o => o.OwnerPlayerID == bot.PlayerID &&
                                                                             bot.Map.Territories[o.ID].ConnectedTo.Keys.All(c => bot.IsTeammateOrUs(bot.Standing.Territories[c].OwnerPlayerID)) &&
                                                                             !bot.AvoidTerritories.Contains(o.ID) &&
                                                                             bot.MakeOrders.GetArmiesAvailable(o.ID) > 0))
            {
                if (bot.PastTime(5))
                {
                    //Extreme cases (i.e. where one player controls all of a big map), this algorithm can take forever.  We don't care about these extreme cases since they've already won.  Stop processing after too long
                    AILog.Log("MoveLandlockedUp", "Giving up due to time");
                    break;
                }

                var moveTowards = bot.MoveTowardsNearestBorder(landlocked.ID, true);

                if (moveTowards.HasValue && !bot.AvoidTerritories.Contains(moveTowards.Value))
                {
                    var armies = bot.MakeOrders.GetArmiesAvailable(landlocked.ID);
                    AILog.Log("MoveLandlockedUp", "Ordering " + armies + " armies from " + bot.TerrString(landlocked.ID) + " to " + bot.TerrString(moveTowards.Value));
                    bot.Orders.AddAttack(landlocked.ID, moveTowards.Value, AttackTransferEnum.Transfer, armies, false);
                }
            }
        }