Skip to content

Commit c5264aa

Browse files
authored
Tweak county development calculation, distribute excess development (#2002) #minor
1 parent 8c308c7 commit c5264aa

File tree

2 files changed

+85
-24
lines changed

2 files changed

+85
-24
lines changed

ImperatorToCK3.UnitTests/CK3/Titles/LandedTitlesTests.cs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -374,11 +374,11 @@ public void DevelopmentIsCorrectlyCalculatedFor1ProvinceTo1BaronyCountyMapping()
374374
var date = config.CK3BookmarkDate;
375375
titles.ImportDevelopmentFromImperator(ck3Provinces, date, defaultConfig.ImperatorCivilizationWorth);
376376

377-
Assert.Equal(6, titles["c_county1"].GetDevelopmentLevel(date)); // 0.4*25=10; 10-sqrt(10)≈6
377+
Assert.Equal(8, titles["c_county1"].GetDevelopmentLevel(date)); // 0.4*(25-sqrt(25)) ≈ 8
378378
}
379379

380380
[Fact]
381-
public void DevelopmentFromImperatorProvinceCanBeSplitForTargetProvinces() {
381+
public void DevelopmentFromImperatorProvinceCanBeUsedForMultipleCK3Provinces() {
382382
var conversionDate = new Date(476, 1, 1);
383383
var config = new Configuration { CK3BookmarkDate = conversionDate };
384384
var titles = new Title.LandedTitles();
@@ -406,9 +406,9 @@ public void DevelopmentFromImperatorProvinceCanBeSplitForTargetProvinces() {
406406
var date = config.CK3BookmarkDate;
407407
titles.ImportDevelopmentFromImperator(ck3Provinces, date, defaultConfig.ImperatorCivilizationWorth);
408408

409-
Assert.Equal(1, titles["c_county1"].GetDevelopmentLevel(date)); // 0.4*7=2.8; 2.8-sqrt(2.8)≈1
410-
Assert.Equal(1, titles["c_county2"].GetDevelopmentLevel(date)); // same as above
411-
Assert.Equal(1, titles["c_county3"].GetDevelopmentLevel(date)); // same as above
409+
Assert.Equal(6, titles["c_county1"].GetDevelopmentLevel(date)); // 0.4 * (21-sqrt(21) ≈ 6
410+
Assert.Equal(6, titles["c_county2"].GetDevelopmentLevel(date)); // same as above
411+
Assert.Equal(6, titles["c_county3"].GetDevelopmentLevel(date)); // same as above
412412
}
413413

414414
[Fact]
@@ -439,8 +439,12 @@ public void DevelopmentOfCountyIsCalculatedFromAllCountyProvinces() {
439439

440440
var date = config.CK3BookmarkDate;
441441
titles.ImportDevelopmentFromImperator(ck3Provinces, date, defaultConfig.ImperatorCivilizationWorth);
442-
443-
Assert.Equal(6, titles["c_county1"].GetDevelopmentLevel(date)); // 0.4*(10+40)/2=10; 10-sqrt(10)≈6
442+
443+
// Dev from province 1: 10-sqrt(10) ≈ 6
444+
// Dev from province 2: 40-sqrt(40) ≈ 33
445+
// Average: (6+33)/2 ≈ 19
446+
// Average multiplied by civilization worth: 0.4*19 ≈ 8
447+
Assert.Equal(8, titles["c_county1"].GetDevelopmentLevel(date));
444448
}
445449

446450
[Fact]

ImperatorToCK3/CK3/Titles/LandedTitles.cs

Lines changed: 74 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,11 +1124,11 @@ private HashSet<string> GetCountyHolderIds(Date date) {
11241124
}
11251125

11261126
public void ImportDevelopmentFromImperator(ProvinceCollection ck3Provinces, Date date, double irCivilizationWorth) {
1127-
static bool IsCountyOutsideImperatorMap(Title county, IReadOnlyDictionary<string, int> impProvsPerCounty) {
1128-
return impProvsPerCounty[county.Id] == 0;
1127+
static bool IsCountyOutsideImperatorMap(Title county, IReadOnlyDictionary<string, int> irProvsPerCounty) {
1128+
return irProvsPerCounty[county.Id] == 0;
11291129
}
11301130

1131-
double CalculateCountyDevelopment(Title county, IReadOnlyDictionary<ulong, int> ck3ProvsPerIRProv) {
1131+
double CalculateCountyDevelopment(Title county) {
11321132
double dev = 0;
11331133
IEnumerable<ulong> countyProvinceIds = county.CountyProvinceIds;
11341134
int provsCount = 0;
@@ -1137,44 +1137,48 @@ double CalculateCountyDevelopment(Title county, IReadOnlyDictionary<ulong, int>
11371137
Logger.Warn($"CK3 province {ck3ProvId} not found!");
11381138
continue;
11391139
}
1140-
++provsCount;
11411140
var sourceProvinces = ck3Province.ImperatorProvinces;
11421141
if (sourceProvinces.Count == 0) {
11431142
continue;
11441143
}
1145-
1146-
dev += sourceProvinces.Sum(srcProv => srcProv.CivilizationValue / ck3ProvsPerIRProv[srcProv.Id]);
1144+
++provsCount;
1145+
1146+
var devFromProvince = sourceProvinces.Average(srcProv => srcProv.CivilizationValue);
1147+
dev += devFromProvince;
11471148
}
11481149

1150+
dev = Math.Max(0, dev - Math.Sqrt(dev));
1151+
if (provsCount > 0) {
1152+
dev /= provsCount;
1153+
}
11491154
dev *= irCivilizationWorth;
1150-
dev /= provsCount;
1151-
dev -= Math.Sqrt(dev);
11521155
return dev;
11531156
}
11541157

11551158
Logger.Info("Importing development from Imperator...");
11561159

11571160
var counties = this.Where(t => t.Rank == TitleRank.county).ToList();
1158-
var (irProvsPerCounty, ck3ProvsPerImperatorProv) = GetIRProvsPerCounty(ck3Provinces, counties);
1161+
var irProvsPerCounty = GetIRProvsPerCounty(ck3Provinces, counties);
11591162

11601163
foreach (var county in counties) {
11611164
if (IsCountyOutsideImperatorMap(county, irProvsPerCounty)) {
11621165
// Don't change development for counties outside of Imperator map.
11631166
continue;
11641167
}
11651168

1166-
double dev = CalculateCountyDevelopment(county, ck3ProvsPerImperatorProv);
1169+
double dev = CalculateCountyDevelopment(county);
11671170

11681171
county.History.Fields.Remove("development_level");
11691172
county.History.AddFieldValue(date, "development_level", "change_development_level", (int)dev);
11701173
}
1174+
1175+
DistributeExcessDevelopment(date);
11711176

11721177
Logger.IncrementProgress();
11731178
return;
11741179

1175-
static (Dictionary<string, int>, Dictionary<ulong, int>) GetIRProvsPerCounty(ProvinceCollection ck3Provinces, IEnumerable<Title> counties) {
1176-
Dictionary<string, int> impProvsPerCounty = [];
1177-
Dictionary<ulong, int> ck3ProvsPerImperatorProv = [];
1180+
static Dictionary<string, int> GetIRProvsPerCounty(ProvinceCollection ck3Provinces, IEnumerable<Title> counties) {
1181+
Dictionary<string, int> irProvsPerCounty = [];
11781182
foreach (var county in counties) {
11791183
HashSet<ulong> imperatorProvs = [];
11801184
foreach (ulong ck3ProvId in county.CountyProvinceIds) {
@@ -1186,15 +1190,68 @@ double CalculateCountyDevelopment(Title county, IReadOnlyDictionary<ulong, int>
11861190
var sourceProvinces = ck3Province.ImperatorProvinces;
11871191
foreach (var irProvince in sourceProvinces) {
11881192
imperatorProvs.Add(irProvince.Id);
1189-
ck3ProvsPerImperatorProv.TryGetValue(irProvince.Id, out var currentValue);
1190-
ck3ProvsPerImperatorProv[irProvince.Id] = currentValue + 1;
11911193
}
11921194
}
11931195

1194-
impProvsPerCounty[county.Id] = imperatorProvs.Count;
1196+
irProvsPerCounty[county.Id] = imperatorProvs.Count;
11951197
}
11961198

1197-
return (impProvsPerCounty, ck3ProvsPerImperatorProv);
1199+
return irProvsPerCounty;
1200+
}
1201+
}
1202+
1203+
private void DistributeExcessDevelopment(Date date) {
1204+
var topRealms = this
1205+
.Where(t => t.Rank > TitleRank.county && t.GetHolderId(date) != "0" && t.GetDeFactoLiege(date) is null)
1206+
.ToList();
1207+
1208+
// For every realm, get list of counties with over 100 development.
1209+
// Distribute the excess development to the realm's least developed counties.
1210+
foreach (var realm in topRealms) {
1211+
var realmCounties = realm.GetDeFactoVassalsAndBelow(date, "c").Values
1212+
.Select(c => new {County = c, Development = c.GetOwnOrInheritedDevelopmentLevel(date)})
1213+
.Where(c => c.Development.HasValue)
1214+
.Select(c => new {c.County, Development = c.Development!.Value})
1215+
.ToList();
1216+
var excessDevCounties = realmCounties
1217+
.Where(c => c.Development > 100)
1218+
.OrderByDescending(c => c.Development)
1219+
.ToList();
1220+
if (excessDevCounties.Count == 0) {
1221+
continue;
1222+
}
1223+
1224+
var leastDevCounties = realmCounties
1225+
.Where(c => c.Development < 100)
1226+
.OrderBy(c => c.Development)
1227+
.Select(c => c.County)
1228+
.ToList();
1229+
if (leastDevCounties.Count == 0) {
1230+
continue;
1231+
}
1232+
1233+
var excessDevSum = excessDevCounties.Sum(c => c.Development - 100);
1234+
Logger.Debug($"Top realm {realm.Id} has {excessDevSum} excess development to distribute among {leastDevCounties.Count} counties.");
1235+
1236+
// Now that we've calculated the excess dev, we can cap the county dev at 100.
1237+
foreach (var excessDevCounty in excessDevCounties) {
1238+
excessDevCounty.County.SetDevelopmentLevel(100, date);
1239+
}
1240+
1241+
while (excessDevSum > 0 && leastDevCounties.Count > 0) {
1242+
var devPerCounty = excessDevSum / leastDevCounties.Count;
1243+
foreach (var county in leastDevCounties.ToList()) {
1244+
var currentDev = county.GetOwnOrInheritedDevelopmentLevel(date) ?? 0;
1245+
var devToAdd = Math.Max(devPerCounty, 100 - currentDev);
1246+
var newDevValue = currentDev + devToAdd;
1247+
1248+
county.SetDevelopmentLevel(newDevValue, date);
1249+
excessDevSum -= devToAdd;
1250+
if (newDevValue >= 100) {
1251+
leastDevCounties.Remove(county);
1252+
}
1253+
}
1254+
}
11981255
}
11991256
}
12001257

0 commit comments

Comments
 (0)