From f0382c37ee7e0211a7412d98293ab765f24a816c Mon Sep 17 00:00:00 2001 From: SamuXarick <43006711+SamuXarick@users.noreply.github.com> Date: Tue, 27 Feb 2024 23:10:50 +0000 Subject: [PATCH] Fix #12193: [YAPF] Don't use the entire docking area when computing closest station tile Don't use CalcClosestStationTile for ships. Instead, use a specialized GetShipDestinationTiles which creates a list of possible destination tiles. For docks, it only takes into consideration the tiles that pass both IsDockingTile and IsShipDestinationTile tests. For the other cases it just takes the ship's current dest_tile. Modified CYapfDestinationTileWaterT class to accept multiple destinations. In this manner, the estimate cost is calculated for each of the destination tiles and the shortest estimate is returned. Some adaptation was necessary to make this possible to work for both the high- and low-level pathfinders and the existing functions. ChooseShipTrack and its FindWaterRegionPath brother both will take the same destination tiles which are calculated at either YapfShipChooseTrack or YapfShipCheckReverse. --- src/pathfinder/pathfinder_func.h | 34 ++++++++++ src/pathfinder/yapf/yapf_ship.cpp | 78 +++++++++++++---------- src/pathfinder/yapf/yapf_ship_regions.cpp | 20 ++---- src/pathfinder/yapf/yapf_ship_regions.h | 2 +- 4 files changed, 85 insertions(+), 49 deletions(-) diff --git a/src/pathfinder/pathfinder_func.h b/src/pathfinder/pathfinder_func.h index 444b100ce7..3297c591d0 100644 --- a/src/pathfinder/pathfinder_func.h +++ b/src/pathfinder/pathfinder_func.h @@ -12,6 +12,40 @@ #include "../tile_cmd.h" #include "../waypoint_base.h" +#include "../ship.h" + +/** + * Creates a list containing possible destination tiles for a ship. + * @param v The ship + * return Vector of tiles filled with all possible destinations. + */ +inline std::vector GetShipDestinationTiles(const Ship *v) +{ + std::vector dest_tiles; + + if (v->current_order.IsType(OT_GOTO_STATION)) { + const StationID station = v->current_order.GetDestination().ToStationID(); + + const BaseStation *st = BaseStation::Get(station); + TileArea ta; + st->GetTileArea(&ta, StationType::Dock); + /* If the dock station is (temporarily) not present, use the station sign to drive near the station. */ + if (ta.tile == INVALID_TILE) { + dest_tiles.push_back(st->xy); + } else { + for (const TileIndex &docking_tile : ta) { + if (!IsDockingTile(docking_tile) || !IsShipDestinationTile(docking_tile, station)) continue; + dest_tiles.push_back(docking_tile); + } + } + } else { + dest_tiles.push_back(v->dest_tile); + } + + assert(!dest_tiles.empty()); + + return dest_tiles; +} /** * Calculates the tile of given station that is closest to a given tile diff --git a/src/pathfinder/yapf/yapf_ship.cpp b/src/pathfinder/yapf/yapf_ship.cpp index f3a953097a..2c5928552a 100644 --- a/src/pathfinder/yapf/yapf_ship.cpp +++ b/src/pathfinder/yapf/yapf_ship.cpp @@ -33,8 +33,7 @@ public: typedef typename Node::Key Key; ///< key to hash tables. protected: - TileIndex dest_tile; - TrackdirBits dest_trackdirs; + std::span dest_tiles; StationID dest_station; bool has_intermediate_dest = false; @@ -42,16 +41,13 @@ protected: WaterRegionPatchDesc intermediate_dest_region_patch; public: - void SetDestination(const Ship *v) + void SetDestination(const Ship *v, const std::span destination_tiles) { + this->dest_tiles = destination_tiles; if (v->current_order.IsType(OT_GOTO_STATION)) { this->dest_station = v->current_order.GetDestination().ToStationID(); - this->dest_tile = CalcClosestStationTile(this->dest_station, v->tile, StationType::Dock); - this->dest_trackdirs = INVALID_TRACKDIR_BIT; } else { this->dest_station = StationID::Invalid(); - this->dest_tile = v->dest_tile; - this->dest_trackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_WATER, 0)); } } @@ -73,11 +69,8 @@ public: /** Called by YAPF to detect if node ends in the desired destination. */ inline bool PfDetectDestination(Node &n) { - return this->PfDetectDestinationTile(n.segment_last_tile, n.segment_last_td); - } + const TileIndex tile = n.segment_last_tile; - inline bool PfDetectDestinationTile(TileIndex tile, Trackdir trackdir) - { if (this->has_intermediate_dest) { /* GetWaterRegionInfo is much faster than GetWaterRegionPatchInfo so we try that first. */ if (GetWaterRegionInfo(tile) != this->intermediate_dest_region_patch) return false; @@ -86,23 +79,14 @@ public: if (this->dest_station != StationID::Invalid()) return IsDockingTile(tile) && IsShipDestinationTile(tile, this->dest_station); - return tile == this->dest_tile && ((this->dest_trackdirs & TrackdirToTrackdirBits(trackdir)) != TRACKDIR_BIT_NONE); + assert(this->dest_tiles.size() == 1); + return tile == this->dest_tiles.front(); } - /** - * Called by YAPF to calculate cost estimate. Calculates distance to the destination - * adds it to the actual cost from origin and stores the sum to the Node::estimate. - */ - inline bool PfCalcEstimate(Node &n) + static inline int CalcEstimate(Node &n, TileIndex destination_tile) { - const TileIndex destination_tile = this->has_intermediate_dest ? this->intermediate_dest_tile : this->dest_tile; - static const int dg_dir_to_x_offs[] = { -1, 0, 1, 0 }; static const int dg_dir_to_y_offs[] = { 0, 1, 0, -1 }; - if (this->PfDetectDestination(n)) { - n.estimate = n.cost; - return true; - } TileIndex tile = n.segment_last_tile; DiagDirection exitdir = TrackdirToExitdir(n.segment_last_td); @@ -115,8 +99,33 @@ public: int dmin = std::min(dx, dy); int dxy = abs(dx - dy); int d = dmin * YAPF_TILE_CORNER_LENGTH + (dxy - 1) * (YAPF_TILE_LENGTH / 2); - n.estimate = n.cost + d; - assert(n.estimate >= n.parent->estimate); + int estimate = n.cost + d; + assert(estimate >= n.parent->estimate); + return estimate; + } + + /** + * Called by YAPF to calculate cost estimate. Calculates distance to the destination + * adds it to the actual cost from origin and stores the sum to the Node::estimate. + */ + inline bool PfCalcEstimate(Node &n) + { + if (this->PfDetectDestination(n)) { + n.estimate = n.cost; + return true; + } + + int shortest_estimate = std::numeric_limits::max(); + if (this->has_intermediate_dest) { + shortest_estimate = this->CalcEstimate(n, this->intermediate_dest_tile); + } else { + for (const TileIndex &destination_tile : this->dest_tiles) { + int estimate = this->CalcEstimate(n, destination_tile); + if (estimate < shortest_estimate) shortest_estimate = estimate; + } + } + + n.estimate = shortest_estimate; return true; } }; @@ -211,10 +220,10 @@ public: return result; } - static Trackdir ChooseShipTrack(const Ship *v, TileIndex tile, TrackdirBits forward_dirs, TrackdirBits reverse_dirs, + static Trackdir ChooseShipTrack(const Ship *v, TileIndex tile, TrackdirBits forward_dirs, TrackdirBits reverse_dirs, const std::span dest_tiles, bool &path_found, ShipPathCache &path_cache, Trackdir &best_origin_dir) { - const std::vector high_level_path = YapfShipFindWaterRegionPath(v, tile, NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1); + const std::vector high_level_path = YapfShipFindWaterRegionPath(v, tile, NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1, dest_tiles); if (high_level_path.empty()) { path_found = false; /* Make the ship move around aimlessly. This prevents repeated pathfinder calls and clearly indicates that the ship is lost. */ @@ -229,7 +238,7 @@ public: /* Set origin and destination nodes */ pf.SetOrigin(v->tile, forward_dirs | reverse_dirs); - pf.SetDestination(v); + pf.SetDestination(v, dest_tiles); const bool is_intermediate_destination = static_cast(high_level_path.size()) >= NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1; if (is_intermediate_destination) pf.SetIntermediateDestination(high_level_path.back()); @@ -297,9 +306,10 @@ public: * Called when leaving depot. * @param v Ship. * @param trackdir [out] the best of all possible reversed trackdirs. + * @param dest_tiles list of destination tiles. * @return true if the reverse direction is better. */ - static bool CheckShipReverse(const Ship *v, Trackdir *trackdir) + static bool CheckShipReverse(const Ship *v, Trackdir *trackdir, const std::span dest_tiles) { bool path_found = false; ShipPathCache dummy_cache; @@ -310,13 +320,13 @@ public: const Trackdir reverse_dir = ReverseTrackdir(v->GetVehicleTrackdir()); const TrackdirBits forward_dirs = TrackdirToTrackdirBits(v->GetVehicleTrackdir()); const TrackdirBits reverse_dirs = TrackdirToTrackdirBits(reverse_dir); - (void)ChooseShipTrack(v, v->tile, forward_dirs, reverse_dirs, path_found, dummy_cache, best_origin_dir); + (void)ChooseShipTrack(v, v->tile, forward_dirs, reverse_dirs, dest_tiles, path_found, dummy_cache, best_origin_dir); return path_found && best_origin_dir == reverse_dir; } else { /* This gets called when a ship suddenly can't move forward, e.g. due to terraforming. */ const DiagDirection entry = ReverseDiagDir(VehicleExitDir(v->direction, v->state)); const TrackdirBits reverse_dirs = DiagdirReachesTrackdirs(entry) & TrackStatusToTrackdirBits(GetTileTrackStatus(v->tile, TRANSPORT_WATER, 0, entry)); - (void)ChooseShipTrack(v, v->tile, TRACKDIR_BIT_NONE, reverse_dirs, path_found, dummy_cache, best_origin_dir); + (void)ChooseShipTrack(v, v->tile, TRACKDIR_BIT_NONE, reverse_dirs, dest_tiles, path_found, dummy_cache, best_origin_dir); *trackdir = path_found && best_origin_dir != INVALID_TRACKDIR ? best_origin_dir : GetRandomTrackdir(reverse_dirs); return true; } @@ -427,13 +437,15 @@ struct CYapfShip : CYapfT dest_tiles = GetShipDestinationTiles(v); Trackdir best_origin_dir = INVALID_TRACKDIR; const TrackdirBits origin_dirs = TrackdirToTrackdirBits(v->GetVehicleTrackdir()); - const Trackdir td_ret = CYapfShip::ChooseShipTrack(v, tile, origin_dirs, TRACKDIR_BIT_NONE, path_found, path_cache, best_origin_dir); + const Trackdir td_ret = CYapfShip::ChooseShipTrack(v, tile, origin_dirs, TRACKDIR_BIT_NONE, dest_tiles, path_found, path_cache, best_origin_dir); return (td_ret != INVALID_TRACKDIR) ? TrackdirToTrack(td_ret) : INVALID_TRACK; } bool YapfShipCheckReverse(const Ship *v, Trackdir *trackdir) { - return CYapfShip::CheckShipReverse(v, trackdir); + std::vector dest_tiles = GetShipDestinationTiles(v); + return CYapfShip::CheckShipReverse(v, trackdir, dest_tiles); } diff --git a/src/pathfinder/yapf/yapf_ship_regions.cpp b/src/pathfinder/yapf/yapf_ship_regions.cpp index e27d063634..a8c0aa7a91 100644 --- a/src/pathfinder/yapf/yapf_ship_regions.cpp +++ b/src/pathfinder/yapf/yapf_ship_regions.cpp @@ -175,7 +175,7 @@ public: inline char TransportTypeChar() const { return '^'; } - static std::vector FindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length) + static std::vector FindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length, const std::span dest_tiles) { const WaterRegionPatchDesc start_water_region_patch = GetWaterRegionPatchInfo(start_tile); @@ -184,18 +184,7 @@ public: Tpf pf(std::min(static_cast(Map::Size() * NODES_PER_REGION) / WATER_REGION_NUMBER_OF_TILES, MAX_NUMBER_OF_NODES)); pf.SetDestination(start_water_region_patch); - if (v->current_order.IsType(OT_GOTO_STATION)) { - StationID station_id = v->current_order.GetDestination().ToStationID(); - const BaseStation *station = BaseStation::Get(station_id); - TileArea tile_area; - station->GetTileArea(&tile_area, StationType::Dock); - for (const auto &tile : tile_area) { - if (IsDockingTile(tile) && IsShipDestinationTile(tile, station_id)) { - pf.AddOrigin(GetWaterRegionPatchInfo(tile)); - } - } - } else { - TileIndex tile = v->dest_tile; + for (const TileIndex &tile : dest_tiles) { pf.AddOrigin(GetWaterRegionPatchInfo(tile)); } @@ -292,9 +281,10 @@ struct CYapfRegionWater : CYapfT YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length) +std::vector YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length, const std::span dest_tiles) { - return CYapfRegionWater::FindWaterRegionPath(v, start_tile, max_returned_path_length); + return CYapfRegionWater::FindWaterRegionPath(v, start_tile, max_returned_path_length, dest_tiles); } diff --git a/src/pathfinder/yapf/yapf_ship_regions.h b/src/pathfinder/yapf/yapf_ship_regions.h index 8b75773cc8..a5023e6dc8 100644 --- a/src/pathfinder/yapf/yapf_ship_regions.h +++ b/src/pathfinder/yapf/yapf_ship_regions.h @@ -16,6 +16,6 @@ struct Ship; -std::vector YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length); +std::vector YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length, const std::span dest_tiles); #endif /* YAPF_SHIP_REGIONS_H */