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);
        }
        private Dictionary <TerritoryIDType, PossibleExpandTarget> GetExpansionWeights(HashSet <TerritoryIDType> terrs)
        {
            var bonusPaths = terrs
                             .SelectMany(o => Bot.Map.Territories[o].PartOfBonuses)
                             .Where(o => Bot.BonusValue(o) > 0)
                             .Distinct()
                             .Select(o =>
            {
                if (Bot.PastTime(7))
                {
                    return(null);    //stop trying to expand if we're slow
                }
                return(BonusPath.TryCreate(Bot, o, ts => ts.OwnerPlayerID == Bot.PlayerID));
            })
                             .Where(o => o != null)
                             .ToDictionary(o => o.BonusID, o => o);

            var turnsToTake = bonusPaths.Keys.ToDictionary(o => o, o => TurnsToTake(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.OwnerPlayerID == Bot.PlayerID, turnsToTake[o].NumTurns));

            AILog.Log("Expand", "GetExpansionWeights called with " + terrs.Count + " territories.  Weighted " + bonusWeights.Count + " bonuses:");
            foreach (var bw in bonusWeights.OrderByDescending(o => o.Value).Take(10))
            {
                AILog.Log("Expand", " - " + Bot.BonusString(bw.Key) + " Weight=" + bw.Value + " " + turnsToTake[bw.Key] + " TurnsToTakeByDistance=" + bonusPaths[bw.Key].TurnsToTakeByDistance + " CriticalPath=" + bonusPaths[bw.Key].TerritoriesOnCriticalPath.Select(o => Bot.TerrString(o)).JoinStrings(", "));
            }

            var ret = new Dictionary <TerritoryIDType, PossibleExpandTarget>();

            foreach (var terr in terrs)
            {
                ret[terr] = new PossibleExpandTarget(Bot, terr, Bot.Map.Territories[terr].PartOfBonuses.Where(b => bonusPaths.ContainsKey(b)).ToDictionary(b => b, b => new PossibleExpandTargetBonus(bonusWeights[b], bonusPaths[b], turnsToTake[b])));
            }

            AILog.Log("Expand", "Finished weighing " + terrs.Count + " territories:");
            foreach (var terr in ret.OrderByDescending(o => o.Value.Weight).Take(10))
            {
                AILog.Log("Expand", " - " + Bot.TerrString(terr.Key) + " Weight=" + terr.Value);
            }

            return(ret);
        }
        private static bool TryTraverseBonus(BotMain bot, GameStanding standing, HashSet <TerritoryIDType> allUnownedTerrsInBonus, TerritoryIDType terrID, HashSet <TerritoryIDType> visited, Stack <MultiAttackPlan> stack, int depth)
        {
            if (depth > 20)
            {
                return(false); //prevent stack overflows. If the bonus is too deep, we'll just skip it
            }
            if (bot.PastTime(10))
            {
                return(false); //don't look for too long. This algorithm can take forever on large maps, so abort if we're slow.
            }
            visited.Add(terrID);
            stack.Push(new MultiAttackPlan(bot, terrID, MultiAttackPlanType.MainStack));

            if (allUnownedTerrsInBonus.All(o => visited.Contains(o)))
            {
                return(true); //we did it, we traversed the bonus
            }
            var nextSteps = bot.Map.Territories[terrID].ConnectedTo.Keys.Where(o => !visited.Contains(o) && allUnownedTerrsInBonus.Contains(o)).ToList();

            //Disable offshoots.  Their plan calculates correctly, however the code that calculates the number of armies we need doesn't take into account offshoots, so it ends up using remainders when we don't really get those remainders.  This makes them not able to complete the bonus.
            //if (nextSteps.Count == 0)
            //{
            //    //We're an offshoot.
            //    stack.Peek().Type = MultiAttackPlanType.OneTerritoryOffshoot;
            //    return false;
            //}

            //Traverse from big to small.  We want to take bigger territories first so that more remainders are left for taking smaller ones
            foreach (var next in nextSteps.OrderByDescending(o => ExpansionHelper.GuessNumberOfArmies(bot, o, standing).DefensePower))
            {
                if (TryTraverseBonus(bot, standing, allUnownedTerrsInBonus, next, visited, stack, depth + 1))
                {
                    return(true);
                }
            }

            //Could not find a solution through here. Back up and keep searching.
            visited.Remove(terrID);
            stack.Pop();
            return(false);
        }
Beispiel #4
0
        public override void Go(int remainingUndeployed, bool highlyWeightedOnly)
        {
            //don't search more than 10% of the map. We won't cross the entire map to get a bonus, we're looking for ones near us.  For small maps, never search fewer than 5.
            var maxDistance = highlyWeightedOnly ? 1 : Math.Max(5, (int)(Bot.Map.Territories.Count / 10));

            var armyMult     = ExpansionHelper.ArmyMultiplier(Bot.Settings.DefenseKillRate);
            var bonusWeights = Bot.Map.Bonuses.Keys.Where(o => Bot.BonusValue(o) > 0).ToDictionary(o => o, o => ExpansionHelper.WeighBonus(Bot, o, ts => ts.OwnerPlayerID == Bot.PlayerID, 1)); //assume 1 turn to take for now, which is wrong, but it gives us an even baseline. We'll adjust for turnsToTake in adjustedBonusWeights.

            AILog.Log("ExpandMultiAttack", "remainingUndeployed=" + remainingUndeployed + ". " + bonusWeights.Count + " base weights: ");
            foreach (var bw in bonusWeights.OrderByDescending(o => o.Value).Take(10))
            {
                AILog.Log("ExpandMultiAttack", " - " + Bot.BonusString(bw.Key) + " Weight=" + bw.Value);
            }
            var armiesLeft = remainingUndeployed;

            TryExpand(ref armiesLeft, maxDistance, armyMult, bonusWeights);

            if (!highlyWeightedOnly) //If we have anything left after the second pass, revert to normal expansion.  Do it even if armiesLeft is 0, as it will use normal border armies
            {
                Normal.Go(armiesLeft, highlyWeightedOnly);
            }
        }
        /// <summary>
        /// Returns null if we can't find a way to take the bonus or if we already own it
        /// </summary>
        /// <param name="bot"></param>
        /// <param name="bonusID"></param>
        /// <returns></returns>
        public static MultiAttackPathToBonus TryCreate(BotMain bot, TerritoryIDType startFrom, BonusIDType bonusID, GameStanding standing, int maxDistance)
        {
            var bonus = bot.Map.Bonuses[bonusID];
            var allUnownedTerrsInBonus = bonus.Territories.Where(o => standing.Territories[o].OwnerPlayerID != bot.PlayerID).ToHashSet(true);

            if (allUnownedTerrsInBonus.Count == 0)
            {
                return(null); //already own it
            }
            HashSet <TerritoryIDType> terrsWeEnterBonus;

            int jumpsToGetToBonus = DistanceToTerrs(bot, startFrom, bonus.Territories.ToHashSet(true), standing, maxDistance, out terrsWeEnterBonus);

            if (jumpsToGetToBonus == int.MaxValue)
            {
                return(null); //can't take it within a reasonable searching distance
            }
            if (jumpsToGetToBonus == 0)
            {
                //We're already in it
                var armiesNeededToCapture = bot.ArmiesToTakeMultiAttack(allUnownedTerrsInBonus.Select(o => ExpansionHelper.GuessNumberOfArmies(bot, o, standing, MultiAttackExpand.GuessOpponentNumberOfArmiesInFog)));
                return(new MultiAttackPathToBonus(bot, startFrom, bonusID, 0, armiesNeededToCapture, 0, new List <TerritoryIDType>()));
            }
            else
            {
                var pathToGetThere = FindPath.TryFindShortestPath(bot, startFrom, t => terrsWeEnterBonus.Contains(t), visit => visit == startFrom || bot.IsTeammateOrUs(standing.Territories[visit].OwnerPlayerID) == false);
                if (pathToGetThere == null)
                {
                    return(null);
                }

                var getThere = pathToGetThere.ExceptOne(pathToGetThere.Last());
                var armiesNeededToCapture = bot.ArmiesToTakeMultiAttack(getThere.Concat(allUnownedTerrsInBonus).Select(o => ExpansionHelper.GuessNumberOfArmies(bot, o, standing, MultiAttackExpand.GuessOpponentNumberOfArmiesInFog)));

                return(new MultiAttackPathToBonus(bot, startFrom, bonusID, jumpsToGetToBonus, armiesNeededToCapture, getThere.Sum(o => ExpansionHelper.GuessNumberOfArmies(bot, o, standing).DefensePower), pathToGetThere));
            }
        }
        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);
                }
            }
        }
        /// <summary>
        /// Estimate how many turns it would take us to complete the bonus, assuming full deployment and usage of armies in or next to it.
        /// </summary>
        /// <param name="bonusID"></param>
        /// <param name="bonusPath"></param>
        /// <returns></returns>
        private CaptureTerritories TurnsToTake(BonusIDType bonusID, BonusPath path)
        {
            var bonus       = Bot.Map.Bonuses[bonusID];
            var terrsToTake = bonus.Territories.Where(o => Bot.Standing.Territories[o].OwnerPlayerID != Bot.PlayerID).ToHashSet(true);

            var terrsWeOwnInOrAroundBonus   = bonus.Territories.Concat(bonus.Territories.SelectMany(o => Bot.Map.Territories[o].ConnectedTo.Keys)).Where(o => Bot.Standing.Territories[o].OwnerPlayerID == Bot.PlayerID).ToHashSet(false);
            var armiesWeHaveInOrAroundBonus = terrsWeOwnInOrAroundBonus.Sum(o => Bot.MakeOrders.GetArmiesAvailable(o));

            var armiesPerTurn = Bot.BaseIncome.FreeArmies;

            return(CaptureTerritories.TryFindTurnsToTake(Bot, path, armiesWeHaveInOrAroundBonus, armiesPerTurn, terrsToTake, o => o.NumArmies.Fogged ? ExpansionHelper.GuessNumberOfArmies(Bot, o.ID).DefensePower : o.NumArmies.DefensePower));
        }
Beispiel #8
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;
                }
            }
        }
        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;
                    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);
            }
        }
Beispiel #10
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);
        }