Skip to content

Commit

Permalink
progess marketTracker
Browse files Browse the repository at this point in the history
  • Loading branch information
gawquon committed Aug 29, 2024
1 parent 5696ec5 commit 4a6f032
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 75 deletions.
14 changes: 9 additions & 5 deletions EU4ToVic3/Source/V3World/EconomyManager/Demand/MarketJobs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,8 @@
#include <numeric>


V3::MarketJobs::MarketJobs(const std::map<std::string, double>& jobsList, const std::vector<std::pair<std::string, int>>& manorHouseRoster):
jobCounts(jobsList), manorHouseRoster(manorHouseRoster)
V3::MarketJobs::MarketJobs(const std::vector<std::pair<std::string, int>>& manorHouseRoster): manorHouseRoster(manorHouseRoster)
{
population = std::accumulate(jobCounts.begin(), jobCounts.end(), 0, [](int sum, const auto& pair) {
return sum + static_cast<int>(pair.second);
});
}

// Returns a map of each job as a percentage of all jobs in the market.
Expand All @@ -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<std::string, double> jobsList)
{
jobCounts = jobsList;
population = std::accumulate(jobCounts.begin(), jobCounts.end(), 0, [](int sum, const auto& pair) {
return sum + static_cast<int>(pair.second);
});
}

double V3::MarketJobs::hireFromWorseJobs(double amount, const int peasantsPerLevel)
{
amount = hireFromUnemployed(amount);
Expand Down
6 changes: 4 additions & 2 deletions EU4ToVic3/Source/V3World/EconomyManager/Demand/MarketJobs.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ class MarketJobs
{
public:
MarketJobs() = default;
MarketJobs(const std::map<std::string, double>& jobsList, const std::vector<std::pair<std::string, int>>& manorHouseRoster);
explicit MarketJobs(const std::vector<std::pair<std::string, int>>& manorHouseRoster);

[[nodsicard]] const auto& getPop() const { return population; }
[[nodsicard]] const auto& getJobCounts() const { return jobCounts; }
[[nodsicard]] std::map<std::string, double> getJobBreakdown() const;

void createJobs(const PopType& popType, double amount, double defaultRatio, double womenJobRate, int peasantsPerLevel);
void clearJobs() { jobCounts.clear(); }
void loadInitialJobs(const std::map<std::string, double> jobsList);

private:
int population = 0;
Expand All @@ -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<std::pair<std::string, int>> manorHouseRoster;
};
Expand Down
137 changes: 133 additions & 4 deletions EU4ToVic3/Source/V3World/EconomyManager/Demand/MarketTracker.cpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,143 @@
#include "MarketTracker.h"
#include "ClayManager/State/State.h"
#include "ClayManager/State/SubState.h"

V3::MarketTracker::MarketTracker(const std::vector<std::string>& possibleGoods,
const std::vector<std::pair<std::string, int>>& manorHouseRoster)
V3::MarketTracker::MarketTracker(const std::map<std::string, Good>& possibleGoods,
const std::set<std::string>& manorHousePMGroups,
const std::map<std::string, ProductionMethodGroup>& PMGroups,
const std::map<std::string, ProductionMethod>& PMs)
{
market = Market(possibleGoods);
marketJobs = MarketJobs({}, manorHouseRoster);
market.loadGoods(possibleGoods);
std::map<std::string, int> manorHouseEmployment;
for (const auto& pmg: manorHousePMGroups)
{
for (const auto& [job, amount]: PMs.at(PMGroups.at(pmg).getPMs()[0]).getEmployment())
{
manorHouseEmployment[job] = amount;
}
}
std::vector<std::pair<std::string, int>> 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<std::string, ProductionMethodGroup>& PMGroups,
const std::map<std::string, ProductionMethod>& PMs,
const std::map<std::string, Building>& buildings,
const std::map<std::string, Law>& 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<std::string, double> 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<std::string>& laws,
const std::map<std::string, PopType>& popTypes,
const std::map<std::string, mappers::CultureDef>& cultures,
const std::map<std::string, mappers::ReligionDef>& religions,
const std::map<std::string, Law>& 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<std::string>& laws,
const std::map<std::string, ProductionMethodGroup>& PMGroups,
const std::map<std::string, ProductionMethod>& 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<std::string>& laws, const std::set<std::string>& 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<std::string>& laws, const std::set<std::string>& targetLaws) const
{
if (targetLaws.empty())
{
return false;
}

return std::ranges::any_of(targetLaws, [laws](const std::string& targetLaw) {
return laws.contains(targetLaw);
});
}
42 changes: 33 additions & 9 deletions EU4ToVic3/Source/V3World/EconomyManager/Demand/MarketTracker.h
Original file line number Diff line number Diff line change
@@ -1,28 +1,52 @@
#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
{
class MarketTracker
{
public:
MarketTracker() = default;
explicit MarketTracker(const std::vector<std::string>& possibleGoods, const std::vector<std::pair<std::string, int>>& manorHouseRoster);

explicit MarketTracker(const std::map<std::string, Good>& possibleGoods,
const std::set<std::string>& manorHousePMGroups,
const std::map<std::string, ProductionMethodGroup>& PMGroups,
const std::map<std::string, ProductionMethod>& PMs);
void resetMarket();
void loadPeasants(const Country& country,
const std::map<std::string, ProductionMethodGroup>& PMGroups,
const std::map<std::string, ProductionMethod>& PMs,
const std::map<std::string, Building>& buildings,
const std::map<std::string, Law>& lawsMap);
void loadCultures(const std::map<std::string, double> cultureData) { marketCulture = cultureData; }

void updatePopNeeds(const Vic3DefinesLoader& defines,
const DemandLoader& demandDefines,
const std::set<std::string>& laws,
const std::map<std::string, PopType>& popTypes,
const std::map<std::string, mappers::CultureDef>& cultures,
const std::map<std::string, mappers::ReligionDef>& religions,
const std::map<std::string, Law>& lawsMap);

void logDebugMarket();

private:
// Returns the index of the first valid PM according to laws.
int getPMAllowedByLaws(const std::string& PMGroup,
const std::set<std::string>& laws,
const std::map<std::string, ProductionMethodGroup>& PMGroups,
const std::map<std::string, ProductionMethod>& PMs) const;
bool hasUnlockingLaws(const std::set<std::string>& laws, const std::set<std::string>& targetLaws) const;
bool hasBlockingLaws(const std::set<std::string>& laws, const std::set<std::string>& targetLaws) const;

Market market;
MarketJobs marketJobs;
std::map<std::string, double> marketCulture;
};
} // namespace V3

Expand Down
62 changes: 16 additions & 46 deletions EU4ToVic3/Source/V3World/EconomyManager/EconomyManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,10 @@ void V3::EconomyManager::balanceNationalBudgets() const
Log(LogLevel::Info) << "<> Industry Sectors Primed.";
}

void V3::EconomyManager::buildBuildings(const std::map<std::string, Law>& lawsMap) const
void V3::EconomyManager::buildBuildings(const std::map<std::string, Law>& lawsMap,
const std::map<std::string, mappers::CultureDef>& cultures,
const std::map<std::string, mappers::ReligionDef>& religions,
const Vic3DefinesLoader& defines) const
{
Log(LogLevel::Info) << "-> Building buildings.";
auto counter = 0;
Expand All @@ -226,60 +229,36 @@ void V3::EconomyManager::buildBuildings(const std::map<std::string, Law>& 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)
{
const auto& sectors = country->getProcessedData().industrySectors;
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.";
}
Expand Down Expand Up @@ -711,8 +690,7 @@ void V3::EconomyManager::negotiateBuilding(const std::shared_ptr<SubState>& subS
const std::vector<std::shared_ptr<SubState>>& subStates,
const std::map<std::string, std::tuple<int, double>>& estimatedPMs,
const std::map<std::string, std::map<std::string, double>>& estimatedOwnershipFracs,
MarketJobs& marketJobs,
Market& market) const
MarketTracker& market) const
{
// Whether or not the negotiation succeeds, a building MUST be built.

Expand Down Expand Up @@ -746,7 +724,7 @@ void V3::EconomyManager::negotiateBuilding(const std::shared_ptr<SubState>& 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;
}
Expand All @@ -755,15 +733,7 @@ void V3::EconomyManager::negotiateBuilding(const std::shared_ptr<SubState>& 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);
}
}

Expand Down
Loading

0 comments on commit 4a6f032

Please sign in to comment.