예제 #1
0
        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);
                            }
                        }
                    }
                }
            }
        }
예제 #2
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);
            }
        }
예제 #3
0
        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);
                }
            }
        }
예제 #4
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;
                }
            }
        }