Update: Translations from eints
[openttd-github.git] / src / pathfinder / yapf / yapf_ship.cpp
blob57d5e9d87742d76f15a81a96f95fbce10e4c82a0
1 /*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
8 /** @file yapf_ship.cpp Implementation of YAPF for ships. */
10 #include "../../stdafx.h"
11 #include "../../ship.h"
12 #include "../../industry.h"
13 #include "../../vehicle_func.h"
15 #include "yapf.hpp"
16 #include "yapf_node_ship.hpp"
17 #include "yapf_ship_regions.h"
18 #include "../water_regions.h"
20 #include "../../safeguards.h"
22 constexpr int NUMBER_OR_WATER_REGIONS_LOOKAHEAD = 4;
23 constexpr int MAX_SHIP_PF_NODES = (NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1) * WATER_REGION_NUMBER_OF_TILES * 4; // 4 possible exit dirs per tile.
25 constexpr int SHIP_LOST_PATH_LENGTH = 8; // The length of the (aimless) path assigned when a ship is lost.
27 template <class Types>
28 class CYapfDestinationTileWaterT
30 public:
31 typedef typename Types::Tpf Tpf; ///< the pathfinder class (derived from THIS class).
32 typedef typename Types::TrackFollower TrackFollower;
33 typedef typename Types::NodeList::Titem Node; ///< this will be our node type.
34 typedef typename Node::Key Key; ///< key to hash tables.
36 protected:
37 TileIndex m_destTile;
38 TrackdirBits m_destTrackdirs;
39 StationID m_destStation;
41 bool m_has_intermediate_dest = false;
42 TileIndex m_intermediate_dest_tile;
43 WaterRegionPatchDesc m_intermediate_dest_region_patch;
45 public:
46 void SetDestination(const Ship *v)
48 if (v->current_order.IsType(OT_GOTO_STATION)) {
49 m_destStation = v->current_order.GetDestination();
50 m_destTile = CalcClosestStationTile(m_destStation, v->tile, STATION_DOCK);
51 m_destTrackdirs = INVALID_TRACKDIR_BIT;
52 } else {
53 m_destStation = INVALID_STATION;
54 m_destTile = v->dest_tile;
55 m_destTrackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_WATER, 0));
59 void SetIntermediateDestination(const WaterRegionPatchDesc &water_region_patch)
61 m_has_intermediate_dest = true;
62 m_intermediate_dest_tile = GetWaterRegionCenterTile(water_region_patch);
63 m_intermediate_dest_region_patch = water_region_patch;
66 protected:
67 /** To access inherited path finder. */
68 inline Tpf& Yapf()
70 return *static_cast<Tpf*>(this);
73 public:
74 /** Called by YAPF to detect if node ends in the desired destination. */
75 inline bool PfDetectDestination(Node &n)
77 return PfDetectDestinationTile(n.m_segment_last_tile, n.m_segment_last_td);
80 inline bool PfDetectDestinationTile(TileIndex tile, Trackdir trackdir)
82 if (m_has_intermediate_dest) {
83 /* GetWaterRegionInfo is much faster than GetWaterRegionPatchInfo so we try that first. */
84 if (GetWaterRegionInfo(tile) != m_intermediate_dest_region_patch) return false;
85 return GetWaterRegionPatchInfo(tile) == m_intermediate_dest_region_patch;
88 if (m_destStation != INVALID_STATION) return IsDockingTile(tile) && IsShipDestinationTile(tile, m_destStation);
90 return tile == m_destTile && ((m_destTrackdirs & TrackdirToTrackdirBits(trackdir)) != TRACKDIR_BIT_NONE);
93 /**
94 * Called by YAPF to calculate cost estimate. Calculates distance to the destination
95 * adds it to the actual cost from origin and stores the sum to the Node::m_estimate.
97 inline bool PfCalcEstimate(Node &n)
99 const TileIndex destination_tile = m_has_intermediate_dest ? m_intermediate_dest_tile : m_destTile;
101 static const int dg_dir_to_x_offs[] = { -1, 0, 1, 0 };
102 static const int dg_dir_to_y_offs[] = { 0, 1, 0, -1 };
103 if (PfDetectDestination(n)) {
104 n.m_estimate = n.m_cost;
105 return true;
108 TileIndex tile = n.m_segment_last_tile;
109 DiagDirection exitdir = TrackdirToExitdir(n.m_segment_last_td);
110 int x1 = 2 * TileX(tile) + dg_dir_to_x_offs[(int)exitdir];
111 int y1 = 2 * TileY(tile) + dg_dir_to_y_offs[(int)exitdir];
112 int x2 = 2 * TileX(destination_tile);
113 int y2 = 2 * TileY(destination_tile);
114 int dx = abs(x1 - x2);
115 int dy = abs(y1 - y2);
116 int dmin = std::min(dx, dy);
117 int dxy = abs(dx - dy);
118 int d = dmin * YAPF_TILE_CORNER_LENGTH + (dxy - 1) * (YAPF_TILE_LENGTH / 2);
119 n.m_estimate = n.m_cost + d;
120 assert(n.m_estimate >= n.m_parent->m_estimate);
121 return true;
125 /** Node Follower module of YAPF for ships */
126 template <class Types>
127 class CYapfFollowShipT
129 public:
130 typedef typename Types::Tpf Tpf; ///< the pathfinder class (derived from THIS class).
131 typedef typename Types::TrackFollower TrackFollower;
132 typedef typename Types::NodeList::Titem Node; ///< this will be our node type.
133 typedef typename Node::Key Key; ///< key to hash tables.
135 protected:
136 /** to access inherited path finder */
137 inline Tpf &Yapf()
139 return *static_cast<Tpf*>(this);
142 std::vector<WaterRegionDesc> m_water_region_corridor;
144 public:
146 * Called by YAPF to move from the given node to the next tile. For each
147 * reachable trackdir on the new tile creates new node, initializes it
148 * and adds it to the open list by calling Yapf().AddNewNode(n)
150 inline void PfFollowNode(Node &old_node)
152 TrackFollower F(Yapf().GetVehicle());
153 if (F.Follow(old_node.m_key.m_tile, old_node.m_key.m_td)) {
154 if (m_water_region_corridor.empty()
155 || std::find(m_water_region_corridor.begin(), m_water_region_corridor.end(),
156 GetWaterRegionInfo(F.m_new_tile)) != m_water_region_corridor.end()) {
157 Yapf().AddMultipleNodes(&old_node, F);
162 /** Restricts the search by creating corridor or water regions through which the ship is allowed to travel. */
163 inline void RestrictSearch(const std::vector<WaterRegionPatchDesc> &path)
165 m_water_region_corridor.clear();
166 for (const WaterRegionPatchDesc &path_entry : path) m_water_region_corridor.push_back(path_entry);
169 /** Return debug report character to identify the transportation type. */
170 inline char TransportTypeChar() const
172 return 'w';
175 /** Returns a random trackdir out of a set of trackdirs. */
176 static Trackdir GetRandomTrackdir(TrackdirBits trackdirs)
178 const int strip_amount = RandomRange(CountBits(trackdirs));
179 for (int s = 0; s < strip_amount; ++s) RemoveFirstTrackdir(&trackdirs);
180 return FindFirstTrackdir(trackdirs);
183 /** Returns a random tile/trackdir that can be reached from the current tile/trackdir, or tile/INVALID_TRACK if none is available. */
184 static std::pair<TileIndex, Trackdir> GetRandomFollowUpTileTrackdir(const Ship *v, TileIndex tile, Trackdir dir)
186 TrackFollower follower(v);
187 if (follower.Follow(tile, dir)) {
188 TrackdirBits dirs = follower.m_new_td_bits;
189 const TrackdirBits dirs_without_90_degree = dirs & ~TrackdirCrossesTrackdirs(dir);
190 if (dirs_without_90_degree != TRACKDIR_BIT_NONE) dirs = dirs_without_90_degree;
191 return { follower.m_new_tile, GetRandomTrackdir(dirs) };
193 return { follower.m_new_tile, INVALID_TRACKDIR };
196 /** Creates a random path, avoids 90 degree turns. */
197 static Trackdir CreateRandomPath(const Ship *v, ShipPathCache &path_cache, int path_length)
199 std::pair<TileIndex, Trackdir> tile_dir = { v->tile, v->GetVehicleTrackdir()};
200 for (int i = 0; i < path_length; ++i) {
201 tile_dir = GetRandomFollowUpTileTrackdir(v, tile_dir.first, tile_dir.second);
202 if (tile_dir.second == INVALID_TRACKDIR) break;
203 path_cache.push_back(tile_dir.second);
206 if (path_cache.empty()) return INVALID_TRACKDIR;
208 const Trackdir result = path_cache.front();
209 path_cache.pop_front();
210 return result;
213 static Trackdir ChooseShipTrack(const Ship *v, TileIndex tile, TrackdirBits forward_dirs, TrackdirBits reverse_dirs,
214 bool &path_found, ShipPathCache &path_cache, Trackdir &best_origin_dir)
216 const std::vector<WaterRegionPatchDesc> high_level_path = YapfShipFindWaterRegionPath(v, tile, NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1);
217 if (high_level_path.empty()) {
218 path_found = false;
219 /* Make the ship move around aimlessly. This prevents repeated pathfinder calls and clearly indicates that the ship is lost. */
220 return CreateRandomPath(v, path_cache, SHIP_LOST_PATH_LENGTH);
223 /* Try one time without restricting the search area, which generally results in better and more natural looking paths.
224 * However the pathfinder can hit the node limit in certain situations such as long aqueducts or maze-like terrain.
225 * If that happens we run the pathfinder again, but restricted only to the regions provided by the region pathfinder. */
226 for (int attempt = 0; attempt < 2; ++attempt) {
227 Tpf pf(MAX_SHIP_PF_NODES);
229 /* Set origin and destination nodes */
230 pf.SetOrigin(v->tile, forward_dirs | reverse_dirs);
231 pf.SetDestination(v);
232 const bool is_intermediate_destination = static_cast<int>(high_level_path.size()) >= NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1;
233 if (is_intermediate_destination) pf.SetIntermediateDestination(high_level_path.back());
235 /* Restrict the search area to prevent the low level pathfinder from expanding too many nodes. This can happen
236 * when the terrain is very "maze-like" or when the high level path "teleports" via a very long aqueduct. */
237 if (attempt > 0) pf.RestrictSearch(high_level_path);
239 /* Find best path. */
240 path_found = pf.FindPath(v);
241 Node *node = pf.GetBestNode();
242 if (attempt == 0 && !path_found) continue; // Try again with restricted search area.
244 /* Make the ship move around aimlessly. This prevents repeated pathfinder calls and clearly indicates that the ship is lost. */
245 if (!path_found) return CreateRandomPath(v, path_cache, SHIP_LOST_PATH_LENGTH);
247 /* Return only the path within the current water region if an intermediate destination was returned. If not, cache the entire path
248 * to the final destination tile. The low-level pathfinder might actually prefer a different docking tile in a nearby region. Without
249 * caching the full path the ship can get stuck in a loop. */
250 const WaterRegionPatchDesc end_water_patch = GetWaterRegionPatchInfo(node->GetTile());
251 assert(GetWaterRegionPatchInfo(tile) == high_level_path.front());
252 const WaterRegionPatchDesc start_water_patch = high_level_path.front();
253 while (node->m_parent) {
254 const WaterRegionPatchDesc node_water_patch = GetWaterRegionPatchInfo(node->GetTile());
256 const bool node_water_patch_on_high_level_path = std::find(high_level_path.begin(), high_level_path.end(), node_water_patch) != high_level_path.end();
257 const bool add_full_path = !is_intermediate_destination && node_water_patch != end_water_patch;
259 /* The cached path must always lead to a region patch that's on the high level path.
260 * This is what can happen when that's not the case https://github.com/OpenTTD/OpenTTD/issues/12176. */
261 if (add_full_path || !node_water_patch_on_high_level_path || node_water_patch == start_water_patch) {
262 path_cache.push_front(node->GetTrackdir());
263 } else {
264 path_cache.clear();
266 node = node->m_parent;
268 assert(node->GetTile() == v->tile);
270 /* Return INVALID_TRACKDIR to trigger a ship reversal if that is the best option. */
271 best_origin_dir = node->GetTrackdir();
272 if ((TrackdirToTrackdirBits(best_origin_dir) & forward_dirs) == TRACKDIR_BIT_NONE) {
273 path_cache.clear();
274 return INVALID_TRACKDIR;
277 /* A empty path means we are already at the destination. The pathfinder shouldn't have been called at all.
278 * Return a random reachable trackdir to hopefully nudge the ship out of this strange situation. */
279 if (path_cache.empty()) return CreateRandomPath(v, path_cache, 1);
281 /* Take out the last trackdir as the result. */
282 const Trackdir result = path_cache.front();
283 path_cache.pop_front();
285 /* Clear path cache when in final water region patch. This is to allow ships to spread over different docking tiles dynamically. */
286 if (start_water_patch == end_water_patch) path_cache.clear();
288 return result;
291 return INVALID_TRACKDIR;
295 * Check whether a ship should reverse to reach its destination.
296 * Called when leaving depot.
297 * @param v Ship.
298 * @param trackdir [out] the best of all possible reversed trackdirs.
299 * @return true if the reverse direction is better.
301 static bool CheckShipReverse(const Ship *v, Trackdir *trackdir)
303 bool path_found = false;
304 ShipPathCache dummy_cache;
305 Trackdir best_origin_dir = INVALID_TRACKDIR;
307 if (trackdir == nullptr) {
308 /* The normal case, typically called when ships leave a dock. */
309 const Trackdir reverse_dir = ReverseTrackdir(v->GetVehicleTrackdir());
310 const TrackdirBits forward_dirs = TrackdirToTrackdirBits(v->GetVehicleTrackdir());
311 const TrackdirBits reverse_dirs = TrackdirToTrackdirBits(reverse_dir);
312 (void)ChooseShipTrack(v, v->tile, forward_dirs, reverse_dirs, path_found, dummy_cache, best_origin_dir);
313 return path_found && best_origin_dir == reverse_dir;
314 } else {
315 /* This gets called when a ship suddenly can't move forward, e.g. due to terraforming. */
316 const DiagDirection entry = ReverseDiagDir(VehicleExitDir(v->direction, v->state));
317 const TrackdirBits reverse_dirs = DiagdirReachesTrackdirs(entry) & TrackStatusToTrackdirBits(GetTileTrackStatus(v->tile, TRANSPORT_WATER, 0, entry));
318 (void)ChooseShipTrack(v, v->tile, TRACKDIR_BIT_NONE, reverse_dirs, path_found, dummy_cache, best_origin_dir);
319 *trackdir = path_found && best_origin_dir != INVALID_TRACKDIR ? best_origin_dir : GetRandomTrackdir(reverse_dirs);
320 return true;
325 /** Cost Provider module of YAPF for ships. */
326 template <class Types>
327 class CYapfCostShipT
329 public:
330 typedef typename Types::Tpf Tpf; ///< the pathfinder class (derived from THIS class).
331 typedef typename Types::TrackFollower TrackFollower;
332 typedef typename Types::NodeList::Titem Node; ///< this will be our node type.
333 typedef typename Node::Key Key; ///< key to hash tables.
335 /** to access inherited path finder */
336 Tpf &Yapf()
338 return *static_cast<Tpf*>(this);
341 public:
342 inline int CurveCost(Trackdir td1, Trackdir td2)
344 assert(IsValidTrackdir(td1));
345 assert(IsValidTrackdir(td2));
347 if (HasTrackdir(TrackdirCrossesTrackdirs(td1), td2)) {
348 /* 90-deg curve penalty. */
349 return Yapf().PfGetSettings().ship_curve90_penalty;
350 } else if (td2 != NextTrackdir(td1)) {
351 /* 45-deg curve penalty. */
352 return Yapf().PfGetSettings().ship_curve45_penalty;
354 return 0;
357 static Vehicle *CountShipProc(Vehicle *v, void *data)
359 uint *count = (uint*)data;
360 /* Ignore other vehicles (aircraft) and ships inside depot. */
361 if (v->type == VEH_SHIP && (v->vehstatus & VS_HIDDEN) == 0) (*count)++;
363 return nullptr;
367 * Called by YAPF to calculate the cost from the origin to the given node.
368 * Calculates only the cost of given node, adds it to the parent node cost
369 * and stores the result into Node::m_cost member.
371 inline bool PfCalcCost(Node &n, const TrackFollower *tf)
373 /* Base tile cost depending on distance. */
374 int c = IsDiagonalTrackdir(n.GetTrackdir()) ? YAPF_TILE_LENGTH : YAPF_TILE_CORNER_LENGTH;
375 /* Additional penalty for curves. */
376 c += CurveCost(n.m_parent->GetTrackdir(), n.GetTrackdir());
378 if (IsDockingTile(n.GetTile())) {
379 /* Check docking tile for occupancy. */
380 uint count = 0;
381 HasVehicleOnPos(n.GetTile(), &count, &CountShipProc);
382 c += count * 3 * YAPF_TILE_LENGTH;
385 /* Skipped tile cost for aqueducts. */
386 c += YAPF_TILE_LENGTH * tf->m_tiles_skipped;
388 /* Ocean/canal speed penalty. */
389 const ShipVehicleInfo *svi = ShipVehInfo(Yapf().GetVehicle()->engine_type);
390 uint8_t speed_frac = (GetEffectiveWaterClass(n.GetTile()) == WATER_CLASS_SEA) ? svi->ocean_speed_frac : svi->canal_speed_frac;
391 if (speed_frac > 0) c += YAPF_TILE_LENGTH * (1 + tf->m_tiles_skipped) * speed_frac / (256 - speed_frac);
393 /* Apply it. */
394 n.m_cost = n.m_parent->m_cost + c;
395 return true;
400 * Config struct of YAPF for ships.
401 * Defines all 6 base YAPF modules as classes providing services for CYapfBaseT.
403 template <class Tpf_, class Ttrack_follower, class Tnode_list>
404 struct CYapfShip_TypesT
406 typedef CYapfShip_TypesT<Tpf_, Ttrack_follower, Tnode_list> Types; ///< Shortcut for this struct type.
407 typedef Tpf_ Tpf; ///< Pathfinder type.
408 typedef Ttrack_follower TrackFollower; ///< Track follower helper class.
409 typedef Tnode_list NodeList;
410 typedef Ship VehicleType;
412 /** Pathfinder components (modules). */
413 typedef CYapfBaseT<Types> PfBase; ///< Base pathfinder class.
414 typedef CYapfFollowShipT<Types> PfFollow; ///< Node follower.
415 typedef CYapfOriginTileT<Types> PfOrigin; ///< Origin provider.
416 typedef CYapfDestinationTileWaterT<Types> PfDestination; ///< Destination/distance provider.
417 typedef CYapfSegmentCostCacheNoneT<Types> PfCache; ///< Segment cost cache provider.
418 typedef CYapfCostShipT<Types> PfCost; ///< Cost provider.
421 struct CYapfShip : CYapfT<CYapfShip_TypesT<CYapfShip, CFollowTrackWater, CShipNodeListExitDir > >
423 explicit CYapfShip(int max_nodes) { m_max_search_nodes = max_nodes; }
426 /** Ship controller helper - path finder invoker. */
427 Track YapfShipChooseTrack(const Ship *v, TileIndex tile, bool &path_found, ShipPathCache &path_cache)
429 Trackdir best_origin_dir = INVALID_TRACKDIR;
430 const TrackdirBits origin_dirs = TrackdirToTrackdirBits(v->GetVehicleTrackdir());
431 const Trackdir td_ret = CYapfShip::ChooseShipTrack(v, tile, origin_dirs, TRACKDIR_BIT_NONE, path_found, path_cache, best_origin_dir);
432 return (td_ret != INVALID_TRACKDIR) ? TrackdirToTrack(td_ret) : INVALID_TRACK;
435 bool YapfShipCheckReverse(const Ship *v, Trackdir *trackdir)
437 return CYapfShip::CheckShipReverse(v, trackdir);