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);
        }
Example #2
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);
        }
Example #3
0
        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);
        }
Example #4
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);
        }
Example #5
0
        private void TryDoAttack(PossibleAttack attack, ref int armiesToOffense)
        {
            bool commanders = true;
            var  toTS       = Bot.Standing.Territories[attack.To];

            int attackWith = Bot.ArmiesToTake(toTS.NumArmies.Fogged == false ? toTS.NumArmies : ExpansionHelper.GuessNumberOfArmies(Bot, toTS.ID));

            //Add a few more to what's required so we're not as predictable.
            if (Bot.UseRandomness)
            {
                attackWith += SharedUtility.Round(attackWith * (RandomUtility.RandomPercentage() * .2));

                //Once in a while, be willing to do a stupid attack.  Sometimes it will work out, sometimes it will fail catastrophically
                if (RandomUtility.RandomNumber(20) == 0)
                {
                    var origAttackWith = attackWith;
                    attackWith = SharedUtility.Round(attackWith * RandomUtility.RandomPercentage());
                    commanders = false;
                    if (attackWith != origAttackWith)
                    {
                        AILog.Log("Offense", "Willing to do a \"stupid\" attack from " + Bot.TerrString(attack.From) + " to " + Bot.TerrString(attack.To) + ": attacking with " + attackWith + " instead of our planned " + origAttackWith);
                    }
                }
            }
            else
            {
                attackWith += SharedUtility.Round(attackWith * 0.1);
            }

            int have = Bot.MakeOrders.GetArmiesAvailable(attack.From);
            int need = Math.Max(0, attackWith - have);

            if (need > armiesToOffense)
            {
                //We can't swing it. Just deploy the rest and quit. Will try again next turn.
                if (armiesToOffense > 0 && Bot.Orders.TryDeploy(attack.From, armiesToOffense))
                {
                    AILog.Log("Offense", "Could not attack from " + Bot.TerrString(attack.From) + " to " + Bot.TerrString(attack.To) + " with " + attackWith + ". Short by " + need + ".  Just deploying " + armiesToOffense + " to the source.");
                    armiesToOffense = 0;
                }
            }
            else
            {
                //We can attack.  First deploy however many we needed
                if (need > 0)
                {
                    if (!Bot.Orders.TryDeploy(attack.From, need))
                    {
                        return;
                    }
                    armiesToOffense -= need;
                    Assert.Fatal(armiesToOffense >= 0);
                }

                //Now issue the attack
                Bot.Orders.AddAttack(attack.From, attack.To, AttackTransferEnum.AttackTransfer, attackWith, false, commanders: commanders);
                AILog.Log("Offense", "Attacking from " + Bot.TerrString(attack.From) + " to " + Bot.TerrString(attack.To) + " with " + attackWith + " by deploying " + need);
            }
        }
        private Moves CalculateDefendTerritoryMoves(BotTerritory territoryToDefend, int maxDeployment, bool useBackgroundArmies, int step, BotTerritory.DeploymentType lowerConservativeLevel, BotTerritory.DeploymentType upperConservativeLevel)
        {
            var outvar             = new Moves();
            var maxAttackingArmies = 0;
            var currentDeployment  = 0;

            foreach (var opponentNeighbor in territoryToDefend.GetOpponentNeighbors())
            {
                currentDeployment += opponentNeighbor.GetTotalDeployment(lowerConservativeLevel);
                var opponentArmies       = opponentNeighbor.GetArmiesAfterDeployment(lowerConservativeLevel).AttackPower;
                var upperOpponentArmies  = opponentNeighbor.GetArmiesAfterDeployment(upperConservativeLevel).AttackPower;
                var deploymentDifference = upperOpponentArmies - opponentArmies;
                for (var i = 0; i < step; i++)
                {
                    if (deploymentDifference > 0)
                    {
                        deploymentDifference--;
                        opponentArmies++;
                        currentDeployment++;
                    }
                }
                var idleArmies = opponentArmies - 1;
                maxAttackingArmies += idleArmies;
            }
            // Adjust stuff so opponent can't deploy eyerything to every territory
            var maxOpponentDeployment  = territoryToDefend.GetOpponentNeighbors().Select(o => o.OwnerPlayerID).Distinct().Max(o => BotState.GetGuessedOpponentIncome(o, BotState.VisibleMap));
            var deploymentDifference_1 = maxOpponentDeployment - currentDeployment;

            maxAttackingArmies -= deploymentDifference_1;
            var opponentKills = SharedUtility.Round(maxAttackingArmies * BotState.Settings.OffenseKillRate);
            var ownArmies     = territoryToDefend.GetArmiesAfterDeploymentAndIncomingMoves().DefensePower;
            var missingArmies = Math.Max(0, opponentKills - ownArmies + 1);

            // First try to pull in more armies
            if (missingArmies > 0 && useBackgroundArmies)
            {
                var neighborsWithIdleArmies = GetNeighborsWithIdleArmies(territoryToDefend);
                foreach (var neighbor in neighborsWithIdleArmies)
                {
                    var armiesToTransfer = Math.Min(missingArmies, neighbor.GetIdleArmies().NumArmies);
                    if (armiesToTransfer > 0)
                    {
                        outvar.AddOrder(new BotOrderAttackTransfer(BotState.Me.ID, neighbor, territoryToDefend, new Armies(armiesToTransfer), "DefendTerritoryTask"));
                        missingArmies -= armiesToTransfer;
                    }
                }
            }
            // Then try to deploy
            if (missingArmies <= maxDeployment && missingArmies > 0)
            {
                outvar.AddOrder(new BotOrderDeploy(BotState.Me.ID, territoryToDefend, missingArmies));
            }
            else if (missingArmies > maxDeployment)
            {
                return(null);
            }

            return(outvar);
        }
        public static void Go(BotMain bot)
        {
            var terrs = bot.Standing.Territories.Values.Where(o => bot.IsTeammateOrUs(o.OwnerPlayerID) && o.NumArmies.NumArmies == bot.Settings.OneArmyMustStandGuardOneOrZero && o.NumArmies.SpecialUnits.Length == 0).Select(o => o.ID).ToHashSet(true);

            foreach (var bonus in bot.Map.Bonuses.Values)
            {
                if (bonus.Territories.All(o => terrs.Contains(o)) && bonus.ControlsBonus(bot.Standing).HasValue == false)
                {
                    //bot bonus is entirely controlled by our team with 1s, but not by a single player. The player with the most territories should take it.
                    var owners = bonus.Territories.GroupBy(o => bot.Standing.Territories[o].OwnerPlayerID).ToList();
                    owners.Sort((f, s) => SharedUtility.CompareInts(s.Count(), f.Count()));

                    Assert.Fatal(owners.Count >= 2);

                    var attacks = bonus.Territories
                                  .Where(o => bot.Standing.Territories[o].OwnerPlayerID != bot.PlayerID)                                                   //Territories in the bonus by our teammate
                                  .Where(o => bot.Map.Territories[o].ConnectedTo.Keys.Any(c => bot.Standing.Territories[c].OwnerPlayerID == bot.PlayerID)) //Where we control an adjacent
                                  .Select(o => new PossibleAttack(bot, bot.Map.Territories[o].ConnectedTo.Keys.First(c => bot.Standing.Territories[c].OwnerPlayerID == bot.PlayerID), o));

                    if (owners[0].Count() == owners[1].Count())
                    {
                        //The top two players have the same number of terrs.  50% chance we should try taking one.
                        if (attacks.Any() && RandomUtility.RandomNumber(2) == 0)
                        {
                            var doAttack1 = bot.UseRandomness ? attacks.Random() : attacks.First();
                            var numArmies = bot.ArmiesToTake(bot.Standing.Territories[doAttack1.To].NumArmies);
                            if (bot.Orders.TryDeploy(doAttack1.From, numArmies))
                            {
                                AILog.Log("ResolveTeamBonuses", "Detected a split bonus " + bot.BonusString(bonus) + ", and we're attempting to break the split by doing a small attack from " + bot.TerrString(doAttack1.From) + " to " + bot.TerrString(doAttack1.To) + " with " + numArmies);
                                bot.Orders.AddAttack(doAttack1.From, doAttack1.To, AttackTransferEnum.Attack, numArmies, true);
                            }
                        }
                    }
                    else if (owners[0].Key == bot.PlayerID)
                    {
                        //We should take the bonus
                        foreach (var doAttack2 in attacks)
                        {
                            var numArmies = bot.ArmiesToTake(bot.Standing.Territories[doAttack2.To].NumArmies);

                            if (bot.Orders.TryDeploy(doAttack2.From, numArmies))
                            {
                                AILog.Log("ResolveTeamBonuses", "Detected we should take bonus " + bot.BonusString(bonus) + ", so we're attacking from " + bot.TerrString(doAttack2.From) + " to " + bot.TerrString(doAttack2.To) + " with " + numArmies);
                                bot.Orders.AddAttack(doAttack2.From, doAttack2.To, AttackTransferEnum.Attack, 2, true);
                            }
                        }
                    }
                }
            }
        }
Example #8
0
        public static Moves CalculatePreventTerritoriesTask(BotMain state, List <BotTerritory> territoriesToPrevent, PlayerIDType opponentID, int maxDeployment, BotTerritory.DeploymentType conservativeLevel)
        {
            var outvar = new Moves();

            if (territoriesToPrevent.Count == 0)
            {
                return(outvar);
            }

            var opponentAttacks = CalculateGuessedOpponentTakeOverMoves(state, territoriesToPrevent, opponentID, true, conservativeLevel);

            if (opponentAttacks == null)
            {
                return(outvar);
            }

            // Just try to prevent the territory with the highest defense territory value
            var          highestDefenceTerritoryValue = 0d;
            BotTerritory highestDefenceValueTerritory = null;

            foreach (var territory in territoriesToPrevent)
            {
                if (territory.OwnerPlayerID == state.Me.ID && territory.DefenceTerritoryValue >= highestDefenceTerritoryValue)
                {
                    highestDefenceValueTerritory = territory;
                    highestDefenceTerritoryValue = territory.DefenceTerritoryValue;
                }
            }
            var currentArmies   = highestDefenceValueTerritory.GetArmiesAfterDeploymentAndIncomingMoves().DefensePower;
            var attackingArmies = CalculateOpponentAttackingArmies(highestDefenceValueTerritory, opponentAttacks);

            var minimumNeededArmies = SharedUtility.Round(attackingArmies.AttackPower * state.Settings.OffensiveKillRate);
            //var minimumNeededArmies = SharedUtility.Round(attackingArmies.AttackPower * state.Settings.OffensiveKillRate);
            var maximumNeededArmies  = minimumNeededArmies;
            var maximumMissingArmies = Math.Max(0, maximumNeededArmies - currentArmies);
            var minimumMissingArmies = Math.Max(0, minimumNeededArmies - currentArmies);

            if (maximumMissingArmies <= maxDeployment && maximumMissingArmies > 0)
            {
                outvar.AddOrder(new BotOrderDeploy(state.Me.ID, highestDefenceValueTerritory, maximumMissingArmies));
            }
            else if (minimumMissingArmies <= maxDeployment && maxDeployment > 0)
            {
                outvar.AddOrder(new BotOrderDeploy(state.Me.ID, highestDefenceValueTerritory, maxDeployment));
            }


            // If no solution then empty moves instead of null
            return(outvar);
        }
Example #9
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();
            }
        }
Example #10
0
        private void ResolveTeamBonuses()
        {
            HashSet <TerritoryIDType> terrs = Standing.Territories.Values.Where(o => this.IsTeammateOrUs(o.OwnerPlayerID) && o.NumArmies.NumArmies == 1).Select(o => o.ID).ToHashSet(true);

            foreach (BonusDetails bonus in Map.Bonuses.Values)
            {
                if (bonus.Territories.All(o => terrs.Contains(o)) && bonus.ControlsBonus(Standing).HasValue == false)
                {
                    //This bonus is entirely controlled by our team with 1s, but not by a single player. The player with the most territories should take it.
                    var owners = bonus.Territories.GroupBy(o => this.Standing.Territories[o].OwnerPlayerID).ToList();
                    owners.Sort((f, s) => SharedUtility.CompareInts(s.Count(), f.Count()));

                    Assert.Fatal(owners.Count >= 2);

                    var attacks = bonus.Territories
                                  .Where(o => this.Standing.Territories[o].OwnerPlayerID != this.PlayerID)                                               //Territories in the bonus by our teammate
                                  .Where(o => this.Map.Territories[o].ConnectedTo.Any(c => this.Standing.Territories[c].OwnerPlayerID == this.PlayerID)) //Where we control an adjacent
                                  .Select(o => new PossibleAttack(this, this.Map.Territories[o].ConnectedTo.RandomWhere(c => this.Standing.Territories[c].OwnerPlayerID == this.PlayerID), o));

                    if (owners[0].Count() == owners[1].Count())
                    {
                        //The top two players have the same number of terrs.  50% chance we should try taking one.
                        if (attacks.Any() && RandomUtility.RandomNumber(2) == 0)
                        {
                            var doAttack1 = attacks.Random();
                            if (TryDeploy(doAttack1.From, 2))
                            {
                                AddAttack(doAttack1.From, doAttack1.To, Settings.AllowAttackOnly ? AttackTransferEnum.Attack : AttackTransferEnum.AttackTransfer, 2, true);
                            }
                        }
                    }
                    else if (owners[0].Key == PlayerID)
                    {
                        //We should take the bonus
                        foreach (var doAttack2 in attacks)
                        {
                            if (TryDeploy(doAttack2.From, 2))
                            {
                                AddAttack(doAttack2.From, doAttack2.To, Settings.AllowAttackOnly ? AttackTransferEnum.Attack : AttackTransferEnum.AttackTransfer, 2, true);
                            }
                        }
                    }
                }
            }
        }
Example #11
0
        public static CaptureTerritories TryFindTurnsToTake(BotMain bot, BonusPath path, int armiesWeHaveInOrAroundBonus, int armiesEarnPerTurn, HashSet <TerritoryIDType> terrsToTake, Func <TerritoryStanding, int> terrToTakeDefenders, int timeout = 30)
        {
            Assert.Fatal(armiesEarnPerTurn >= 0, "Negative armiesEarnPerTurn");
            var armiesDefending = terrsToTake.Sum(o => terrToTakeDefenders(bot.Standing.Territories[o]));

            var avgAttackersNeededPerTerritory = bot.ArmiesToTake(new Armies(SharedUtility.Round((double)armiesDefending / (double)terrsToTake.Count)));
            var avgRemaindersPerTerritory      = avgAttackersNeededPerTerritory - SharedUtility.Round(avgAttackersNeededPerTerritory * bot.Settings.DefenseKillRate);


            var armiesNeeded = bot.ArmiesToTake(new Armies(armiesDefending));

            for (int turns = path.TurnsToTakeByDistance; ; turns++)
            {
                if (turns >= timeout)
                {
                    return(null); //could not find a solution in time
                }
                var totalDeployed = armiesEarnPerTurn * turns;
                var totalArmies   = armiesWeHaveInOrAroundBonus + totalDeployed;

                var territoriesGettingRemaindersFrom = Math.Min(terrsToTake.Count - SharedUtility.Round(terrsToTake.Count / (float)turns), terrsToTake.Count - 1);
                var remaindersCanUse = territoriesGettingRemaindersFrom * avgRemaindersPerTerritory;

                var armiesToStandGuard = territoriesGettingRemaindersFrom * bot.Settings.OneArmyMustStandGuardOneOrZero;


                var totalNeed = armiesNeeded + armiesToStandGuard;
                var totalHave = totalArmies + remaindersCanUse;
                if (totalHave >= totalNeed)
                {
                    return(new CaptureTerritories(turns, totalDeployed - (totalHave - totalNeed)));
                }

                if (armiesEarnPerTurn == 0)
                {
                    return(null); //we can't take it with what we have, and we are earning no armies.  Just abort rather than infinite loop
                }
            }

#if CSSCALA
            throw new Exception();
#endif
        }
Example #12
0
        /// <summary>Calculates the minimum opponent moves that he needs to make if we don't deploy.
        /// </summary>
        /// <param name="state"></param>
        /// <param name="ownedTerritories"></param>
        /// <returns></returns>
        private static Moves CalculateMinimumOpponentMoves(BotMain state, PlayerIDType opponentID, List <BotTerritory> ownedTerritories, BotTerritory.DeploymentType conservativeLevel)
        {
            var outvar = new Moves();

            foreach (var ownedTerritory in ownedTerritories)
            {
                var attackingOpponentTerritory = GetOpponentNeighborMaxIdleArmies(state, opponentID, ownedTerritory, outvar);

                if (attackingOpponentTerritory == null)
                {
                    continue;
                }

                var stilIdleArmies          = CalculateStillOpponentIdleArmies(state, attackingOpponentTerritory, outvar);
                var attackingOpponentArmies = SharedUtility.Round(ownedTerritory.GetArmiesAfterDeploymentAndIncomingAttacks(conservativeLevel).DefensePower / state.Settings.OffensiveKillRate);
                var opponentDeployment      = Math.Max(0, attackingOpponentArmies - stilIdleArmies.DefensePower);
                if (opponentDeployment > 0)
                {
                    outvar.AddOrder(new BotOrderDeploy(opponentID, attackingOpponentTerritory, opponentDeployment));
                }

                outvar.AddOrder(new BotOrderAttackTransfer(opponentID, attackingOpponentTerritory, ownedTerritory, new Armies(attackingOpponentArmies), "PreventTerritoriesTask"));
            }
            // Now let's assume that the opponent doesen't leave armies idle
            var hasSomethingChanged = true;

            while (hasSomethingChanged)
            {
                hasSomethingChanged = false;
                foreach (var attackTransferMove in outvar.Orders.OfType <BotOrderAttackTransfer>())
                {
                    var stillIdleArmies = CalculateStillOpponentIdleArmies(state, attackTransferMove.From, outvar);
                    if (stillIdleArmies.IsEmpty == false)
                    {
                        hasSomethingChanged       = true;
                        attackTransferMove.Armies = attackTransferMove.Armies.Add(new Armies(1));
                    }
                }
            }
            return(outvar);
        }
Example #13
0
        public int ArmiesToTakeMultiAttack(IEnumerable <Armies> defenseArmiesOnManyTerritories)
        {
            var list = defenseArmiesOnManyTerritories.ToList();

            if (list.Count == 0)
            {
                return(0);
            }
            list.Reverse();

            var ret = ArmiesToTake(list[0]);

            foreach (var def in list.Skip(1))
            {
                var toTake = ArmiesToTake(def);
                var mustOccupyTerritory = ret + Settings.OneArmyMustStandGuardOneOrZero;
                var willLoseInFight     = SharedUtility.Round(def.DefensePower * Settings.DefenseKillRate);
                ret = Math.Max(toTake, mustOccupyTerritory + willLoseInFight);
            }

            return(ret);
        }
Example #14
0
 public BossRoute(string rawCmd, BotMain bot)
 {
     this.Bot   = bot;
     this.Route = new List <TerritoryIDType>();
     foreach (var split in rawCmd.RemoveFromStartOfString("BossRoute ").Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries))
     {
         if (split.StartsWith("Chance="))
         {
             this.Chance = SharedUtility.ParseOrZero(split.RemoveFromStartOfString("Chance=")) / 100.0;
         }
         else if (split.StartsWith("Weight="))
         {
             this.Weight = SharedUtility.ParseOrZero(split.RemoveFromStartOfString("Weight=")) / 100.0;
         }
         else if (split.StartsWith("Name="))
         {
             this.Name = split.RemoveFromStartOfString("Name=");
         }
         else
         {
             this.Route.Add((TerritoryIDType)int.Parse(split));
         }
     }
 }
Example #15
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;
                }
            }
        }
Example #16
0
        /// <returns>A list of the territories leading from start to any of finish.  Will not include the start territory in the list, but will include the finish territory.</returns>
        public static List <TerritoryIDType> TryFindShortestPath(BotMain bot, TerritoryIDType start, Func <TerritoryIDType, bool> finish, Func <TerritoryIDType, bool> visitOpt = null)
        {
            if (finish(start))
            {
                var ret = new List <TerritoryIDType>();
                ret.Add(start);
                return(ret);
            }

            var previous  = new Dictionary <TerritoryIDType, TerritoryIDType>();
            var distances = new Dictionary <TerritoryIDType, int>();
            var nodes     = new List <TerritoryIDType>();

            foreach (var vertex in bot.Map.Territories.Keys.Where(o => visitOpt == null || visitOpt(o)))
            {
                if (vertex == start)
                {
                    distances[vertex] = 0;
                }
                else
                {
                    distances[vertex] = int.MaxValue;
                }

                nodes.Add(vertex);
            }

            while (true)
            {
                if (bot.PastTime(10))
                {
                    return(null); //if we're taking too long, just abort.  This algorithm can take forever on big maps.
                }
                if (nodes.Count == 0)
                {
                    return(null);
                }

                nodes.Sort((x, y) => SharedUtility.CompareInts(distances[x], distances[y]));

                var smallest = nodes[0];

                nodes.Remove(smallest);

                if (finish(smallest))
                {
                    var ret = new List <TerritoryIDType>();
                    while (previous.ContainsKey(smallest))
                    {
                        ret.Add(smallest);
                        smallest = previous[smallest];
                    }

                    if (ret.Count == 0)
                    {
                        return(null);
                    }

                    ret.Reverse();
                    return(ret);
                }

                if (distances[smallest] == int.MaxValue)
                {
                    return(null);
                }

                foreach (var neighbor in bot.Map.Territories[smallest].ConnectedTo.Keys.Where(o => visitOpt == null || visitOpt(o)))
                {
                    var alt = distances[smallest] + 1;
                    if (alt < distances[neighbor])
                    {
                        distances[neighbor] = alt;
                        previous[neighbor]  = smallest;
                    }
                }
            }


#if CSSCALA
            throw new Exception();
#endif
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="QueuesOptions"/> class.
 /// </summary>
 public QueuesOptions()
 {
     _newBatchThreshold = -1;
     _processorCount    = SharedUtility.GetProcessorCount();
 }
Example #18
0
        private Dictionary <TerritoryIDType, int> ConstructCaptureCosts(CowzowBot Bot)
        {
            var captureCosts = new Dictionary <TerritoryIDType, int>();

            foreach (var r in BotMap.VisibleTerritories)
            {
                if (r.OwnerPlayerID == TerritoryStanding.NeutralPlayerID)
                {
                    var est = ArmiesNeededToCapture(r.Armies);
                    captureCosts[r.ID] = est;
                }
                else
                {
                    if (IsOpponent(r.OwnerPlayerID))
                    {
                        if (OpponentOrders.OfType <GameOrderDeploy>().None(o => o.DeployOn == r.ID) && r.Bonuses.Any(Analyzer.MightBeOwned))
                        {
                            var maxThreat = 0;
                            foreach (var n in r.Neighbors)
                            {
                                if (n.IsVisible && n.OwnerPlayerID == Me.ID)
                                {
                                    var threat = OpponentVision[n.ID];
                                    if (threat > maxThreat)
                                    {
                                        maxThreat = threat;
                                    }
                                }
                            }
                            var visibleCount = r.Bonuses.SelectMany(o => o.Territories).Where(o => o.IsVisible).Select(o => o.ID).Distinct().Count();

                            var est = Math.Max(maxThreat - 1, ArmiesNeededToCapture(r.Armies + 2));
                            if (visibleCount == 1 && r.Bonuses.Any(b => b.ArmiesReward >= 3)) //TODO: Magic numbers
                            {
                                var defendEst = SharedUtility.Ceiling(Settings.DefenseKillRate * Math.Min(Analyzer.TroopEstimate, MyIncome.Total));
                                est = Math.Max(est, ArmiesNeededToCapture(defendEst));
                            }
                            // est = Math.max(est, armiesNeededToCapture(r.getArmies()));
                            // est = Math.max(est, 5);
                            captureCosts[r.ID] = est;
                        }
                        else
                        {
                            if (OpponentOrders.OfType <GameOrderDeploy>().None(o => o.DeployOn == r.ID) && !r.Bonuses.Any(Analyzer.MightBeOwned))
                            {
                                var count = 0;
                                foreach (var tmp in BotMap.VisibleTerritories)
                                {
                                    if (IsOpponent(tmp.OwnerPlayerID))
                                    {
                                        count++;
                                    }
                                }
                                var est = ArmiesNeededToCapture(r.Armies + Math.Min(2, (int)(Analyzer.GetEffectiveTroops() / Math.Max(count, 1))));
                                captureCosts[r.ID] = est;
                            }
                            else
                            {
                                var oppDeployed = OpponentOrders.OfType <GameOrderDeploy>().Single(o => o.DeployOn == r.ID).NumArmies;
                                var guess       = r.Armies;
                                guess += (int)((oppDeployed + 0.5) / 2);

                                foreach (var move in Bot.PreviousTurn.OfType <GameOrderAttackTransfer>().Where(o => o.PlayerID == Bot.Me.ID))
                                {
                                    if (move.To == r.ID)
                                    {
                                        guess += (int)(oppDeployed / 2);
                                        break;
                                    }
                                }

                                if (r.Bonuses.Any(Analyzer.MightBeOwned))
                                {
                                    guess += 1;
                                }
                                var est = ArmiesNeededToCapture(guess);
                                captureCosts[r.ID] = est;
                            }
                        }
                    }
                    else
                    {
                        var est = (int)Math.Max(ArmiesNeededToCapture(r.Armies), r.GetStrongestNearestEnemy() + 2);
                        est = Math.Max(est, r.Armies + (int)(Analyzer.TroopEstimate / 2));
                        captureCosts[r.ID] = est;
                    }
                }
            }
            return(captureCosts);
        }
Example #19
0
 private int ArmiesNeededToCapture(int troops)
 {
     return(SharedUtility.Ceiling(troops / Settings.OffenseKillRate));
 }
Example #20
0
        public void UpdateExpansionMap()
        {
            BotState.ExpansionMap = BotState.VisibleMap.GetMapCopy();
            var visibleMap           = BotState.VisibleMap;
            var expansionMap         = BotState.ExpansionMap;
            var vmNeutralTerritories = visibleMap.GetNeutralTerritories();
            List <BotTerritory> vmNeutralTerritoriesThatWeTake = new List <BotTerritory>();

            // find out which territories we are taking by expansion
            foreach (var vmNeutralTerritory in vmNeutralTerritories)
            {
                if (vmNeutralTerritory.IsVisible)
                {
                    var attackingArmies = vmNeutralTerritory.GetIncomingArmies();
                    if (vmNeutralTerritory.getOwnKills(attackingArmies.AttackPower, vmNeutralTerritory.Armies.DefensePower) >= vmNeutralTerritory.Armies.DefensePower)
                    {
                        vmNeutralTerritoriesThatWeTake.Add(vmNeutralTerritory);
                    }
                }
            }
            // update the expansionMap according to our expansion
            foreach (var vmTakenTerritory in vmNeutralTerritoriesThatWeTake)
            {
                var emTakenTerritory = expansionMap.Territories[vmTakenTerritory.ID];
                emTakenTerritory.OwnerPlayerID = BotState.Me.ID;
                emTakenTerritory.Armies        = vmTakenTerritory.GetIncomingArmies().Subtract(new Armies(SharedUtility.Round(vmTakenTerritory.Armies.DefensePower * BotState.Settings.DefenseKillRate)));
            }
        }
Example #21
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);
        }
Example #22
0
 // TODO
 public void UpdateMap(BotMap mapToUpdate)
 {
     BotState.WorkingMap = BotState.VisibleMap.GetMapCopy();
     foreach (var wmTerritory in BotState.WorkingMap.Territories.Values)
     {
         var vmTerritory = BotState.VisibleMap.Territories[wmTerritory.ID];
         if (vmTerritory.OwnerPlayerID != BotState.Me.ID && vmTerritory.IsVisible)
         {
             var toBeKilledArmies = vmTerritory.GetArmiesAfterDeployment(BotTerritory.DeploymentType.Normal);
             var attackingArmies  = vmTerritory.GetIncomingArmies();
             if (vmTerritory.getOwnKills(attackingArmies.AttackPower, toBeKilledArmies.DefensePower) >= vmTerritory.Armies.DefensePower)
             //if (Math.Round(attackingArmies.AttackPower * BotState.Settings.OffensiveKillRate) >= toBeKilledArmies.DefensePower)
             {
                 wmTerritory.OwnerPlayerID = BotState.Me.ID;
                 wmTerritory.Armies        = vmTerritory.GetIncomingArmies().Subtract(new Armies(SharedUtility.Round(vmTerritory.GetArmiesAfterDeployment(BotTerritory.DeploymentType.Normal).DefensePower *BotState.Settings.DefensiveKillRate)));
             }
             else
             {
                 // TODO
                 wmTerritory.Armies = vmTerritory.GetArmiesAfterDeployment(BotTerritory.DeploymentType.Normal).Subtract(new Armies(SharedUtility.Round(BotState.Settings.OffensiveKillRate * vmTerritory.GetIncomingArmies().AttackPower)));
             }
         }
     }
 }
Example #23
0
        private void Expand(int useArmies, int minWeight)
        {
            AILog.Log("Expand called with useArmies=" + useArmies + ", minWeight=" + minWeight);

            if (useArmies == 0)
            {
                return;
            }


            int armiesToExpandWithRemaining = useArmies;

            AILog.Log("Finding attackable neutrals");

            var attackableNeutrals = AttackableTerritories.Where(o => o.IsNeutral).ToDictionary(o => o.ID, o => 0);

            AssignExpansionWeights(attackableNeutrals);

            AILog.Log("Before filter, " + PlayerID + "'s " + useArmies + " armies got " + attackableNeutrals.Count + " attackable spots with weights " + string.Join(" ; ", attackableNeutrals.Select(o => o.ToString()).ToArray()));


            //Sort by weight
            var expandToList = attackableNeutrals
                               .Where(o => o.Value > minWeight) //Don't bother with anything less than the min weight
                               .OrderByDescending(o => o.Value)
                               .Select(o => o.Key)
                               .ToList();

            AILog.Log(PlayerID + " Got " + expandToList.Count + " items over weight " + minWeight + ", top finals are " + string.Join(",", expandToList.Take(4).Select(o => o.ToString()).ToArray()));

            foreach (var expandTo in expandToList)
            {
                //If we've already attacked this, quit
                if (Orders.OfType <GameOrderAttackTransfer>().Any(o => o.To == expandTo))
                {
                    continue;
                }

                int attackWith = SharedUtility.MathMax(1, Standing.Territories[expandTo].NumArmies.DefensePower * 2);

                AILog.Log("Figuring out where to attack from");

                var attackFrom = Map.Territories[expandTo].ConnectedTo
                                 .Select(o => this.Standing.Territories[o])
                                 .Where(o => o.OwnerPlayerID == this.PlayerID)
                                 .ToDictionary(o => o.ID, o => this.GetArmiesAvailable(o.ID))
                                 .OrderByDescending(o => o.Value).First();

                AILog.Log("Attacking from " + attackFrom);

                int armiesNeeded = attackWith - attackFrom.Value + 1; //Add one since one army must remain

                if (armiesNeeded > armiesToExpandWithRemaining)
                {
                    break; //Can't manage it, stop expanding.  TODO: We should still continue looking at other opportunities, since we may already have enough armies on hand.
                }
                //Deploy if needed
                if (armiesNeeded > 0)
                {
                    armiesToExpandWithRemaining -= armiesNeeded;
                    if (!TryDeploy(attackFrom.Key, armiesNeeded))
                    {
                        continue;
                    }
                }

                //Attack
                AddAttack(attackFrom.Key, expandTo, AttackTransferEnum.AttackTransfer, attackWith, true); //TODO: Why is it attacking teammates here?  It shouldn't.
            }
        }