diff options
author | hop311 <hop3114@gmail.com> | 2024-01-03 01:13:39 +0100 |
---|---|---|
committer | hop311 <hop3114@gmail.com> | 2024-01-03 01:13:39 +0100 |
commit | e067471f55cd11c6cd86920f9486991d6b552726 (patch) | |
tree | fa0904084e5e6a842e0ca9060155ed7ad30dc9a8 /src/openvic-simulation/politics | |
parent | d893c0ad8c6a0c347dcec72762be49f20886a90a (diff) |
Mutually exclusive rule groups + modded building rules
Diffstat (limited to 'src/openvic-simulation/politics')
-rw-r--r-- | src/openvic-simulation/politics/Issue.cpp | 20 | ||||
-rw-r--r-- | src/openvic-simulation/politics/Rule.cpp | 201 | ||||
-rw-r--r-- | src/openvic-simulation/politics/Rule.hpp | 43 |
3 files changed, 197 insertions, 67 deletions
diff --git a/src/openvic-simulation/politics/Issue.cpp b/src/openvic-simulation/politics/Issue.cpp index 3e64af4..28e34cb 100644 --- a/src/openvic-simulation/politics/Issue.cpp +++ b/src/openvic-simulation/politics/Issue.cpp @@ -95,8 +95,19 @@ bool IssueManager::add_reform( } if (group->get_type().is_uncivilised()) { - if (technology_cost <= 0) { - Logger::warning("Non-positive technology cost ", technology_cost, " found in uncivilised reform ", identifier, "!"); + if (ordinal == 0) { + if (technology_cost != 0) { + Logger::warning( + "Non-zero technology cost ", technology_cost, " found in ordinal 0 uncivilised reform ", identifier, "!" + ); + } + } else { + if (technology_cost <= 0) { + Logger::warning( + "Non-positive technology cost ", technology_cost, " found in ordinal ", ordinal, + " uncivilised reform ", identifier, "!" + ); + } } } else if (technology_cost != 0) { Logger::warning("Non-zero technology cost ", technology_cost, " found in civilised reform ", identifier, "!"); @@ -184,8 +195,11 @@ bool IssueManager::load_issues_file(ModifierManager const& modifier_manager, Rul if (key == "party_issues") { return expect_length(add_variable_callback(expected_issue_groups))(value); } else { + static const string_set_t uncivilised_reform_groups { + "economic_reforms", "education_reforms", "military_reforms" + }; return expect_length(add_variable_callback(expected_reform_groups))(value) - & add_reform_type(key, key == "economic_reforms" || key == "education_reforms" || key == "military_reforms"); + & add_reform_type(key, uncivilised_reform_groups.contains(key)); } } )(root); diff --git a/src/openvic-simulation/politics/Rule.cpp b/src/openvic-simulation/politics/Rule.cpp index 12db4f4..9ac992a 100644 --- a/src/openvic-simulation/politics/Rule.cpp +++ b/src/openvic-simulation/politics/Rule.cpp @@ -1,19 +1,90 @@ #include "Rule.hpp" +#include "openvic-simulation/economy/BuildingType.hpp" +#include "openvic-simulation/utility/TslHelper.hpp" + using namespace OpenVic; using namespace OpenVic::NodeTools; -Rule::Rule(std::string_view new_identifier) : HasIdentifier { new_identifier } {} +Rule::Rule(std::string_view new_identifier, rule_group_t new_group, size_t new_index) + : HasIdentifier { new_identifier }, group { new_group }, index { new_index } {} -RuleSet::RuleSet(rule_map_t&& new_rules) : rules { std::move(new_rules) } {} +RuleSet::RuleSet(rule_group_map_t&& new_rule_groups) : rule_groups { std::move(new_rule_groups) } {} + +bool RuleSet::trim_and_resolve_conflicts(bool log) { + bool ret = true; + for (auto [group, rule_map] : mutable_iterator(rule_groups)) { + if (Rule::is_mutually_exclusive_group(group)) { + Rule const* primary_rule = nullptr; + for (auto const& [rule, value] : rule_map) { + if (value) { + if (primary_rule == nullptr) { + primary_rule = rule; + } else { + if (primary_rule->get_index() < rule->get_index()) { + primary_rule = rule; + } + ret = false; + } + } + } + if (log) { + for (auto const& [rule, value] : rule_map) { + if (value) { + if (rule != primary_rule) { + Logger::error( + "Conflicting mutually exclusive rule: ", rule, " superceeded by ", primary_rule, " - removing!" + ); + } + } else { + Logger::warning("Disabled mutually exclusive rule: ", rule, " - removing!"); + } + } + } + rule_map.clear(); + if (primary_rule != nullptr) { + rule_map.emplace(primary_rule, true); + } + } + } + return ret; +} + +size_t RuleSet::get_rule_group_count() const { + return rule_groups.size(); +} size_t RuleSet::get_rule_count() const { - return rules.size(); + size_t ret = 0; + for (auto const& [group, rule_map] : rule_groups) { + ret += rule_map.size(); + } + return ret; +} + +RuleSet::rule_map_t const& RuleSet::get_rule_group(Rule::rule_group_t group, bool* successful) const { + const rule_group_map_t::const_iterator it = rule_groups.find(group); + if (it != rule_groups.end()) { + if (successful != nullptr) { + *successful = true; + } + return it->second; + } + if (successful != nullptr) { + *successful = false; + } + static const rule_map_t empty_map {}; + return empty_map; } -bool RuleSet::get_rule(Rule const* rule, bool* successful) { - const rule_map_t::const_iterator it = rules.find(rule); - if (it != rules.end()) { +bool RuleSet::get_rule(Rule const* rule, bool* successful) const { + if (rule == nullptr) { + Logger::error("Invalid rule - null!"); + return false; + } + rule_map_t const& rule_map = get_rule_group(rule->get_group()); + const rule_map_t::const_iterator it = rule_map.find(rule); + if (it != rule_map.end()) { if (successful != nullptr) { *successful = true; } @@ -22,16 +93,31 @@ bool RuleSet::get_rule(Rule const* rule, bool* successful) { if (successful != nullptr) { *successful = false; } - return false; + return Rule::is_default_enabled(rule->get_group()); } bool RuleSet::has_rule(Rule const* rule) const { - return rules.contains(rule); + if (rule == nullptr) { + Logger::error("Invalid rule - null!"); + return false; + } + return get_rule_group(rule->get_group()).contains(rule); +} + +bool RuleSet::set_rule(Rule const* rule, bool value) { + if (rule == nullptr) { + Logger::error("Invalid rule - null!"); + return false; + } + rule_map_t& rule_map = rule_groups[rule->get_group()]; + return rule_groups[rule->get_group()].emplace(rule, value).second; } RuleSet& RuleSet::operator|=(RuleSet const& right) { - for (rule_map_t::value_type const& value : right.rules) { - rules[value.first] |= value.second; + for (auto const& [group, rule_map] : right.rule_groups) { + for (auto const& [rule, value] : rule_map) { + rule_groups[group][rule] |= value; + } } return *this; } @@ -41,61 +127,57 @@ RuleSet RuleSet::operator|(RuleSet const& right) const { return ret |= right; } -bool RuleManager::add_rule(std::string_view identifier) { +bool RuleManager::add_rule(std::string_view identifier, Rule::rule_group_t group) { if (identifier.empty()) { Logger::error("Invalid rule identifier - empty!"); return false; } - return rules.add_item({ identifier }); + return rules.add_item({ identifier, group, rule_group_sizes[group]++ }); } -bool RuleManager::setup_rules() { +bool RuleManager::setup_rules(BuildingTypeManager const& building_type_manager) { bool ret = true; - /* Economic Rules */ - ret &= add_rule("build_factory"); - ret &= add_rule("expand_factory"); - ret &= add_rule("open_factory"); - ret &= add_rule("destroy_factory"); - - ret &= add_rule("pop_build_factory"); - ret &= add_rule("pop_expand_factory"); - ret &= add_rule("pop_open_factory"); - - ret &= add_rule("build_railway"); - ret &= add_rule("can_subsidise"); - ret &= add_rule("factory_priority"); - ret &= add_rule("delete_factory_if_no_input"); - - ret &= add_rule("build_factory_invest"); - ret &= add_rule("expand_factory_invest"); - ret &= add_rule("open_factory_invest"); - ret &= add_rule("build_railway_invest"); - - ret &= add_rule("pop_build_factory_invest"); - ret &= add_rule("pop_expand_factory_invest"); - - ret &= add_rule("can_invest_in_pop_projects"); - ret &= add_rule("allow_foreign_investment"); - - /* Citizenship Rules */ - ret &= add_rule("primary_culture_voting"); - ret &= add_rule("culture_voting"); - ret &= add_rule("all_voting"); + using enum Rule::rule_group_t; + + static const ordered_map<Rule::rule_group_t, std::vector<std::string_view>> hardcoded_rules { + { ECONOMY, { + "expand_factory", "open_factory", "destroy_factory", "pop_build_factory", "pop_expand_factory", "pop_open_factory", + "can_subsidise", "factory_priority", "delete_factory_if_no_input", "build_factory_invest", "expand_factory_invest", + "open_factory_invest", "build_railway_invest", "pop_build_factory_invest", "pop_expand_factory_invest", + "can_invest_in_pop_projects", "allow_foreign_investment" + } }, + { CITIZENSHIP, { "primary_culture_voting", "culture_voting", "all_voting" } }, + { SLAVERY, { "slavery_allowed" } }, + { UPPER_HOUSE, { "same_as_ruling_party", "rich_only", "state_vote", "population_vote" } }, + { VOTING, { + "largest_share" /* First Past the Post */, + "dhont" /* Jefferson Method */, + "sainte_laque" /* Proportional Representation */ + } } + }; - /* Slavery Rule */ - ret &= add_rule("slavery_allowed"); + size_t rule_count = building_type_manager.get_building_type_types().size(); + for (auto const& [group, rule_list] : hardcoded_rules) { + rule_count += rule_list.size(); + } + rules.reserve(rule_count); - /* Upper House Composition Rules */ - ret &= add_rule("same_as_ruling_party"); - ret &= add_rule("rich_only"); - ret &= add_rule("state_vote"); - ret &= add_rule("population_vote"); + for (auto const& [group, rule_list] : hardcoded_rules) { + for (std::string_view const& rule : rule_list) { + ret &= add_rule(rule, group); + } + } - /* Voting System Rules */ - ret &= add_rule("largest_share"); // First Past the Post - ret &= add_rule("dhont"); // Jefferson Method - ret &= add_rule("sainte_laque"); // Proportional Representation + for (std::string const& type : building_type_manager.get_building_type_types()) { + std::string build_rule_string = "build_"; + if (type != "infrastructure") { + build_rule_string += type; + } else { + build_rule_string += "railway"; + } + ret &= add_rule(build_rule_string, ECONOMY); + } lock_rules(); @@ -111,8 +193,8 @@ node_callback_t RuleManager::expect_rule_set(callback_t<RuleSet&&> ruleset_callb if (rule != nullptr) { return expect_bool( [&ruleset, rule](bool value) -> bool { - if (!ruleset.rules.emplace(rule, value).second) { - Logger::warning("Duplicate rule entry: ", rule, " - overwriting existing value!"); + if (!ruleset.rule_groups[rule->get_group()].emplace(rule, value).second) { + Logger::warning("Duplicate rule entry: ", rule, " - ignoring!"); } return true; } @@ -123,6 +205,7 @@ node_callback_t RuleManager::expect_rule_set(callback_t<RuleSet&&> ruleset_callb } } )(root); + ret &= ruleset.trim_and_resolve_conflicts(true); ret &= ruleset_callback(std::move(ruleset)); return ret; }; @@ -130,8 +213,10 @@ node_callback_t RuleManager::expect_rule_set(callback_t<RuleSet&&> ruleset_callb namespace OpenVic { // so the compiler shuts up (copied from Modifier.cpp) std::ostream& operator<<(std::ostream& stream, RuleSet const& value) { - for (RuleSet::rule_map_t::value_type const& rule : value.rules) { - stream << rule.first << ": " << (rule.second ? "yes" : "no") << "\n"; + for (auto const& [group, rule_map] : value.rule_groups) { + for (auto const& [rule, value] : rule_map) { + stream << rule << ": " << (value ? "yes" : "no") << "\n"; + } } return stream; } diff --git a/src/openvic-simulation/politics/Rule.hpp b/src/openvic-simulation/politics/Rule.hpp index 4b47abf..70a59e8 100644 --- a/src/openvic-simulation/politics/Rule.hpp +++ b/src/openvic-simulation/politics/Rule.hpp @@ -5,12 +5,32 @@ namespace OpenVic { struct RuleManager; + struct BuildingTypeManager; struct Rule : HasIdentifier { friend struct RuleManager; + enum class rule_group_t : uint8_t { + ECONOMY, CITIZENSHIP, SLAVERY, UPPER_HOUSE, VOTING + }; + + static constexpr bool is_mutually_exclusive_group(rule_group_t group) { + using enum rule_group_t; + return group == CITIZENSHIP || group == UPPER_HOUSE || group == VOTING; + } + + /* Mutually exclusive groups must be default disabled! */ + static constexpr bool is_default_enabled(rule_group_t group) { + using enum rule_group_t; + return !is_mutually_exclusive_group(group) && group != SLAVERY; + } + private: - Rule(std::string_view new_identifier); + const rule_group_t PROPERTY(group); + /* The index of the Rule within its group, used to determine precedence in mutually exclusive rule groups. */ + const size_t PROPERTY(index); + + Rule(std::string_view new_identifier, rule_group_t new_group, size_t new_index); public: Rule(Rule&&) = default; @@ -20,24 +40,34 @@ namespace OpenVic { friend struct RuleManager; using rule_map_t = ordered_map<Rule const*, bool>; + using rule_group_map_t = ordered_map<Rule::rule_group_t, rule_map_t>; private: - rule_map_t rules; + rule_group_map_t rule_groups; public: RuleSet() = default; - RuleSet(rule_map_t&& new_rules); + RuleSet(rule_group_map_t&& new_rule_groups); RuleSet(RuleSet const&) = default; RuleSet(RuleSet&&) = default; RuleSet& operator=(RuleSet const&) = default; RuleSet& operator=(RuleSet&&) = default; + /* Removes conflicting and disabled mutually exclusive rules. If log is true, a warning will be emitted for each + * removed disabled rule and an error will be emitted for each removed conflicting rule. Returns true if no conflicts + * are found (regardless of whether disabled rules are removed or not), false otherwise. */ + bool trim_and_resolve_conflicts(bool log); + size_t get_rule_group_count() const; size_t get_rule_count() const; - bool get_rule(Rule const* rule, bool* successful = nullptr); + rule_map_t const& get_rule_group(Rule::rule_group_t group, bool* successful = nullptr) const; + bool get_rule(Rule const* rule, bool* successful = nullptr) const; bool has_rule(Rule const* rule) const; + /* Sets the rule to the specified value. Returns false if there was an existing rule, regardless of its value. */ + bool set_rule(Rule const* rule, bool value); + RuleSet& operator|=(RuleSet const& right); RuleSet operator|(RuleSet const& right) const; @@ -47,11 +77,12 @@ namespace OpenVic { struct RuleManager { private: IdentifierRegistry<Rule> IDENTIFIER_REGISTRY(rule); + ordered_map<Rule::rule_group_t, size_t> rule_group_sizes; public: - bool add_rule(std::string_view identifier); + bool add_rule(std::string_view identifier, Rule::rule_group_t group); - bool setup_rules(); + bool setup_rules(BuildingTypeManager const& building_type_manager); NodeTools::node_callback_t expect_rule_set(NodeTools::callback_t<RuleSet&&> ruleset_callback) const; }; |