OpenTTD Source 20250829-master-g5c285f3e0c
yapf_ship_regions.cpp
Go to the documentation of this file.
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 */
7
10#include "../../stdafx.h"
11#include "../../ship.h"
12
13#include "yapf.hpp"
14#include "yapf_ship_regions.h"
15#include "../water_regions.h"
16
17#include "../../safeguards.h"
18
19constexpr int DIRECT_NEIGHBOUR_COST = 100;
20constexpr int NODES_PER_REGION = 4;
21constexpr int MAX_NUMBER_OF_NODES = 65536;
22
25 WaterRegionPatchDesc water_region_patch;
26
27 inline void Set(const WaterRegionPatchDesc &water_region_patch)
28 {
29 this->water_region_patch = water_region_patch;
30 }
31
32 inline int CalcHash() const { return CalculateWaterRegionPatchHash(this->water_region_patch); }
33 inline bool operator==(const CYapfRegionPatchNodeKey &other) const { return this->CalcHash() == other.CalcHash(); }
34};
35
36inline uint ManhattanDistance(const CYapfRegionPatchNodeKey &a, const CYapfRegionPatchNodeKey &b)
37{
38 return (std::abs(a.water_region_patch.x - b.water_region_patch.x) + std::abs(a.water_region_patch.y - b.water_region_patch.y)) * DIRECT_NEIGHBOUR_COST;
39}
40
42template <class Tkey_>
43struct CYapfRegionNodeT : CYapfNodeT<Tkey_, CYapfRegionNodeT<Tkey_>> {
44 typedef Tkey_ Key;
46
47 inline void Set(Node *parent, const WaterRegionPatchDesc &water_region_patch)
48 {
49 this->key.Set(water_region_patch);
50 this->hash_next = nullptr;
51 this->parent = parent;
52 this->cost = 0;
53 this->estimate = 0;
54 }
55
56 inline void Set(Node *parent, const Key &key)
57 {
58 this->Set(parent, key.water_region_patch);
59 }
60
61 DiagDirection GetDiagDirFromParent() const
62 {
63 if (this->parent == nullptr) return INVALID_DIAGDIR;
64 const int dx = this->key.water_region_patch.x - this->parent->key.water_region_patch.x;
65 const int dy = this->key.water_region_patch.y - this->parent->key.water_region_patch.y;
66 if (dx > 0 && dy == 0) return DIAGDIR_SW;
67 if (dx < 0 && dy == 0) return DIAGDIR_NE;
68 if (dx == 0 && dy > 0) return DIAGDIR_SE;
69 if (dx == 0 && dy < 0) return DIAGDIR_NW;
70 return INVALID_DIAGDIR;
71 }
72};
73
75template <class Types>
77public:
78 typedef typename Types::Tpf Tpf;
79 typedef typename Types::NodeList::Item Node;
80 typedef typename Node::Key Key;
81
82protected:
83 inline Tpf &Yapf() { return *static_cast<Tpf*>(this); }
84
85private:
86 std::vector<CYapfRegionPatchNodeKey> origin_keys;
87
88public:
89 void AddOrigin(const WaterRegionPatchDesc &water_region_patch)
90 {
91 if (water_region_patch.label == INVALID_WATER_REGION_PATCH) return;
92 if (!HasOrigin(water_region_patch)) this->origin_keys.emplace_back(water_region_patch);
93 }
94
95 bool HasOrigin(const WaterRegionPatchDesc &water_region_patch)
96 {
97 return std::ranges::find(this->origin_keys, CYapfRegionPatchNodeKey{ water_region_patch }) != this->origin_keys.end();
98 }
99
100 void PfSetStartupNodes()
101 {
102 for (const CYapfRegionPatchNodeKey &origin_key : this->origin_keys) {
103 Node &node = Yapf().CreateNewNode();
104 node.Set(nullptr, origin_key);
105 Yapf().AddStartupNode(node);
106 }
107 }
108};
109
111template <class Types>
113public:
114 typedef typename Types::Tpf Tpf;
115 typedef typename Types::NodeList::Item Node;
116 typedef typename Node::Key Key;
117
118protected:
119 Key dest;
120
121public:
122 void SetDestination(const WaterRegionPatchDesc &water_region_patch)
123 {
124 this->dest.Set(water_region_patch);
125 }
126
127protected:
128 Tpf &Yapf() { return *static_cast<Tpf*>(this); }
129
130public:
131 inline bool PfDetectDestination(Node &n) const
132 {
133 return n.key == this->dest;
134 }
135
136 inline bool PfCalcEstimate(Node &n)
137 {
138 if (this->PfDetectDestination(n)) {
139 n.estimate = n.cost;
140 return true;
141 }
142
143 n.estimate = n.cost + ManhattanDistance(n.key, this->dest);
144
145 return true;
146 }
147};
148
150template <class Types>
152public:
153 typedef typename Types::Tpf Tpf;
154 typedef typename Types::TrackFollower TrackFollower;
155 typedef typename Types::NodeList::Item Node;
156 typedef typename Node::Key Key;
157
158protected:
159 inline Tpf &Yapf() { return *static_cast<Tpf*>(this); }
160
161public:
162 inline void PfFollowNode(Node &old_node)
163 {
164 VisitWaterRegionPatchCallback visit_func = [&](const WaterRegionPatchDesc &water_region_patch)
165 {
166 Node &node = Yapf().CreateNewNode();
167 node.Set(&old_node, water_region_patch);
168 Yapf().AddNewNode(node, TrackFollower{});
169 };
170 VisitWaterRegionPatchNeighbours(old_node.key.water_region_patch, visit_func);
171 }
172
173 inline char TransportTypeChar() const { return '^'; }
174
175 static std::vector<WaterRegionPatchDesc> FindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length)
176 {
177 const WaterRegionPatchDesc start_water_region_patch = GetWaterRegionPatchInfo(start_tile);
178
179 /* We reserve 4 nodes (patches) per water region. The vast majority of water regions have 1 or 2 regions so this should be a pretty
180 * safe limit. We cap the limit at 65536 which is at a region size of 16x16 is equivalent to one node per region for a 4096x4096 map. */
181 Tpf pf(std::min(static_cast<int>(Map::Size() * NODES_PER_REGION) / WATER_REGION_NUMBER_OF_TILES, MAX_NUMBER_OF_NODES));
182 pf.SetDestination(start_water_region_patch);
183
184 if (v->current_order.IsType(OT_GOTO_STATION)) {
185 StationID station_id = v->current_order.GetDestination().ToStationID();
186 const BaseStation *station = BaseStation::Get(station_id);
187 for (const auto &tile : station->GetTileArea(StationType::Dock)) {
188 if (IsDockingTile(tile) && IsShipDestinationTile(tile, station_id)) {
189 pf.AddOrigin(GetWaterRegionPatchInfo(tile));
190 }
191 }
192 } else {
193 TileIndex tile = v->dest_tile;
194 pf.AddOrigin(GetWaterRegionPatchInfo(tile));
195 }
196
197 /* If origin and destination are the same we simply return that water patch. */
198 std::vector<WaterRegionPatchDesc> path = { start_water_region_patch };
199 path.reserve(max_returned_path_length);
200 if (pf.HasOrigin(start_water_region_patch)) return path;
201
202 /* Find best path. */
203 if (!pf.FindPath(v)) return {}; // Path not found.
204
205 Node *node = pf.GetBestNode();
206 for (int i = 0; i < max_returned_path_length - 1; ++i) {
207 if (node != nullptr) {
208 node = node->parent;
209 if (node != nullptr) path.push_back(node->key.water_region_patch);
210 }
211 }
212
213 assert(!path.empty());
214 return path;
215 }
216};
217
219template <class Types>
221public:
222 typedef typename Types::Tpf Tpf;
223 typedef typename Types::TrackFollower TrackFollower;
224 typedef typename Types::NodeList::Item Node;
225 typedef typename Node::Key Key;
226
227protected:
229 Tpf &Yapf() { return *static_cast<Tpf*>(this); }
230
231public:
237 inline bool PfCalcCost(Node &n, const TrackFollower *)
238 {
239 n.cost = n.parent->cost + ManhattanDistance(n.key, n.parent->key);
240
241 /* Incentivise zigzagging by adding a slight penalty when the search continues in the same direction. */
242 Node *grandparent = n.parent->parent;
243 if (grandparent != nullptr) {
244 const DiagDirDiff dir_diff = DiagDirDifference(n.parent->GetDiagDirFromParent(), n.GetDiagDirFromParent());
245 if (dir_diff != DIAGDIRDIFF_90LEFT && dir_diff != DIAGDIRDIFF_90RIGHT) n.cost += 1;
246 }
247
248 return true;
249 }
250};
251
252/* We don't need a follower but YAPF requires one. */
254
259template <class Tpf_, class Tnode_list>
275
277
278struct CYapfRegionWater : CYapfT<CYapfRegion_TypesT<CYapfRegionWater, CRegionNodeListWater>> {
279 explicit CYapfRegionWater(int max_nodes) { this->max_search_nodes = max_nodes; }
280};
281
289std::vector<WaterRegionPatchDesc> YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length)
290{
291 return CYapfRegionWater::FindWaterRegionPath(v, start_tile, max_returned_path_length);
292}
CYapfBaseT - A-star type path finder base class.
Definition yapf_base.hpp:49
Cost Provider of YAPF for water regions.
Types::Tpf Tpf
The pathfinder class (derived from THIS class).
Types::NodeList::Item Node
This will be our node type.
Node::Key Key
Key to hash tables.
bool PfCalcCost(Node &n, const TrackFollower *)
Called by YAPF to calculate the cost from the origin to the given node.
Tpf & Yapf()
To access inherited path finder.
YAPF destination provider for water regions.
Node::Key Key
Key to hash tables.
Types::Tpf Tpf
The pathfinder class (derived from THIS class).
Types::NodeList::Item Node
This will be our node type.
YAPF node following for water region pathfinding.
Types::NodeList::Item Node
This will be our node type.
Types::Tpf Tpf
The pathfinder class (derived from THIS class).
Node::Key Key
Key to hash tables.
YAPF origin for water regions.
Types::NodeList::Item Node
This will be our node type.
Node::Key Key
Key to hash tables.
Types::Tpf Tpf
The pathfinder class (derived from THIS class).
CYapfSegmentCostCacheNoneT - the formal only yapf cost cache provider that implements PfNodeCacheFetc...
YAPF template that uses Ttypes template argument to determine all YAPF components (base classes) from...
Hash table based node list multi-container class.
Definition nodelist.hpp:21
DiagDirDiff DiagDirDifference(DiagDirection d0, DiagDirection d1)
Calculate the difference between two DiagDirection values.
DiagDirDiff
Enumeration for the difference between to DiagDirection.
@ DIAGDIRDIFF_90RIGHT
90 degrees right
@ DIAGDIRDIFF_90LEFT
90 degrees left
DiagDirection
Enumeration for diagonal directions.
@ DIAGDIR_NE
Northeast, upper right on your monitor.
@ DIAGDIR_NW
Northwest.
@ DIAGDIR_SE
Southeast.
@ INVALID_DIAGDIR
Flag for an invalid DiagDirection.
@ DIAGDIR_SW
Southwest.
bool IsShipDestinationTile(TileIndex tile, StationID station)
Test if a tile is a docking tile for the given station.
Definition ship_cmd.cpp:634
StationType
Station types.
Base class for all station-ish types.
Track follower helper template class (can serve pathfinders and vehicle controllers).
Yapf Node base.
Definition yapf_node.hpp:61
Yapf Node for water regions.
Yapf Node Key that represents a single patch of interconnected water within a water region.
Config struct of YAPF for route planning.
CYapfRegion_TypesT< Tpf_, Tnode_list > Types
Shortcut for this struct type.
DummyFollower TrackFollower
Track follower helper class.
CYapfDestinationRegionT< Types > PfDestination
Destination/distance provider.
CYapfBaseT< Types > PfBase
Pathfinder components (modules).
CYapfSegmentCostCacheNoneT< Types > PfCache
Segment cost cache provider.
CYapfFollowRegionT< Types > PfFollow
Node follower.
CYapfOriginRegionT< Types > PfOrigin
Origin provider.
Tpf_ Tpf
Pathfinder type.
CYapfCostRegionT< Types > PfCost
Cost provider.
Node of the link graph.
Definition linkgraph.h:90
static debug_inline uint Size()
Get the size of the map.
Definition map_func.h:287
DestinationID GetDestination() const
Gets the destination of this order.
Definition order_base.h:99
bool IsType(OrderType type) const
Check whether this order is of the given type.
Definition order_base.h:66
static Titem * Get(auto index)
Returns Titem with given index.
All ships have this type.
Definition ship.h:32
Order current_order
The current order (+ status, like: loading)
TileIndex dest_tile
Heading for this tile.
Describes a single interconnected patch of water within a particular water region.
int y
The Y coordinate of the water region, i.e. Y=2 is the 3rd water region along the Y-axis.
int x
The X coordinate of the water region, i.e. X=2 is the 3rd water region along the X-axis.
WaterRegionPatchLabel label
Unique label identifying the patch within the region.
bool IsDockingTile(Tile t)
Checks whether the tile is marked as a dockling tile.
Definition water_map.h:371
WaterRegionPatchDesc GetWaterRegionPatchInfo(TileIndex tile)
Returns basic water region patch information for the provided tile.
int CalculateWaterRegionPatchHash(const WaterRegionPatchDesc &water_region_patch)
Calculates a number that uniquely identifies the provided water region patch.
void VisitWaterRegionPatchNeighbours(const WaterRegionPatchDesc &water_region_patch, VisitWaterRegionPatchCallback &callback)
Calls the provided callback function on all accessible water region patches in each cardinal directio...
Base includes/functions for YAPF.
std::vector< WaterRegionPatchDesc > YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length)
Finds a path at the water region level.
Implementation of YAPF for water regions, which are used for finding intermediate ship destinations.