Ejemplo n.º 1
0
 public static bool IsNuke(JSON.Army army)
 {
     return
         (BattleSimulator.TotalAttackPower(army) > 4e5 &&
          ArmyStats.CalculateTotalPopulation(army.OfType(JSON.UnitBuild.Offensive)) > 10000 &&
          ArmyStats.CalculateTotalPopulation(army) > FullArmyPopulation);
 }
Ejemplo n.º 2
0
        public JSON.TroopType TravelTroopType(JSON.Army army)
        {
            JSON.TroopType slowestType = JSON.TroopType.Spy;

            foreach (var type in army.Where(kvp => kvp.Value > 0).Select(kvp => kvp.Key))
            {
                if (Native.ArmyStats.TravelSpeed[type] > Native.ArmyStats.TravelSpeed[slowestType])
                {
                    slowestType = type;
                }
            }

            return(slowestType);
        }
Ejemplo n.º 3
0
        public static JSON.Army OfType(this JSON.Army army, JSON.UnitBuild build)
        {
            if (army == null)
            {
                return(new JSON.Army());
            }

            var result = new JSON.Army();

            foreach (var type in army.Keys)
            {
                if (Native.ArmyStats.UnitBuild[type] == build)
                {
                    result.Add(type, army[type]);
                }
            }
            return(result);
        }
Ejemplo n.º 4
0
        public static Army Max(params Army[] armies)
        {
            var result = new Army();

            foreach (var army in armies.Where(a => a != null))
            {
                foreach (var type in army.Keys)
                {
                    if (!result.ContainsKey(type))
                    {
                        result.Add(type, army[type]);
                    }
                    else if (result[type] < army[type])
                    {
                        result[type] = army[type];
                    }
                }
            }

            return(result);
        }
Ejemplo n.º 5
0
 public static bool IsStacked(JSON.Army army)
 {
     return(BattleSimulator.TotalDefensePower(army) >= StackDefensePower);
 }
Ejemplo n.º 6
0
 public static bool IsOffensive(JSON.Army army)
 {
     return(army.GetValueOrDefault(JSON.TroopType.Axe, 0) > 500 || army.GetValueOrDefault(JSON.TroopType.Light, 0) > 250);
 }
Ejemplo n.º 7
0
        public JSON.Army CalculatePossibleArmyRecruitment(TimeSpan timeSpan, List <JSON.TroopType> troopTypes, Dictionary <JSON.TroopType, int> maxCounts = null)
        {
            //  Keep time span reasonable so we don't get integer overflow
            if (timeSpan.TotalDays > 30)
            {
                timeSpan = TimeSpan.FromDays(30);
            }

            var result = new JSON.Army();

            foreach (var type in troopTypes)
            {
                result.Add(type, 0);
            }

            var typesBySource = troopTypes.GroupBy(t => Native.ArmyStats.UnitSource[t]).ToDictionary((s) => s.Key, s => s.Select(t => t));

            //  Simulate recruitment by time interval
            var interval      = TimeSpan.FromHours(6);
            var simulatedTime = TimeSpan.Zero;
            int totalPop      = 0;
            int previousPop   = -1;

            bool ReachedCountLimit(JSON.TroopType troopType)
            {
                return(maxCounts != null && maxCounts.ContainsKey(troopType) && result[troopType] >= maxCounts[troopType]);
            }

            while (simulatedTime < timeSpan && totalPop < MaxPopulation && totalPop != previousPop)
            {
                foreach (var source in typesBySource.Keys)
                {
                    var types = typesBySource[source].Where(t => !ReachedCountLimit(t));

                    var sumTimeSeconds = types.Sum(t => Native.ArmyStats.BaseRecruitTime[t].TotalSeconds);
                    foreach (var type in types)
                    {
                        var factor = 1 - Native.ArmyStats.BaseRecruitTime[type].TotalSeconds / sumTimeSeconds;

                        //  If there's only 1 unit type for this building
                        if (factor <= float.Epsilon)
                        {
                            factor = 1;
                        }

                        var troops = CalculatePossibleUnitRecruitment(type, interval * factor);
                        result[type] += troops;
                    }
                }

                if (maxCounts != null)
                {
                    foreach (var type in maxCounts.Keys)
                    {
                        if (!result.ContainsKey(type))
                        {
                            continue;
                        }

                        if (result[type] > maxCounts[type])
                        {
                            result[type] = maxCounts[type];
                        }
                    }
                }

                //  Keep track of previous/total in case we reach max counts before time limit or pop limit are reached
                //  (otherwise we may get infinite loop)
                previousPop    = totalPop;
                totalPop       = Native.ArmyStats.CalculateTotalPopulation(result);
                simulatedTime += interval;
            }

            return(result);
        }
Ejemplo n.º 8
0
        public static JSON.Army ArmyToJson(Scaffold.IScaffoldArmy army)
        {
            if (army == null)
            {
                return(null);
            }

            var result = new JSON.Army();

            if (army.Spear != null)
            {
                result[JSON.TroopType.Spear] = army.Spear.Value;
            }
            if (army.Sword != null)
            {
                result[JSON.TroopType.Sword] = army.Sword.Value;
            }
            if (army.Axe != null)
            {
                result[JSON.TroopType.Axe] = army.Axe.Value;
            }
            if (army.Archer != null)
            {
                result[JSON.TroopType.Archer] = army.Archer.Value;
            }
            if (army.Spy != null)
            {
                result[JSON.TroopType.Spy] = army.Spy.Value;
            }
            if (army.Light != null)
            {
                result[JSON.TroopType.Light] = army.Light.Value;
            }
            if (army.Marcher != null)
            {
                result[JSON.TroopType.Marcher] = army.Marcher.Value;
            }
            if (army.Heavy != null)
            {
                result[JSON.TroopType.Heavy] = army.Heavy.Value;
            }
            if (army.Ram != null)
            {
                result[JSON.TroopType.Ram] = army.Ram.Value;
            }
            if (army.Catapult != null)
            {
                result[JSON.TroopType.Catapult] = army.Catapult.Value;
            }
            if (army.Knight != null)
            {
                result[JSON.TroopType.Knight] = army.Knight.Value;
            }
            if (army.Snob != null)
            {
                result[JSON.TroopType.Snob] = army.Snob.Value;
            }
            if (army.Militia != null)
            {
                result[JSON.TroopType.Militia] = army.Militia.Value;
            }

            return(result);
        }
Ejemplo n.º 9
0
        private static T JsonToArmy <T>(JSON.Army armyCounts, short worldId, T existingArmy = null, Scaffold.VaultContext context = null, bool emptyIfNull = false) where T : class, new()
        {
            if (Object.ReferenceEquals(armyCounts, null))
            {
                if (emptyIfNull)
                {
                    armyCounts = JSON.Army.Empty;
                }
                else
                {
                    if (existingArmy != null && context != null)
                    {
                        context.Remove(existingArmy);
                    }

                    return(null);
                }
            }

            T result;

            if (existingArmy != null)
            {
                result = existingArmy;
            }
            else
            {
                result = new T();
            }

            var scaffoldArmyType = typeof(T);

            foreach (var troopType in Enum.GetValues(typeof(JSON.TroopType)).Cast <JSON.TroopType>())
            {
                var troopCount    = GetOrNull(armyCounts, troopType);
                var troopProperty = scaffoldArmyType.GetProperty(troopType.ToString(), System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
                var currentCount  = (int?)troopProperty.GetValue(result);

                if (currentCount == troopCount)
                {
                    continue;
                }

                if (typeof(short?).IsAssignableFrom(troopProperty.PropertyType))
                {
                    troopProperty.SetValue(result, troopCount?.ToShort());
                }
                else
                {
                    troopProperty.SetValue(result, troopCount);
                }
            }

            var worldIdProperty = scaffoldArmyType.GetProperty("WorldId", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);

            if (worldIdProperty != null)
            {
                worldIdProperty.SetValue(result, worldId);
            }

            if (existingArmy == null && context != null)
            {
                context.Add(result);
            }

            return(result);
        }
Ejemplo n.º 10
0
 public static Scaffold.CurrentArmy JsonToArmy(JSON.Army armyCounts, short worldId, Scaffold.CurrentArmy existingArmy = null, Scaffold.VaultContext context = null, bool emptyIfNull = false) =>
 JsonToArmy <Scaffold.CurrentArmy>(armyCounts, worldId, existingArmy, context, emptyIfNull);
Ejemplo n.º 11
0
        public TimeSpan ArmyFieldSpeed(JSON.Army army)
        {
            var slowestSpeed = Native.ArmyStats.TravelSpeed[TravelTroopType(army)];

            return(TimeSpan.FromMinutes(slowestSpeed));
        }
Ejemplo n.º 12
0
        public async Task <IActionResult> GetIncomingTags([FromBody] List <long> incomingsIds)
        {
            // Preload world data since we need world settings within queries below
            PreloadWorldData();
            //  Lots of data read but only updating some of it; whenever we do SaveChanges it checks
            //  for changes against all queried objects. Disable tracking by default and track explicitly if necessary
            context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;

            var incomingData = await Profile("Get existing commands", () => (
                                                 from command in CurrentSets.Command
                                                 .Include(c => c.SourceVillage)
                                                 let currentVillage = command.SourceVillage.CurrentVillage.FirstOrDefault(cv => cv.AccessGroupId == CurrentAccessGroupId)
                                                                      where incomingsIds.Contains(command.CommandId)
                                                                      select new { Command = command, CurrentVillage = currentVillage }
                                                 ).ToListAsync()
                                             );

            if (incomingData == null)
            {
                return(NotFound());
            }

            //  Load in actual CurrentArmy data for incomings
            //  (Didn't need this previously but EF Core can be dumb, .Include on a `join .. into` doesn't actually include the given properties)
            {
                IEnumerable <long> SelectCurrentArmyIds(Scaffold.CurrentVillage currentVillage)
                {
                    if (currentVillage == null)
                    {
                        yield break;
                    }

                    if (currentVillage.ArmyOwnedId != null)
                    {
                        yield return(currentVillage.ArmyOwnedId.Value);
                    }

                    if (currentVillage.ArmyStationedId != null)
                    {
                        yield return(currentVillage.ArmyStationedId.Value);
                    }

                    if (currentVillage.ArmyTravelingId != null)
                    {
                        yield return(currentVillage.ArmyTravelingId.Value);
                    }
                }

                var currentArmyIds = incomingData.SelectMany(d => SelectCurrentArmyIds(d.CurrentVillage)).ToList();
                var currentArmies  = await CurrentSets.CurrentArmy.Where(army => currentArmyIds.Contains(army.ArmyId)).ToDictionaryAsync(a => a.ArmyId, a => a);

                foreach (var village in incomingData.Select(d => d.CurrentVillage).Where(v => v != null))
                {
                    if (village.ArmyOwnedId != null)
                    {
                        village.ArmyOwned = currentArmies[village.ArmyOwnedId.Value];
                    }
                    if (village.ArmyStationedId != null)
                    {
                        village.ArmyStationed = currentArmies[village.ArmyStationedId.Value];
                    }
                    if (village.ArmyTravelingId != null)
                    {
                        village.ArmyTraveling = currentArmies[village.ArmyTravelingId.Value];
                    }
                }
            }

            var uploadHistory = await Profile("Get user upload history", () =>
                                              context.UserUploadHistory.Where(h => h.Uid == CurrentUserId).FirstOrDefaultAsync()
                                              );

            var           validationInfo     = UploadRestrictionsValidate.ValidateInfo.FromTaggingRestrictions(CurrentUser, uploadHistory);
            List <String> needsUpdateReasons = UploadRestrictionsValidate.GetNeedsUpdateReasons(CurrentServerTime, validationInfo);

            if (needsUpdateReasons != null && needsUpdateReasons.Any())
            {
                return(StatusCode(423, needsUpdateReasons.Select(r => Translate(r)).ToList())); // Status code "Locked"
            }

            //  NOTE - We pull data for all villas requested but only return data for villas not in vaultOwnedVillages,
            //  should stop querying that other data at some point
            var commandSourceVillageIds = incomingData.Select(inc => inc.Command.SourceVillageId).Distinct().ToList();
            var commandTargetVillageIds = incomingData.Select(inc => inc.Command.TargetVillageId).Distinct().ToList();

            var relevantVillages = await(
                from village in CurrentSets.Village
                where commandSourceVillageIds.Contains(village.VillageId) || commandTargetVillageIds.Contains(village.VillageId)
                select new { village.PlayerId, village.VillageId, village.VillageName, X = village.X.Value, Y = village.Y.Value }
                ).ToDictionaryAsync(v => v.VillageId, v => v);

            var sourcePlayerIds = relevantVillages.Values.Where(v => commandSourceVillageIds.Contains(v.VillageId)).Select(v => v.PlayerId ?? 0).ToList();

            //  Don't do any tagging for villages owned by players registered with the vault (so players in other tribes
            //  also using the vault can't infer villa builds)
            var vaultOwnedVillages = await Profile("Get villages owned by vault users", () => (
                                                       from user in CurrentSets.User
                                                       join village in CurrentSets.Village on user.PlayerId equals village.PlayerId
                                                       where user.Enabled
                                                       select village.VillageId
                                                       ).ToListAsync());

            var sourcePlayerNames = await Profile("Get player names", () => (
                                                      from player in CurrentSets.Player
                                                      where sourcePlayerIds.Contains(player.PlayerId)
                                                      select new { player.PlayerId, player.PlayerName }
                                                      ).ToDictionaryAsync(p => p.PlayerId, p => p.PlayerName));

            var countsByVillage = await Profile("Get command counts", () => (
                                                    from command in CurrentSets.Command
                                                    where !command.IsReturning && command.LandsAt > CurrentServerTime
                                                    group command by command.SourceVillageId into villageCommands
                                                    select new { VillageId = villageCommands.Key, Count = villageCommands.Count() }
                                                    ).ToDictionaryAsync(vc => vc.VillageId, vc => vc.Count));

            var travelCalculator = new Features.Simulation.TravelCalculator(CurrentWorldSettings.GameSpeed, CurrentWorldSettings.UnitSpeed);

            DateTime CommandLaunchedAt(Scaffold.Command command) => command.LandsAt - travelCalculator.CalculateTravelTime(
                (command.TroopType ?? "ram").ToTroopType(),
                relevantVillages[command.SourceVillageId].X, relevantVillages[command.SourceVillageId].Y,
                relevantVillages[command.TargetVillageId].X, relevantVillages[command.TargetVillageId].Y
                );

            var earliestLaunchTime = incomingData.Select(inc => CommandLaunchedAt(inc.Command)).DefaultIfEmpty(CurrentServerTime).Min();

            var commandsReturningByVillageId = await Profile("Process returning commands for all source villages", async() =>
            {
                var commandSeenThreshold = earliestLaunchTime - TimeSpan.FromDays(1);

                var sentCommands = await Profile("Query returning commands for all source villages", () => (
                                                     from command in CurrentSets.Command.AsTracking()
                                                     .Include(c => c.Army)
                                                     where command.FirstSeenAt > commandSeenThreshold
                                                     where command.Army != null
                                                     where commandSourceVillageIds.Contains(command.SourceVillageId)
                                                     select command
                                                     ).ToListAsync());

                bool updatedCommands = false;
                var result           = commandSourceVillageIds.ToDictionary(vid => vid, vid => new List <Scaffold.Command>());

                Profile("Update command returning and sort into dictionary", () =>
                {
                    foreach (var cmd in sentCommands)
                    {
                        if (cmd.LandsAt <= CurrentServerTime)
                        {
                            if (!cmd.IsReturning)
                            {
                                updatedCommands = true;
                                cmd.IsReturning = true;
                            }

                            result[cmd.SourceVillageId].Add(cmd);
                        }
                    }
                });

                if (updatedCommands)
                {
                    await Profile("Save commands now set to returning", () => context.SaveChangesAsync());
                }

                return(result);
            });

            var otherTargetedVillageIds = commandsReturningByVillageId.SelectMany(kvp => kvp.Value).Select(c => c.TargetVillageId).Distinct().Except(relevantVillages.Keys);
            var otherTargetVillages     = await Profile("Get other villages targeted by inc source villas", () =>
                                                        CurrentSets.Village.Where(v => otherTargetedVillageIds.Contains(v.VillageId)).ToListAsync()
                                                        );

            foreach (var id in otherTargetedVillageIds)
            {
                var village = otherTargetVillages.First(v => v.VillageId == id);
                relevantVillages.Add(id, new
                {
                    village.PlayerId, village.VillageId, village.VillageName, X = village.X.Value, Y = village.Y.Value
                });
            }

            var launchTimesByCommandId = commandsReturningByVillageId.SelectMany(kvp => kvp.Value).Where(cmd => !vaultOwnedVillages.Contains(cmd.SourceVillageId)).ToDictionary(
                cmd => cmd.CommandId,
                cmd => CommandLaunchedAt(cmd)
                );

            IEnumerable <Scaffold.Command> RelevantCommandsForIncoming(Scaffold.Command incoming)
            {
                if (!relevantVillages.ContainsKey(incoming.SourceVillageId))
                {
                    return(Enumerable.Empty <Scaffold.Command>());
                }

                var launchTime        = CommandLaunchedAt(incoming);
                var returningCommands = commandsReturningByVillageId.GetValueOrDefault(incoming.SourceVillageId);

                if (returningCommands == null)
                {
                    return(Enumerable.Empty <Scaffold.Command>());
                }

                return(returningCommands.Where(cmd => cmd.ReturnsAt > launchTime || (launchTimesByCommandId.ContainsKey(cmd.CommandId) && launchTimesByCommandId[cmd.CommandId] > launchTime)));
            }

            var duplicates = incomingData.GroupBy(i => i.Command.CommandId).Where(g => g.Count() > 1).ToDictionary(g => g.Key, g => g.ToList());

            Dictionary <long, JSON.IncomingTag> resultTags = new Dictionary <long, JSON.IncomingTag>();

            Profile("Make incomings tags", () =>
            {
                foreach (var data in incomingData)
                {
                    var incoming        = data.Command;
                    var sourceVillageId = incoming.SourceVillageId;
                    if (vaultOwnedVillages.Contains(sourceVillageId))
                    {
                        continue;
                    }

                    var sourceCurrentVillage = data.CurrentVillage;
                    var commandsReturning    = RelevantCommandsForIncoming(incoming);

                    var armyOwned     = sourceCurrentVillage?.ArmyOwned;
                    var armyTraveling = sourceCurrentVillage?.ArmyTraveling;
                    var armyStationed = sourceCurrentVillage?.ArmyStationed;

                    //  TODO - Make this a setting
                    var maxUpdateTime = TimeSpan.FromDays(4);

                    if (armyOwned?.LastUpdated != null && (CurrentServerTime - armyOwned.LastUpdated.Value > maxUpdateTime))
                    {
                        armyOwned = null;
                    }

                    if (armyTraveling?.LastUpdated != null && (CurrentServerTime - armyTraveling.LastUpdated.Value > maxUpdateTime))
                    {
                        armyTraveling = null;
                    }

                    if (armyStationed?.LastUpdated != null && (CurrentServerTime - armyStationed.LastUpdated.Value > maxUpdateTime))
                    {
                        armyStationed = null;
                    }

                    if (armyOwned != null && armyOwned.IsEmpty())
                    {
                        armyOwned = null;
                    }
                    if (armyTraveling != null && armyTraveling.IsEmpty())
                    {
                        armyTraveling = null;
                    }
                    if (armyStationed != null && armyStationed.IsEmpty())
                    {
                        armyStationed = null;
                    }

                    var troopsReturning = new JSON.Army();
                    if (commandsReturning != null)
                    {
                        foreach (var command in commandsReturning)
                        {
                            troopsReturning += ArmyConvert.ArmyToJson(command.Army);
                        }
                    }

                    Scaffold.CurrentArmy effectiveArmy = null;
                    bool isConfidentArmy = true;
                    if (armyOwned != null)
                    {
                        effectiveArmy = armyOwned;
                    }
                    else if (armyTraveling != null)
                    {
                        effectiveArmy = armyTraveling;
                    }
                    else if (armyStationed != null)
                    {
                        effectiveArmy   = armyStationed;
                        isConfidentArmy = false;
                    }

                    var tag            = new JSON.IncomingTag();
                    tag.CommandId      = incoming.CommandId;
                    tag.OriginalTag    = incoming.UserLabel;
                    tag.NumFromVillage = countsByVillage.GetValueOrDefault(sourceVillageId);
                    tag.TroopType      = TroopTypeConvert.StringToTroopType(incoming.TroopType);

                    var sourceVillage       = relevantVillages[incoming.SourceVillageId];
                    var targetVillage       = relevantVillages[incoming.TargetVillageId];
                    tag.SourceVillageCoords = $"{sourceVillage.X}|{sourceVillage.Y}";
                    tag.TargetVillageCoords = $"{targetVillage.X}|{targetVillage.Y}";
                    tag.SourcePlayerName    = sourcePlayerNames.GetValueOrDefault(incoming.SourcePlayerId, Translate("UNKNOWN")).UrlDecode();
                    tag.SourceVillageName   = sourceVillage.VillageName.UrlDecode();
                    tag.TargetVillageName   = targetVillage.VillageName.UrlDecode();
                    tag.Distance            = new Coordinate {
                        X = sourceVillage.X, Y = sourceVillage.Y
                    }.DistanceTo(targetVillage.X, targetVillage.Y);

                    if (effectiveArmy != null)
                    {
                        //  TODO - Make this a setting
                        bool isOffense = ArmyStats.IsOffensive(effectiveArmy);

                        tag.VillageType = isOffense ? Translate("OFFENSE") : Translate("DEFENSE");

                        if (!isOffense && isConfidentArmy && (effectiveArmy.Snob == null || effectiveArmy.Snob == 0) && incoming.TroopType != JSON.TroopType.Snob.ToTroopString())
                        {
                            tag.DefiniteFake = true;
                        }

                        var offensiveArmy = effectiveArmy.OfType(JSON.UnitBuild.Offensive);
                        var jsonArmy      = ArmyConvert.ArmyToJson(offensiveArmy);
                        var pop           = Native.ArmyStats.CalculateTotalPopulation(jsonArmy);

                        var returningOffensiveArmy = troopsReturning.OfType(JSON.UnitBuild.Offensive);
                        var returningPop           = Native.ArmyStats.CalculateTotalPopulation(returningOffensiveArmy);

                        tag.OffensivePopulation = pop - returningPop;
                        if (tag.OffensivePopulation < 0)
                        {
                            tag.OffensivePopulation = 0;
                        }

                        if ((tag.OffensivePopulation > 100 || returningPop > 5000) && tag.OffensivePopulation < 5000 && isConfidentArmy)
                        {
                            tag.DefiniteFake = true;
                        }

                        tag.ReturningPopulation = returningPop;

                        tag.NumCats = effectiveArmy.Catapult;
                    }

                    resultTags.Add(incoming.CommandId, tag);
                }
            });

            return(Ok(resultTags));
        }