From 4a6f032d1e618c8d2d483c3b75febf2b1fe35db8 Mon Sep 17 00:00:00 2001 From: Gawquon Date: Thu, 29 Aug 2024 01:19:04 -0400 Subject: [PATCH] progess marketTracker --- .../EconomyManager/Demand/MarketJobs.cpp | 14 +- .../EconomyManager/Demand/MarketJobs.h | 6 +- .../EconomyManager/Demand/MarketTracker.cpp | 137 +++++++++++++++++- .../EconomyManager/Demand/MarketTracker.h | 42 ++++-- .../V3World/EconomyManager/EconomyManager.cpp | 62 ++------ .../V3World/EconomyManager/EconomyManager.h | 16 +- .../DemandTests/MarketJobsTests.cpp | 18 ++- 7 files changed, 220 insertions(+), 75 deletions(-) diff --git a/EU4ToVic3/Source/V3World/EconomyManager/Demand/MarketJobs.cpp b/EU4ToVic3/Source/V3World/EconomyManager/Demand/MarketJobs.cpp index df6eebbb8..7eeec5b7f 100644 --- a/EU4ToVic3/Source/V3World/EconomyManager/Demand/MarketJobs.cpp +++ b/EU4ToVic3/Source/V3World/EconomyManager/Demand/MarketJobs.cpp @@ -3,12 +3,8 @@ #include -V3::MarketJobs::MarketJobs(const std::map& jobsList, const std::vector>& manorHouseRoster): - jobCounts(jobsList), manorHouseRoster(manorHouseRoster) +V3::MarketJobs::MarketJobs(const std::vector>& manorHouseRoster): manorHouseRoster(manorHouseRoster) { - population = std::accumulate(jobCounts.begin(), jobCounts.end(), 0, [](int sum, const auto& pair) { - return sum + static_cast(pair.second); - }); } // Returns a map of each job as a percentage of all jobs in the market. @@ -32,6 +28,14 @@ void V3::MarketJobs::createJobs(const PopType& popType, double amount, const dou jobCounts[popType.getType()] += amount - shortage; } +void V3::MarketJobs::loadInitialJobs(const std::map jobsList) +{ + jobCounts = jobsList; + population = std::accumulate(jobCounts.begin(), jobCounts.end(), 0, [](int sum, const auto& pair) { + return sum + static_cast(pair.second); + }); +} + double V3::MarketJobs::hireFromWorseJobs(double amount, const int peasantsPerLevel) { amount = hireFromUnemployed(amount); diff --git a/EU4ToVic3/Source/V3World/EconomyManager/Demand/MarketJobs.h b/EU4ToVic3/Source/V3World/EconomyManager/Demand/MarketJobs.h index 3be11a744..4234cf5bf 100644 --- a/EU4ToVic3/Source/V3World/EconomyManager/Demand/MarketJobs.h +++ b/EU4ToVic3/Source/V3World/EconomyManager/Demand/MarketJobs.h @@ -9,13 +9,15 @@ class MarketJobs { public: MarketJobs() = default; - MarketJobs(const std::map& jobsList, const std::vector>& manorHouseRoster); + explicit MarketJobs(const std::vector>& manorHouseRoster); [[nodsicard]] const auto& getPop() const { return population; } [[nodsicard]] const auto& getJobCounts() const { return jobCounts; } [[nodsicard]] std::map getJobBreakdown() const; void createJobs(const PopType& popType, double amount, double defaultRatio, double womenJobRate, int peasantsPerLevel); + void clearJobs() { jobCounts.clear(); } + void loadInitialJobs(const std::map jobsList); private: int population = 0; @@ -24,7 +26,7 @@ class MarketJobs double hireFromWorseJobs(double amount, int peasantsPerLevel); // Returns the amount of jobs with no workers available. double hireFromUnemployed(double amount); // Returns the amount of jobs with no unemployed available. double hireFromPeasants(double amount, int peasantsPerLevel); // Returns the amount of jobs with no peasants available. - void downsizeManorHouses(double peasantAmount, int peasantsPerLevel); // Removes employment from Manor houses based on # of peasants who got real jobs. + void downsizeManorHouses(double peasantAmount, int peasantsPerLevel); // Removes employment from Manor Houses based on # of peasants who got real jobs. std::vector> manorHouseRoster; }; diff --git a/EU4ToVic3/Source/V3World/EconomyManager/Demand/MarketTracker.cpp b/EU4ToVic3/Source/V3World/EconomyManager/Demand/MarketTracker.cpp index 6fd1b3812..480e21f57 100644 --- a/EU4ToVic3/Source/V3World/EconomyManager/Demand/MarketTracker.cpp +++ b/EU4ToVic3/Source/V3World/EconomyManager/Demand/MarketTracker.cpp @@ -1,14 +1,143 @@ #include "MarketTracker.h" +#include "ClayManager/State/State.h" +#include "ClayManager/State/SubState.h" -V3::MarketTracker::MarketTracker(const std::vector& possibleGoods, - const std::vector>& manorHouseRoster) +V3::MarketTracker::MarketTracker(const std::map& possibleGoods, + const std::set& manorHousePMGroups, + const std::map& PMGroups, + const std::map& PMs) { - market = Market(possibleGoods); - marketJobs = MarketJobs({}, manorHouseRoster); + market.loadGoods(possibleGoods); + std::map manorHouseEmployment; + for (const auto& pmg: manorHousePMGroups) + { + for (const auto& [job, amount]: PMs.at(PMGroups.at(pmg).getPMs()[0]).getEmployment()) + { + manorHouseEmployment[job] = amount; + } + } + std::vector> mhr; + for (const auto& pair: manorHouseEmployment) + { + mhr.push_back(pair); + } + marketJobs = MarketJobs(mhr); } void V3::MarketTracker::resetMarket() { market.clearMarket(); marketJobs.clearJobs(); + marketCulture.clear(); +} + +void V3::MarketTracker::loadPeasants(const Country& country, + const std::map& PMGroups, + const std::map& PMs, + const std::map& buildings, + const std::map& lawsMap) +{ + // For each state check peasant PM + // from PM calc Jobs per arable land + // multiply Unit Jobs per filled arable land + // load in jobs + // "unemployed" for all who don't fit on the arable land + + for (const auto& subState: country.getSubStates()) + { + const std::string& subsistenceBuildingName = subState->getHomeState()->getSubsistenceBuilding(); + if (const auto& subsistenceBuildingIter = buildings.find(subsistenceBuildingName); subsistenceBuildingIter != buildings.end()) + { + std::map subsistenceEmployment; + double total = 0; + + const auto& subsistenceBuilding = subsistenceBuildingIter->second; + for (const auto& PMg: subsistenceBuilding.getPMGroups()) + { + const auto& PMName = PMGroups.at(PMg).getPMs()[getPMAllowedByLaws(PMg, country.getProcessedData().laws, PMGroups, PMs)]; + const auto& thePM = PMs.at(PMName); + for (const auto& [job, amount]: thePM.getEmployment()) // TODO(Gawquon) Note that we still need to add the market goods for subsistence farms + { + subsistenceEmployment[job] += amount; + total += amount; + } + } + + int peasantsPerLevel = subsistenceEmployment.at("peasants"); // TODO validate + // TODO (stuff) + + marketJobs.loadInitialJobs() + // Create the Unit Employment by accounting for subsistence farm size and manor house stuff. + // Employ people base on ratios in unit employment untill arabe land is filled + // the rest are unemployed, + } + else + { + Log(LogLevel::Warning) << "Subsistence Building: " << subsistenceBuildingName << " has no definition."; + } + } +} + +void V3::MarketTracker::updatePopNeeds(const Vic3DefinesLoader& defines, + const DemandLoader& demandDefines, + const std::set& laws, + const std::map& popTypes, + const std::map& cultures, + const std::map& religions, + const std::map& lawsMap) +{ + market.calcPopOrders(marketJobs.getPop(), marketJobs.getJobBreakdown(), marketCulture, defines, demandDefines, popTypes, cultures, religions, laws, lawsMap); +} + +int V3::MarketTracker::getPMAllowedByLaws(const std::string& PMGroup, + const std::set& laws, + const std::map& PMGroups, + const std::map& PMs) const +{ + const auto& groupPMs = PMGroups.at(PMGroup).getPMs(); + + // Validate every PM in group + for (const auto& PM: groupPMs) + { + if (!PMs.contains(PM)) + { + Log(LogLevel::Error) << "Unknown PM: " << PM << "."; + return 0; + } + } + + // Walk the group + int pick = 0; + for (const auto& PM: groupPMs) + { + const auto& thePM = PMs.at(PM); + if (hasUnlockingLaws(laws, thePM.getUnlockingLaws()) && !hasBlockingLaws(laws, thePM.getBlockingLaws())) + return std::max(pick - 1, 0); + ++pick; + } + return std::max(pick - 1, 0); +} + +bool V3::MarketTracker::hasUnlockingLaws(const std::set& laws, const std::set& targetLaws) const +{ + if (targetLaws.empty()) + { + return true; + } + + return std::ranges::any_of(targetLaws, [laws](const std::string& targetLaw) { + return laws.contains(targetLaw); + }); +} + +bool V3::MarketTracker::hasBlockingLaws(const std::set& laws, const std::set& targetLaws) const +{ + if (targetLaws.empty()) + { + return false; + } + + return std::ranges::any_of(targetLaws, [laws](const std::string& targetLaw) { + return laws.contains(targetLaw); + }); } \ No newline at end of file diff --git a/EU4ToVic3/Source/V3World/EconomyManager/Demand/MarketTracker.h b/EU4ToVic3/Source/V3World/EconomyManager/Demand/MarketTracker.h index f8bdb4205..843810840 100644 --- a/EU4ToVic3/Source/V3World/EconomyManager/Demand/MarketTracker.h +++ b/EU4ToVic3/Source/V3World/EconomyManager/Demand/MarketTracker.h @@ -1,14 +1,11 @@ #ifndef V3_MARKET_TRACKER_H #define V3_MARKET_TRACKER_H -#include "CultureMapper/CultureDefinitionLoader/CultureDef.h" -#include "Loaders/DefinesLoader/Vic3DefinesLoader.h" -#include "Loaders/DemandLoader/DemandLoader.h" -#include "Loaders/LawLoader/Law.h" +#include "EconomyManager/Building/ProductionMethods/ProductionMethod.h" +#include "EconomyManager/Building/ProductionMethods/ProductionMethodGroup.h" #include "Market.h" #include "MarketJobs.h" -#include "PopManager/Pops/PopType.h" -#include "ReligionMapper/ReligionDefinitionLoader/ReligionDef.h" -#include "string" +#include "PoliticalManager/Country/Country.h" + namespace V3 { @@ -16,13 +13,40 @@ class MarketTracker { public: MarketTracker() = default; - explicit MarketTracker(const std::vector& possibleGoods, const std::vector>& manorHouseRoster); - + explicit MarketTracker(const std::map& possibleGoods, + const std::set& manorHousePMGroups, + const std::map& PMGroups, + const std::map& PMs); void resetMarket(); + void loadPeasants(const Country& country, + const std::map& PMGroups, + const std::map& PMs, + const std::map& buildings, + const std::map& lawsMap); + void loadCultures(const std::map cultureData) { marketCulture = cultureData; } + + void updatePopNeeds(const Vic3DefinesLoader& defines, + const DemandLoader& demandDefines, + const std::set& laws, + const std::map& popTypes, + const std::map& cultures, + const std::map& religions, + const std::map& lawsMap); + + void logDebugMarket(); private: + // Returns the index of the first valid PM according to laws. + int getPMAllowedByLaws(const std::string& PMGroup, + const std::set& laws, + const std::map& PMGroups, + const std::map& PMs) const; + bool hasUnlockingLaws(const std::set& laws, const std::set& targetLaws) const; + bool hasBlockingLaws(const std::set& laws, const std::set& targetLaws) const; + Market market; MarketJobs marketJobs; + std::map marketCulture; }; } // namespace V3 diff --git a/EU4ToVic3/Source/V3World/EconomyManager/EconomyManager.cpp b/EU4ToVic3/Source/V3World/EconomyManager/EconomyManager.cpp index d317d5eb9..03a4f5385 100644 --- a/EU4ToVic3/Source/V3World/EconomyManager/EconomyManager.cpp +++ b/EU4ToVic3/Source/V3World/EconomyManager/EconomyManager.cpp @@ -210,7 +210,10 @@ void V3::EconomyManager::balanceNationalBudgets() const Log(LogLevel::Info) << "<> Industry Sectors Primed."; } -void V3::EconomyManager::buildBuildings(const std::map& lawsMap) const +void V3::EconomyManager::buildBuildings(const std::map& lawsMap, + const std::map& cultures, + const std::map& religions, + const Vic3DefinesLoader& defines) const { Log(LogLevel::Info) << "-> Building buildings."; auto counter = 0; @@ -226,25 +229,7 @@ void V3::EconomyManager::buildBuildings(const std::map& lawsMa // 3c. packet size is based on the mean amount of CP states have left to build and is configurable // 4. If a substate ends up with less CP than the cost for any possible valid building, they relinquish it to the next sector/substate - Market market; - market.loadGoods(demand.getGoodsMap()); - - // Load up peasants - for (const auto& country: centralizedCountries) - { - for (const auto& subState: country->getSubStates()) - { - const auto& subsistenceBuildingName = subState->getHomeState()->getSubsistenceBuilding(); - if (const auto& subsistenceBuildingIter = buildings.find(subsistenceBuildingName); subsistenceBuildingIter != buildings.end()) - { - const auto& subsistenceBuilding = subsistenceBuildingIter->second; - } - else - { - Log(LogLevel::Warning) << "Subsistence Building: " << subsistenceBuildingName << " has no definition."; - } - } - } + MarketTracker market(demand.getGoodsMap(), buildings.at("building_manor_house").getPMGroups()); for (const auto& country: centralizedCountries) { @@ -252,34 +237,28 @@ void V3::EconomyManager::buildBuildings(const std::map& lawsMa auto subStatesByBudget = prepareSubStatesByBudget(country, lawsMap); const auto& estimatedPMs = PMMapper.estimatePMs(*country, PMs, PMGroups, buildings); const auto& estimatedOwnershipFracs = estimateInvestorBuildings(*country); - const auto& cultureData = country->getCultureBreakdown(); - MarketJobs marketJobs; // TODO(Gawquon): Init Market Jobs + market.resetMarket(); + market.loadPeasants(*country, PMGroups, PMs, buildings, lawsMap); + market.loadCultures(country->getCultureBreakdown()); // Until every substate is unable to build anything while (!subStatesByBudget.empty()) { // Enter negotiation + // Update the market - market.calcPopOrders(country->getPopCount(), - marketJobs.getJobBreakdown(), - cultureData, - {}, // defines, // TODO(Gawquon): Load in defines - demand, - popTypeLoader.getPopTypes(), - {}, // TODO(Gawquon): Load in cultures - {}, // TODO(Gawquon): Load in religions - country->getProcessedData().laws, - lawsMap); + market.updatePopNeeds(defines, demand, country->getProcessedData().laws, popTypeLoader.getPopTypes(), cultures, religions, lawsMap); // Pick the substate with the most budget - negotiateBuilding(subStatesByBudget[0], sectors, lawsMap, subStatesByBudget, estimatedPMs, estimatedOwnershipFracs, marketJobs, market); + negotiateBuilding(subStatesByBudget[0], sectors, lawsMap, subStatesByBudget, estimatedPMs, estimatedOwnershipFracs, market); ++counter; // A Building has now been built, process for next round std::sort(subStatesByBudget.begin(), subStatesByBudget.end(), SubState::greaterBudget); removeSubStateIfFinished(subStatesByBudget, subStatesByBudget.end() - 1, lawsMap); } - // TODO(Gawquon): Add a debug printout of the estimated market. + // DEBUG + market.logDebugMarket(); } Log(LogLevel::Info) << "<> Built " << counter << " buildings world-wide."; } @@ -711,8 +690,7 @@ void V3::EconomyManager::negotiateBuilding(const std::shared_ptr& subS const std::vector>& subStates, const std::map>& estimatedPMs, const std::map>& estimatedOwnershipFracs, - MarketJobs& marketJobs, - Market& market) const + MarketTracker& market) const { // Whether or not the negotiation succeeds, a building MUST be built. @@ -746,7 +724,7 @@ void V3::EconomyManager::negotiateBuilding(const std::shared_ptr& subS } // So we're a valid building in a valid sector and there is budget for us. Great! - buildBuilding(building, subState, sectors.at(sector.value()), lawsMap, subStates, estimatedPMs, estimatedOwnershipFracs, marketJobs, market); + buildBuilding(building, subState, sectors.at(sector.value()), lawsMap, subStates, estimatedPMs, estimatedOwnershipFracs, market); talksFail = false; break; } @@ -755,15 +733,7 @@ void V3::EconomyManager::negotiateBuilding(const std::shared_ptr& subS { // Negotiation failed // State picks its favorite building, takes from biggest sector - buildBuilding(subState->getBuildings()[0], - subState, - getSectorWithMostBudget(sectors), - lawsMap, - subStates, - estimatedPMs, - estimatedOwnershipFracs, - marketJobs, - market); + buildBuilding(subState->getBuildings()[0], subState, getSectorWithMostBudget(sectors), lawsMap, subStates, estimatedPMs, estimatedOwnershipFracs, market); } } diff --git a/EU4ToVic3/Source/V3World/EconomyManager/EconomyManager.h b/EU4ToVic3/Source/V3World/EconomyManager/EconomyManager.h index 41d6633ff..ff841ca1c 100644 --- a/EU4ToVic3/Source/V3World/EconomyManager/EconomyManager.h +++ b/EU4ToVic3/Source/V3World/EconomyManager/EconomyManager.h @@ -6,8 +6,8 @@ #include "BuildingMapper/ProductionMethodMapper/ProductionMethodMapper.h" #include "ClayManager/State/StateModifier.h" #include "Configuration.h" -#include "Demand/Market.h" -#include "Demand/MarketJobs.h" +#include "CultureMapper/CultureDefinitionLoader/CultureDef.h" +#include "Demand/MarketTracker.h" #include "EconomyManager/Building/ProductionMethods/ProductionMethod.h" #include "EconomyManager/Building/ProductionMethods/ProductionMethodGroup.h" #include "Loaders/BuildingLoader/OwnershipLoader/OwnershipLoader.h" @@ -18,6 +18,7 @@ #include "Loaders/PopLoader/PopTypeLoader.h" #include "Loaders/TechLoader/TechLoader.h" #include "PoliticalManager/PoliticalManager.h" +#include "ReligionMapper/ReligionDefinitionLoader/ReligionDef.h" namespace V3 { @@ -68,7 +69,10 @@ class EconomyManager bool vn = false) const; void assignSubStateCPBudgets(Configuration::ECONOMY economyType) const; void balanceNationalBudgets() const; - void buildBuildings(const std::map& lawsMap) const; + void buildBuildings(const std::map& lawsMap, + const std::map& cultures, + const std::map& religions, + const Vic3DefinesLoader& defines) const; void investCapital(const std::map>& countries) const; void setPMs() const; @@ -103,8 +107,7 @@ class EconomyManager const std::vector>& subStates, const std::map>& estimatedPMs, const std::map>& estimatedOwnershipFracs, - MarketJobs& marketJobs, - Market& market) const; + MarketTracker& marketTracker) const; [[nodiscard]] static std::shared_ptr getSectorWithMostBudget(const std::map>& sectors); void buildBuilding(const std::shared_ptr& building, const std::shared_ptr& subState, @@ -113,8 +116,7 @@ class EconomyManager const std::vector>& subStates, const std::map>& estimatedPMs, const std::map>& estimatedOwnershipFracs, - MarketJobs& marketJobs, - Market& market) const; + MarketTracker& marketTracker) const; void removeSubStateIfFinished(std::vector>& subStates, const std::vector>::iterator& subState, const std::map& lawsMap) const; diff --git a/EU4ToVic3Tests/V3WorldTests/EconomyManagerTests/DemandTests/MarketJobsTests.cpp b/EU4ToVic3Tests/V3WorldTests/EconomyManagerTests/DemandTests/MarketJobsTests.cpp index 8d0341ccd..15b91c8b2 100644 --- a/EU4ToVic3Tests/V3WorldTests/EconomyManagerTests/DemandTests/MarketJobsTests.cpp +++ b/EU4ToVic3Tests/V3WorldTests/EconomyManagerTests/DemandTests/MarketJobsTests.cpp @@ -14,16 +14,30 @@ TEST(V3World_MarketJobsTests, DefaultsDefaultToDefaults) TEST(V3World_MarketJobsTests, JobBreakdownReturnsPercentages) { std::map jobList({{"poors", 250}, {"unemployed", 750}, {"peasants", 1000}}); - const V3::MarketJobs marketJobs(jobList, {}); + V3::MarketJobs marketJobs({}); + marketJobs.loadInitialJobs(jobList); EXPECT_THAT(marketJobs.getJobBreakdown(), testing::UnorderedElementsAre(testing::Pair("poors", .125), testing::Pair("unemployed", .375), testing::Pair("peasants", .5))); } +TEST(V3World_MarketJobsTests, ClearJobsEmptiesMap) +{ + std::map jobList({{"poors", 250}, {"unemployed", 750}, {"peasants", 1000}}); + V3::MarketJobs marketJobs({}); + marketJobs.loadInitialJobs(jobList); + + EXPECT_THAT(marketJobs.getJobCounts(), + testing::UnorderedElementsAre(testing::Pair("poors", 250), testing::Pair("unemployed", 750), testing::Pair("peasants", 1000))); + marketJobs.clearJobs(); + EXPECT_TRUE(marketJobs.getJobCounts().empty()); +} + TEST(V3World_MarketJobsTests, CreateJobsTakesFromUnemployedAndPeasants) { std::map jobList({{"poors", 250}, {"unemployed", 750}, {"peasants", 1000}}); - V3::MarketJobs marketJobs(jobList, {}); + V3::MarketJobs marketJobs({}); + marketJobs.loadInitialJobs(jobList); std::stringstream input; input << "working_adult_ratio = 0.25\n";