From 1df5d145c9d627f00dbb4197e33b585153c65289 Mon Sep 17 00:00:00 2001 From: wvpm <24685035+wvpm@users.noreply.github.com> Date: Fri, 14 Nov 2025 11:40:52 +0100 Subject: [PATCH] Market use country index instead of instance --- .../country/CountryInstance.cpp | 53 +++++---- .../country/CountryInstance.hpp | 4 +- .../economy/GoodInstance.hpp | 1 + .../production/ResourceGatheringOperation.cpp | 5 +- .../economy/trading/BuyUpToOrder.hpp | 15 +-- .../economy/trading/GoodMarket.cpp | 71 ++++++------ .../economy/trading/GoodMarket.hpp | 16 +-- .../economy/trading/MarketSellOrder.hpp | 14 ++- src/openvic-simulation/population/Pop.cpp | 10 +- .../types/IndexedFlatMap.hpp | 20 ++++ src/openvic-simulation/types/TypedIndices.hpp | 14 ++- src/openvic-simulation/utility/ThreadPool.cpp | 19 +++- tests/src/economy/trading/GoodMarket.cpp | 103 ++++++++++++++++++ 13 files changed, 249 insertions(+), 96 deletions(-) create mode 100644 tests/src/economy/trading/GoodMarket.cpp diff --git a/src/openvic-simulation/country/CountryInstance.cpp b/src/openvic-simulation/country/CountryInstance.cpp index faf882ee..c0c5a46e 100644 --- a/src/openvic-simulation/country/CountryInstance.cpp +++ b/src/openvic-simulation/country/CountryInstance.cpp @@ -1928,7 +1928,7 @@ void CountryInstance::country_tick_before_map( memory::vector, VECTORS_FOR_COUNTRY_TICK > reusable_vectors, - memory::vector& reusable_index_vector + memory::vector& reusable_good_index_vector ) { //TODO AI sliders // + reparations + war subsidies @@ -2015,7 +2015,7 @@ void CountryInstance::country_tick_before_map( manage_national_stockpile( reusable_goods_mask, reusable_vectors, - reusable_index_vector, + reusable_good_index_vector, available_funds ); @@ -2052,22 +2052,26 @@ void CountryInstance::manage_national_stockpile( memory::vector, VECTORS_FOR_COUNTRY_TICK > reusable_vectors, - memory::vector& reusable_index_vector, + memory::vector& reusable_good_index_vector, fixed_point_t& available_funds ) { IndexedFlatMap& wants_more_mask = reusable_goods_mask; const size_t mask_size = wants_more_mask.get_keys().size(); - memory::vector& max_quantity_to_buy_per_good = reusable_vectors[0]; - max_quantity_to_buy_per_good.resize(mask_size, 0); - memory::vector& max_costs_per_good = reusable_vectors[1]; - max_costs_per_good.resize(mask_size, 0); - memory::vector& weights = reusable_vectors[2]; - weights.resize(mask_size, 0); - memory::vector& good_indices_to_buy = reusable_index_vector; + + reusable_vectors[0].resize(mask_size, 0); + TypedSpan max_quantity_to_buy_per_good { reusable_vectors[0] }; + + reusable_vectors[1].resize(mask_size, 0); + TypedSpan max_costs_per_good { reusable_vectors[1] }; + + reusable_vectors[2].resize(mask_size, 0); + TypedSpan weights { reusable_vectors[2] }; + + memory::vector& good_indices_to_buy = reusable_good_index_vector; fixed_point_t weights_sum = 0; for (auto [good_instance, good_data] : goods_data) { - const size_t index = type_safe::get(good_instance.index); + const good_index_t good_index = good_instance.index; if (good_data.is_automated || !good_data.is_selling) { const fixed_point_t quantity_to_allocate_for = good_data.is_automated ? good_data.government_needs - good_data.stockpile_amount @@ -2079,15 +2083,15 @@ void CountryInstance::manage_national_stockpile( continue; } - good_indices_to_buy.push_back(index); - max_quantity_to_buy_per_good[index] = max_quantity_to_buy; + good_indices_to_buy.push_back(good_index); + max_quantity_to_buy_per_good[good_index] = max_quantity_to_buy; - const fixed_point_t max_money_to_spend = max_costs_per_good[index] = market_instance.get_max_money_to_allocate_to_buy_quantity( + const fixed_point_t max_money_to_spend = max_costs_per_good[good_index] = market_instance.get_max_money_to_allocate_to_buy_quantity( good_instance.good_definition, quantity_to_allocate_for ); wants_more_mask.set(good_instance.good_definition, true); - const fixed_point_t weight = weights[index] = fixed_point_t::usable_max / max_money_to_spend; + const fixed_point_t weight = weights[good_index] = fixed_point_t::usable_max / max_money_to_spend; weights_sum += weight; } else { const fixed_point_t quantity_to_sell = good_data.stockpile_amount - good_data.stockpile_cutoff; @@ -2097,7 +2101,7 @@ void CountryInstance::manage_national_stockpile( market_instance.place_market_sell_order( { good_instance.good_definition, - this, + index, quantity_to_sell, this, after_sell, @@ -2108,13 +2112,14 @@ void CountryInstance::manage_national_stockpile( } if (weights_sum > 0) { - memory::vector& money_to_spend_per_good = reusable_vectors[3]; - money_to_spend_per_good.resize(mask_size, 0); + reusable_vectors[3].resize(mask_size, 0); + TypedSpan money_to_spend_per_good { reusable_vectors[3] }; + fixed_point_t cash_left_to_spend_draft = available_funds; bool needs_redistribution = true; while (needs_redistribution) { needs_redistribution = false; - for (const size_t good_index : good_indices_to_buy) { + for (const good_index_t good_index : good_indices_to_buy) { char& wants_more = wants_more_mask.at_index(good_index); if (!wants_more) { continue; @@ -2138,7 +2143,7 @@ void CountryInstance::manage_national_stockpile( break; } - GoodInstance const& good_instance = goods_data.get_keys()[good_index]; + GoodInstance const& good_instance = goods_data.get_key_at_index(good_index); GoodDefinition const& good_definition = good_instance.good_definition; const fixed_point_t max_possible_quantity_bought = cash_available_for_good / market_instance.get_min_next_price(good_definition); if (max_possible_quantity_bought < fixed_point_t::epsilon) { @@ -2149,20 +2154,20 @@ void CountryInstance::manage_national_stockpile( } } - for (const size_t good_index : good_indices_to_buy) { + for (const good_index_t good_index : good_indices_to_buy) { const fixed_point_t max_quantity_to_buy = max_quantity_to_buy_per_good[good_index]; const fixed_point_t money_to_spend = money_to_spend_per_good[good_index]; if (money_to_spend <= 0) { continue; } - GoodInstance const& good_instance = goods_data.get_keys()[good_index]; + GoodInstance const& good_instance = goods_data.get_key_at_index(good_index); GoodDefinition const& good_definition = good_instance.good_definition; available_funds -= money_to_spend; market_instance.place_buy_up_to_order( { good_definition, - this, + index, max_quantity_to_buy, money_to_spend, this, @@ -2176,7 +2181,7 @@ void CountryInstance::manage_national_stockpile( for (auto& reusable_vector : reusable_vectors) { reusable_vector.clear(); } - reusable_index_vector.clear(); + reusable_good_index_vector.clear(); } void CountryInstance::country_tick_after_map(const Date today) { diff --git a/src/openvic-simulation/country/CountryInstance.hpp b/src/openvic-simulation/country/CountryInstance.hpp index 13ddda28..7910baca 100644 --- a/src/openvic-simulation/country/CountryInstance.hpp +++ b/src/openvic-simulation/country/CountryInstance.hpp @@ -627,7 +627,7 @@ namespace OpenVic { memory::vector, VECTORS_FOR_COUNTRY_TICK > reusable_vectors, - memory::vector& reusable_index_vector, + memory::vector& reusable_good_index_vector, fixed_point_t& available_funds ); @@ -665,7 +665,7 @@ namespace OpenVic { memory::vector, VECTORS_FOR_COUNTRY_TICK > reusable_vectors, - memory::vector& reusable_index_vector + memory::vector& reusable_good_index_vector ); void country_tick_after_map(const Date today); diff --git a/src/openvic-simulation/economy/GoodInstance.hpp b/src/openvic-simulation/economy/GoodInstance.hpp index fc39b01a..bcac92bd 100644 --- a/src/openvic-simulation/economy/GoodInstance.hpp +++ b/src/openvic-simulation/economy/GoodInstance.hpp @@ -3,6 +3,7 @@ #include "openvic-simulation/economy/trading/GoodMarket.hpp" #include "openvic-simulation/types/HasIndex.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" +#include "openvic-simulation/types/IndexedFlatMap.hpp" #include "openvic-simulation/types/TypedIndices.hpp" namespace OpenVic { diff --git a/src/openvic-simulation/economy/production/ResourceGatheringOperation.cpp b/src/openvic-simulation/economy/production/ResourceGatheringOperation.cpp index 03f5185c..2a189a9e 100644 --- a/src/openvic-simulation/economy/production/ResourceGatheringOperation.cpp +++ b/src/openvic-simulation/economy/production/ResourceGatheringOperation.cpp @@ -13,6 +13,7 @@ #include "openvic-simulation/population/PopType.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #include "openvic-simulation/types/PopSize.hpp" +#include "openvic-simulation/types/TypedIndices.hpp" #include "openvic-simulation/utility/Logger.hpp" #include "openvic-simulation/utility/Containers.hpp" @@ -147,7 +148,9 @@ void ResourceGatheringOperation::rgo_tick(memory::vector& reusabl market_instance.place_market_sell_order( { production_type.output_good, - country_to_report_economy_nullable, + country_to_report_economy_nullable == nullptr + ? std::nullopt + : std::optional{country_to_report_economy_nullable->index}, output_quantity_yesterday, this, after_sell, diff --git a/src/openvic-simulation/economy/trading/BuyUpToOrder.hpp b/src/openvic-simulation/economy/trading/BuyUpToOrder.hpp index 111c92fa..79afa18f 100644 --- a/src/openvic-simulation/economy/trading/BuyUpToOrder.hpp +++ b/src/openvic-simulation/economy/trading/BuyUpToOrder.hpp @@ -1,10 +1,11 @@ #pragma once +#include + #include "openvic-simulation/economy/trading/BuyResult.hpp" +#include "openvic-simulation/types/TypedIndices.hpp" namespace OpenVic { - struct CountryInstance; - struct GoodBuyUpToOrder { using actor_t = void*; using callback_t = void(*)(const actor_t, BuyResult const&); @@ -14,17 +15,17 @@ namespace OpenVic { const callback_t after_trade; public: - CountryInstance const* const country_nullable; + const std::optional country_index_optional; const fixed_point_t max_quantity; const fixed_point_t money_to_spend; constexpr GoodBuyUpToOrder( - CountryInstance const* const new_country_nullable, + const std::optional new_country_index_optional, const fixed_point_t new_max_quantity, const fixed_point_t new_money_to_spend, const actor_t new_actor, const callback_t new_after_trade - ) : country_nullable { new_country_nullable }, + ) : country_index_optional { new_country_index_optional }, max_quantity { new_max_quantity }, money_to_spend { new_money_to_spend }, actor { new_actor }, @@ -47,13 +48,13 @@ namespace OpenVic { constexpr BuyUpToOrder( GoodDefinition const& new_good, - CountryInstance const* const new_country_nullable, + const std::optional new_country_index_optional, const fixed_point_t new_max_quantity, const fixed_point_t new_money_to_spend, const actor_t new_actor, const callback_t new_after_trade ) : GoodBuyUpToOrder { - new_country_nullable, + new_country_index_optional, new_max_quantity, new_money_to_spend, new_actor, diff --git a/src/openvic-simulation/economy/trading/GoodMarket.cpp b/src/openvic-simulation/economy/trading/GoodMarket.cpp index 27ce0ba2..e140d847 100644 --- a/src/openvic-simulation/economy/trading/GoodMarket.cpp +++ b/src/openvic-simulation/economy/trading/GoodMarket.cpp @@ -1,6 +1,6 @@ #include "GoodMarket.hpp" +#include -#include "openvic-simulation/country/CountryInstance.hpp" #include "openvic-simulation/economy/GoodDefinition.hpp" #include "openvic-simulation/economy/trading/BuyUpToOrder.hpp" #include "openvic-simulation/economy/trading/MarketSellOrder.hpp" @@ -66,9 +66,9 @@ void GoodMarket::add_market_sell_order(GoodMarketSellOrder&& market_sell_order) } void GoodMarket::execute_orders( - IndexedFlatMap& reusable_country_map_0, - IndexedFlatMap& reusable_country_map_1, - utility::forwardable_span< + TypedSpan reusable_country_map_0, + TypedSpan reusable_country_map_1, + std::span< memory::vector, VECTORS_FOR_EXECUTE_ORDERS > reusable_vectors @@ -120,12 +120,12 @@ void GoodMarket::execute_orders( } } } else { - IndexedFlatMap& supply_per_country = reusable_country_map_0; - IndexedFlatMap& actual_bought_per_country = reusable_country_map_1; + TypedSpan supply_per_country = reusable_country_map_0; + TypedSpan actual_bought_per_country = reusable_country_map_1; for (GoodMarketSellOrder const& market_sell_order : market_sell_orders) { - CountryInstance const* const country_nullable = market_sell_order.country_nullable; - if (country_nullable != nullptr) { - supply_per_country.at(*country_nullable) += market_sell_order.quantity; + const std::optional country_index_optional = market_sell_order.country_index_optional; + if (country_index_optional.has_value()) { + supply_per_country[country_index_optional.value()] += market_sell_order.quantity; } supply_sum += market_sell_order.quantity; } @@ -191,10 +191,10 @@ void GoodMarket::execute_orders( continue; } - CountryInstance const* const country_nullable = buy_up_to_order.country_nullable; - if (country_nullable != nullptr) { + const std::optional country_index_optional = buy_up_to_order.country_index_optional; + if (country_index_optional.has_value()) { //subtract as it might be updated below - actual_bought_per_country.at(*country_nullable) -= distributed_supply; + actual_bought_per_country[country_index_optional.value()] -= distributed_supply; } distributed_supply = fixed_point_t::mul_div( @@ -210,8 +210,8 @@ void GoodMarket::execute_orders( purchasing_power_sum -= purchasing_power_per_order[i]; } - if (country_nullable != nullptr) { - actual_bought_per_country.at(*country_nullable) += distributed_supply; + if (country_index_optional.has_value()) { + actual_bought_per_country[country_index_optional.value()] += distributed_supply; } if (someone_bought_max_quantity) { @@ -280,9 +280,9 @@ void GoodMarket::execute_orders( buy_up_to_order.money_to_spend / new_price ); - CountryInstance const* const country_nullable = buy_up_to_order.country_nullable; - if (country_nullable != nullptr) { - actual_bought_per_country.at(*country_nullable) += quantity_bought_per_order[i]; + const std::optional country_index_optional = buy_up_to_order.country_index_optional; + if (country_index_optional.has_value()) { + actual_bought_per_country[country_index_optional.value()] += quantity_bought_per_order[i]; } } @@ -324,8 +324,9 @@ void GoodMarket::execute_orders( } else { //quantity is evenly divided after taking domestic buyers into account fixed_point_t total_quantity_traded_domestically = 0; - for (auto const& [country, actual_bought] : actual_bought_per_country) { - const fixed_point_t supply = supply_per_country.at(country); + for (country_index_t country_index(0); country_index < actual_bought_per_country.size(); ++country_index) { + const fixed_point_t actual_bought = actual_bought_per_country[country_index]; + const fixed_point_t supply = supply_per_country[country_index]; const fixed_point_t traded_domestically = std::min(supply, actual_bought); total_quantity_traded_domestically += traded_domestically; } @@ -337,13 +338,14 @@ void GoodMarket::execute_orders( fixed_point_t quantity_sold_domestically; fixed_point_t quantity_offered_as_export; - CountryInstance const* const country_nullable = market_sell_order.country_nullable; - if (country_nullable == nullptr) { + const std::optional country_index_optional = market_sell_order.country_index_optional; + if (!country_index_optional.has_value()) { quantity_sold_domestically = 0; quantity_offered_as_export = quantity_offered; } else { - const fixed_point_t total_bought_domestically = actual_bought_per_country.at(*country_nullable); - const fixed_point_t total_domestic_supply = supply_per_country.at(*country_nullable); + const country_index_t country_index = country_index_optional.value(); + const fixed_point_t total_bought_domestically = actual_bought_per_country[country_index]; + const fixed_point_t total_domestic_supply = supply_per_country[country_index]; quantity_sold_domestically = total_bought_domestically >= total_domestic_supply ? quantity_offered : fixed_point_t::mul_div( @@ -381,11 +383,11 @@ void GoodMarket::execute_orders( } } - reusable_country_map_0.fill(0); - reusable_country_map_1.fill(0); + std::fill(reusable_country_map_0.begin(), reusable_country_map_0.end(), 0); + std::fill(reusable_country_map_1.begin(), reusable_country_map_1.end(), 0); market_sell_orders.clear(); - supply_per_country.fill(0); - actual_bought_per_country.fill(0); + std::fill(supply_per_country.begin(), supply_per_country.end(), 0); + std::fill(actual_bought_per_country.begin(), actual_bought_per_country.end(), 0); } price_change_yesterday = new_price - price; @@ -400,8 +402,8 @@ void GoodMarket::execute_orders( void GoodMarket::execute_buy_orders( const fixed_point_t new_price, - IndexedFlatMap const& actual_bought_per_country, - IndexedFlatMap const& supply_per_country, + TypedSpan actual_bought_per_country, + TypedSpan supply_per_country, std::span quantity_bought_per_order ) { quantity_traded_yesterday = 0; @@ -418,15 +420,16 @@ void GoodMarket::execute_buy_orders( fixed_point_t::epsilon //we know from purchasing power that you can afford it. ); - fixed_point_t money_spent_on_imports; - CountryInstance const* const country_nullable = buy_up_to_order.country_nullable; - if (country_nullable == nullptr) { + fixed_point_t money_spent_on_imports; + const std::optional country_index_optional = buy_up_to_order.country_index_optional; + if (!country_index_optional.has_value()) { //could be trade between native Americans and tribal Africa, so it's all imported money_spent_on_imports = money_spent_total; } else { + const country_index_t country_index = country_index_optional.value(); //must be > 0, since quantity_bought > 0 - const fixed_point_t actual_bought_in_my_country = actual_bought_per_country.at(*country_nullable); - const fixed_point_t supply_in_my_country = supply_per_country.at(*country_nullable); + const fixed_point_t actual_bought_in_my_country = actual_bought_per_country[country_index]; + const fixed_point_t supply_in_my_country = supply_per_country[country_index]; if (supply_in_my_country >= actual_bought_in_my_country) { //no imports diff --git a/src/openvic-simulation/economy/trading/GoodMarket.hpp b/src/openvic-simulation/economy/trading/GoodMarket.hpp index 3ed0019d..37d420b9 100644 --- a/src/openvic-simulation/economy/trading/GoodMarket.hpp +++ b/src/openvic-simulation/economy/trading/GoodMarket.hpp @@ -1,17 +1,17 @@ #pragma once #include +#include #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/IndexedFlatMap.hpp" +#include "openvic-simulation/types/TypedIndices.hpp" +#include "openvic-simulation/types/TypedSpan.hpp" #include "openvic-simulation/types/ValueHistory.hpp" #include "openvic-simulation/utility/Containers.hpp" -#include "openvic-simulation/utility/ForwardableSpan.hpp" namespace OpenVic { - struct CountryInstance; struct GameRulesManager; struct GoodDefinition; @@ -30,8 +30,8 @@ namespace OpenVic { void execute_buy_orders( const fixed_point_t new_price, - IndexedFlatMap const& actual_bought_per_country, - IndexedFlatMap const& supply_per_country, + TypedSpan actual_bought_per_country, + TypedSpan supply_per_country, std::span quantity_bought_per_order ); @@ -66,9 +66,9 @@ namespace OpenVic { //not thread safe static constexpr size_t VECTORS_FOR_EXECUTE_ORDERS = 2; void execute_orders( - IndexedFlatMap& reusable_country_map_0, - IndexedFlatMap& reusable_country_map_1, - utility::forwardable_span< + TypedSpan reusable_country_map_0, + TypedSpan reusable_country_map_1, + std::span< memory::vector, VECTORS_FOR_EXECUTE_ORDERS > reusable_vectors diff --git a/src/openvic-simulation/economy/trading/MarketSellOrder.hpp b/src/openvic-simulation/economy/trading/MarketSellOrder.hpp index 1ea47ea5..975c18aa 100644 --- a/src/openvic-simulation/economy/trading/MarketSellOrder.hpp +++ b/src/openvic-simulation/economy/trading/MarketSellOrder.hpp @@ -1,10 +1,12 @@ #pragma once +#include + #include "openvic-simulation/economy/trading/SellResult.hpp" +#include "openvic-simulation/types/TypedIndices.hpp" #include "openvic-simulation/utility/Containers.hpp" namespace OpenVic { - struct CountryInstance; struct GoodDefinition; struct GoodMarketSellOrder { @@ -16,15 +18,15 @@ namespace OpenVic { const callback_t after_trade; public: - CountryInstance const* const country_nullable; + const std::optional country_index_optional; const fixed_point_t quantity; constexpr GoodMarketSellOrder( - CountryInstance const* const new_country_nullable, + const std::optional new_country_index_optional, const fixed_point_t new_quantity, const actor_t new_actor, const callback_t new_after_trade - ) : country_nullable { new_country_nullable }, + ) : country_index_optional { new_country_index_optional }, quantity { new_quantity }, actor { new_actor }, after_trade { new_after_trade } @@ -41,12 +43,12 @@ namespace OpenVic { constexpr MarketSellOrder( GoodDefinition const& new_good, - CountryInstance const* const new_country_nullable, + const std::optional new_country_index_optional, const fixed_point_t new_quantity, const actor_t new_actor, const callback_t new_after_trade ) : GoodMarketSellOrder { - new_country_nullable, + new_country_index_optional, new_quantity, new_actor, new_after_trade diff --git a/src/openvic-simulation/population/Pop.cpp b/src/openvic-simulation/population/Pop.cpp index 65c1b011..3ea121fe 100644 --- a/src/openvic-simulation/population/Pop.cpp +++ b/src/openvic-simulation/population/Pop.cpp @@ -5,6 +5,7 @@ #include // IWYU pragma: keep for lambda #include #include +#include #include #include @@ -35,6 +36,7 @@ #include "openvic-simulation/types/fixed_point/FixedPointMap.hpp" #include "openvic-simulation/types/IndexedFlatMap.hpp" #include "openvic-simulation/types/OrderedContainers.hpp" +#include "openvic-simulation/types/TypedIndices.hpp" #include "openvic-simulation/utility/Containers.hpp" #include "openvic-simulation/utility/FormatValidate.hpp" #include "openvic-simulation/utility/Logger.hpp" @@ -606,6 +608,10 @@ void Pop::pop_tick_without_cleanup( OV_DO_FOR_ALL_NEED_CATEGORIES(ALLOCATE_FOR_NEEDS) #undef ALLOCATE_FOR_NEEDS + const std::optional country_index_optional = country_to_report_economy_nullable == nullptr + ? std::nullopt + : std::optional{country_to_report_economy_nullable->index}; + for (auto it = good_keys.begin(); it < good_keys.end(); it++) { const ptrdiff_t i = it - good_keys.begin(); const fixed_point_t max_quantity_to_buy = max_quantity_to_buy_per_good[i]; @@ -619,7 +625,7 @@ void Pop::pop_tick_without_cleanup( market_instance.place_buy_up_to_order({ good_definition, - country_to_report_economy_nullable, + country_index_optional, max_quantity_to_buy, money_to_spend, this, @@ -639,7 +645,7 @@ void Pop::pop_tick_without_cleanup( market_instance.place_market_sell_order( { good, - country_to_report_economy_nullable, + country_index_optional, quantity_to_sell, this, after_sell diff --git a/src/openvic-simulation/types/IndexedFlatMap.hpp b/src/openvic-simulation/types/IndexedFlatMap.hpp index 46fc48ed..cc838d45 100644 --- a/src/openvic-simulation/types/IndexedFlatMap.hpp +++ b/src/openvic-simulation/types/IndexedFlatMap.hpp @@ -516,6 +516,26 @@ namespace OpenVic { return values[index - min_index]; } + template + requires requires { type_safe::get(std::declval()); } + constexpr KeyType const& get_key_at_index(I const& index) const { + return get_key_at_index(type_safe::get(index)); + } + constexpr KeyType const& get_key_at_index(const size_t index) const { + if (index < min_index || index > max_index) { + spdlog::error_s( + "DEVELOPER: OpenVic::IndexedFlatMap<{},{}> attempted to access index {} which is outside the map's defined range [{}, {}].", + utility::type_name(), + utility::type_name(), + index, + min_index, + max_index + ); + assert(index >= min_index && index <= max_index); + } + return keys[index - min_index]; + } + constexpr bool contains(KeyType const& key) const { static_assert(has_index); return contains_index(key.index); diff --git a/src/openvic-simulation/types/TypedIndices.hpp b/src/openvic-simulation/types/TypedIndices.hpp index a3835f42..220d2adc 100644 --- a/src/openvic-simulation/types/TypedIndices.hpp +++ b/src/openvic-simulation/types/TypedIndices.hpp @@ -11,8 +11,9 @@ #define TYPED_INDEX_CUSTOM(name, base_type) \ namespace OpenVic { \ struct name : type_safe::strong_typedef, \ - type_safe::strong_typedef_op::equality_comparison, \ - type_safe::strong_typedef_op::relational_comparison { \ + type_safe::strong_typedef_op::equality_comparison, \ + type_safe::strong_typedef_op::relational_comparison, \ + type_safe::strong_typedef_op::integer_arithmetic { \ using strong_typedef::strong_typedef; \ }; \ } \ @@ -49,10 +50,11 @@ TYPED_INDEX(terrain_type_index_t) namespace OpenVic { struct province_index_t : type_safe::strong_typedef, - type_safe::strong_typedef_op::equality_comparison, - type_safe::strong_typedef_op::relational_comparison, - type_safe::strong_typedef_op::mixed_addition, - type_safe::strong_typedef_op::mixed_subtraction { + type_safe::strong_typedef_op::equality_comparison, + type_safe::strong_typedef_op::relational_comparison, + type_safe::strong_typedef_op::integer_arithmetic, + type_safe::strong_typedef_op::mixed_addition, + type_safe::strong_typedef_op::mixed_subtraction { using strong_typedef::strong_typedef; }; } diff --git a/src/openvic-simulation/utility/ThreadPool.cpp b/src/openvic-simulation/utility/ThreadPool.cpp index f204d180..193ed99d 100644 --- a/src/openvic-simulation/utility/ThreadPool.cpp +++ b/src/openvic-simulation/utility/ThreadPool.cpp @@ -5,6 +5,9 @@ #include "openvic-simulation/economy/GoodInstance.hpp" #include "openvic-simulation/economy/trading/GoodMarket.hpp" #include "openvic-simulation/map/ProvinceInstance.hpp" +#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" +#include "openvic-simulation/types/TypedIndices.hpp" +#include "openvic-simulation/types/TypedSpan.hpp" #include "openvic-simulation/utility/Containers.hpp" using namespace OpenVic; @@ -22,8 +25,12 @@ void ThreadPool::loop_until_cancelled( utility::forwardable_span work_bundles ) { IndexedFlatMap reusable_goods_mask { good_keys }; - IndexedFlatMap reusable_country_map_0 { country_keys }, - reusable_country_map_1 { country_keys }; + + memory::FixedVector reusable_country_map_0 { country_keys.size() }; + memory::FixedVector reusable_country_map_1 { country_keys.size() }; + TypedSpan reusable_country_map_0_span { reusable_country_map_0 }; + TypedSpan reusable_country_map_1_span { reusable_country_map_1 }; + static constexpr size_t VECTOR_COUNT = std::max( GoodMarket::VECTORS_FOR_EXECUTE_ORDERS, std::max( @@ -33,7 +40,7 @@ void ThreadPool::loop_until_cancelled( ); std::array, VECTOR_COUNT> reusable_vectors; std::span, VECTOR_COUNT> reusable_vectors_span = std::span(reusable_vectors); - memory::vector reusable_index_vector; + memory::vector reusable_good_index_vector; PopValuesFromProvince reusable_pop_values { game_rules_manager, good_instance_manager, @@ -68,8 +75,8 @@ void ThreadPool::loop_until_cancelled( for (WorkBundle& work_bundle : work_bundles) { for (GoodMarket& good : work_bundle.goods_chunk) { good.execute_orders( - reusable_country_map_0, - reusable_country_map_1, + reusable_country_map_0_span, + reusable_country_map_1_span, reusable_vectors_span.first() ); } @@ -107,7 +114,7 @@ void ThreadPool::loop_until_cancelled( country.country_tick_before_map( reusable_goods_mask, reusable_vectors_span.first(), - reusable_index_vector + reusable_good_index_vector ); } } diff --git a/tests/src/economy/trading/GoodMarket.cpp b/tests/src/economy/trading/GoodMarket.cpp new file mode 100644 index 00000000..f8932c94 --- /dev/null +++ b/tests/src/economy/trading/GoodMarket.cpp @@ -0,0 +1,103 @@ +#include "openvic-simulation/economy/GoodDefinition.hpp" +#include "openvic-simulation/economy/trading/GoodMarket.hpp" +#include +#include "openvic-simulation/misc/GameRulesManager.hpp" +#include "openvic-simulation/types/Colour.hpp" +#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" +#include "openvic-simulation/types/TypedIndices.hpp" +#include "openvic-simulation/types/TypedSpan.hpp" + +#include +#include + +using namespace OpenVic; + +GoodCategory good_category {"test_good_category"}; + +const fixed_point_t base_price = 16; +constexpr bool is_not_available_from_start = false; +constexpr bool is_tradeable = true; +constexpr bool is_not_money = false; +constexpr bool does_not_counter_overseas_penalty = false; + +GoodDefinition good_definition { + "test_good", + colour_rgb_t {}, + good_index_t{0}, + good_category, + base_price, + is_not_available_from_start, + is_tradeable, + is_not_money, + does_not_counter_overseas_penalty +}; +GameRulesManager game_rules_manager {}; + +struct Trader { +public: + std::function buy_callback = []( + BuyResult const& buy_result + ) -> void { + FAIL("Did not expect buy callback"); + }; + std::function&)> sell_callback = []( + SellResult const& sell_result, memory::vector& reusable_vector + ) -> void { + FAIL("Did not expect sell callback"); + }; + + static void after_buy(void* actor, BuyResult const& buy_result) { + } + static void after_sell(void* actor, SellResult const& sell_result, memory::vector& reusable_vector) { + } +}; + +TEST_CASE("GoodMarket no trading when good isn't available", "[GoodMarket]") { + GoodMarket good_market { game_rules_manager, good_definition }; + const std::optional country_index_optional = std::nullopt; + + Trader buyer { + .buy_callback=[](BuyResult const& buy_result) -> void { + CHECK(buy_result.good_definition == good_definition); + CHECK(buy_result.quantity_bought == 0); + CHECK(buy_result.money_spent_total == 0); + CHECK(buy_result.money_spent_on_imports == 0); + } + }; + const fixed_point_t quantity_to_buy = 1; + const fixed_point_t money_to_spend = quantity_to_buy * good_market.get_max_next_price(); + good_market.add_buy_up_to_order({ + country_index_optional, + quantity_to_buy, + money_to_spend, + &buyer, + Trader::after_buy + }); + + Trader seller { + .sell_callback=[](SellResult const& sell_result, memory::vector& reusable_vector) -> void { + CHECK(sell_result.good_definition == good_definition); + CHECK(sell_result.quantity_sold == 0); + CHECK(sell_result.money_gained == 0); + } + }; + const fixed_point_t quantity_to_sell = 1; + good_market.add_market_sell_order({ + country_index_optional, + quantity_to_sell, + &seller, + Trader::after_sell + }); + + TypedSpan reusable_country_map_0 {}; + TypedSpan reusable_country_map_1 {}; + std::array, GoodMarket::VECTORS_FOR_EXECUTE_ORDERS> reusable_vectors; + good_market.execute_orders( + reusable_country_map_0, + reusable_country_map_1, + reusable_vectors + ); + + CHECK(good_market.get_price() == base_price); + CHECK(good_market.get_price_change_yesterday() == 0); +} \ No newline at end of file