Changeset - r28577:f173638a0323
[Not reviewed]
master
0 4 0
Kuhnovic - 3 months ago 2024-01-27 14:06:14
68320206+Kuhnovic@users.noreply.github.com
Fix #5713: FindClosestShipDepot only considers depots that are actually reachable (#11768)
4 files changed with 62 insertions and 18 deletions:
0 comments (0 inline, 0 general)
src/pathfinder/water_regions.cpp
Show inline comments
 
@@ -25,6 +25,7 @@ constexpr TWaterRegionPatchLabel FIRST_R
 
constexpr TWaterRegionPatchLabel INVALID_WATER_REGION_PATCH = 0;
 

	
 
static_assert(sizeof(TWaterRegionTraversabilityBits) * 8 == WATER_REGION_EDGE_LENGTH);
 
static_assert(sizeof(TWaterRegionPatchLabel) == sizeof(byte)); // Important for the hash calculation.
 

	
 
static inline TrackBits GetWaterTracks(TileIndex tile) { return TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_WATER, 0)); }
 
static inline bool IsAqueductTile(TileIndex tile) { return IsBridgeTile(tile) && GetTunnelBridgeTransportType(tile) == TRANSPORT_WATER; }
 
@@ -225,8 +226,8 @@ WaterRegion &GetUpdatedWaterRegion(TileI
 
}
 

	
 
/**
 
 * Returns the index of the water region
 
 * @param water_region The Water region to return the index for
 
 * Returns the index of the water region.
 
 * @param water_region The water region to return the index for.
 
 */
 
TWaterRegionIndex GetWaterRegionIndex(const WaterRegionDesc &water_region)
 
{
 
@@ -234,6 +235,15 @@ TWaterRegionIndex GetWaterRegionIndex(co
 
}
 

	
 
/**
 
 * Calculates a number that uniquely identifies the provided water region patch.
 
 * @param water_region_patch The Water region to calculate the hash for.
 
 */
 
int CalculateWaterRegionPatchHash(const WaterRegionPatchDesc &water_region_patch)
 
{
 
	return water_region_patch.label | GetWaterRegionIndex(water_region_patch) << 8;
 
}
 

	
 
/**
 
 * Returns the center tile of a particular water region.
 
 * @param water_region The water region to find the center tile for.
 
 * @returns The center tile of the water region.
src/pathfinder/water_regions.h
Show inline comments
 
@@ -50,6 +50,8 @@ struct WaterRegionDesc
 

	
 
TWaterRegionIndex GetWaterRegionIndex(const WaterRegionDesc &water_region);
 

	
 
int CalculateWaterRegionPatchHash(const WaterRegionPatchDesc &water_region_patch);
 

	
 
TileIndex GetWaterRegionCenterTile(const WaterRegionDesc &water_region);
 

	
 
WaterRegionDesc GetWaterRegionInfo(TileIndex tile);
src/pathfinder/yapf/yapf_ship_regions.cpp
Show inline comments
 
@@ -24,14 +24,12 @@ constexpr int MAX_NUMBER_OF_NODES = 6553
 
struct CYapfRegionPatchNodeKey {
 
	WaterRegionPatchDesc m_water_region_patch;
 

	
 
	static_assert(sizeof(TWaterRegionPatchLabel) == sizeof(byte)); // Important for the hash calculation.
 

	
 
	inline void Set(const WaterRegionPatchDesc &water_region_patch)
 
	{
 
		m_water_region_patch = water_region_patch;
 
	}
 

	
 
	inline int CalcHash() const { return m_water_region_patch.label | GetWaterRegionIndex(m_water_region_patch) << 8; }
 
	inline int CalcHash() const { return CalculateWaterRegionPatchHash(m_water_region_patch); }
 
	inline bool operator==(const CYapfRegionPatchNodeKey &other) const { return CalcHash() == other.CalcHash(); }
 
};
 

	
src/ship_cmd.cpp
Show inline comments
 
@@ -18,6 +18,7 @@
 
#include "station_base.h"
 
#include "newgrf_engine.h"
 
#include "pathfinder/yapf/yapf.h"
 
#include "pathfinder/yapf/yapf_ship_regions.h"
 
#include "newgrf_sound.h"
 
#include "spritecache.h"
 
#include "strings_func.h"
 
@@ -39,8 +40,13 @@
 

	
 
#include "table/strings.h"
 

	
 
#include <unordered_set>
 

	
 
#include "safeguards.h"
 

	
 
/** Max distance in tiles (as the crow flies) to search for depots when user clicks "go to depot". */
 
constexpr int MAX_SHIP_DEPOT_SEARCH_DISTANCE = 80;
 

	
 
/**
 
 * Determine the effective #WaterClass for a ship travelling on a tile.
 
 * @param tile Tile of interest
 
@@ -145,21 +151,49 @@ void Ship::GetImage(Direction direction,
 

	
 
static const Depot *FindClosestShipDepot(const Vehicle *v, uint max_distance)
 
{
 
	/* Find the closest depot */
 
	const int max_region_distance = (max_distance / WATER_REGION_EDGE_LENGTH) + 1;
 

	
 
	static std::unordered_set<int> visited_patch_hashes;
 
	static std::deque<WaterRegionPatchDesc> patches_to_search;
 
	visited_patch_hashes.clear();
 
	patches_to_search.clear();
 

	
 
	/* Step 1: find a set of reachable Water Region Patches using BFS. */
 
	const WaterRegionPatchDesc start_patch = GetWaterRegionPatchInfo(v->tile);
 
	patches_to_search.push_back(start_patch);
 
	visited_patch_hashes.insert(CalculateWaterRegionPatchHash(start_patch));
 

	
 
	while (!patches_to_search.empty()) {
 
		/* Remove first patch from the queue and make it the current patch. */
 
		const WaterRegionPatchDesc current_node = patches_to_search.front();
 
		patches_to_search.pop_front();
 

	
 
		/* Add neighbors of the current patch to the search queue. */
 
		TVisitWaterRegionPatchCallBack visitFunc = [&](const WaterRegionPatchDesc &water_region_patch) {
 
			/* Note that we check the max distance per axis, not the total distance. */
 
			if (std::abs(water_region_patch.x - start_patch.x) > max_region_distance ||
 
					std::abs(water_region_patch.y - start_patch.y) > max_region_distance) return;
 

	
 
			const int hash = CalculateWaterRegionPatchHash(water_region_patch);
 
			if (visited_patch_hashes.count(hash) == 0) {
 
				visited_patch_hashes.insert(hash);
 
				patches_to_search.push_back(water_region_patch);
 
			}
 
		};
 

	
 
		VisitWaterRegionPatchNeighbors(current_node, visitFunc);
 
	}
 

	
 
	/* Step 2: Find the closest depot within the reachable Water Region Patches. */
 
	const Depot *best_depot = nullptr;
 
	/* If we don't have a maximum distance, i.e. distance = 0,
 
	 * we want to find any depot so the best distance of no
 
	 * depot must be more than any correct distance. On the
 
	 * other hand if we have set a maximum distance, any depot
 
	 * further away than max_distance can safely be ignored. */
 
	uint best_dist = max_distance == 0 ? UINT_MAX : max_distance + 1;
 

	
 
	uint best_dist_sq = std::numeric_limits<uint>::max();
 
	for (const Depot *depot : Depot::Iterate()) {
 
		TileIndex tile = depot->xy;
 
		const TileIndex tile = depot->xy;
 
		if (IsShipDepotTile(tile) && IsTileOwner(tile, v->owner)) {
 
			uint dist = DistanceManhattan(tile, v->tile);
 
			if (dist < best_dist) {
 
				best_dist = dist;
 
			const uint dist_sq = DistanceSquare(tile, v->tile);
 
			if (dist_sq < best_dist_sq && dist_sq <= max_distance * max_distance &&
 
					visited_patch_hashes.count(CalculateWaterRegionPatchHash(GetWaterRegionPatchInfo(tile))) > 0) {
 
				best_dist_sq = dist_sq;
 
				best_depot = depot;
 
			}
 
		}
 
@@ -934,7 +968,7 @@ CommandCost CmdBuildShip(DoCommandFlag f
 

	
 
ClosestDepot Ship::FindClosestDepot()
 
{
 
	const Depot *depot = FindClosestShipDepot(this, 0);
 
	const Depot *depot = FindClosestShipDepot(this, MAX_SHIP_DEPOT_SEARCH_DISTANCE);
 
	if (depot == nullptr) return ClosestDepot();
 

	
 
	return ClosestDepot(depot->xy, depot->index);
0 comments (0 inline, 0 general)