From 463e6457f909c2efa25ab429ceaf5c6e0545165b 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] Implement artisanal production --- src/openvic-simulation/InstanceManager.cpp | 8 +- .../economy/GoodInstance.cpp | 68 +++++++++- .../economy/GoodInstance.hpp | 8 ++ .../economy/production/ArtisanalProducer.cpp | 121 +++++++++++++++++- .../economy/production/ArtisanalProducer.hpp | 20 ++- .../ArtisanalProducerFactoryPattern.cpp | 44 +++++++ .../ArtisanalProducerFactoryPattern.hpp | 28 ++++ .../economy/trading/BuyResult.cpp | 11 ++ .../economy/trading/BuyResult.hpp | 16 +++ .../economy/trading/BuyUpToOrder.cpp | 25 ++++ .../economy/trading/BuyUpToOrder.hpp | 35 +++++ .../economy/trading/MarketInstance.cpp | 5 + .../economy/trading/MarketInstance.hpp | 2 + .../economy/trading/MarketSellOrder.cpp | 4 +- src/openvic-simulation/map/MapInstance.cpp | 9 +- src/openvic-simulation/map/MapInstance.hpp | 7 +- .../map/ProvinceInstance.cpp | 12 +- .../map/ProvinceInstance.hpp | 2 +- src/openvic-simulation/pop/Pop.cpp | 59 +++++++-- src/openvic-simulation/pop/Pop.hpp | 43 +++++-- 20 files changed, 477 insertions(+), 50 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 2085d6d7..be730e02 100644 --- a/src/openvic-simulation/InstanceManager.cpp +++ b/src/openvic-simulation/InstanceManager.cpp @@ -128,11 +128,17 @@ bool InstanceManager::load_bookmark(Bookmark const* new_bookmark) { today = bookmark->get_date(); + const 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 ca001704..ff543849 100644 --- a/src/openvic-simulation/economy/GoodInstance.cpp +++ b/src/openvic-simulation/economy/GoodInstance.cpp @@ -1,16 +1,38 @@ #include "GoodInstance.hpp" +#include "types/fixed_point/FixedPoint.hpp" 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 const&& 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 const&& market_sell_order) { const std::lock_guard lock {*sell_lock}; @@ -18,20 +40,52 @@ void GoodInstance::add_market_sell_order(GoodMarketSellOrder const&& market_sell } 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 058f96dd..9d094c2d 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 const&& buy_up_to_order); void add_market_sell_order(GoodMarketSellOrder const&& 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..2b98b469 100644 --- a/src/openvic-simulation/economy/production/ArtisanalProducer.cpp +++ b/src/openvic-simulation/economy/production/ArtisanalProducer.cpp @@ -1,13 +1,120 @@ #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" +#include "types/fixed_point/FixedPoint.hpp" + using namespace OpenVic; ArtisanalProducer::ArtisanalProducer( - ProductionType const& new_production_type, + MarketInstance& new_market_instance, + ModifierEffectCache const& new_modifier_effect_cache, + Pop& new_pop, 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 }, + pop { new_pop }, + stockpile { new_stockpile }, + production_type { new_production_type }, + current_production { new_current_production } + {} + +void ArtisanalProducer::artisan_tick() { + 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, 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, + [this](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..0f3b982c 100644 --- a/src/openvic-simulation/economy/production/ArtisanalProducer.hpp +++ b/src/openvic-simulation/economy/production/ArtisanalProducer.hpp @@ -1,22 +1,32 @@ #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; + Pop& pop; + 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, + Pop& new_pop, + GoodDefinition::good_definition_map_t&& new_stockpile, + ProductionType const& new_production_type, + fixed_point_t new_current_production ); + void artisan_tick(); }; } diff --git a/src/openvic-simulation/economy/production/ArtisanalProducerFactoryPattern.cpp b/src/openvic-simulation/economy/production/ArtisanalProducerFactoryPattern.cpp new file mode 100644 index 00000000..3cdb1d5f --- /dev/null +++ b/src/openvic-simulation/economy/production/ArtisanalProducerFactoryPattern.cpp @@ -0,0 +1,44 @@ +#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 +) : market_instance { new_market_instance }, + modifier_effect_cache { new_modifier_effect_cache }, + production_type_manager { new_production_type_manager } + {} + +std::unique_ptr ArtisanalProducerFactoryPattern::CreateNewArtisanalProducer(Pop& pop) const { + ProductionType const* random_artisanal_production_type = nullptr; + + 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; + } + random_artisanal_production_type = &production_type; + break; //TODO actually randomise it instead of picking the first. + } + } + + if (random_artisanal_production_type == nullptr) { + Logger::error("CreateNewArtisanalProducer was called but there are no artisanal production types."); + } + + return std::make_unique( + market_instance, + modifier_effect_cache, + pop, + 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..0d422c14 --- /dev/null +++ b/src/openvic-simulation/economy/production/ArtisanalProducerFactoryPattern.hpp @@ -0,0 +1,28 @@ +#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: + 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(Pop& pop) const; + }; +} \ 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..a220653c --- /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 const&& 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 const&& 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..7945131b --- /dev/null +++ b/src/openvic-simulation/economy/trading/BuyUpToOrder.hpp @@ -0,0 +1,35 @@ +#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); + const std::function PROPERTY(after_trade); + + public: + GoodBuyUpToOrder( + const fixed_point_t new_max_quantity, + const fixed_point_t new_money_to_spend, + std::function const&& new_after_trade + ); + }; + + 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 const&& new_after_trade + ); + }; +} \ 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 4a648cd9..550c5742 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 const&& 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 const&& 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 a75df491..4e268ca5 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 const&& buy_up_to_order); void place_market_sell_order(MarketSellOrder const&& market_sell_order); void execute_orders(); }; diff --git a/src/openvic-simulation/economy/trading/MarketSellOrder.cpp b/src/openvic-simulation/economy/trading/MarketSellOrder.cpp index bdec241d..4977dab8 100644 --- a/src/openvic-simulation/economy/trading/MarketSellOrder.cpp +++ b/src/openvic-simulation/economy/trading/MarketSellOrder.cpp @@ -4,7 +4,7 @@ using namespace OpenVic; GoodMarketSellOrder::GoodMarketSellOrder( const fixed_point_t new_quantity, - std::functionconst&& new_after_trade + std::function const&& new_after_trade ): quantity { new_quantity }, after_trade { std::move(new_after_trade) } @@ -13,7 +13,7 @@ GoodMarketSellOrder::GoodMarketSellOrder( MarketSellOrder::MarketSellOrder( GoodDefinition const& new_good, const fixed_point_t new_quantity, - std::functionconst&& new_after_trade + std::function const&& new_after_trade ): GoodMarketSellOrder(new_quantity, std::move(new_after_trade)), good { new_good } {} \ No newline at end of file diff --git a/src/openvic-simulation/map/MapInstance.cpp b/src/openvic-simulation/map/MapInstance.cpp index 2cea6970..175783c9 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 const& 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..47799c3d 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 const& 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..f5316c38 100644 --- a/src/openvic-simulation/map/ProvinceInstance.cpp +++ b/src/openvic-simulation/map/ProvinceInstance.cpp @@ -13,6 +13,7 @@ #include "openvic-simulation/modifier/StaticModifierCache.hpp" #include "openvic-simulation/politics/Ideology.hpp" #include "openvic-simulation/utility/Logger.hpp" +#include "economy/production/ArtisanalProducerFactoryPattern.hpp" using namespace OpenVic; @@ -155,11 +156,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 const& 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 +376,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..a03d36f2 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 const& 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..e63b5df9 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 const& artisanal_producer_factory_pattern +) : PopBase { pop_base }, + artisanal_producer_nullable { + type->get_is_artisan() + ? artisanal_producer_factory_pattern.CreateNewArtisanalProducer(*this) + : 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,18 +168,47 @@ 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 diff --git a/src/openvic-simulation/pop/Pop.hpp b/src/openvic-simulation/pop/Pop.hpp index e6124ac6..1ba1857c 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 const& 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