private void DoOffense(int armiesToOffense, List <PossibleAttack> orderedAttacks)
        {
            AILog.Log("Offense", orderedAttacks.Count + " attack ops: ");
            foreach (var attack in orderedAttacks.Take(10))
            {
                AILog.Log("Offense", " - " + attack);
            }

            var armiesLeft = armiesToOffense;


            if (!Bot.UseRandomness)
            {
                int attackIndex = 0;
                while (attackIndex < orderedAttacks.Count)
                {
                    TryDoAttack(orderedAttacks[attackIndex], ref armiesLeft);
                    attackIndex++;
                }
            }
            else
            {
                while (orderedAttacks.Count > 0)
                {
                    if (armiesLeft == 0 && Bot.PastTime(8))
                    {
                        return; //if we're running slowly and have no armies to deploy, just skip attacks.  We just miss out on attacks that we could have done with standing armies.
                    }
                    var i = RandomUtility.WeightedRandomIndex(orderedAttacks, o => o.OffenseImportance);
                    TryDoAttack(orderedAttacks[i], ref armiesLeft);
                    orderedAttacks.RemoveAt(i);
                }
            }
        }
        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);
        }
        /// <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);
                }
            }
        }
Beispiel #5
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;
                }
            }
        }