Skip to content

Commit

Permalink
Tweak county development calculation, distribute excess development (#…
Browse files Browse the repository at this point in the history
…2002) #minor
  • Loading branch information
IhateTrains authored Jun 22, 2024
1 parent 8c308c7 commit c5264aa
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 24 deletions.
18 changes: 11 additions & 7 deletions ImperatorToCK3.UnitTests/CK3/Titles/LandedTitlesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -374,11 +374,11 @@ public void DevelopmentIsCorrectlyCalculatedFor1ProvinceTo1BaronyCountyMapping()
var date = config.CK3BookmarkDate;
titles.ImportDevelopmentFromImperator(ck3Provinces, date, defaultConfig.ImperatorCivilizationWorth);

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

[Fact]
public void DevelopmentFromImperatorProvinceCanBeSplitForTargetProvinces() {
public void DevelopmentFromImperatorProvinceCanBeUsedForMultipleCK3Provinces() {
var conversionDate = new Date(476, 1, 1);
var config = new Configuration { CK3BookmarkDate = conversionDate };
var titles = new Title.LandedTitles();
Expand Down Expand Up @@ -406,9 +406,9 @@ public void DevelopmentFromImperatorProvinceCanBeSplitForTargetProvinces() {
var date = config.CK3BookmarkDate;
titles.ImportDevelopmentFromImperator(ck3Provinces, date, defaultConfig.ImperatorCivilizationWorth);

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

[Fact]
Expand Down Expand Up @@ -439,8 +439,12 @@ public void DevelopmentOfCountyIsCalculatedFromAllCountyProvinces() {

var date = config.CK3BookmarkDate;
titles.ImportDevelopmentFromImperator(ck3Provinces, date, defaultConfig.ImperatorCivilizationWorth);

Assert.Equal(6, titles["c_county1"].GetDevelopmentLevel(date)); // 0.4*(10+40)/2=10; 10-sqrt(10)≈6

// Dev from province 1: 10-sqrt(10) ≈ 6
// Dev from province 2: 40-sqrt(40) ≈ 33
// Average: (6+33)/2 ≈ 19
// Average multiplied by civilization worth: 0.4*19 ≈ 8
Assert.Equal(8, titles["c_county1"].GetDevelopmentLevel(date));
}

[Fact]
Expand Down
91 changes: 74 additions & 17 deletions ImperatorToCK3/CK3/Titles/LandedTitles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1124,11 +1124,11 @@ private HashSet<string> GetCountyHolderIds(Date date) {
}

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

double CalculateCountyDevelopment(Title county, IReadOnlyDictionary<ulong, int> ck3ProvsPerIRProv) {
double CalculateCountyDevelopment(Title county) {
double dev = 0;
IEnumerable<ulong> countyProvinceIds = county.CountyProvinceIds;
int provsCount = 0;
Expand All @@ -1137,44 +1137,48 @@ double CalculateCountyDevelopment(Title county, IReadOnlyDictionary<ulong, int>
Logger.Warn($"CK3 province {ck3ProvId} not found!");
continue;
}
++provsCount;
var sourceProvinces = ck3Province.ImperatorProvinces;
if (sourceProvinces.Count == 0) {
continue;
}

dev += sourceProvinces.Sum(srcProv => srcProv.CivilizationValue / ck3ProvsPerIRProv[srcProv.Id]);
++provsCount;

var devFromProvince = sourceProvinces.Average(srcProv => srcProv.CivilizationValue);
dev += devFromProvince;
}

dev = Math.Max(0, dev - Math.Sqrt(dev));
if (provsCount > 0) {
dev /= provsCount;
}
dev *= irCivilizationWorth;
dev /= provsCount;
dev -= Math.Sqrt(dev);
return dev;
}

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

var counties = this.Where(t => t.Rank == TitleRank.county).ToList();
var (irProvsPerCounty, ck3ProvsPerImperatorProv) = GetIRProvsPerCounty(ck3Provinces, counties);
var irProvsPerCounty = GetIRProvsPerCounty(ck3Provinces, counties);

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

double dev = CalculateCountyDevelopment(county, ck3ProvsPerImperatorProv);
double dev = CalculateCountyDevelopment(county);

county.History.Fields.Remove("development_level");
county.History.AddFieldValue(date, "development_level", "change_development_level", (int)dev);
}

DistributeExcessDevelopment(date);

Logger.IncrementProgress();
return;

static (Dictionary<string, int>, Dictionary<ulong, int>) GetIRProvsPerCounty(ProvinceCollection ck3Provinces, IEnumerable<Title> counties) {
Dictionary<string, int> impProvsPerCounty = [];
Dictionary<ulong, int> ck3ProvsPerImperatorProv = [];
static Dictionary<string, int> GetIRProvsPerCounty(ProvinceCollection ck3Provinces, IEnumerable<Title> counties) {
Dictionary<string, int> irProvsPerCounty = [];
foreach (var county in counties) {
HashSet<ulong> imperatorProvs = [];
foreach (ulong ck3ProvId in county.CountyProvinceIds) {
Expand All @@ -1186,15 +1190,68 @@ double CalculateCountyDevelopment(Title county, IReadOnlyDictionary<ulong, int>
var sourceProvinces = ck3Province.ImperatorProvinces;
foreach (var irProvince in sourceProvinces) {
imperatorProvs.Add(irProvince.Id);
ck3ProvsPerImperatorProv.TryGetValue(irProvince.Id, out var currentValue);
ck3ProvsPerImperatorProv[irProvince.Id] = currentValue + 1;
}
}

impProvsPerCounty[county.Id] = imperatorProvs.Count;
irProvsPerCounty[county.Id] = imperatorProvs.Count;
}

return (impProvsPerCounty, ck3ProvsPerImperatorProv);
return irProvsPerCounty;
}
}

private void DistributeExcessDevelopment(Date date) {
var topRealms = this
.Where(t => t.Rank > TitleRank.county && t.GetHolderId(date) != "0" && t.GetDeFactoLiege(date) is null)
.ToList();

// For every realm, get list of counties with over 100 development.
// Distribute the excess development to the realm's least developed counties.
foreach (var realm in topRealms) {
var realmCounties = realm.GetDeFactoVassalsAndBelow(date, "c").Values
.Select(c => new {County = c, Development = c.GetOwnOrInheritedDevelopmentLevel(date)})
.Where(c => c.Development.HasValue)
.Select(c => new {c.County, Development = c.Development!.Value})
.ToList();
var excessDevCounties = realmCounties
.Where(c => c.Development > 100)
.OrderByDescending(c => c.Development)
.ToList();
if (excessDevCounties.Count == 0) {
continue;
}

var leastDevCounties = realmCounties
.Where(c => c.Development < 100)
.OrderBy(c => c.Development)
.Select(c => c.County)
.ToList();
if (leastDevCounties.Count == 0) {
continue;
}

var excessDevSum = excessDevCounties.Sum(c => c.Development - 100);
Logger.Debug($"Top realm {realm.Id} has {excessDevSum} excess development to distribute among {leastDevCounties.Count} counties.");

// Now that we've calculated the excess dev, we can cap the county dev at 100.
foreach (var excessDevCounty in excessDevCounties) {
excessDevCounty.County.SetDevelopmentLevel(100, date);
}

while (excessDevSum > 0 && leastDevCounties.Count > 0) {
var devPerCounty = excessDevSum / leastDevCounties.Count;
foreach (var county in leastDevCounties.ToList()) {
var currentDev = county.GetOwnOrInheritedDevelopmentLevel(date) ?? 0;
var devToAdd = Math.Max(devPerCounty, 100 - currentDev);
var newDevValue = currentDev + devToAdd;

county.SetDevelopmentLevel(newDevValue, date);
excessDevSum -= devToAdd;
if (newDevValue >= 100) {
leastDevCounties.Remove(county);
}
}
}
}
}

Expand Down

0 comments on commit c5264aa

Please sign in to comment.