diff --git a/src/openvic-simulation/InstanceManager.cpp b/src/openvic-simulation/InstanceManager.cpp index ee9f4da85..5e9891678 100644 --- a/src/openvic-simulation/InstanceManager.cpp +++ b/src/openvic-simulation/InstanceManager.cpp @@ -10,14 +10,15 @@ InstanceManager::InstanceManager( DefinitionManager const& new_definition_manager, gamestate_updated_func_t gamestate_updated_callback, SimulationClock::state_changed_function_t clock_state_changed_callback -) : game_rules_manager { new_game_rules_manager }, +) : definition_manager { new_definition_manager }, + game_rules_manager { new_game_rules_manager }, + good_instance_manager { new_definition_manager.get_economy_manager().get_good_definition_manager() }, market_instance { good_instance_manager }, artisanal_producer_factory_pattern { market_instance, new_definition_manager.get_modifier_manager().get_modifier_effect_cache(), new_definition_manager.get_economy_manager().get_production_type_manager() }, - definition_manager { new_definition_manager }, global_flags { "global" }, country_instance_manager { new_definition_manager.get_country_definition_manager() }, politics_instance_manager { *this }, @@ -71,6 +72,10 @@ void InstanceManager::tick() { unit_instance_manager.tick(); market_instance.execute_orders(); + if (today.is_month_start()) { + market_instance.record_price_history(); + } + set_gamestate_needs_update(); } @@ -81,7 +86,6 @@ bool InstanceManager::setup() { } bool ret = good_instance_manager.setup_goods( - definition_manager.get_economy_manager().get_good_definition_manager(), game_rules_manager ); ret &= map_instance.setup( @@ -101,9 +105,11 @@ bool InstanceManager::setup() { definition_manager.get_politics_manager().get_government_type_manager().get_government_types(), definition_manager.get_crime_manager().get_crime_modifiers(), definition_manager.get_pop_manager().get_pop_types(), + good_instance_manager.get_good_instances(), definition_manager.get_military_manager().get_unit_type_manager().get_regiment_types(), definition_manager.get_military_manager().get_unit_type_manager().get_ship_types(), - definition_manager.get_pop_manager().get_stratas() + definition_manager.get_pop_manager().get_stratas(), + good_instance_manager ); game_instance_setup = true; diff --git a/src/openvic-simulation/InstanceManager.hpp b/src/openvic-simulation/InstanceManager.hpp index 14cd6b848..874f82636 100644 --- a/src/openvic-simulation/InstanceManager.hpp +++ b/src/openvic-simulation/InstanceManager.hpp @@ -22,11 +22,12 @@ namespace OpenVic { using gamestate_updated_func_t = std::function; private: + DefinitionManager const& PROPERTY(definition_manager); + GameRulesManager const& game_rules_manager; GoodInstanceManager PROPERTY_REF(good_instance_manager); MarketInstance PROPERTY_REF(market_instance); ArtisanalProducerFactoryPattern artisanal_producer_factory_pattern; - DefinitionManager const& PROPERTY(definition_manager); FlagStrings PROPERTY_REF(global_flags); diff --git a/src/openvic-simulation/country/CountryInstance.cpp b/src/openvic-simulation/country/CountryInstance.cpp index 13e698980..d0c6b4d16 100644 --- a/src/openvic-simulation/country/CountryInstance.cpp +++ b/src/openvic-simulation/country/CountryInstance.cpp @@ -33,9 +33,11 @@ CountryInstance::CountryInstance( decltype(government_flag_overrides)::keys_type const& government_type_keys, decltype(crime_unlock_levels)::keys_type const& crime_keys, decltype(pop_type_distribution)::keys_type const& pop_type_keys, + decltype(goods_data)::keys_type const& good_instances_keys, decltype(regiment_type_unlock_levels)::keys_type const& regiment_type_unlock_levels_keys, decltype(ship_type_unlock_levels)::keys_type const& ship_type_unlock_levels_keys, - decltype(tax_rate_by_strata)::keys_type const& strata_keys + decltype(tax_rate_by_strata)::keys_type const& strata_keys, + GoodInstanceManager& good_instance_manager ) : FlagStrings { "country" }, /* Main attributes */ country_definition { new_country_definition }, @@ -68,6 +70,7 @@ CountryInstance::CountryInstance( vote_distribution { nullptr }, /* Trade */ + goods_data { &good_instances_keys }, /* Diplomacy */ @@ -82,7 +85,7 @@ CountryInstance::CountryInstance( for (BuildingType const& building_type : *building_type_unlock_levels.get_keys()) { if (building_type.is_default_enabled()) { - unlock_building_type(building_type); + unlock_building_type(building_type, good_instance_manager); } } @@ -448,7 +451,9 @@ bool CountryInstance::is_unit_type_unlocked(UnitType const& unit_type) const { } } -bool CountryInstance::modify_building_type_unlock(BuildingType const& building_type, unlock_level_t unlock_level_change) { +bool CountryInstance::modify_building_type_unlock( + BuildingType const& building_type, unlock_level_t unlock_level_change, GoodInstanceManager& good_instance_manager +) { decltype(building_type_unlock_levels)::value_ref_type unlock_level = building_type_unlock_levels[building_type]; // This catches subtracting below 0 or adding above the int types maximum value @@ -464,11 +469,15 @@ bool CountryInstance::modify_building_type_unlock(BuildingType const& building_t unlock_level += unlock_level_change; + if (building_type.get_production_type() != nullptr) { + good_instance_manager.enable_good(building_type.get_production_type()->get_output_good()); + } + return true; } -bool CountryInstance::unlock_building_type(BuildingType const& building_type) { - return modify_building_type_unlock(building_type, 1); +bool CountryInstance::unlock_building_type(BuildingType const& building_type, GoodInstanceManager& good_instance_manager) { + return modify_building_type_unlock(building_type, 1, good_instance_manager); } bool CountryInstance::is_building_type_unlocked(BuildingType const& building_type) const { @@ -594,7 +603,9 @@ CountryInstance::unit_variant_t CountryInstance::get_max_unlocked_unit_variant() return unit_variant_unlock_levels.size(); } -bool CountryInstance::modify_technology_unlock(Technology const& technology, unlock_level_t unlock_level_change) { +bool CountryInstance::modify_technology_unlock( + Technology const& technology, unlock_level_t unlock_level_change, GoodInstanceManager& good_instance_manager +) { decltype(technology_unlock_levels)::value_ref_type unlock_level = technology_unlock_levels[technology]; // This catches subtracting below 0 or adding above the int types maximum value @@ -621,26 +632,30 @@ bool CountryInstance::modify_technology_unlock(Technology const& technology, unl ret &= modify_unit_type_unlock(*unit, unlock_level_change); } for (BuildingType const* building : technology.get_activated_buildings()) { - ret &= modify_building_type_unlock(*building, unlock_level_change); + ret &= modify_building_type_unlock(*building, unlock_level_change, good_instance_manager); } return ret; } -bool CountryInstance::set_technology_unlock_level(Technology const& technology, unlock_level_t unlock_level) { +bool CountryInstance::set_technology_unlock_level( + Technology const& technology, unlock_level_t unlock_level, GoodInstanceManager& good_instance_manager +) { const unlock_level_t unlock_level_change = unlock_level - technology_unlock_levels[technology]; - return unlock_level_change != 0 ? modify_technology_unlock(technology, unlock_level_change) : true; + return unlock_level_change != 0 ? modify_technology_unlock(technology, unlock_level_change, good_instance_manager) : true; } -bool CountryInstance::unlock_technology(Technology const& technology) { - return modify_technology_unlock(technology, 1); +bool CountryInstance::unlock_technology(Technology const& technology, GoodInstanceManager& good_instance_manager) { + return modify_technology_unlock(technology, 1, good_instance_manager); } bool CountryInstance::is_technology_unlocked(Technology const& technology) const { return technology_unlock_levels[technology] > 0; } -bool CountryInstance::modify_invention_unlock(Invention const& invention, unlock_level_t unlock_level_change) { +bool CountryInstance::modify_invention_unlock( + Invention const& invention, unlock_level_t unlock_level_change, GoodInstanceManager& good_instance_manager +) { decltype(invention_unlock_levels)::value_ref_type unlock_level = invention_unlock_levels[invention]; // This catches subtracting below 0 or adding above the int types maximum value @@ -664,7 +679,7 @@ bool CountryInstance::modify_invention_unlock(Invention const& invention, unlock ret &= modify_unit_type_unlock(*unit, unlock_level_change); } for (BuildingType const* building : invention.get_activated_buildings()) { - ret &= modify_building_type_unlock(*building, unlock_level_change); + ret &= modify_building_type_unlock(*building, unlock_level_change, good_instance_manager); } for (Crime const* crime : invention.get_enabled_crimes()) { ret &= modify_crime_unlock(*crime, unlock_level_change); @@ -679,13 +694,15 @@ bool CountryInstance::modify_invention_unlock(Invention const& invention, unlock return ret; } -bool CountryInstance::set_invention_unlock_level(Invention const& invention, unlock_level_t unlock_level) { +bool CountryInstance::set_invention_unlock_level( + Invention const& invention, unlock_level_t unlock_level, GoodInstanceManager& good_instance_manager +) { const unlock_level_t unlock_level_change = unlock_level - invention_unlock_levels[invention]; - return unlock_level_change != 0 ? modify_invention_unlock(invention, unlock_level_change) : true; + return unlock_level_change != 0 ? modify_invention_unlock(invention, unlock_level_change, good_instance_manager) : true; } -bool CountryInstance::unlock_invention(Invention const& invention) { - return modify_invention_unlock(invention, 1); +bool CountryInstance::unlock_invention(Invention const& invention, GoodInstanceManager& good_instance_manager) { + return modify_invention_unlock(invention, 1, good_instance_manager); } bool CountryInstance::is_invention_unlocked(Invention const& invention) const { @@ -798,12 +815,16 @@ bool CountryInstance::apply_history_to_country(CountryHistoryEntry const& entry, target[*key] = value; } }; + + GoodInstanceManager& good_instance_manager = instance_manager.get_good_instance_manager(); + for (auto const& [technology, level] : entry.get_technologies()) { - ret &= set_technology_unlock_level(*technology, level); + ret &= set_technology_unlock_level(*technology, level, good_instance_manager); } for (auto const& [invention, activated] : entry.get_inventions()) { - ret &= set_invention_unlock_level(*invention, activated ? 1 : 0); + ret &= set_invention_unlock_level(*invention, activated ? 1 : 0, good_instance_manager); } + apply_foreign_investments(entry.get_foreign_investment(), instance_manager.get_country_instance_manager()); set_optional(releasable_vassal, entry.is_releasable_vassal()); @@ -1333,7 +1354,7 @@ void CountryInstance::tick(InstanceManager& instance_manager) { invested_research_points += research_points_spent; if (invested_research_points >= current_research_cost) { - unlock_technology(*current_research); + unlock_technology(*current_research, instance_manager.get_good_instance_manager()); current_research = nullptr; invested_research_points = fixed_point_t::_0(); current_research_cost = fixed_point_t::_0(); @@ -1502,9 +1523,11 @@ bool CountryInstanceManager::generate_country_instances( decltype(CountryInstance::government_flag_overrides)::keys_type const& government_type_keys, decltype(CountryInstance::crime_unlock_levels)::keys_type const& crime_keys, decltype(CountryInstance::pop_type_distribution)::keys_type const& pop_type_keys, + decltype(CountryInstance::goods_data)::keys_type const& good_instances_keys, decltype(CountryInstance::regiment_type_unlock_levels)::keys_type const& regiment_type_unlock_levels_keys, decltype(CountryInstance::ship_type_unlock_levels)::keys_type const& ship_type_unlock_levels_keys, - decltype(CountryInstance::tax_rate_by_strata):: keys_type const& strata_keys + decltype(CountryInstance::tax_rate_by_strata):: keys_type const& strata_keys, + GoodInstanceManager& good_instance_manager ) { reserve_more(country_instances, country_definition_manager.get_country_definition_count()); @@ -1521,9 +1544,11 @@ bool CountryInstanceManager::generate_country_instances( government_type_keys, crime_keys, pop_type_keys, + good_instances_keys, regiment_type_unlock_levels_keys, ship_type_unlock_levels_keys, - strata_keys + strata_keys, + good_instance_manager })) { // We need to update the country's ModifierSum's source here as the country's address is finally stable // after changing between its constructor call and now due to being std::move'd into the registry. diff --git a/src/openvic-simulation/country/CountryInstance.hpp b/src/openvic-simulation/country/CountryInstance.hpp index 674fec3ea..fa734cc0f 100644 --- a/src/openvic-simulation/country/CountryInstance.hpp +++ b/src/openvic-simulation/country/CountryInstance.hpp @@ -37,6 +37,8 @@ namespace OpenVic { struct Culture; struct Religion; struct BuildingType; + struct GoodInstance; + struct GoodInstanceManager; struct CountryHistoryEntry; struct DefineManager; struct ModifierEffectCache; @@ -186,7 +188,29 @@ namespace OpenVic { // TODO - national foci /* Trade */ - // TODO - total amount of each good exported and imported + public: + struct good_data_t { + fixed_point_t stockpile_amount; + fixed_point_t stockpile_change_yesterday; // positive if we bought, negative if we sold + + bool is_automated = true; + bool is_selling = false; // buying if false + fixed_point_t stockpile_cutoff; + + fixed_point_t exported_amount; // negative if net importing + + fixed_point_t government_needs; + fixed_point_t army_needs; + fixed_point_t navy_needs; + fixed_point_t production_needs; + fixed_point_t overseas_needs; + fixed_point_t factory_needs; + fixed_point_t pop_needs; + fixed_point_t available_amount; + }; + + private: + IndexedMap PROPERTY(goods_data); /* Diplomacy */ fixed_point_t PROPERTY(prestige); @@ -253,9 +277,11 @@ namespace OpenVic { decltype(government_flag_overrides)::keys_type const& government_type_keys, decltype(crime_unlock_levels)::keys_type const& crime_keys, decltype(pop_type_distribution)::keys_type const& pop_type_keys, + decltype(goods_data)::keys_type const& good_instances_keys, decltype(regiment_type_unlock_levels)::keys_type const& regiment_type_unlock_levels_keys, decltype(ship_type_unlock_levels)::keys_type const& ship_type_unlock_levels_keys, - decltype(tax_rate_by_strata)::keys_type const& strata_keys + decltype(tax_rate_by_strata)::keys_type const& strata_keys, + GoodInstanceManager& good_instance_manager ); public: @@ -399,8 +425,10 @@ namespace OpenVic { bool unlock_unit_type(UnitType const& unit_type); bool is_unit_type_unlocked(UnitType const& unit_type) const; - bool modify_building_type_unlock(BuildingType const& building_type, unlock_level_t unlock_level_change); - bool unlock_building_type(BuildingType const& building_type); + bool modify_building_type_unlock( + BuildingType const& building_type, unlock_level_t unlock_level_change, GoodInstanceManager& good_instance_manager + ); + bool unlock_building_type(BuildingType const& building_type, GoodInstanceManager& good_instance_manager); bool is_building_type_unlocked(BuildingType const& building_type) const; bool modify_crime_unlock(Crime const& crime, unlock_level_t unlock_level_change); @@ -419,14 +447,22 @@ namespace OpenVic { bool unlock_unit_variant(unit_variant_t unit_variant); unit_variant_t get_max_unlocked_unit_variant() const; - bool modify_technology_unlock(Technology const& technology, unlock_level_t unlock_level_change); - bool set_technology_unlock_level(Technology const& technology, unlock_level_t unlock_level); - bool unlock_technology(Technology const& technology); + bool modify_technology_unlock( + Technology const& technology, unlock_level_t unlock_level_change, GoodInstanceManager& good_instance_manager + ); + bool set_technology_unlock_level( + Technology const& technology, unlock_level_t unlock_level, GoodInstanceManager& good_instance_manager + ); + bool unlock_technology(Technology const& technology, GoodInstanceManager& good_instance_manager); bool is_technology_unlocked(Technology const& technology) const; - bool modify_invention_unlock(Invention const& invention, unlock_level_t unlock_level_change); - bool set_invention_unlock_level(Invention const& invention, unlock_level_t unlock_level); - bool unlock_invention(Invention const& invention); + bool modify_invention_unlock( + Invention const& invention, unlock_level_t unlock_level_change, GoodInstanceManager& good_instance_manager + ); + bool set_invention_unlock_level( + Invention const& invention, unlock_level_t unlock_level, GoodInstanceManager& good_instance_manager + ); + bool unlock_invention(Invention const& invention, GoodInstanceManager& good_instance_manager); bool is_invention_unlocked(Invention const& invention) const; bool is_primary_culture(Culture const& culture) const; @@ -517,9 +553,11 @@ namespace OpenVic { decltype(CountryInstance::government_flag_overrides)::keys_type const& government_type_keys, decltype(CountryInstance::crime_unlock_levels)::keys_type const& crime_keys, decltype(CountryInstance::pop_type_distribution)::keys_type const& pop_type_keys, + decltype(CountryInstance::goods_data)::keys_type const& good_instances_keys, decltype(CountryInstance::regiment_type_unlock_levels)::keys_type const& regiment_type_unlock_levels_keys, decltype(CountryInstance::ship_type_unlock_levels)::keys_type const& ship_type_unlock_levels_keys, - decltype(CountryInstance::tax_rate_by_strata):: keys_type const& strata_keys + decltype(CountryInstance::tax_rate_by_strata):: keys_type const& strata_keys, + GoodInstanceManager& good_instance_manager ); bool apply_history_to_countries(CountryHistoryManager const& history_manager, InstanceManager& instance_manager); diff --git a/src/openvic-simulation/economy/GoodDefinition.cpp b/src/openvic-simulation/economy/GoodDefinition.cpp index 5440b94a9..3bb96e055 100644 --- a/src/openvic-simulation/economy/GoodDefinition.cpp +++ b/src/openvic-simulation/economy/GoodDefinition.cpp @@ -26,16 +26,22 @@ GoodDefinition::GoodDefinition( is_money { new_is_money }, counters_overseas_penalty { new_counters_overseas_penalty } {} -bool GoodDefinitionManager::add_good_category(std::string_view identifier) { +bool GoodDefinitionManager::add_good_category(std::string_view identifier, size_t expected_goods_in_category) { if (identifier.empty()) { Logger::error("Invalid good category identifier - empty!"); return false; } - return good_categories.add_item({ identifier }); + + if (good_categories.add_item({ identifier })) { + good_categories.back().good_definitions.reserve(expected_goods_in_category); + return true; + } else { + return false; + } } bool GoodDefinitionManager::add_good_definition( - std::string_view identifier, colour_t colour, GoodCategory const& category, fixed_point_t base_price, + std::string_view identifier, colour_t colour, GoodCategory& category, fixed_point_t base_price, bool is_available_from_start, bool is_tradeable, bool is_money, bool has_overseas_penalty ) { if (identifier.empty()) { @@ -46,46 +52,72 @@ bool GoodDefinitionManager::add_good_definition( Logger::error("Invalid base price for ", identifier, ": ", base_price); return false; } - return good_definitions.add_item({ + + if (good_definitions.add_item({ identifier, colour, get_good_definition_count(), category, base_price, is_available_from_start, is_tradeable, is_money, has_overseas_penalty - }); + })) { + category.good_definitions.push_back(&get_back_good_definition()); + return true; + } else { + return false; + } } bool GoodDefinitionManager::load_goods_file(ast::NodeCPtr root) { size_t total_expected_goods = 0; + bool ret = expect_dictionary_reserve_length( good_categories, [this, &total_expected_goods](std::string_view key, ast::NodeCPtr value) -> bool { - bool ret = expect_length(add_variable_callback(total_expected_goods))(value); - ret &= add_good_category(key); + size_t expected_goods_in_category = 0; + + bool ret = expect_length(assign_variable_callback(expected_goods_in_category))(value); + + if (add_good_category(key, expected_goods_in_category)) { + total_expected_goods += expected_goods_in_category; + } else { + ret = false; + } + return ret; } )(root); + lock_good_categories(); reserve_more_good_definitions(total_expected_goods); - ret &= expect_good_category_dictionary([this](GoodCategory const& good_category, ast::NodeCPtr good_category_value) -> bool { - return expect_dictionary([this, &good_category](std::string_view key, ast::NodeCPtr value) -> bool { - colour_t colour = colour_t::null(); - fixed_point_t base_price; - bool is_available_from_start = true, is_tradeable = true; - bool is_money = false, has_overseas_penalty = false; - - bool ret = expect_dictionary_keys( - "color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)), - "cost", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(base_price)), - "available_from_start", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_available_from_start)), - "tradeable", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_tradeable)), - "money", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_money)), - "overseas_penalty", ZERO_OR_ONE, expect_bool(assign_variable_callback(has_overseas_penalty)) - )(value); - ret &= add_good_definition( - key, colour, good_category, base_price, is_available_from_start, is_tradeable, is_money, has_overseas_penalty - ); - return ret; - })(good_category_value); - })(root); + + ret &= good_categories.expect_item_dictionary( + [this](GoodCategory& good_category, ast::NodeCPtr good_category_value) -> bool { + return expect_dictionary( + [this, &good_category](std::string_view key, ast::NodeCPtr value) -> bool { + colour_t colour = colour_t::null(); + fixed_point_t base_price; + bool is_available_from_start = true, is_tradeable = true; + bool is_money = false, has_overseas_penalty = false; + + bool ret = expect_dictionary_keys( + "color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)), + "cost", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(base_price)), + "available_from_start", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_available_from_start)), + "tradeable", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_tradeable)), + "money", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_money)), + "overseas_penalty", ZERO_OR_ONE, expect_bool(assign_variable_callback(has_overseas_penalty)) + )(value); + + ret &= add_good_definition( + key, colour, good_category, base_price, is_available_from_start, is_tradeable, is_money, + has_overseas_penalty + ); + + return ret; + } + )(good_category_value); + } + )(root); + lock_good_definitions(); + return ret; } diff --git a/src/openvic-simulation/economy/GoodDefinition.hpp b/src/openvic-simulation/economy/GoodDefinition.hpp index 7cb87cc2c..04a708f33 100644 --- a/src/openvic-simulation/economy/GoodDefinition.hpp +++ b/src/openvic-simulation/economy/GoodDefinition.hpp @@ -4,11 +4,14 @@ namespace OpenVic { struct GoodDefinitionManager; + struct GoodDefinition; struct GoodCategory : HasIdentifier { friend struct GoodDefinitionManager; private: + std::vector PROPERTY(good_definitions); + GoodCategory(std::string_view new_identifier); public: @@ -58,10 +61,10 @@ namespace OpenVic { IdentifierRegistry IDENTIFIER_REGISTRY(good_definition); public: - bool add_good_category(std::string_view identifier); + bool add_good_category(std::string_view identifier, size_t expected_goods_in_category); bool add_good_definition( - std::string_view identifier, colour_t colour, GoodCategory const& category, fixed_point_t base_price, + std::string_view identifier, colour_t colour, GoodCategory& category, fixed_point_t base_price, bool is_available_from_start, bool is_tradeable, bool is_money, bool has_overseas_penalty ); diff --git a/src/openvic-simulation/economy/GoodInstance.cpp b/src/openvic-simulation/economy/GoodInstance.cpp index 59ec9fd9c..6f1164cb8 100644 --- a/src/openvic-simulation/economy/GoodInstance.cpp +++ b/src/openvic-simulation/economy/GoodInstance.cpp @@ -2,23 +2,20 @@ using namespace OpenVic; +static constexpr size_t MONTHS_OF_PRICE_HISTORY = 36; + GoodInstance::GoodInstance(GoodDefinition const& new_good_definition, GameRulesManager const& new_game_rules_manager) : HasIdentifierAndColour { new_good_definition }, + good_definition { new_good_definition }, buy_lock { std::make_unique() }, sell_lock { std::make_unique() }, game_rules_manager { new_game_rules_manager }, - good_definition { new_good_definition }, price { new_good_definition.get_base_price() }, - price_change_yesterday { fixed_point_t::_0() }, - 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() }, - quantity_traded_yesterday { fixed_point_t::_0() }, - buy_up_to_orders {}, - market_sell_orders {} - { update_next_price_limits(); } + price_history { MONTHS_OF_PRICE_HISTORY, new_good_definition.get_base_price() } { + + update_next_price_limits(); +} void GoodInstance::update_next_price_limits() { if(game_rules_manager.get_use_exponential_price_changes()) { @@ -71,7 +68,7 @@ void GoodInstance::execute_orders() { for (GoodMarketSellOrder const& market_sell_order : market_sell_orders) { supply_running_total += market_sell_order.get_quantity(); } - + fixed_point_t new_price; if (demand_running_total > supply_running_total) { new_price = max_next_price; @@ -83,7 +80,7 @@ void GoodInstance::execute_orders() { quantity_traded_yesterday = demand_running_total; new_price = price; } - + for (GoodBuyUpToOrder const& buy_up_to_order : buy_up_to_orders) { const fixed_point_t money_spend = buy_up_to_order.get_money_to_spend() * quantity_traded_yesterday / demand_running_total; const fixed_point_t quantity_bought = money_spend / new_price; @@ -109,9 +106,18 @@ void GoodInstance::execute_orders() { if (new_price != price) { update_next_price_limits(); } + + price = new_price; } -bool GoodInstanceManager::setup_goods(GoodDefinitionManager const& good_definition_manager, GameRulesManager const& game_rules_manager) { +void GoodInstance::record_price_history() { + price_history.push_back(price); +} + +GoodInstanceManager::GoodInstanceManager(GoodDefinitionManager const& new_good_definition_manager) + : good_definition_manager { new_good_definition_manager } {} + +bool GoodInstanceManager::setup_goods(GameRulesManager const& game_rules_manager) { if (good_instances_are_locked()) { Logger::error("Cannot set up good instances - they are already locked!"); return false; @@ -136,4 +142,8 @@ GoodInstance& GoodInstanceManager::get_good_instance_from_definition(GoodDefinit GoodInstance const& GoodInstanceManager::get_good_instance_from_definition(GoodDefinition const& good) const { return good_instances.get_items()[good.get_index()]; -} \ No newline at end of file +} + +void GoodInstanceManager::enable_good(GoodDefinition const& good) { + get_good_instance_from_definition(good).is_available = true; +} diff --git a/src/openvic-simulation/economy/GoodInstance.hpp b/src/openvic-simulation/economy/GoodInstance.hpp index 1e58cf0c1..ab3625714 100644 --- a/src/openvic-simulation/economy/GoodInstance.hpp +++ b/src/openvic-simulation/economy/GoodInstance.hpp @@ -11,6 +11,7 @@ #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" #include "openvic-simulation/types/IdentifierRegistry.hpp" +#include "openvic-simulation/types/ValueHistory.hpp" #include "openvic-simulation/utility/Getters.hpp" namespace OpenVic { @@ -20,10 +21,11 @@ namespace OpenVic { friend struct GoodInstanceManager; private: + GoodDefinition const& PROPERTY(good_definition); + std::unique_ptr buy_lock; std::unique_ptr sell_lock; GameRulesManager const& game_rules_manager; - GoodDefinition const& PROPERTY(good_definition); fixed_point_t PROPERTY(price); fixed_point_t PROPERTY(price_change_yesterday); fixed_point_t PROPERTY(max_next_price); @@ -34,7 +36,8 @@ namespace OpenVic { fixed_point_t PROPERTY(quantity_traded_yesterday); std::deque buy_up_to_orders; std::deque market_sell_orders; - + ValueHistory PROPERTY(price_history); + GoodInstance(GoodDefinition const& new_good_definition, GameRulesManager const& new_game_rules_manager); void update_next_price_limits(); @@ -47,17 +50,24 @@ namespace OpenVic { //not thread safe void execute_orders(); + void record_price_history(); }; struct GoodInstanceManager { private: + GoodDefinitionManager const& PROPERTY(good_definition_manager); + IdentifierRegistry IDENTIFIER_REGISTRY(good_instance); public: + GoodInstanceManager(GoodDefinitionManager const& new_good_definition_manager); + IDENTIFIER_REGISTRY_NON_CONST_ACCESSORS(good_instance); - bool setup_goods(GoodDefinitionManager const& good_definition_manager, GameRulesManager const& game_rules_manager); + bool setup_goods(GameRulesManager const& game_rules_manager); GoodInstance& get_good_instance_from_definition(GoodDefinition const& good); GoodInstance const& get_good_instance_from_definition(GoodDefinition const& good) const; + + void enable_good(GoodDefinition const& good); }; } diff --git a/src/openvic-simulation/economy/trading/MarketInstance.cpp b/src/openvic-simulation/economy/trading/MarketInstance.cpp index bf532b38e..8a8770fa6 100644 --- a/src/openvic-simulation/economy/trading/MarketInstance.cpp +++ b/src/openvic-simulation/economy/trading/MarketInstance.cpp @@ -45,3 +45,9 @@ void MarketInstance::execute_orders() { } ); } + +void MarketInstance::record_price_history() { + for (GoodInstance& good_instance : good_instance_manager.get_good_instances()) { + good_instance.record_price_history(); + } +} diff --git a/src/openvic-simulation/economy/trading/MarketInstance.hpp b/src/openvic-simulation/economy/trading/MarketInstance.hpp index 48bf1f8fa..e7abc6344 100644 --- a/src/openvic-simulation/economy/trading/MarketInstance.hpp +++ b/src/openvic-simulation/economy/trading/MarketInstance.hpp @@ -5,7 +5,7 @@ #include "openvic-simulation/economy/trading/MarketSellOrder.hpp" namespace OpenVic { - struct MarketInstance { + struct MarketInstance { private: GoodInstanceManager& PROPERTY(good_instance_manager); public: @@ -13,5 +13,6 @@ namespace OpenVic { void place_buy_up_to_order(BuyUpToOrder&& buy_up_to_order); void place_market_sell_order(MarketSellOrder&& market_sell_order); void execute_orders(); + void record_price_history(); }; -} \ No newline at end of file +} diff --git a/src/openvic-simulation/types/ValueHistory.hpp b/src/openvic-simulation/types/ValueHistory.hpp new file mode 100644 index 000000000..d405b0381 --- /dev/null +++ b/src/openvic-simulation/types/ValueHistory.hpp @@ -0,0 +1,102 @@ +#pragma once + +#include +#include +#include + +namespace OpenVic { + + template + struct ValueHistory : private std::vector { + using base_type = std::vector; + + using typename base_type::iterator; + using typename base_type::const_iterator; + using typename base_type::reverse_iterator; + using typename base_type::const_reverse_iterator; + using typename base_type::reference; + using typename base_type::const_reference; + using typename base_type::pointer; + using typename base_type::const_pointer; + using typename base_type::difference_type; + using base_type::operator[]; + using base_type::data; + using base_type::size; + using base_type::empty; + using base_type::begin; + using base_type::end; + using base_type::cbegin; + using base_type::cend; + using base_type::rbegin; + using base_type::rend; + using base_type::crbegin; + using base_type::crend; + using base_type::front; + using base_type::back; + + ValueHistory() = default; + ValueHistory(size_t history_size, T const& fill_value = {}) : base_type(history_size, fill_value) {} + ValueHistory(ValueHistory&&) = default; + + void fill(T const& value) { + std::fill(begin(), end(), value); + } + + T get_total() const { + return std::accumulate(begin(), end(), T {}); + } + + void push_back(T const& value) { + if (!empty()) { + // Move all values back by one (with the first value being discarded) and place the new value at the end + for (iterator it = begin(); it != end(); ++it) { + *it = *(it + 1); + } + back() = value; + } + } + + void set_size(size_t new_size, T const& fill_value = {}) { + const difference_type size_diff = new_size - size(); + + if (size_diff < 0) { + // Move the last new_size elements to the front, discarding the values before them + for (iterator it = begin(); it != begin() + new_size; ++it) { + *it = *(it - size_diff); + } + + base_type::resize(new_size); + } else if (size_diff > 0) { + // Move the existing values to the end and fill the front with fill_value + const size_t old_size = size(); + base_type::resize(new_size); + + for (reverse_iterator rit = rbegin(); rit != rbegin() + old_size; ++rit) { + *rit = *(rit + size_diff); + } + for (iterator it = begin(); it != begin() + size_diff; ++it) { + *it = fill_value; + } + } + } + }; + + template + std::ostream& operator<<(std::ostream& stream, ValueHistory const& history) { + stream << "["; + + bool add_comma = false; + + for (T const& value : history) { + if (add_comma) { + stream << ", "; + } else { + add_comma = true; + } + + stream << value; + } + + return stream << "]"; + } +}