From 4542410b4177573f486ad02ed1ad9363a7501ee1 Mon Sep 17 00:00:00 2001 From: Andreas Schmitt Date: Sun, 13 Jun 2021 12:03:53 +0200 Subject: [PATCH] Revert "Replace original algorithm with a simpler one" This reverts commit 8cb3d80402f626034c08a162b8956dce03eb79f6. --- src/road.cpp | 157 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 98 insertions(+), 59 deletions(-) diff --git a/src/road.cpp b/src/road.cpp index ca24e8421c..7e525d9a72 100644 --- a/src/road.cpp +++ b/src/road.cpp @@ -246,6 +246,8 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u CommandCost CmdBuildTunnel(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text = nullptr); CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text = nullptr); +static std::vector _town_centers; +static std::vector _towns_visited_along_the_way; static bool _has_tunnel_in_path; static RoadType _public_road_type; static const uint _public_road_hash_size = 8U; ///< The number of bits the hash for river finding should have. @@ -261,15 +263,14 @@ static uint PublicRoad_Hash(uint tile, uint dir) return GB(TileHash(TileX(tile), TileY(tile)), 0, _public_road_hash_size); } -static const int32 _base_cost = 1; // Cost for utilizing an existing road, bridge, or tunnel. +static const int32 BASE_COST = 1; // Cost for utilizing an existing road, bridge, or tunnel. +static const int32 COST_FOR_NEW_ROAD = 10; // Cost for building a new road. +static const int32 COST_FOR_SLOPE = 5; // Additional cost if the road heads up or down a slope. /** AyStar callback for getting the cost of the current node. */ static int32 PublicRoad_CalculateG(AyStar *, AyStarNode *current, OpenListNode *parent) { - static const int32 COST_FOR_NEW_ROAD = 10; // Cost for building a new road. - static const int32 COST_FOR_SLOPE = 5; // Additional cost if the road heads up or down a slope. - - int32 cost = _base_cost; + int32 cost = BASE_COST; if (!IsTileType(current->tile, MP_ROAD)) { if (!AreTilesAdjacent(parent->path.node.tile, current->tile)) @@ -299,7 +300,7 @@ static int32 PublicRoad_CalculateG(AyStar *, AyStarNode *current, OpenListNode * /** AyStar callback for getting the estimated cost to the destination. */ static int32 PublicRoad_CalculateH(AyStar *aystar, AyStarNode *current, OpenListNode *parent) { - return DistanceManhattan(*static_cast(aystar->user_target), current->tile) * _base_cost; + return DistanceManhattan(*static_cast(aystar->user_target), current->tile) * BASE_COST; } /** Helper function to check if a tile along a certain direction is going up an inclined slope. */ @@ -632,6 +633,14 @@ static void PublicRoad_GetNeighbours(AyStar *aystar, OpenListNode *current) /** AyStar callback for checking whether we reached our destination. */ static int32 PublicRoad_EndNodeCheck(const AyStar *aystar, const OpenListNode *current) { + // Mark towns visited along the way. + const auto search_result = + std::find(_town_centers.begin(), _town_centers.end(), current->path.node.tile); + + if (search_result != _town_centers.end()) { + _towns_visited_along_the_way.push_back(current->path.node.tile); + } + return current->path.node.tile == *static_cast(aystar->user_target) ? AYSTAR_FOUND_END_NODE : AYSTAR_DONE; } @@ -741,87 +750,117 @@ void GeneratePublicRoads() using namespace std; if (_settings_game.game_creation.build_public_roads == PRC_NONE) return; - - vector cities; - vector villages; - vector unconnected_villages; - _public_road_type = GetTownRoadType(*Town::Iterate().begin()); + _town_centers.clear(); + _towns_visited_along_the_way.clear(); - for (const Town *town : Town::Iterate()) { - if (town->larger_town) { - cities.push_back(town->xy); - } else { - villages.push_back(town->xy); + vector towns; + towns.clear(); + { + for (const Town *town : Town::Iterate()) { + towns.push_back(town->xy); + _town_centers.push_back(town->xy); } } - SetGeneratingWorldProgress(GWP_PUBLIC_ROADS, uint(cities.size() + villages.size())); + if (towns.empty()) { + return; + } + + SetGeneratingWorldProgress(GWP_PUBLIC_ROADS, uint(towns.size())); - // Try to connect every village to the nearest city. - for (auto village : villages) { - sort(cities.begin(), cities.end(), [&](auto a, auto b) { return DistanceManhattan(village, a) < DistanceManhattan(village, b); }); + // Create a list of networks which also contain a value indicating how many times we failed to connect to them. + vector>>> town_networks; + unordered_map>> towns_reachable_networks; - bool is_connected = false; - - for (auto city : cities) { - AyStar finder {}; + TileIndex main_town = *towns.begin(); + towns.erase(towns.begin()); - auto found_path = FindPath(finder, village, city); + _public_road_type = GetTownRoadType(Town::GetByTile(main_town)); - finder.Free(); + auto main_network = make_shared>(); + main_network->push_back(main_town); - if (found_path){ - is_connected = true; - break; - } - } + town_networks.emplace_back(0, main_network); + IncreaseGeneratingWorldProgress(GWP_PUBLIC_ROADS); - if (!is_connected) { - unconnected_villages.push_back(village); - } else { - IncreaseGeneratingWorldProgress(GWP_PUBLIC_ROADS); - } - } + sort(towns.begin(), towns.end(), [&](auto a, auto b) { return DistanceManhattan(main_town, a) < DistanceManhattan(main_town, b); }); + + for (auto begin_town : towns) { + // Check if we can connect to any of the networks. + _towns_visited_along_the_way.clear(); + + auto reachable_network_iter = towns_reachable_networks.find(begin_town); + bool found_easy_path = false; - vector villages_copy(unconnected_villages); + if (reachable_network_iter != towns_reachable_networks.end()) { + auto reachable_network = reachable_network_iter->second; - // If a village could not be connected to a city, try to connect it to another unconnected village. - for (auto village : unconnected_villages) { - sort(villages_copy.begin(), villages_copy.end(), [&](auto a, auto b) { return DistanceManhattan(village, a) < DistanceManhattan(village, b); }); + sort(reachable_network->begin(), reachable_network->end(), [&](auto a, auto b) { return DistanceManhattan(begin_town, a) < DistanceManhattan(begin_town, b); }); + + const TileIndex end_town = *reachable_network->begin(); - for (auto other_village : villages_copy) { AyStar finder {}; - auto found_path = FindPath(finder, village, other_village); + found_easy_path = FindPath(finder, begin_town, end_town); finder.Free(); + } - if (found_path) { - break; + if (found_easy_path) { + reachable_network_iter->second->push_back(begin_town); + + for (const TileIndex visited_town : _towns_visited_along_the_way) { + if (visited_town != begin_town) towns_reachable_networks[visited_town] = reachable_network_iter->second; } - } + } else { + // Sort networks by failed connection attempts, so we try the most likely one first. + sort(town_networks.begin(), town_networks.end(), [&](auto a, auto b) { return a.first < b.first; }); - IncreaseGeneratingWorldProgress(GWP_PUBLIC_ROADS); - } + std::function>>)> can_reach_network = [&](auto network_pair) { + AyStar finder {}; - vector cities_copy(cities); + auto network = network_pair.second; - // Connect the cities with each-other. - for (auto city : cities) { - sort(cities_copy.begin(), cities_copy.end(), [&](auto a, auto b) { return DistanceManhattan(city, a) < DistanceManhattan(city, b); }); + // Try to connect to the town in the network that is closest to us. + // If we can't connect to that one, we can't connect to any of them since they are all interconnected. + sort(network->begin(), network->end(), [&](auto a, auto b) { return DistanceManhattan(begin_town, a) < DistanceManhattan(begin_town, b); }); + const TileIndex end_town = *network->begin(); - for (auto other_city : cities_copy) { - if (other_city == city) continue; + const bool found_path = FindPath(finder, begin_town, end_town); - AyStar finder {}; + if (found_path) { + network->push_back(begin_town); + + for (auto visited_town : _towns_visited_along_the_way) { + if (visited_town != begin_town) towns_reachable_networks[visited_town] = network; + } + } - auto found_path = FindPath(finder, city, other_city); + // Increase number of failed attempts if necessary. + network_pair.first += (found_path ? (network_pair.first > 0 ? -1 : 0) : 1); - finder.Free(); + finder.Free(); - if (found_path) { - break; + return found_path; + + }; + + if (!any_of(town_networks.begin(), town_networks.end(), can_reach_network)) { + // We failed to connect to any network, so we are a separate network. Let future towns try to connect to us. + auto new_network = make_shared>(); + new_network->push_back(begin_town); + + // We basically failed to connect to this many towns. + int towns_already_in_networks = std::accumulate(town_networks.begin(), town_networks.end(), 0, [&](int accumulator, auto network_pair) { + return accumulator + static_cast(network_pair.second->size()); + }); + + town_networks.emplace_back(towns_already_in_networks, new_network); + + for (const TileIndex visited_town : _towns_visited_along_the_way) { + if (visited_town != begin_town) towns_reachable_networks.insert(make_pair(visited_town, new_network)); + } } }