From 626674b502aefb51b0369bf95ed3fa64400232a5 Mon Sep 17 00:00:00 2001 From: wvpm <24685035+wvpm@users.noreply.github.com> Date: Mon, 4 Nov 2024 13:17:55 +0100 Subject: [PATCH 1/2] Implement artisanal production --- src/openvic-simulation/InstanceManager.cpp | 8 +- .../economy/GoodInstance.cpp | 67 ++++++++-- .../economy/GoodInstance.hpp | 8 ++ .../economy/production/ArtisanalProducer.cpp | 118 ++++++++++++++++-- .../economy/production/ArtisanalProducer.hpp | 18 ++- .../ArtisanalProducerFactoryPattern.cpp | 50 ++++++++ .../ArtisanalProducerFactoryPattern.hpp | 30 +++++ .../economy/trading/BuyResult.cpp | 11 ++ .../economy/trading/BuyResult.hpp | 16 +++ .../economy/trading/BuyUpToOrder.cpp | 25 ++++ .../economy/trading/BuyUpToOrder.hpp | 37 ++++++ .../economy/trading/MarketInstance.cpp | 5 + .../economy/trading/MarketInstance.hpp | 2 + src/openvic-simulation/map/MapInstance.cpp | 9 +- src/openvic-simulation/map/MapInstance.hpp | 7 +- .../map/ProvinceInstance.cpp | 11 +- .../map/ProvinceInstance.hpp | 2 +- src/openvic-simulation/pop/Pop.cpp | 63 ++++++++-- src/openvic-simulation/pop/Pop.hpp | 43 +++++-- 19 files changed, 482 insertions(+), 48 deletions(-) create mode 100644 src/openvic-simulation/economy/production/ArtisanalProducerFactoryPattern.cpp create mode 100644 src/openvic-simulation/economy/production/ArtisanalProducerFactoryPattern.hpp create mode 100644 src/openvic-simulation/economy/trading/BuyResult.cpp create mode 100644 src/openvic-simulation/economy/trading/BuyResult.hpp create mode 100644 src/openvic-simulation/economy/trading/BuyUpToOrder.cpp create mode 100644 src/openvic-simulation/economy/trading/BuyUpToOrder.hpp diff --git a/src/openvic-simulation/InstanceManager.cpp b/src/openvic-simulation/InstanceManager.cpp index 08e36fce..37eb385c 100644 --- a/src/openvic-simulation/InstanceManager.cpp +++ b/src/openvic-simulation/InstanceManager.cpp @@ -129,11 +129,17 @@ bool InstanceManager::load_bookmark(Bookmark const* new_bookmark) { today = bookmark->get_date(); + ArtisanalProducerFactoryPattern artisanal_producer_factory_pattern { + market_instance, + definition_manager.get_modifier_manager().get_modifier_effect_cache(), + definition_manager.get_economy_manager().get_production_type_manager() + }; bool ret = map_instance.apply_history_to_provinces( definition_manager.get_history_manager().get_province_manager(), today, country_instance_manager, // TODO - the following argument is for generating test pop attributes - definition_manager.get_politics_manager().get_issue_manager() + definition_manager.get_politics_manager().get_issue_manager(), + artisanal_producer_factory_pattern ); ret &= country_instance_manager.apply_history_to_countries( diff --git a/src/openvic-simulation/economy/GoodInstance.cpp b/src/openvic-simulation/economy/GoodInstance.cpp index 620b047e..98885827 100644 --- a/src/openvic-simulation/economy/GoodInstance.cpp +++ b/src/openvic-simulation/economy/GoodInstance.cpp @@ -4,13 +4,34 @@ using namespace OpenVic; GoodInstance::GoodInstance(GoodDefinition const& new_good_definition) : HasIdentifierAndColour { new_good_definition }, + buy_lock { std::make_unique() }, sell_lock { std::make_unique() }, good_definition { new_good_definition }, price { new_good_definition.get_base_price() }, + max_next_price {}, + min_next_price {}, is_available { new_good_definition.get_is_available_from_start() }, + total_demand_yesterday { fixed_point_t::_0() }, total_supply_yesterday { fixed_point_t::_0() }, + buy_up_to_orders {}, market_sell_orders {} - {} + { update_next_price_limits(); } + +void GoodInstance::update_next_price_limits() { + max_next_price = std::min( + good_definition.get_base_price() * 5, + price + fixed_point_t::_1() / fixed_point_t::_100() + ); + min_next_price = std::max( + good_definition.get_base_price() * 22 / fixed_point_t::_100(), + price - fixed_point_t::_1() / fixed_point_t::_100() + ); +} + +void GoodInstance::add_buy_up_to_order(GoodBuyUpToOrder&& buy_up_to_order) { + const std::lock_guard lock {*buy_lock}; + buy_up_to_orders.push_back(std::move(buy_up_to_order)); +} void GoodInstance::add_market_sell_order(GoodMarketSellOrder&& market_sell_order) { const std::lock_guard lock {*sell_lock}; @@ -18,20 +39,52 @@ void GoodInstance::add_market_sell_order(GoodMarketSellOrder&& market_sell_order } void GoodInstance::execute_orders() { - const fixed_point_t price = get_price(); + fixed_point_t demand_running_total = fixed_point_t::_0(); + for (GoodBuyUpToOrder const& buy_up_to_order : buy_up_to_orders) { + demand_running_total += buy_up_to_order.get_max_quantity(); + } fixed_point_t supply_running_total = fixed_point_t::_0(); - for(GoodMarketSellOrder const& market_sell_order : market_sell_orders) { - const fixed_point_t market_sell_quantity = market_sell_order.get_quantity(); - supply_running_total += market_sell_quantity; + for (GoodMarketSellOrder const& market_sell_order : market_sell_orders) { + supply_running_total += market_sell_order.get_quantity(); + } + + fixed_point_t new_price; + fixed_point_t fraction_bought = fixed_point_t::_1(); + fixed_point_t fraction_sold = fixed_point_t::_1(); + if (demand_running_total > supply_running_total) { + new_price = max_next_price; + fraction_bought = supply_running_total / demand_running_total; + } else if (demand_running_total < supply_running_total) { + new_price = min_next_price; + fraction_sold = demand_running_total / supply_running_total; + } else { + new_price = price; + } + + for (GoodBuyUpToOrder const& buy_up_to_order : buy_up_to_orders) { + const fixed_point_t quantity_bought = fraction_bought * buy_up_to_order.get_money_to_spend() / new_price; + buy_up_to_order.get_after_trade()({ + quantity_bought, + buy_up_to_order.get_money_to_spend() - quantity_bought * new_price + }); + } + + for (GoodMarketSellOrder const& market_sell_order : market_sell_orders) { + const fixed_point_t quantity_sold = fraction_sold * market_sell_order.get_quantity(); market_sell_order.get_after_trade()({ - market_sell_quantity, - market_sell_quantity * price + quantity_sold, + quantity_sold * new_price }); } + total_demand_yesterday = demand_running_total; total_supply_yesterday = supply_running_total; + buy_up_to_orders.clear(); market_sell_orders.clear(); + if (new_price != price) { + update_next_price_limits(); + } } bool GoodInstanceManager::setup(GoodDefinitionManager const& good_definition_manager) { diff --git a/src/openvic-simulation/economy/GoodInstance.hpp b/src/openvic-simulation/economy/GoodInstance.hpp index 76c14a84..48c7a5f8 100644 --- a/src/openvic-simulation/economy/GoodInstance.hpp +++ b/src/openvic-simulation/economy/GoodInstance.hpp @@ -5,6 +5,7 @@ #include #include "openvic-simulation/economy/GoodDefinition.hpp" +#include "openvic-simulation/economy/trading/BuyUpToOrder.hpp" #include "openvic-simulation/economy/trading/MarketSellOrder.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" @@ -18,19 +19,26 @@ namespace OpenVic { friend struct GoodInstanceManager; private: + std::unique_ptr buy_lock; std::unique_ptr sell_lock; GoodDefinition const& PROPERTY(good_definition); fixed_point_t PROPERTY(price); + fixed_point_t PROPERTY(max_next_price); + fixed_point_t PROPERTY(min_next_price); bool PROPERTY(is_available); + fixed_point_t PROPERTY(total_demand_yesterday); fixed_point_t PROPERTY(total_supply_yesterday); + std::deque buy_up_to_orders; std::deque market_sell_orders; GoodInstance(GoodDefinition const& new_good_definition); + void update_next_price_limits(); public: GoodInstance(GoodInstance&&) = default; //thread safe + void add_buy_up_to_order(GoodBuyUpToOrder&& buy_up_to_order); void add_market_sell_order(GoodMarketSellOrder&& market_sell_order); //not thread safe diff --git a/src/openvic-simulation/economy/production/ArtisanalProducer.cpp b/src/openvic-simulation/economy/production/ArtisanalProducer.cpp index d5cc3d31..f79f5d01 100644 --- a/src/openvic-simulation/economy/production/ArtisanalProducer.cpp +++ b/src/openvic-simulation/economy/production/ArtisanalProducer.cpp @@ -1,13 +1,117 @@ #include "ArtisanalProducer.hpp" +#include "openvic-simulation/economy/GoodDefinition.hpp" +#include "openvic-simulation/economy/trading/BuyResult.hpp" +#include "openvic-simulation/economy/trading/SellResult.hpp" +#include "openvic-simulation/pop/Pop.hpp" + using namespace OpenVic; ArtisanalProducer::ArtisanalProducer( - ProductionType const& new_production_type, + MarketInstance& new_market_instance, + ModifierEffectCache const& new_modifier_effect_cache, GoodDefinition::good_definition_map_t&& new_stockpile, - fixed_point_t new_current_production, - GoodDefinition::good_definition_map_t&& new_current_needs -) : production_type { new_production_type }, - stockpile { std::move(new_stockpile) }, - current_production { new_current_production }, - current_needs { std::move(new_current_needs) } {} + ProductionType const& new_production_type, + fixed_point_t new_current_production +) : market_instance { new_market_instance }, + modifier_effect_cache { new_modifier_effect_cache }, + stockpile { new_stockpile }, + production_type { new_production_type }, + current_production { new_current_production } + {} + +void ArtisanalProducer::artisan_tick(Pop& pop) { + GoodDefinition::good_definition_map_t goods_to_buy_and_max_price { }; + GoodDefinition::good_definition_map_t demand { }; + fixed_point_t inputs_bought_scalar = fixed_point_t::_1(); + if (production_type.get_input_goods().empty()) { + inputs_bought_scalar = fixed_point_t::_1(); + } else { + GoodInstanceManager const& good_instance_manager = market_instance.get_good_instance_manager(); + for (auto const& [input_good_ptr, base_desired_quantity] : production_type.get_input_goods()) { + const fixed_point_t desired_quantity = demand[input_good_ptr] = base_desired_quantity * pop.get_size() / production_type.get_base_workforce_size(); + if (desired_quantity == fixed_point_t::_0()) { + continue; + } + inputs_bought_scalar = std::min(stockpile[input_good_ptr] / desired_quantity, inputs_bought_scalar); + GoodInstance const& good = good_instance_manager.get_good_instance_from_definition(*input_good_ptr); + goods_to_buy_and_max_price[input_good_ptr] = good.get_max_next_price(); + } + + if (inputs_bought_scalar > fixed_point_t::_0()) { + for (auto const& [input_good_ptr, base_desired_quantity] : production_type.get_input_goods()) { + const fixed_point_t desired_quantity = demand[input_good_ptr]; + stockpile[input_good_ptr] = std::max( + fixed_point_t::_0(), + stockpile[input_good_ptr] - desired_quantity * inputs_bought_scalar + ); + + if (stockpile[input_good_ptr] >= desired_quantity) { + goods_to_buy_and_max_price.erase(input_good_ptr); + } + } + } + + const fixed_point_t total_cash_to_spend = pop.get_cash(); + if (total_cash_to_spend > 0 && !goods_to_buy_and_max_price.empty()) { + fixed_point_t max_possible_satisfaction = fixed_point_t::_1(); + + bool at_or_below_optimum = false; + while (!at_or_below_optimum) { + at_or_below_optimum = true; + fixed_point_t total_demand_value = fixed_point_t::_0(); + fixed_point_t total_stockpile_value = fixed_point_t::_0(); + for (auto const& [input_good_ptr, max_price] : goods_to_buy_and_max_price) { + total_demand_value += max_price * demand[input_good_ptr]; + total_stockpile_value += max_price * stockpile[input_good_ptr]; + } + + max_possible_satisfaction = total_demand_value == fixed_point_t::_0() + ? fixed_point_t::_1() + : std::min( + fixed_point_t::_1(), + (total_stockpile_value + total_cash_to_spend) / total_demand_value + ); + + for (auto const& [input_good_ptr, max_price] : goods_to_buy_and_max_price) { + const fixed_point_t optimal_quantity = demand[input_good_ptr] * max_possible_satisfaction; + if (stockpile[input_good_ptr] >= optimal_quantity) { + goods_to_buy_and_max_price.erase(input_good_ptr); + at_or_below_optimum = false; + } + } + } + + for (auto const& [input_good_ptr, max_price] : goods_to_buy_and_max_price) { + const fixed_point_t optimal_quantity = demand[input_good_ptr] * max_possible_satisfaction; + const fixed_point_t max_quantity_to_buy = demand[input_good_ptr] - stockpile[input_good_ptr]; + const fixed_point_t money_to_spend = optimal_quantity * max_price; + pop.add_artisan_inputs_expense(money_to_spend); + market_instance.place_buy_up_to_order({ + *input_good_ptr, + max_quantity_to_buy, + money_to_spend, + [this, &pop, input_good_ptr](const BuyResult buy_result) -> void { + pop.add_artisan_inputs_expense(-buy_result.get_money_left()); + stockpile[input_good_ptr] += buy_result.get_quantity_bought(); + } + }); + } + } + } + + current_production = production_type.get_base_output_quantity() + * inputs_bought_scalar + * pop.get_size() / production_type.get_base_workforce_size(); + + GoodDefinition const& output_good = production_type.get_output_good(); + if (current_production > 0) { + market_instance.place_market_sell_order({ + output_good, + current_production, + [&pop](const SellResult sell_result) -> void { + pop.add_artisanal_income(sell_result.get_money_gained()); + } + }); + } +} \ No newline at end of file diff --git a/src/openvic-simulation/economy/production/ArtisanalProducer.hpp b/src/openvic-simulation/economy/production/ArtisanalProducer.hpp index 65aa3fa0..7ecdff98 100644 --- a/src/openvic-simulation/economy/production/ArtisanalProducer.hpp +++ b/src/openvic-simulation/economy/production/ArtisanalProducer.hpp @@ -1,22 +1,30 @@ #pragma once -#include "openvic-simulation/economy/GoodDefinition.hpp" #include "openvic-simulation/economy/production/ProductionType.hpp" +#include "openvic-simulation/economy/trading/MarketInstance.hpp" +#include "openvic-simulation/modifier/ModifierEffectCache.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #include "openvic-simulation/utility/Getters.hpp" namespace OpenVic { + struct Pop; + struct ArtisanalProducer { private: + MarketInstance& market_instance; + ModifierEffectCache const& modifier_effect_cache; + GoodDefinition::good_definition_map_t stockpile; ProductionType const& PROPERTY(production_type); - GoodDefinition::good_definition_map_t PROPERTY(stockpile); fixed_point_t PROPERTY(current_production); - GoodDefinition::good_definition_map_t PROPERTY(current_needs); public: ArtisanalProducer( - ProductionType const& new_production_type, GoodDefinition::good_definition_map_t&& new_stockpile, - fixed_point_t new_current_production, GoodDefinition::good_definition_map_t&& new_current_needs + MarketInstance& new_market_instance, + ModifierEffectCache const& new_modifier_effect_cache, + GoodDefinition::good_definition_map_t&& new_stockpile, + ProductionType const& new_production_type, + fixed_point_t new_current_production ); + void artisan_tick(Pop& pop); }; } diff --git a/src/openvic-simulation/economy/production/ArtisanalProducerFactoryPattern.cpp b/src/openvic-simulation/economy/production/ArtisanalProducerFactoryPattern.cpp new file mode 100644 index 00000000..6345ba9c --- /dev/null +++ b/src/openvic-simulation/economy/production/ArtisanalProducerFactoryPattern.cpp @@ -0,0 +1,50 @@ +#include "ArtisanalProducerFactoryPattern.hpp" + +#include "openvic-simulation/economy/GoodInstance.hpp" + +using namespace OpenVic; + +ArtisanalProducerFactoryPattern::ArtisanalProducerFactoryPattern( + MarketInstance& new_market_instance, + ModifierEffectCache const& new_modifier_effect_cache, + ProductionTypeManager const& new_production_type_manager +) : index { -1 }, + unlocked_artisanal_production_types { }, + market_instance { new_market_instance }, + modifier_effect_cache { new_modifier_effect_cache }, + production_type_manager { new_production_type_manager } + { } + +std::unique_ptr ArtisanalProducerFactoryPattern::CreateNewArtisanalProducer() { + //TODO update unlocked_artisanal_production_types when goods are unlocked + if (index == -1) { + for (ProductionType const& production_type : production_type_manager.get_production_types()) { + if (production_type.get_template_type() == ProductionType::template_type_t::ARTISAN) { + GoodInstance const& good_instance = market_instance.get_good_instance_manager().get_good_instance_from_definition( + production_type.get_output_good() + ); + if (!good_instance.get_is_available()) { + continue; + } + + unlocked_artisanal_production_types.push_back(&production_type); + } + } + } + + if (unlocked_artisanal_production_types.size() == 0) { + Logger::error("CreateNewArtisanalProducer was called but there are no artisanal production types."); + } + + //TODO select production type the way Victoria 2 does it (random?) + index = (index+1) % unlocked_artisanal_production_types.size(); + ProductionType const* random_artisanal_production_type = unlocked_artisanal_production_types[index]; + + return std::make_unique( + market_instance, + modifier_effect_cache, + GoodDefinition::good_definition_map_t{}, + *random_artisanal_production_type, + fixed_point_t::_0() + ); +} \ No newline at end of file diff --git a/src/openvic-simulation/economy/production/ArtisanalProducerFactoryPattern.hpp b/src/openvic-simulation/economy/production/ArtisanalProducerFactoryPattern.hpp new file mode 100644 index 00000000..eb8d105e --- /dev/null +++ b/src/openvic-simulation/economy/production/ArtisanalProducerFactoryPattern.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include "openvic-simulation/economy/production/ArtisanalProducer.hpp" +#include "openvic-simulation/economy/production/ProductionType.hpp" +#include "openvic-simulation/economy/trading/MarketInstance.hpp" +#include "openvic-simulation/modifier/ModifierEffectCache.hpp" + +namespace OpenVic { + struct Pop; + + struct ArtisanalProducerFactoryPattern { + private: + int index; + std::vector unlocked_artisanal_production_types; + MarketInstance& market_instance; + ModifierEffectCache const& modifier_effect_cache; + ProductionTypeManager const& production_type_manager; + + public: + ArtisanalProducerFactoryPattern( + MarketInstance& new_market_instance, + ModifierEffectCache const& new_modifier_effect_cache, + ProductionTypeManager const& new_production_type_manager + ); + + std::unique_ptr CreateNewArtisanalProducer(); + }; +} \ No newline at end of file diff --git a/src/openvic-simulation/economy/trading/BuyResult.cpp b/src/openvic-simulation/economy/trading/BuyResult.cpp new file mode 100644 index 00000000..452f3b9d --- /dev/null +++ b/src/openvic-simulation/economy/trading/BuyResult.cpp @@ -0,0 +1,11 @@ +#include "BuyResult.hpp" + +using namespace OpenVic; + +BuyResult::BuyResult( + const fixed_point_t new_quantity_bought, + const fixed_point_t new_money_left +) : + quantity_bought { new_quantity_bought }, + money_left { new_money_left } + {} \ No newline at end of file diff --git a/src/openvic-simulation/economy/trading/BuyResult.hpp b/src/openvic-simulation/economy/trading/BuyResult.hpp new file mode 100644 index 00000000..9eabff7c --- /dev/null +++ b/src/openvic-simulation/economy/trading/BuyResult.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" + +namespace OpenVic { + struct BuyResult { + private: + fixed_point_t PROPERTY(quantity_bought); + fixed_point_t PROPERTY(money_left); + public: + BuyResult( + const fixed_point_t new_quantity_bought, + const fixed_point_t new_money_left + ); + }; +} \ No newline at end of file diff --git a/src/openvic-simulation/economy/trading/BuyUpToOrder.cpp b/src/openvic-simulation/economy/trading/BuyUpToOrder.cpp new file mode 100644 index 00000000..ddff357f --- /dev/null +++ b/src/openvic-simulation/economy/trading/BuyUpToOrder.cpp @@ -0,0 +1,25 @@ +#include "BuyUpToOrder.hpp" + +using namespace OpenVic; + +GoodBuyUpToOrder::GoodBuyUpToOrder( + const fixed_point_t new_max_quantity, + const fixed_point_t new_money_to_spend, + std::function&& new_after_trade +) : max_quantity { new_max_quantity }, + money_to_spend { new_money_to_spend }, + after_trade { std::move(new_after_trade) } + {} + +BuyUpToOrder::BuyUpToOrder( + GoodDefinition const& new_good, + const fixed_point_t new_max_quantity, + const fixed_point_t new_money_to_spend, + std::function&& new_after_trade +) : GoodBuyUpToOrder( + new_max_quantity, + new_money_to_spend, + std::move(new_after_trade) + ), + good { new_good } + {} \ No newline at end of file diff --git a/src/openvic-simulation/economy/trading/BuyUpToOrder.hpp b/src/openvic-simulation/economy/trading/BuyUpToOrder.hpp new file mode 100644 index 00000000..bf4d0d35 --- /dev/null +++ b/src/openvic-simulation/economy/trading/BuyUpToOrder.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "openvic-simulation/economy/GoodDefinition.hpp" +#include "openvic-simulation/economy/trading/BuyResult.hpp" +#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" +#include "openvic-simulation/utility/Getters.hpp" + +namespace OpenVic { + struct GoodBuyUpToOrder { + private: + const fixed_point_t PROPERTY(max_quantity); + const fixed_point_t PROPERTY(money_to_spend); + std::function PROPERTY(after_trade); + + public: + GoodBuyUpToOrder( + const fixed_point_t new_max_quantity, + const fixed_point_t new_money_to_spend, + std::function&& new_after_trade + ); + GoodBuyUpToOrder(GoodBuyUpToOrder&&) = default; + }; + + struct BuyUpToOrder : GoodBuyUpToOrder { + private: + GoodDefinition const& PROPERTY(good); + + public: + BuyUpToOrder( + GoodDefinition const& new_good, + const fixed_point_t new_max_quantity, + const fixed_point_t new_money_to_spend, + std::function&& new_after_trade + ); + BuyUpToOrder(BuyUpToOrder&&) = default; + }; +} \ No newline at end of file diff --git a/src/openvic-simulation/economy/trading/MarketInstance.cpp b/src/openvic-simulation/economy/trading/MarketInstance.cpp index 3c9198a2..21eb384c 100644 --- a/src/openvic-simulation/economy/trading/MarketInstance.cpp +++ b/src/openvic-simulation/economy/trading/MarketInstance.cpp @@ -5,6 +5,11 @@ using namespace OpenVic; MarketInstance::MarketInstance(GoodInstanceManager& new_good_instance_manager) : good_instance_manager { new_good_instance_manager} {} +void MarketInstance::place_buy_up_to_order(BuyUpToOrder&& buy_up_to_order) { + GoodDefinition const& good = buy_up_to_order.get_good(); + GoodInstance& good_instance = good_instance_manager.get_good_instance_from_definition(good); + good_instance.add_buy_up_to_order(std::move(buy_up_to_order)); +} void MarketInstance::place_market_sell_order(MarketSellOrder&& market_sell_order) { GoodDefinition const& good = market_sell_order.get_good(); GoodInstance& good_instance = good_instance_manager.get_good_instance_from_definition(good); diff --git a/src/openvic-simulation/economy/trading/MarketInstance.hpp b/src/openvic-simulation/economy/trading/MarketInstance.hpp index 3f7e3692..48bf1f8f 100644 --- a/src/openvic-simulation/economy/trading/MarketInstance.hpp +++ b/src/openvic-simulation/economy/trading/MarketInstance.hpp @@ -1,6 +1,7 @@ #pragma once #include "openvic-simulation/economy/GoodInstance.hpp" +#include "openvic-simulation/economy/trading/BuyUpToOrder.hpp" #include "openvic-simulation/economy/trading/MarketSellOrder.hpp" namespace OpenVic { @@ -9,6 +10,7 @@ namespace OpenVic { GoodInstanceManager& PROPERTY(good_instance_manager); public: MarketInstance(GoodInstanceManager& new_good_instance_manager); + void place_buy_up_to_order(BuyUpToOrder&& buy_up_to_order); void place_market_sell_order(MarketSellOrder&& market_sell_order); void execute_orders(); }; diff --git a/src/openvic-simulation/map/MapInstance.cpp b/src/openvic-simulation/map/MapInstance.cpp index 2cea6970..a433bb65 100644 --- a/src/openvic-simulation/map/MapInstance.cpp +++ b/src/openvic-simulation/map/MapInstance.cpp @@ -89,8 +89,11 @@ bool MapInstance::setup( } bool MapInstance::apply_history_to_provinces( - ProvinceHistoryManager const& history_manager, const Date date, CountryInstanceManager& country_manager, - IssueManager const& issue_manager + ProvinceHistoryManager const& history_manager, + const Date date, + CountryInstanceManager& country_manager, + IssueManager const& issue_manager, + ArtisanalProducerFactoryPattern& artisanal_producer_factory_pattern ) { bool ret = true; @@ -124,7 +127,7 @@ bool MapInstance::apply_history_to_provinces( if (pop_history_entry == nullptr) { Logger::warning("No pop history entry for province ",province.get_identifier(), " for date ", date.to_string()); } else { - province.add_pop_vec(pop_history_entry->get_pops()); + province.add_pop_vec(pop_history_entry->get_pops(), artisanal_producer_factory_pattern); province.setup_pop_test_values(issue_manager); } diff --git a/src/openvic-simulation/map/MapInstance.hpp b/src/openvic-simulation/map/MapInstance.hpp index 5c3c0937..58fe5746 100644 --- a/src/openvic-simulation/map/MapInstance.hpp +++ b/src/openvic-simulation/map/MapInstance.hpp @@ -51,8 +51,11 @@ namespace OpenVic { decltype(ProvinceInstance::ideology_distribution)::keys_t const& ideology_keys ); bool apply_history_to_provinces( - ProvinceHistoryManager const& history_manager, const Date date, CountryInstanceManager& country_manager, - IssueManager const& issue_manager + ProvinceHistoryManager const& history_manager, + const Date date, + CountryInstanceManager& country_manager, + IssueManager const& issue_manager, + ArtisanalProducerFactoryPattern& artisanal_producer_factory_pattern ); void update_modifier_sums(const Date today, StaticModifierCache const& static_modifier_cache); diff --git a/src/openvic-simulation/map/ProvinceInstance.cpp b/src/openvic-simulation/map/ProvinceInstance.cpp index 2cc580e1..0e57a2d3 100644 --- a/src/openvic-simulation/map/ProvinceInstance.cpp +++ b/src/openvic-simulation/map/ProvinceInstance.cpp @@ -155,11 +155,15 @@ bool ProvinceInstance::add_pop(Pop&& pop) { } } -bool ProvinceInstance::add_pop_vec(std::vector const& pop_vec) { +bool ProvinceInstance::add_pop_vec(std::vector const& pop_vec, ArtisanalProducerFactoryPattern& artisanal_producer_factory_pattern) { if (!province_definition.is_water()) { reserve_more(pops, pop_vec.size()); for (PopBase const& pop : pop_vec) { - _add_pop(Pop { pop, *ideology_distribution.get_keys() }); + _add_pop(Pop { + pop, + *ideology_distribution.get_keys(), + artisanal_producer_factory_pattern + }); } return true; } else { @@ -371,6 +375,9 @@ void ProvinceInstance::update_gamestate(const Date today, DefineManager const& d } void ProvinceInstance::province_tick(const Date today) { + for (Pop& pop : pops) { + pop.pop_tick(); + } for (BuildingInstance& building : buildings.get_items()) { building.tick(today); } diff --git a/src/openvic-simulation/map/ProvinceInstance.hpp b/src/openvic-simulation/map/ProvinceInstance.hpp index 14ea63cd..9a0366bd 100644 --- a/src/openvic-simulation/map/ProvinceInstance.hpp +++ b/src/openvic-simulation/map/ProvinceInstance.hpp @@ -143,7 +143,7 @@ namespace OpenVic { bool expand_building(size_t building_index); bool add_pop(Pop&& pop); - bool add_pop_vec(std::vector const& pop_vec); + bool add_pop_vec(std::vector const& pop_vec, ArtisanalProducerFactoryPattern& artisanal_producer_factory_pattern); size_t get_pop_count() const; void update_modifier_sum(Date today, StaticModifierCache const& static_modifier_cache); diff --git a/src/openvic-simulation/pop/Pop.cpp b/src/openvic-simulation/pop/Pop.cpp index 3ff9aee7..b0a32f89 100644 --- a/src/openvic-simulation/pop/Pop.cpp +++ b/src/openvic-simulation/pop/Pop.cpp @@ -1,8 +1,12 @@ #define KEEP_DO_FOR_ALL_TYPES_OF_INCOME +#define KEEP_DO_FOR_ALL_TYPES_OF_EXPENSES #include "Pop.hpp" #undef KEEP_DO_FOR_ALL_TYPES_OF_INCOME +#undef KEEP_DO_FOR_ALL_TYPES_OF_EXPENSES #include "openvic-simulation/DefinitionManager.hpp" +#include "openvic-simulation/economy/production/ArtisanalProducer.hpp" +#include "openvic-simulation/economy/production/ArtisanalProducerFactoryPattern.hpp" using namespace OpenVic; @@ -12,8 +16,17 @@ PopBase::PopBase( ) : type { &new_type }, culture { new_culture }, religion { new_religion }, size { new_size }, militancy { new_militancy }, consciousness { new_consciousness }, rebel_type { new_rebel_type } {} -Pop::Pop(PopBase const& pop_base, decltype(ideologies)::keys_t const& ideology_keys) +Pop::Pop( + PopBase const& pop_base, + decltype(ideologies)::keys_t const& ideology_keys, + ArtisanalProducerFactoryPattern& artisanal_producer_factory_pattern +) : PopBase { pop_base }, + artisanal_producer_nullable { + type->get_is_artisan() + ? artisanal_producer_factory_pattern.CreateNewArtisanalProducer() + : nullptr + }, location { nullptr }, total_change { 0 }, num_grown { 0 }, @@ -33,11 +46,12 @@ Pop::Pop(PopBase const& pop_base, decltype(ideologies)::keys_t const& ideology_k life_needs_fulfilled { 0 }, everyday_needs_fulfilled { 0 }, luxury_needs_fulfilled { 0 }, - #define INITALIZE_POP_INCOME_STORES(name)\ + #define INITALIZE_POP_MONEY_STORES(name)\ name { 0 }, - DO_FOR_ALL_TYPES_OF_POP_INCOME(INITALIZE_POP_INCOME_STORES) - #undef INITALIZE_POP_INCOME_STORES + DO_FOR_ALL_TYPES_OF_POP_INCOME(INITALIZE_POP_MONEY_STORES) + DO_FOR_ALL_TYPES_OF_POP_EXPENSES(INITALIZE_POP_MONEY_STORES) + #undef INITALIZE_POP_MONEY_STORES max_supported_regiments { 0 } {} void Pop::setup_pop_test_values(IssueManager const& issue_manager) { @@ -154,19 +168,52 @@ void Pop::update_gamestate( } #define DEFINE_ADD_INCOME_FUNCTIONS(name)\ - void Pop::add_##name(const fixed_point_t pop_income){\ - name += pop_income;\ - income += pop_income;\ + void Pop::add_##name(const fixed_point_t amount){\ + if (amount == 0) { \ + Logger::warning("Adding ",#name, " of 0 to pop."); \ + return; \ + } \ + if (amount < 0) { \ + Logger::error("Adding negative ", #name, " to pop."); \ + return; \ + } \ + name += amount;\ + income += amount;\ + cash += amount;\ } DO_FOR_ALL_TYPES_OF_POP_INCOME(DEFINE_ADD_INCOME_FUNCTIONS) #undef DEFINE_ADD_INCOME_FUNCTIONS +#define DEFINE_ADD_EXPENSE_FUNCTIONS(name)\ + void Pop::add_##name(const fixed_point_t amount){\ + if (amount == 0) { \ + Logger::warning("Adding ",#name, " of 0 to pop."); \ + return; \ + } \ + name += amount;\ + expenses += amount;\ + if (expenses < 0) { \ + Logger::error("Total expenses became negative after adding ", #name, " to pop."); \ + } \ + cash -= amount;\ + if (cash < 0) { \ + Logger::error("Total cash became negative after adding ", #name, " to pop."); \ + } \ + } + +DO_FOR_ALL_TYPES_OF_POP_EXPENSES(DEFINE_ADD_EXPENSE_FUNCTIONS) +#undef DEFINE_ADD_EXPENSE_FUNCTIONS + #define SET_ALL_INCOME_TO_ZERO(name)\ name = fixed_point_t::_0(); -void Pop::clear_all_income(){ +void Pop::pop_tick() { DO_FOR_ALL_TYPES_OF_POP_INCOME(SET_ALL_INCOME_TO_ZERO) #undef DO_FOR_ALL_TYPES_OF_POP_INCOME #undef SET_ALL_INCOME_TO_ZERO + + if (artisanal_producer_nullable != nullptr) { + artisanal_producer_nullable->artisan_tick(*this); + } } \ No newline at end of file diff --git a/src/openvic-simulation/pop/Pop.hpp b/src/openvic-simulation/pop/Pop.hpp index e6124ac6..e45de1bd 100644 --- a/src/openvic-simulation/pop/Pop.hpp +++ b/src/openvic-simulation/pop/Pop.hpp @@ -1,6 +1,9 @@ #pragma once +#include + #include "openvic-simulation/country/CountryDefinition.hpp" +#include "openvic-simulation/economy/production/ArtisanalProducerFactoryPattern.hpp" #include "openvic-simulation/pop/PopType.hpp" namespace OpenVic { @@ -40,11 +43,17 @@ namespace OpenVic { F(event_and_decision_income)\ F(loan_interest_payments) - #define DECLARE_POP_INCOME_STORES(income_type)\ - fixed_point_t PROPERTY(income_type); + #define DO_FOR_ALL_TYPES_OF_POP_EXPENSES(F)\ + F(life_needs_expense)\ + F(everyday_needs_expense)\ + F(luxury_needs_expense)\ + F(artisan_inputs_expense) + + #define DECLARE_POP_MONEY_STORES(money_type)\ + fixed_point_t PROPERTY(money_type); - #define DECLARE_POP_INCOME_STORE_FUNCTIONS(name)\ - void add_##name(const fixed_point_t pop_income); + #define DECLARE_POP_MONEY_STORE_FUNCTIONS(name)\ + void add_##name(const fixed_point_t amount); /* REQUIREMENTS: * POP-18, POP-19, POP-20, POP-21, POP-34, POP-35, POP-36, POP-37 @@ -55,6 +64,7 @@ namespace OpenVic { static constexpr pop_size_t MAX_SIZE = std::numeric_limits::max(); private: + std::unique_ptr artisanal_producer_nullable; ProvinceInstance const* PROPERTY(location); /* Last day's size change by source. */ @@ -75,18 +85,23 @@ namespace OpenVic { fixed_point_t PROPERTY(unemployment); fixed_point_t PROPERTY(cash); fixed_point_t PROPERTY(income); - fixed_point_t PROPERTY(expenses); + fixed_point_t PROPERTY(expenses); //positive value means POP paid for goods. This is displayed * -1 in UI. fixed_point_t PROPERTY(savings); fixed_point_t PROPERTY(life_needs_fulfilled); fixed_point_t PROPERTY(everyday_needs_fulfilled); fixed_point_t PROPERTY(luxury_needs_fulfilled); - DO_FOR_ALL_TYPES_OF_POP_INCOME(DECLARE_POP_INCOME_STORES); - #undef DECLARE_POP_INCOME_STORES + DO_FOR_ALL_TYPES_OF_POP_INCOME(DECLARE_POP_MONEY_STORES); + DO_FOR_ALL_TYPES_OF_POP_EXPENSES(DECLARE_POP_MONEY_STORES); + #undef DECLARE_POP_MONEY_STORES size_t PROPERTY(max_supported_regiments); - Pop(PopBase const& pop_base, decltype(ideologies)::keys_t const& ideology_keys); + Pop( + PopBase const& pop_base, + decltype(ideologies)::keys_t const& ideology_keys, + ArtisanalProducerFactoryPattern& artisanal_producer_factory_pattern + ); public: Pop(Pop const&) = delete; @@ -104,12 +119,16 @@ namespace OpenVic { const fixed_point_t pop_size_per_regiment_multiplier ); - DO_FOR_ALL_TYPES_OF_POP_INCOME(DECLARE_POP_INCOME_STORE_FUNCTIONS) - #undef DECLARE_POP_INCOME_STORE_FUNCTIONS - void clear_all_income(); - + DO_FOR_ALL_TYPES_OF_POP_INCOME(DECLARE_POP_MONEY_STORE_FUNCTIONS) + DO_FOR_ALL_TYPES_OF_POP_EXPENSES(DECLARE_POP_MONEY_STORE_FUNCTIONS) + #undef DECLARE_POP_MONEY_STORE_FUNCTIONS + void pop_tick(); }; } #ifndef KEEP_DO_FOR_ALL_TYPES_OF_INCOME #undef DO_FOR_ALL_TYPES_OF_POP_INCOME #endif + +#ifndef KEEP_DO_FOR_ALL_TYPES_OF_EXPENSES + #undef DO_FOR_ALL_TYPES_OF_POP_EXPENSES +#endif From b9882a8b60bed62fdfdc9a66b904dd62d59ac2df Mon Sep 17 00:00:00 2001 From: wvpm <24685035+wvpm@users.noreply.github.com> Date: Sun, 1 Dec 2024 14:34:18 +0100 Subject: [PATCH 2/2] PARALLELISE_IF_SUPPORTED --- .../economy/trading/MarketInstance.cpp | 15 ++++++++--- src/openvic-simulation/map/MapInstance.cpp | 26 ++++++++++++++----- .../map/ProvinceInstance.cpp | 11 +++++--- .../utility/CompilerFeatureTesting.hpp | 22 ++++++++++++++++ 4 files changed, 60 insertions(+), 14 deletions(-) create mode 100644 src/openvic-simulation/utility/CompilerFeatureTesting.hpp diff --git a/src/openvic-simulation/economy/trading/MarketInstance.cpp b/src/openvic-simulation/economy/trading/MarketInstance.cpp index 21eb384c..f9dd2b1f 100644 --- a/src/openvic-simulation/economy/trading/MarketInstance.cpp +++ b/src/openvic-simulation/economy/trading/MarketInstance.cpp @@ -1,5 +1,8 @@ #include "MarketInstance.hpp" +#include "openvic-simulation/utility/CompilerFeatureTesting.hpp" +#include "economy/GoodInstance.hpp" + using namespace OpenVic; MarketInstance::MarketInstance(GoodInstanceManager& new_good_instance_manager) @@ -17,8 +20,12 @@ void MarketInstance::place_market_sell_order(MarketSellOrder&& market_sell_order } void MarketInstance::execute_orders() { - std::vector& good_instances = good_instance_manager.get_good_instances(); - for (GoodInstance& good_instance : good_instances) { - good_instance.execute_orders(); - } + auto& good_instances = good_instance_manager.get_good_instances(); + try_parallel_for_each( + good_instances.begin(), + good_instances.end(), + [](GoodInstance& good_instance) -> void { + good_instance.execute_orders(); + } + ); } diff --git a/src/openvic-simulation/map/MapInstance.cpp b/src/openvic-simulation/map/MapInstance.cpp index a433bb65..0155df2f 100644 --- a/src/openvic-simulation/map/MapInstance.cpp +++ b/src/openvic-simulation/map/MapInstance.cpp @@ -2,7 +2,9 @@ #include "openvic-simulation/history/ProvinceHistory.hpp" #include "openvic-simulation/map/MapDefinition.hpp" +#include "openvic-simulation/utility/CompilerFeatureTesting.hpp" #include "openvic-simulation/utility/Logger.hpp" +#include "map/ProvinceInstance.hpp" using namespace OpenVic; @@ -164,9 +166,14 @@ void MapInstance::update_gamestate(const Date today, DefineManager const& define } void MapInstance::map_tick(const Date today) { - for (ProvinceInstance& province : province_instances.get_items()) { - province.province_tick(today); - } + auto& provinces = province_instances.get_items(); + try_parallel_for_each( + provinces.begin(), + provinces.end(), + [today](ProvinceInstance& province) -> void { + province.province_tick(today); + } + ); } void MapInstance::initialise_for_new_game( @@ -174,8 +181,13 @@ void MapInstance::initialise_for_new_game( DefineManager const& define_manager ) { update_gamestate(today, define_manager); - for (ProvinceInstance& province : province_instances.get_items()) { - province.initialise_rgo(); - province.province_tick(today); - } + auto& provinces = province_instances.get_items(); + try_parallel_for_each( + provinces.begin(), + provinces.end(), + [today](ProvinceInstance& province) -> void { + province.initialise_rgo(); + province.province_tick(today); + } + ); } \ No newline at end of file diff --git a/src/openvic-simulation/map/ProvinceInstance.cpp b/src/openvic-simulation/map/ProvinceInstance.cpp index 0e57a2d3..9768a95b 100644 --- a/src/openvic-simulation/map/ProvinceInstance.cpp +++ b/src/openvic-simulation/map/ProvinceInstance.cpp @@ -12,6 +12,7 @@ #include "openvic-simulation/military/UnitInstanceGroup.hpp" #include "openvic-simulation/modifier/StaticModifierCache.hpp" #include "openvic-simulation/politics/Ideology.hpp" +#include "openvic-simulation/utility/CompilerFeatureTesting.hpp" #include "openvic-simulation/utility/Logger.hpp" using namespace OpenVic; @@ -375,9 +376,13 @@ void ProvinceInstance::update_gamestate(const Date today, DefineManager const& d } void ProvinceInstance::province_tick(const Date today) { - for (Pop& pop : pops) { - pop.pop_tick(); - } + try_parallel_for_each( + pops.begin(), + pops.end(), + [](Pop& pop) -> void { + pop.pop_tick(); + } + ); for (BuildingInstance& building : buildings.get_items()) { building.tick(today); } diff --git a/src/openvic-simulation/utility/CompilerFeatureTesting.hpp b/src/openvic-simulation/utility/CompilerFeatureTesting.hpp new file mode 100644 index 00000000..dcf2bc5d --- /dev/null +++ b/src/openvic-simulation/utility/CompilerFeatureTesting.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include + +namespace OpenVic { + template + inline constexpr void try_parallel_for_each( + InputIt first, + InputIt last, + UnaryFunc f + ) { + #ifdef __cpp_lib_execution + if constexpr (__cpp_lib_execution >= 201603L) { + std::for_each(std::execution::par, first, last, f); + } else { + std::for_each(first, last, f); + } + #else + std::for_each(first, last, f); + #endif + } +} \ No newline at end of file