static void TestRecruitment() { var calculator = new RecruitmentCalculator(2, 25, 20, 10, 1); for (int i = 0; i < 5; i++) { var timeSpan = TimeSpan.FromDays(3 * (i + 1)); var defense = calculator.CalculatePossibleDefenseRecruitment(timeSpan); var offense = calculator.CalculatePossibleOffenseRecruitment(timeSpan); Console.WriteLine("\n\nIn {0} hours:", timeSpan.TotalHours); Console.WriteLine("[Offense]"); PrintArmy(offense); Console.WriteLine("\n[Defense]"); PrintArmy(defense); } }
public async Task<IActionResult> GetVillageArmy(long villageId, int? morale) { if (!await CanReadVillage(villageId)) return StatusCode(401); PreloadWorldData(); var uploadHistory = await Profile("Get user upload history", () => context.UserUploadHistory.Where(h => h.Uid == CurrentUserId).FirstOrDefaultAsync() ); var validationInfo = UploadRestrictionsValidate.ValidateInfo.FromMapRestrictions(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" } // Start getting village data var currentVillage = await Profile("Get current village", () => ( from cv in CurrentSets.CurrentVillage .Include(v => v.ArmyAtHome) .Include(v => v.ArmyOwned) .Include(v => v.ArmyStationed) .Include(v => v.ArmyTraveling) .Include(v => v.ArmyRecentLosses) .Include(v => v.CurrentBuilding) where cv.VillageId == villageId select cv ).FirstOrDefaultAsync()); var commandsToVillage = await Profile("Get commands to village", () => ( from command in CurrentSets.Command .Include(c => c.Army) where command.TargetVillageId == villageId where !command.IsReturning where command.LandsAt > CurrentServerTime select command ).ToListAsync()); var latestConquerTimestamp = await Profile("Get latest conquer", () => ( from conquer in CurrentSets.Conquer where conquer.VillageId == villageId orderby conquer.UnixTimestamp descending select conquer.UnixTimestamp ).FirstOrDefaultAsync()); var jsonData = new JSON.VillageData(); // Return empty data if no data is available for the village if (currentVillage == null) return Ok(jsonData); Profile("Populate JSON data", () => { if (currentVillage.ArmyOwned?.LastUpdated != null) { jsonData.OwnedArmy = ArmyConvert.ArmyToJson(currentVillage.ArmyOwned); jsonData.OwnedArmySeenAt = currentVillage.ArmyOwned.LastUpdated; } if (currentVillage.ArmyRecentLosses?.LastUpdated != null) { jsonData.RecentlyLostArmy = ArmyConvert.ArmyToJson(currentVillage.ArmyRecentLosses); jsonData.RecentlyLostArmySeenAt = currentVillage.ArmyRecentLosses.LastUpdated; } if (currentVillage.ArmyStationed?.LastUpdated != null) { jsonData.StationedArmy = ArmyConvert.ArmyToJson(currentVillage.ArmyStationed); jsonData.StationedSeenAt = currentVillage.ArmyStationed.LastUpdated; } if (currentVillage.ArmyTraveling?.LastUpdated != null) { jsonData.TravelingArmy = ArmyConvert.ArmyToJson(currentVillage.ArmyTraveling); jsonData.TravelingSeenAt = currentVillage.ArmyTraveling.LastUpdated; } if (currentVillage.ArmyAtHome?.LastUpdated != null) { jsonData.AtHomeArmy = ArmyConvert.ArmyToJson(currentVillage.ArmyAtHome); jsonData.AtHomeSeenAt = currentVillage.ArmyAtHome.LastUpdated; } jsonData.LastLoyalty = currentVillage.Loyalty; jsonData.LastLoyaltySeenAt = currentVillage.LoyaltyLastUpdated; if (currentVillage.Loyalty != null) { var loyaltyCalculator = new LoyaltyCalculator(CurrentWorldSettings.GameSpeed); jsonData.PossibleLoyalty = loyaltyCalculator.PossibleLoyalty(currentVillage.Loyalty.Value, CurrentServerTime - currentVillage.LoyaltyLastUpdated.Value); } jsonData.LastBuildings = BuildingConvert.CurrentBuildingToJson(currentVillage.CurrentBuilding); jsonData.LastBuildingsSeenAt = currentVillage.CurrentBuilding?.LastUpdated; if (currentVillage.CurrentBuilding?.LastUpdated != null) { var constructionCalculator = new ConstructionCalculator(); jsonData.PossibleBuildings = constructionCalculator.CalculatePossibleBuildings(jsonData.LastBuildings, CurrentServerTime - currentVillage.CurrentBuilding.LastUpdated.Value); } if (currentVillage.ArmyStationed?.LastUpdated != null) { var battleSimulator = new BattleSimulator(); short wallLevel = currentVillage.CurrentBuilding?.Wall ?? 20; short hqLevel = currentVillage.CurrentBuilding?.Main ?? 20; if (currentVillage.CurrentBuilding != null) wallLevel += new ConstructionCalculator().CalculateLevelsInTimeSpan(BuildingType.Wall, hqLevel, wallLevel, CurrentServerTime - currentVillage.CurrentBuilding.LastUpdated.Value); var nukeEstimation = battleSimulator.EstimateRequiredNukes(jsonData.StationedArmy, wallLevel, CurrentWorldSettings.ArchersEnabled, morale ?? 100); jsonData.NukesRequired = nukeEstimation.NukesRequired; jsonData.LastNukeLossPercent = (int)(nukeEstimation.LastNukeLossesPercent); } // Might have CurrentArmy entries but they're just empty/null - not based on any report data if (jsonData.OwnedArmy != null && (jsonData.OwnedArmySeenAt == null || jsonData.OwnedArmy.Count == 0)) { jsonData.OwnedArmy = null; jsonData.OwnedArmySeenAt = null; } if (jsonData.StationedArmy != null && (jsonData.StationedSeenAt == null || jsonData.StationedArmy.Count == 0)) { jsonData.StationedArmy = null; jsonData.StationedSeenAt = null; } if (jsonData.TravelingArmy != null && (jsonData.TravelingSeenAt == null || jsonData.TravelingArmy.Count == 0)) { jsonData.TravelingArmy = null; jsonData.TravelingSeenAt = null; } if (jsonData.RecentlyLostArmy != null && (jsonData.RecentlyLostArmySeenAt == null || jsonData.RecentlyLostArmy.Count == 0)) { jsonData.RecentlyLostArmy = null; jsonData.RecentlyLostArmySeenAt = null; } var armyCalculator = new RecruitmentCalculator(2, jsonData.LastBuildings); DateTime? localArmyLastSeenAt = null; int? availableArmyPopulation = null; if (jsonData.StationedArmy != null) { localArmyLastSeenAt = jsonData.StationedSeenAt.Value; var existingPop = ArmyStats.CalculateTotalPopulation(jsonData.StationedArmy); availableArmyPopulation = Math.Max(0, armyCalculator.MaxPopulation - existingPop); } var conquerTime = DateTimeOffset.FromUnixTimeMilliseconds(latestConquerTimestamp).UtcDateTime; bool useConquer = false; if (localArmyLastSeenAt == null) useConquer = true; else useConquer = conquerTime > localArmyLastSeenAt.Value; if (useConquer) { localArmyLastSeenAt = conquerTime; availableArmyPopulation = armyCalculator.MaxPopulation; } // Add recruitment estimations if (localArmyLastSeenAt != null) { var timeSinceSeen = CurrentServerTime - localArmyLastSeenAt.Value; armyCalculator.MaxPopulation = availableArmyPopulation.Value; // No point in estimating troops if there's been 2 weeks since we saw stationed troops if (timeSinceSeen.TotalDays < 14) { jsonData.PossibleRecruitedOffensiveArmy = armyCalculator.CalculatePossibleOffenseRecruitment(timeSinceSeen); jsonData.PossibleRecruitedDefensiveArmy = armyCalculator.CalculatePossibleDefenseRecruitment(timeSinceSeen); } } // Add command summaries jsonData.DVs = new Dictionary<long, int>(); jsonData.Fakes = new List<long>(); jsonData.Nukes = new List<long>(); jsonData.Nobles = new List<long>(); jsonData.Players = commandsToVillage.Select(c => c.SourcePlayerId).Distinct().ToList(); foreach (var command in commandsToVillage.Where(c => c.Army != null)) { var army = ArmyConvert.ArmyToJson(command.Army); var offensivePop = ArmyStats.CalculateTotalPopulation(army.OfType(JSON.UnitBuild.Offensive)); var defensivePop = ArmyStats.CalculateTotalPopulation(army.OfType(JSON.UnitBuild.Defensive)); var isNoble = command.Army.Snob > 0 && command.IsAttack; bool isFake = false; bool isNuke = false; if (!army.Values.Any(cnt => cnt > 1) && !isNoble) { isFake = true; } else if (command.IsAttack && offensivePop > 10000) { isNuke = true; } if (isFake) jsonData.Fakes.Add(command.CommandId); else if (isNuke) jsonData.Nukes.Add(command.CommandId); else if (defensivePop > 3000 && !command.IsAttack) jsonData.DVs.Add(command.CommandId, defensivePop); if (isNoble) jsonData.Nobles.Add(command.CommandId); } }); return Ok(jsonData); }