Skip to content

Commit

Permalink
Add skeleton unit pathing function + test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
Hop311 committed Nov 25, 2024
1 parent df5e690 commit e4ca991
Show file tree
Hide file tree
Showing 4 changed files with 208 additions and 64 deletions.
154 changes: 122 additions & 32 deletions src/headless/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,113 @@ static void print_rgo(ProvinceInstance const& province) {
"\n\temployees:"
);

auto const& employee_count_per_type_cache=rgo.get_employee_count_per_type_cache();
IndexedMap<PopType, pop_size_t> const& employee_count_per_type_cache = rgo.get_employee_count_per_type_cache();
for (PopType const& pop_type : *employee_count_per_type_cache.get_keys()) {
const pop_size_t employees_of_type = employee_count_per_type_cache[pop_type];
if (employees_of_type > 0) {
text += StringUtils::append_string_views("\n\t\t", std::to_string(employees_of_type), " ", pop_type.get_identifier());
text += StringUtils::append_string_views(
"\n\t\t", std::to_string(employees_of_type), " ", pop_type.get_identifier()
);
}
}
Logger::info("", text);
}
}

static void test_rgo(InstanceManager const& instance_manager) {
std::vector<CountryInstance*> const& great_powers = instance_manager.get_country_instance_manager().get_great_powers();

for (size_t i = 0; i < std::min<size_t>(3, great_powers.size()); ++i) {
CountryInstance const* great_power = great_powers[i];
if (great_power == nullptr) {
Logger::warning("Great power index ", i, " is null.");
continue;
}

ProvinceInstance const* const capital_province = great_power->get_capital();
if (capital_province == nullptr) {
Logger::warning(great_power->get_identifier(), " has no capital ProvinceInstance set.");
} else {
print_rgo(*capital_province);
}
}
}

template<UnitType::branch_t Branch>
static bool test_pathing(
InstanceManager& instance_manager, std::string_view country_identifier, size_t unit_group_index,
size_t target_province_index, bool continue_movement, bool expect_success = true
) {
using enum UnitType::branch_t;

CountryInstance* country =
instance_manager.get_country_instance_manager().get_country_instance_by_identifier(country_identifier);
if (country == nullptr) {
Logger::error("Country ", country_identifier, " not found!");
return false;
}

ordered_set<UnitInstanceGroupBranched<Branch>*> const& unit_groups = country->get_unit_instance_groups<Branch>();
if (unit_groups.size() <= unit_group_index) {
Logger::error(
Branch == LAND ? "Land" : "Naval", " unit group index ", unit_group_index, " out of bounds for country ",
country_identifier
);
return false;
}
UnitInstanceGroupBranched<Branch>* unit_group = unit_groups.data()[unit_group_index];

MapInstance const& map_instance = instance_manager.get_map_instance();

ProvinceInstance const* target_province = map_instance.get_province_instance_by_index(target_province_index);
if (target_province == nullptr) {
Logger::error("Province index ", target_province_index, " out of bounds!");
return false;
}

const bool path_found = unit_group->path_to(*target_province, continue_movement, map_instance);

if (expect_success) {
if (path_found) {
Logger::info(
"Path found for ", Branch == LAND ? "land" : "naval", " unit group \"", unit_group->get_name(), "\" to ",
target_province->get_identifier(), ". New full path:"
);

for (ProvinceInstance const* province : unit_group->get_path()) {
Logger::info(" ", province->get_identifier());
}

return true;
} else {
Logger::error(
"Failed to find path for ", Branch == LAND ? "land" : "naval", " unit group \"", unit_group->get_name(), "\" to ",
target_province->get_identifier()
);
return false;
}
} else {
if (path_found) {
Logger::error(
"Found path for ", Branch == LAND ? "land" : "naval", " unit group \"", unit_group->get_name(), "\" to ",
target_province->get_identifier(), ", despite expecting to fail. New (invalid) path:"
);

for (ProvinceInstance const* province : unit_group->get_path()) {
Logger::info(" ", province->get_identifier());
}

return false;
} else {
Logger::info(
"Did not find path for ", Branch == LAND ? "land" : "naval", " unit group \"", unit_group->get_name(), "\" to ",
target_province->get_identifier(), ", as expected"
);
return true;
}
}
}

static bool run_headless(Dataloader::path_vector_t const& roots, bool run_tests) {
bool ret = true;

Expand Down Expand Up @@ -90,40 +186,34 @@ static bool run_headless(Dataloader::path_vector_t const& roots, bool run_tests)
ret &= game_manager.update_clock();

// TODO - REMOVE TEST CODE
Logger::info("===== Ranking system test... =====");
if (game_manager.get_instance_manager()) {
const auto print_ranking_list = [](std::string_view title, std::vector<CountryInstance*> const& countries) -> void {
std::string text;
for (CountryInstance const* country : countries) {
text += StringUtils::append_string_views(
"\n ", country->get_identifier(),
" - Total #", std::to_string(country->get_total_rank()), " (", country->get_total_score().to_string(1),
"), Prestige #", std::to_string(country->get_prestige_rank()), " (", country->get_prestige().to_string(1),
"), Industry #", std::to_string(country->get_industrial_rank()), " (", country->get_industrial_power().to_string(1),
"), Military #", std::to_string(country->get_military_rank()), " (", country->get_military_power().to_string(1), ")"
);
}
Logger::info(title, ":", text);
};
InstanceManager& instance_manager = *game_manager.get_instance_manager();

CountryInstanceManager const& country_instance_manager =
game_manager.get_instance_manager()->get_country_instance_manager();
Logger::info("===== RGO test... =====");
test_rgo(instance_manager);

std::vector<CountryInstance*> const& great_powers = country_instance_manager.get_great_powers();
print_ranking_list("Great Powers", great_powers);
print_ranking_list("Secondary Powers", country_instance_manager.get_secondary_powers());
print_ranking_list("All countries", country_instance_manager.get_total_ranking());
Logger::info("===== Pathing test... =====");
using enum UnitType::branch_t;
// Move "Garde Royale" from Paris to Brest
ret &= test_pathing<LAND>(instance_manager, "FRA", 0, 420, false);
// Move "Garde Royale" from Brest to Toulon
ret &= test_pathing<LAND>(instance_manager, "FRA", 0, 470, true);
// Move "Garde Royale" from Paris to Strasbourg
ret &= test_pathing<LAND>(instance_manager, "FRA", 0, 409, false);

Logger::info("===== RGO test... =====");
for (size_t i = 0; i < std::min<size_t>(3, great_powers.size()); ++i) {
CountryInstance const& great_power = *great_powers[i];
ProvinceInstance const* const capital_province = great_power.get_capital();
if (capital_province == nullptr) {
Logger::warning(great_power.get_identifier(), " has no capital ProvinceInstance set.");
} else {
print_rgo(*capital_province);
}
}
// Move "The South Africa Garrison" from Cape Town to Djibouti
ret &= test_pathing<LAND>(instance_manager, "ENG", 6, 1875, false);
// Move "The South Africa Garrison" from Djibouti to Socotra (expected to fail)
ret &= test_pathing<LAND>(instance_manager, "ENG", 6, 1177, true, false);
// Move "The South Africa Garrison" from Cape Town to Tananarive (expected to fail)
ret &= test_pathing<LAND>(instance_manager, "ENG", 6, 2115, false, false);

// Move "Imperial Fleet" from Kyoto to Taiwan Strait
ret &= test_pathing<NAVAL>(instance_manager, "JAP", 0, 2825, false);
// Move "Imperial Fleet" from Kyoto to Falkland Plateau (should go East)
ret &= test_pathing<NAVAL>(instance_manager, "JAP", 0, 3047, false);
// Move "Imperial Fleet" from Kyoto to Coast of South Georgia (should go West)
ret &= test_pathing<NAVAL>(instance_manager, "JAP", 0, 3200, false);
} else {
Logger::error("Instance manager not available!");
ret = false;
Expand Down
10 changes: 6 additions & 4 deletions src/openvic-simulation/country/CountryInstance.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,6 @@ namespace OpenVic {
unlock_level_t PROPERTY(gas_defence_unlock_level);
std::vector<unlock_level_t> PROPERTY(unit_variant_unlock_levels);

UNIT_BRANCHED_GETTER(get_unit_instance_groups, armies, navies);
UNIT_BRANCHED_GETTER(get_leaders, generals, admirals);
UNIT_BRANCHED_GETTER(get_unit_type_unlock_levels, regiment_type_unlock_levels, ship_type_unlock_levels);

CountryInstance(
CountryDefinition const* new_country_definition,
decltype(building_type_unlock_levels)::keys_t const& building_type_keys,
Expand Down Expand Up @@ -239,6 +235,10 @@ namespace OpenVic {
void set_strata_tax_rate(Strata const& strata, const StandardSliderValue::int_type new_value);
void set_tariff_rate(const StandardSliderValue::int_type new_value);

UNIT_BRANCHED_GETTER(get_unit_instance_groups, armies, navies);
UNIT_BRANCHED_GETTER(get_leaders, generals, admirals);
UNIT_BRANCHED_GETTER(get_unit_type_unlock_levels, regiment_type_unlock_levels, ship_type_unlock_levels);

template<UnitType::branch_t Branch>
bool add_unit_instance_group(UnitInstanceGroup<Branch>& group);
template<UnitType::branch_t Branch>
Expand Down Expand Up @@ -353,6 +353,8 @@ namespace OpenVic {
void update_rankings(Date today, DefineManager const& define_manager);

public:
IDENTIFIER_REGISTRY_NON_CONST_ACCESSORS(country_instance);

CountryInstance& get_country_instance_from_definition(CountryDefinition const& country);
CountryInstance const& get_country_instance_from_definition(CountryDefinition const& country) const;

Expand Down
82 changes: 68 additions & 14 deletions src/openvic-simulation/military/UnitInstanceGroup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@

using namespace OpenVic;

MovementInfo::MovementInfo() : path {}, movement_progress {} {}

//TODO: pathfinding logic
MovementInfo::MovementInfo(ProvinceInstance const* starting_province, ProvinceInstance const* target_province)
: path { starting_province, target_province }, movement_progress { 0 } {}
using enum UnitType::branch_t;

template<UnitType::branch_t Branch>
UnitInstanceGroup<Branch>::UnitInstanceGroup(
Expand Down Expand Up @@ -142,20 +138,80 @@ bool UnitInstanceGroup<Branch>::set_leader(_Leader* new_leader) {
return ret;
}

template struct OpenVic::UnitInstanceGroup<UnitType::branch_t::LAND>;
template struct OpenVic::UnitInstanceGroup<UnitType::branch_t::NAVAL>;
template<UnitType::branch_t Branch>
bool UnitInstanceGroup<Branch>::path_to(
ProvinceInstance const& target_province, bool continue_movement, MapInstance const& map
) {
using adjacency_t = ProvinceDefinition::adjacency_t;
using distance_t = ProvinceDefinition::distance_t;

ProvinceInstance const* start_province = continue_movement && !path.empty() ? path.back() : position;

if (start_province == nullptr) {
Logger::error("Unit group ", name, " has no position to start movement from!");
return false;
}

// Examples of how to use adjacency_t
std::vector<adjacency_t> const& adjacencies = target_province.get_province_definition().get_adjacencies();

bool path_found = false;
adjacency_t const* shorted_adjacency = nullptr;
distance_t shortest_distance = fixed_point_t::_0();

for (adjacency_t const& adjacency : adjacencies) {
// Skip non-land/non-water adjacencies for LAND/NAVAL unit groups
if constexpr (Branch == LAND) {
if (adjacency.get_type() != adjacency_t::type_t::LAND) {
continue;
}
} else if constexpr (Branch == NAVAL) {
if (adjacency.get_type() != adjacency_t::type_t::WATER) {
continue;
}
}

if (adjacency.get_distance() < shortest_distance || shorted_adjacency == nullptr) {
shorted_adjacency = &adjacency;
shortest_distance = adjacency.get_distance();
}

// You can go from ProvinceInstance to ProvinceDefinition using get_province_definition()
// To go the other way you must use map.get_province_instance_from_definition(province_definition)
if (adjacency.get_to() == &target_province.get_province_definition()) {
path_found = true;
break;
}
}

if (path_found) {
if (!continue_movement) {
path.clear();
movement_progress = fixed_point_t::_0();
}

// TODO - add new provinces to path (everything after start_province up to and including &target_province)

return true;
} else {
return false;
}
}

template struct OpenVic::UnitInstanceGroup<LAND>;
template struct OpenVic::UnitInstanceGroup<NAVAL>;

UnitInstanceGroupBranched<UnitType::branch_t::LAND>::UnitInstanceGroupBranched(
UnitInstanceGroupBranched<LAND>::UnitInstanceGroupBranched(
std::string_view new_name,
std::vector<RegimentInstance*>&& new_units
) : UnitInstanceGroup { new_name, std::move(new_units) } {}

UnitInstanceGroupBranched<UnitType::branch_t::NAVAL>::UnitInstanceGroupBranched(
UnitInstanceGroupBranched<NAVAL>::UnitInstanceGroupBranched(
std::string_view new_name,
std::vector<ShipInstance*>&& new_units
) : UnitInstanceGroup { new_name, std::move(new_units) } {}

fixed_point_t UnitInstanceGroupBranched<UnitType::branch_t::NAVAL>::get_total_consumed_supply() const {
fixed_point_t UnitInstanceGroupBranched<NAVAL>::get_total_consumed_supply() const {
fixed_point_t total_consumed_supply = 0;

for (ShipInstance const* ship : get_units()) {
Expand All @@ -171,13 +227,13 @@ bool UnitInstanceManager::generate_unit_instance(
) {
unit_instance = &*get_unit_instances<Branch>().insert(
[&unit_deployment]() -> UnitInstanceBranched<Branch> {
if constexpr (Branch == UnitType::branch_t::LAND) {
if constexpr (Branch == LAND) {
return {
unit_deployment.get_name(), unit_deployment.get_type(),
nullptr, // TODO - get pop from Province unit_deployment.get_home()
false // Not mobilised
};
} else if constexpr (Branch == UnitType::branch_t::NAVAL) {
} else if constexpr (Branch == NAVAL) {
return { unit_deployment.get_name(), unit_deployment.get_type() };
}
}()
Expand Down Expand Up @@ -256,8 +312,6 @@ bool UnitInstanceManager::generate_deployment(
}
};

using enum UnitType::branch_t;

generate_group.template operator()<LAND>();
generate_group.template operator()<NAVAL>();

Expand Down
26 changes: 12 additions & 14 deletions src/openvic-simulation/military/UnitInstanceGroup.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,12 @@
#include "openvic-simulation/utility/Getters.hpp"

namespace OpenVic {
struct ProvinceInstance;

struct MovementInfo {
private:
std::vector<ProvinceInstance const*> PROPERTY(path);
fixed_point_t PROPERTY(movement_progress);

public:
MovementInfo();
// contains/calls pathfinding logic
MovementInfo(ProvinceInstance const* starting_province, ProvinceInstance const* target_province);
};

template<UnitType::branch_t>
struct LeaderBranched;

struct ProvinceInstance;
struct CountryInstance;
struct MapInstance;

template<UnitType::branch_t Branch>
struct UnitInstanceGroup {
Expand All @@ -40,7 +29,9 @@ namespace OpenVic {
std::vector<_UnitInstance*> PROPERTY(units);
_Leader* PROPERTY(leader);

MovementInfo PROPERTY_REF(movement_info);
// Movement attributes
std::vector<ProvinceInstance const*> PROPERTY(path);
fixed_point_t PROPERTY(movement_progress);

protected:
ProvinceInstance* PROPERTY_ACCESS(position, protected);
Expand All @@ -64,6 +55,13 @@ namespace OpenVic {
bool set_position(ProvinceInstance* new_position);
bool set_country(CountryInstance* new_country);
bool set_leader(_Leader* new_leader);

/* Attempt to find a path to the target province, starting from either the end of the unit group's current path if
* continue_movement is true, or the current position of the unit group if continue_movement is false. If a path is
* found add it to the unit group's path and return true, resetting any pre-existing path if continue_movement is
* false. If no path can be found, return false and do not modify the unit group's path regardless of
* continue_movement's value. */
bool path_to(ProvinceInstance const& target_province, bool continue_movement, MapInstance const& map);
};

template<UnitType::branch_t>
Expand Down

0 comments on commit e4ca991

Please sign in to comment.