From 7df9426000ebdcb4c029095cf5a0b8ec2747e4f3 Mon Sep 17 00:00:00 2001 From: SamuXarick <43006711+SamuXarick@users.noreply.github.com> Date: Thu, 3 Oct 2024 18:37:48 +0100 Subject: [PATCH] Change: Find nearest depot to entry edge in intermediate region When using an intermediate region, find the depot closest to the edge where it entered the region from. --- src/pathfinder/water_regions.cpp | 69 +++++++++++++++++++++-- src/pathfinder/water_regions.h | 3 +- src/pathfinder/yapf/yapf_ship.cpp | 6 +- src/pathfinder/yapf/yapf_ship_regions.cpp | 2 +- 4 files changed, 70 insertions(+), 10 deletions(-) diff --git a/src/pathfinder/water_regions.cpp b/src/pathfinder/water_regions.cpp index 88fb21493f..1393438194 100644 --- a/src/pathfinder/water_regions.cpp +++ b/src/pathfinder/water_regions.cpp @@ -433,12 +433,12 @@ void PrintWaterRegionDebugInfo(TileIndex tile) /** * Tests the provided callback function on all tiles of the water patch of the region - * and returns the first tile that passes the callback test. + * and returns true on the first tile that passes the callback test. * @param callback The test function that will be called for the water patch. * @param water_region_patch Water patch within the water region to test the callback. - * @return the first tile which passed the callback test, or INVALID_TILE if the callback failed. + * @return true if it passes the callback test, or false if the callback failed. */ -TileIndex GetTileInWaterRegionPatch(const WaterRegionPatchDesc &water_region_patch, TestTileIndexCallBack &callback) +bool TestTileInWaterRegionPatch(const WaterRegionPatchDesc &water_region_patch, TestTileIndexCallBack &callback) { const WaterRegion region = GetUpdatedWaterRegion(water_region_patch.x, water_region_patch.y); @@ -446,8 +446,67 @@ TileIndex GetTileInWaterRegionPatch(const WaterRegionPatchDesc &water_region_pat for (const TileIndex tile : region) { if (region.GetLabel(tile) != water_region_patch.label || !callback(tile)) continue; - return tile; + return true; } - return INVALID_TILE; + return false; +} + +/** + * Tests the provided callback function on all tiles of the current water patch of the region, collects the + * tiles which passed the callback and returns the tile closest to the edge from where the region is entered from. + * @param high_level_path A span containing at least current and parent water patches. + * @param callback The test function that will be called for each tile in the water patch. + * @return The tile closest to the edge from where it came from that passed the callback test, or INVALID_TILE if no tile passed. + */ +TileIndex FindClosestEnteringTile(const std::span high_level_path, TestTileIndexCallBack &callback) +{ + assert(high_level_path.size() > 1); + + const WaterRegionPatchDesc ¤t_water_region_patch = high_level_path.back(); + const WaterRegion current_region = GetUpdatedWaterRegion(current_water_region_patch.x, current_water_region_patch.y); + + /* Check if the current region has a tile which passes the callback test. */ + std::vector tile_list; + for (const TileIndex tile : current_region) { + if (current_region.GetLabel(tile) != current_water_region_patch.label || !callback(tile)) continue; + + /* We collect the tiles when we know which region we came from for further evaluation. */ + tile_list.push_back(tile); + } + + /* If there aren't any tiles that passed the callback, return with an invalid tile. */ + if (tile_list.empty()) return INVALID_TILE; + + /* If there's only one, just return it. */ + if (tile_list.size() == 1) return tile_list.front(); + + const TileIndex top_tile = current_region.begin(); + const TileIndex bot_tile = TileAddXY(top_tile, WATER_REGION_EDGE_LENGTH - 1, WATER_REGION_EDGE_LENGTH - 1); + + /* Get the side from which the current region is entered from. */ + const WaterRegionPatchDesc &parent_water_region_patch = high_level_path[high_level_path.size() - 2]; + const WaterRegion parent_region = GetUpdatedWaterRegion(parent_water_region_patch.x, parent_water_region_patch.y); + const DiagDirection side = DiagdirBetweenTiles(top_tile, parent_region.begin()); + + /* Depending on the side, determine which corner tile to use to extract their x or y coordinates. */ + const bool is_at_top = side == DIAGDIR_NE || side == DIAGDIR_NW; + const TileIndex edge_tile = is_at_top ? top_tile : bot_tile; + const bool is_axis_x = DiagDirToAxis(side) == AXIS_X; + const int x_or_y_edge = is_axis_x ? TileX(edge_tile) : TileY(edge_tile); + + /* With more than one tile passing the callback, calculate the tile that is closest to the edge from whence it came. */ + TileIndex best_tile = INVALID_TILE; + int best_dist = WATER_REGION_EDGE_LENGTH; + for (const TileIndex &tile : tile_list) { + const int x_or_y_tile = is_axis_x ? TileX(tile) : TileY(tile); + const int dist_to_edge = std::abs(x_or_y_tile - x_or_y_edge); + assert(dist_to_edge < WATER_REGION_EDGE_LENGTH); + if (dist_to_edge >= best_dist) continue; + + best_dist = dist_to_edge; + best_tile = tile; + } + + return best_tile; } diff --git a/src/pathfinder/water_regions.h b/src/pathfinder/water_regions.h index bb457bc183..6599d6adda 100644 --- a/src/pathfinder/water_regions.h +++ b/src/pathfinder/water_regions.h @@ -65,6 +65,7 @@ void AllocateWaterRegions(); void PrintWaterRegionDebugInfo(TileIndex tile); using TestTileIndexCallBack = std::function; -TileIndex GetTileInWaterRegionPatch(const WaterRegionPatchDesc &water_region_patch, TestTileIndexCallBack &callback); +bool TestTileInWaterRegionPatch(const WaterRegionPatchDesc &water_region_patch, TestTileIndexCallBack &callback); +TileIndex FindClosestEnteringTile(const std::span high_level_path, TestTileIndexCallBack &callback); #endif /* WATER_REGIONS_H */ diff --git a/src/pathfinder/yapf/yapf_ship.cpp b/src/pathfinder/yapf/yapf_ship.cpp index 06307b54bb..9704bd913b 100644 --- a/src/pathfinder/yapf/yapf_ship.cpp +++ b/src/pathfinder/yapf/yapf_ship.cpp @@ -101,9 +101,9 @@ public: return tile == this->dest_tile && ((this->dest_trackdirs & TrackdirToTrackdirBits(trackdir)) != TRACKDIR_BIT_NONE); } - inline TileIndex GetShipDepotDestination(const WaterRegionPatchDesc &water_region_patch) + inline TileIndex GetShipDepotDestination(const std::span high_level_path) { - return GetTileInWaterRegionPatch(water_region_patch, this->detect_ship_depot); + return FindClosestEnteringTile(high_level_path, this->detect_ship_depot); } /** @@ -283,7 +283,7 @@ public: /* Return early when only searching for the closest depot tile. */ if (find_closest_depot) { - tile = is_intermediate_destination ? pf.GetShipDepotDestination(high_level_path.back()) : node->GetTile(); + tile = is_intermediate_destination ? pf.GetShipDepotDestination(high_level_path) : node->GetTile(); return INVALID_TRACKDIR; } diff --git a/src/pathfinder/yapf/yapf_ship_regions.cpp b/src/pathfinder/yapf/yapf_ship_regions.cpp index 1991321475..a469fae280 100644 --- a/src/pathfinder/yapf/yapf_ship_regions.cpp +++ b/src/pathfinder/yapf/yapf_ship_regions.cpp @@ -144,7 +144,7 @@ public: inline bool PfDetectDestination(Node &n) { if (this->any_ship_depot) { - return GetTileInWaterRegionPatch(n.key.water_region_patch, this->detect_ship_depot) != INVALID_TILE; + return TestTileInWaterRegionPatch(n.key.water_region_patch, this->detect_ship_depot); } return n.key == this->dest;