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);
                            }
                        }
                    }
                }
            }
        }
Esempio n. 2
0
        public void Weight(Dictionary <PlayerIDType, int> weightedNeighbors)
        {
            var opponentID = Bot.Standing.Territories[this.To].OwnerPlayerID;

            Assert.Fatal(opponentID != TerritoryStanding.NeutralPlayerID);
            Assert.Fatal(!Bot.IsTeammateOrUs(opponentID));

            //Seed the border weight with a lessened neighbor weight
            this.DefenseImportance = !weightedNeighbors.ContainsKey(opponentID) ? 0 : (weightedNeighbors[opponentID] / 10.0);
            this.OffenseImportance = this.DefenseImportance;

            //Are we defending a bonus we control?
            foreach (var defendingBonus in Bot.Map.Territories[this.From].PartOfBonuses
                     .Select(b => Bot.Map.Bonuses[b])
                     .Where(b => Bot.PlayerControlsBonus(b) &&
                            Bot.Map.Territories[this.To].PartOfBonuses
                            .Select(b2 => Bot.Map.Bonuses[b2])
                            .Any(b2 => !Bot.PlayerControlsBonus(b2))))
            {
                //Defend importance is bonus value * 10
                this.DefenseImportance += Bot.BonusValue(defendingBonus.ID) * 10.0;
            }

            //Would attacking break an opponents bonus?
            foreach (var attackingBonus in Bot.Map.Territories[this.To].PartOfBonuses
                     .Select(b => Bot.Map.Bonuses[b])
                     .Where(b => Bot.OpponentMightControlBonus(b)))
            {
                this.OffenseImportance += Bot.BonusValue(attackingBonus.ID) * (Bot.IsFFA ? 4.0 : 10);  //be conservative in FFAs, but aggressive in heads up.
            }

            var toTs = Bot.Standing.Territories[this.To];

            //How is our current ratio
            var ourArmies   = Bot.Standing.Territories[this.From].NumArmies.NumArmies;
            var theirArmies = !toTs.NumArmies.Fogged ? toTs.NumArmies.DefensePower : Bot.UseRandomness ? RandomUtility.RandomNumber(ourArmies * 2) : (int)(ourArmies / 2);
            var ratio       = (double)theirArmies / (double)ourArmies;

            if (ourArmies + theirArmies < 10)
            {
                ratio = 1; //Small numbers change so rapidly anyway that we just consider it equal.
            }
            this.OffenseImportance *= ratio;
            this.DefenseImportance *= ratio;

            //AILog.Log("Returning " + possibleAttack.OffenseImportance + "," + possibleAttack.DefenseImportance);
        }
Esempio n. 3
0
        private static void DoBoss(BotMain bot, TerritoryStanding terr, SpecialUnit su)
        {
            AILog.Log("SpecialUnits", "Considering boss " + su.ID + " on " + bot.TerrString(terr.ID));

            var routes = bot.Directives.Where(o => o.StartsWith("BossRoute ")).Select(o => new BossRoute(o, bot)).ToList();

            var routeNexts = routes.Select(o => new { Route = o, Terr = o.NextTerr(terr.ID) }).Where(o => o.Terr.HasValue).ToList();

            if (routeNexts.Count > 0)
            {
                var routeNext = routeNexts.WeightedRandom(o => o.Route.Weight);
                AILog.Log("SpecialUnits", routeNexts.Count + " matching routes: " + routeNexts.Select(o => o.Route.Name).JoinStrings(", ") + ", selected " + routeNext.Route);

                if (RandomUtility.RandomPercentage() > routeNext.Route.Chance)
                {
                    AILog.Log("SpecialUnits", "Skipping boss route to " + routeNext.Terr.Value + " due to failed random chance. ");
                }
                else
                {
                    AILog.Log("SpecialUnits", "Moving boss along route to " + bot.TerrString(routeNext.Terr.Value) + ". ");
                    bot.Orders.AddAttack(terr.ID, routeNext.Terr.Value, AttackTransferEnum.AttackTransfer, 0, true, bosses: true);
                    bot.AvoidTerritories.Add(routeNext.Terr.Value);
                    return;
                }
            }
            else if (routes.Count > 0)
            {
                //Move towards the nearest route territory. If there's a tie, take the one that's furthest along in that route
                var terrRoutes = routes.SelectMany(r => r.Route.Select((t, i) => new { Route = r, Terr = t, Index = i }))
                                 .GroupBy(o => o.Terr)
                                 .Select(o => o.MaxSelectorOrDefault(r => r.Index))
                                 .ToDictionary(o => o.Terr, o => o);

                var visited = new HashSet <TerritoryIDType>();
                visited.Add(terr.ID);

                while (true)
                {
                    var visit = visited.SelectMany(o => bot.Map.Territories[o].ConnectedTo.Keys).ToHashSet(false);
                    if (visit.Count == 0)
                    {
                        throw new Exception("Never found route territory");
                    }

                    var visitOnRoute = visit.Where(o => terrRoutes.ContainsKey(o)).ToList();
                    if (visitOnRoute.Count > 0)
                    {
                        var final = visitOnRoute.Select(o => terrRoutes[o]).MaxSelectorOrDefault(o => o.Index);
                        if (RandomUtility.RandomPercentage() > final.Route.Chance)
                        {
                            AILog.Log("SpecialUnits", "Skipping moving boss to route due to failed random check: " + final.Route);
                            break;
                        }
                        else
                        {
                            var move = FindPath.TryFindShortestPath(bot, terr.ID, t => t == final.Terr);
                            AILog.Log("SpecialUnits", "Moving boss to get back to route. Moving to " + bot.TerrString(move[0]) + " to get to " + bot.TerrString(final.Terr) + " index=" + final.Index + " " + final.Route);
                            bot.Orders.AddAttack(terr.ID, move[0], AttackTransferEnum.AttackTransfer, 0, true, bosses: true);
                            bot.AvoidTerritories.Add(final.Terr);
                            return;
                        }
                    }

                    visited.AddRange(visit);
                }
            }

            var attackCandidates = bot.Map.Territories[terr.ID].ConnectedTo.Keys.Select(o => bot.Standing.Territories[o])
                                   .Where(o => !bot.IsTeammateOrUs(o.OwnerPlayerID) && o.NumArmies.DefensePower < 300 && !bot.AvoidTerritories.Contains(o.ID))
                                   .ToList();

            if (attackCandidates.Count > 0)
            {
                var ranks = attackCandidates.ToDictionary(o => o.ID, ts =>
                {
                    if (bot.IsOpponent(ts.OwnerPlayerID))
                    {
                        return(bot.Players[ts.OwnerPlayerID].IsAI ? 3 : 2); //prefer human player
                    }
                    else if (ts.OwnerPlayerID == TerritoryStanding.NeutralPlayerID)
                    {
                        return(1);
                    }
                    else
                    {
                        throw new Exception("Unexpected owner " + ts.OwnerPlayerID);
                    }
                });

                var max = ranks.Values.Max();
                var to  = ranks.Where(o => o.Value == max).Random().Key;
                AILog.Log("SpecialUnits", "Normal boss move to " + bot.TerrString(to));
                bot.Orders.AddAttack(terr.ID, to, AttackTransferEnum.AttackTransfer, 0, false, bosses: true);
                bot.AvoidTerritories.Add(to);
            }
            else
            {
                //Surrounded by ourself or teammates. Move towards enemy
                var move = bot.MoveTowardsNearestBorderNonNeutralThenNeutral(terr.ID);
                if (move.HasValue)
                {
                    AILog.Log("SpecialUnits", "Landlocked boss move to " + bot.TerrString(move.Value));
                    bot.Orders.AddAttack(terr.ID, move.Value, AttackTransferEnum.AttackTransfer, 0, false, bosses: true);
                }
            }
        }
        /// <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));
            }
        }
Esempio n. 5
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);
        }
        /// <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);
                }
            }
        }