OpenTTD Source: src/pathfinder/yapf/yapf_ship_regions.cpp Source File

OpenTTD Source 14.0-beta3

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
19 constexpr int DIRECT_NEIGHBOR_COST = 100;
20 constexpr int NODES_PER_REGION = 4;
21 constexpr int MAX_NUMBER_OF_NODES = 65536;
22
24 struct CYapfRegionPatchNodeKey {
25 WaterRegionPatchDesc m_water_region_patch;
26
27 inline void Set(const WaterRegionPatchDesc &water_region_patch)
28 {
29 m_water_region_patch = water_region_patch;
30 }
31
32 inline int CalcHash() const { return CalculateWaterRegionPatchHash(m_water_region_patch); }
33 inline bool operator==(const CYapfRegionPatchNodeKey &other) const { return CalcHash() == other.CalcHash(); }
34 };
35
36 inline uint ManhattanDistance(const CYapfRegionPatchNodeKey &a, const CYapfRegionPatchNodeKey &b)
37 {
38 return (std::abs(a.m_water_region_patch.x - b.m_water_region_patch.x) + std::abs(a.m_water_region_patch.y - b.m_water_region_patch.y)) * DIRECT_NEIGHBOR_COST;
39 }
40
42 template <class Tkey_>
43 struct CYapfRegionNodeT {
44 typedef Tkey_ Key;
45 typedef CYapfRegionNodeT<Tkey_> Node;
46
47 Tkey_ m_key;
48 Node *m_hash_next;
49 Node *m_parent;
50 int m_cost;
51 int m_estimate;
52
53 inline void Set(Node *parent, const WaterRegionPatchDesc &water_region_patch)
54 {
55 m_key.Set(water_region_patch);
56 m_hash_next = nullptr;
57 m_parent = parent;
58 m_cost = 0;
59 m_estimate = 0;
60 }
61
62 inline void Set(Node *parent, const Key &key)
63 {
64 Set(parent, key.m_water_region_patch);
65 }
66
67 DiagDirection GetDiagDirFromParent() const
68 {
69 if (!m_parent) return INVALID_DIAGDIR;
70 const int dx = m_key.m_water_region_patch.x - m_parent->m_key.m_water_region_patch.x;
71 const int dy = m_key.m_water_region_patch.y - m_parent->m_key.m_water_region_patch.y;
72 if (dx > 0 && dy == 0) return DIAGDIR_SW;
73 if (dx < 0 && dy == 0) return DIAGDIR_NE;
74 if (dx == 0 && dy > 0) return DIAGDIR_SE;
75 if (dx == 0 && dy < 0) return DIAGDIR_NW;
76 return INVALID_DIAGDIR;
77 }
78
79 inline Node *GetHashNext() { return m_hash_next; }
80 inline void SetHashNext(Node *pNext) { m_hash_next = pNext; }
81 inline const Tkey_ &GetKey() const { return m_key; }
82 inline int GetCost() { return m_cost; }
83 inline int GetCostEstimate() { return m_estimate; }
84 inline bool operator<(const Node &other) const { return m_estimate < other.m_estimate; }
85 };
86
88 template <class Types>
89 class CYapfOriginRegionT
90 {
91 public:
92 typedef typename Types::Tpf Tpf;
93 typedef typename Types::NodeList::Titem Node;
94 typedef typename Node::Key Key;
95
96 protected:
97 inline Tpf &Yapf() { return *static_cast<Tpf*>(this); }
98
99 private:
100 std::vector<CYapfRegionPatchNodeKey> m_origin_keys;
101
102 public:
103 void AddOrigin(const WaterRegionPatchDesc &water_region_patch)
104 {
105 if (!HasOrigin(water_region_patch)) m_origin_keys.push_back(CYapfRegionPatchNodeKey{ water_region_patch });
106 }
107
108 bool HasOrigin(const WaterRegionPatchDesc &water_region_patch)
109 {
110 return std::find(m_origin_keys.begin(), m_origin_keys.end(), CYapfRegionPatchNodeKey{ water_region_patch }) != m_origin_keys.end();
111 }
112
113 void PfSetStartupNodes()
114 {
115 for (const CYapfRegionPatchNodeKey &origin_key : m_origin_keys) {
116 Node &node = Yapf().CreateNewNode();
117 node.Set(nullptr, origin_key);
118 Yapf().AddStartupNode(node);
119 }
120 }
121 };
122
124 template <class Types>
125 class CYapfDestinationRegionT
126 {
127 public:
128 typedef typename Types::Tpf Tpf;
129 typedef typename Types::NodeList::Titem Node;
130 typedef typename Node::Key Key;
131
132 protected:
133 Key m_dest;
134
135 public:
136 void SetDestination(const WaterRegionPatchDesc &water_region_patch)
137 {
138 m_dest.Set(water_region_patch);
139 }
140
141 protected:
142 Tpf &Yapf() { return *static_cast<Tpf*>(this); }
143
144 public:
145 inline bool PfDetectDestination(Node &n) const
146 {
147 return n.m_key == m_dest;
148 }
149
150 inline bool PfCalcEstimate(Node &n)
151 {
152 if (PfDetectDestination(n)) {
153 n.m_estimate = n.m_cost;
154 return true;
155 }
156
157 n.m_estimate = n.m_cost + ManhattanDistance(n.m_key, m_dest);
158
159 return true;
160 }
161 };
162
164 template <class Types>
165 class CYapfFollowRegionT
166 {
167 public:
168 typedef typename Types::Tpf Tpf;
169 typedef typename Types::TrackFollower TrackFollower;
170 typedef typename Types::NodeList::Titem Node;
171 typedef typename Node::Key Key;
172
173 protected:
174 inline Tpf &Yapf() { return *static_cast<Tpf*>(this); }
175
176 public:
177 inline void PfFollowNode(Node &old_node)
178 {
179 TVisitWaterRegionPatchCallBack visitFunc = [&](const WaterRegionPatchDesc &water_region_patch)
180 {
181 Node &node = Yapf().CreateNewNode();
182 node.Set(&old_node, water_region_patch);
183 Yapf().AddNewNode(node, TrackFollower{});
184 };
185 VisitWaterRegionPatchNeighbors(old_node.m_key.m_water_region_patch, visitFunc);
186 }
187
188 inline char TransportTypeChar() const { return '^'; }
189
190 static std::vector<WaterRegionPatchDesc> FindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length)
191 {
192 const WaterRegionPatchDesc start_water_region_patch = GetWaterRegionPatchInfo(start_tile);
193
194 /* 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
195 * 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. */
196 Tpf pf(std::min(static_cast<int>(Map::Size() * NODES_PER_REGION) / WATER_REGION_NUMBER_OF_TILES, MAX_NUMBER_OF_NODES));
197 pf.SetDestination(start_water_region_patch);
198
199 if (v->current_order.IsType(OT_GOTO_STATION)) {
200 DestinationID station_id = v->current_order.GetDestination();
201 const BaseStation *station = BaseStation::Get(station_id);
202 TileArea tile_area;
203 station->GetTileArea(&tile_area, STATION_DOCK);
204 for (const auto &tile : tile_area) {
205 if (IsDockingTile(tile) && IsShipDestinationTile(tile, station_id)) {
206 pf.AddOrigin(GetWaterRegionPatchInfo(tile));
207 }
208 }
209 } else {
210 TileIndex tile = v->dest_tile;
211 pf.AddOrigin(GetWaterRegionPatchInfo(tile));
212 }
213
214 /* If origin and destination are the same we simply return that water patch. */
215 std::vector<WaterRegionPatchDesc> path = { start_water_region_patch };
216 path.reserve(max_returned_path_length);
217 if (pf.HasOrigin(start_water_region_patch)) return path;
218
219 /* Find best path. */
220 if (!pf.FindPath(v)) return {}; // Path not found.
221
222 Node *node = pf.GetBestNode();
223 for (int i = 0; i < max_returned_path_length - 1; ++i) {
224 if (node != nullptr) {
225 node = node->m_parent;
226 if (node != nullptr) path.push_back(node->m_key.m_water_region_patch);
227 }
228 }
229
230 assert(!path.empty());
231 return path;
232 }
233 };
234
236 template <class Types>
237 class CYapfCostRegionT
238 {
239 public:
240 typedef typename Types::Tpf Tpf;
241 typedef typename Types::TrackFollower TrackFollower;
242 typedef typename Types::NodeList::Titem Node;
243 typedef typename Node::Key Key;
244
245 protected:
247 Tpf &Yapf() { return *static_cast<Tpf*>(this); }
248
249 public:
255 inline bool PfCalcCost(Node &n, const TrackFollower *)
256 {
257 n.m_cost = n.m_parent->m_cost + ManhattanDistance(n.m_key, n.m_parent->m_key);
258
259 /* Incentivise zigzagging by adding a slight penalty when the search continues in the same direction. */
260 Node *grandparent = n.m_parent->m_parent;
261 if (grandparent != nullptr) {
262 const DiagDirDiff dir_diff = DiagDirDifference(n.m_parent->GetDiagDirFromParent(), n.GetDiagDirFromParent());
263 if (dir_diff != DIAGDIRDIFF_90LEFT && dir_diff != DIAGDIRDIFF_90RIGHT) n.m_cost += 1;
264 }
265
266 return true;
267 }
268 };
269
270 /* We don't need a follower but YAPF requires one. */
271 struct DummyFollower : public CFollowTrackWater {};
272
277 template <class Tpf_, class Tnode_list>
278 struct CYapfRegion_TypesT
279 {
280 typedef CYapfRegion_TypesT<Tpf_, Tnode_list> Types;
281 typedef Tpf_ Tpf;
282 typedef DummyFollower TrackFollower;
283 typedef Tnode_list NodeList;
284 typedef Ship VehicleType;
285
287 typedef CYapfBaseT<Types> PfBase;
288 typedef CYapfFollowRegionT<Types> PfFollow;
289 typedef CYapfOriginRegionT<Types> PfOrigin;
290 typedef CYapfDestinationRegionT<Types> PfDestination;
291 typedef CYapfSegmentCostCacheNoneT<Types> PfCache;
292 typedef CYapfCostRegionT<Types> PfCost;
293 };
294
295 typedef CNodeList_HashTableT<CYapfRegionNodeT<CYapfRegionPatchNodeKey>, 12, 12> CRegionNodeListWater;
296
297 struct CYapfRegionWater : CYapfT<CYapfRegion_TypesT<CYapfRegionWater, CRegionNodeListWater>>
298 {
299 explicit CYapfRegionWater(int max_nodes) { m_max_search_nodes = max_nodes; }
300 };
301
309 std::vector<WaterRegionPatchDesc> YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length)
310 {
311 return CYapfRegionWater::FindWaterRegionPath(v, start_tile, max_returned_path_length);
312 }
CYapfRegion_TypesT::PfFollow
CYapfFollowRegionT< Types > PfFollow
Node follower.
Definition: yapf_ship_regions.cpp:288
DIAGDIR_SE
@ DIAGDIR_SE
Southeast.
Definition: direction_type.h:76
Order::IsType
bool IsType(OrderType type) const
Check whether this order is of the given type.
Definition: order_base.h:71
Pool::PoolItem<&_station_pool >::Get
static Titem * Get(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:335
BaseStation::GetTileArea
virtual void GetTileArea(TileArea *ta, StationType type) const =0
Get the tile area for a given station type.
WaterRegionPatchDesc
Describes a single interconnected patch of water within a particular water region.
Definition: water_regions.h:25
CYapfCostRegionT
Cost Provider of YAPF for water regions.
Definition: yapf_ship_regions.cpp:237
Order::GetDestination
DestinationID GetDestination() const
Gets the destination of this order.
Definition: order_base.h:104
CYapfRegion_TypesT
Config struct of YAPF for route planning.
Definition: yapf_ship_regions.cpp:278
DiagDirDiff
DiagDirDiff
Enumeration for the difference between to DiagDirection.
Definition: direction_type.h:95
CYapfRegion_TypesT::PfCache
CYapfSegmentCostCacheNoneT< Types > PfCache
Segment cost cache provider.
Definition: yapf_ship_regions.cpp:291
CYapfFollowRegionT::Tpf
Types::Tpf Tpf
The pathfinder class (derived from THIS class).
Definition: yapf_ship_regions.cpp:168
yapf.hpp
CYapfRegionNodeT
Yapf Node for water regions.
Definition: yapf_ship_regions.cpp:43
CYapfOriginRegionT::Key
Node::Key Key
Key to hash tables.
Definition: yapf_ship_regions.cpp:94
CYapfRegion_TypesT::PfDestination
CYapfDestinationRegionT< Types > PfDestination
Destination/distance provider.
Definition: yapf_ship_regions.cpp:290
DIAGDIRDIFF_90LEFT
@ DIAGDIRDIFF_90LEFT
90 degrees left
Definition: direction_type.h:100
CYapfCostRegionT::Key
Node::Key Key
Key to hash tables.
Definition: yapf_ship_regions.cpp:243
WaterRegionPatchDesc::y
int y
The Y coordinate of the water region, i.e. Y=2 is the 3rd water region along the Y-axis.
Definition: water_regions.h:28
CYapfRegion_TypesT::PfCost
CYapfCostRegionT< Types > PfCost
Cost provider.
Definition: yapf_ship_regions.cpp:292
CYapfFollowRegionT
YAPF node following for water region pathfinding.
Definition: yapf_ship_regions.cpp:165
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > >
DIAGDIR_NW
@ DIAGDIR_NW
Northwest.
Definition: direction_type.h:78
CYapfT
YAPF template that uses Ttypes template argument to determine all YAPF components (base classes) from...
Definition: yapf_common.hpp:183
CYapfDestinationRegionT::Node
Types::NodeList::Titem Node
This will be our node type.
Definition: yapf_ship_regions.cpp:129
CYapfCostRegionT::Yapf
Tpf & Yapf()
To access inherited path finder.
Definition: yapf_ship_regions.cpp:247
CYapfRegion_TypesT::Types
CYapfRegion_TypesT< Tpf_, Tnode_list > Types
Shortcut for this struct type.
Definition: yapf_ship_regions.cpp:280
CYapfCostRegionT::Node
Types::NodeList::Titem Node
This will be our node type.
Definition: yapf_ship_regions.cpp:242
DIAGDIR_SW
@ DIAGDIR_SW
Southwest.
Definition: direction_type.h:77
GetWaterRegionPatchInfo
WaterRegionPatchDesc GetWaterRegionPatchInfo(TileIndex tile)
Returns basic water region patch information for the provided tile.
Definition: water_regions.cpp:290
Vehicle::dest_tile
TileIndex dest_tile
Heading for this tile.
Definition: vehicle_base.h:267
CYapfRegionWater
Definition: yapf_ship_regions.cpp:297
CYapfOriginRegionT
YAPF origin for water regions.
Definition: yapf_ship_regions.cpp:89
yapf_ship_regions.h
CYapfRegionPatchNodeKey
Yapf Node Key that represents a single patch of interconnected water within a water region.
Definition: yapf_ship_regions.cpp:24
DiagDirDifference
DiagDirDiff DiagDirDifference(DiagDirection d0, DiagDirection d1)
Calculate the difference between two DiagDirection values.
Definition: direction_func.h:131
Vehicle::current_order
Order current_order
The current order (+ status, like: loading)
Definition: vehicle_base.h:349
CYapfFollowRegionT::Node
Types::NodeList::Titem Node
This will be our node type.
Definition: yapf_ship_regions.cpp:170
OrthogonalTileArea
Represents the covered area of e.g.
Definition: tilearea_type.h:18
DIAGDIRDIFF_90RIGHT
@ DIAGDIRDIFF_90RIGHT
90 degrees right
Definition: direction_type.h:98
CNodeList_HashTableT
Hash table based node list multi-container class.
Definition: nodelist.hpp:23
VisitWaterRegionPatchNeighbors
void VisitWaterRegionPatchNeighbors(const WaterRegionPatchDesc &water_region_patch, TVisitWaterRegionPatchCallBack &callback)
Calls the provided callback function on all accessible water region patches in each cardinal directio...
Definition: water_regions.cpp:368
INVALID_DIAGDIR
@ INVALID_DIAGDIR
Flag for an invalid DiagDirection.
Definition: direction_type.h:80
DiagDirection
DiagDirection
Enumeration for diagonal directions.
Definition: direction_type.h:73
CYapfRegion_TypesT::Tpf
Tpf_ Tpf
Pathfinder type.
Definition: yapf_ship_regions.cpp:281
CalculateWaterRegionPatchHash
int CalculateWaterRegionPatchHash(const WaterRegionPatchDesc &water_region_patch)
Calculates a number that uniquely identifies the provided water region patch.
Definition: water_regions.cpp:262
CYapfDestinationRegionT
YAPF destination provider for water regions.
Definition: yapf_ship_regions.cpp:125
CYapfSegmentCostCacheNoneT
CYapfSegmentCostCacheNoneT - the formal only yapf cost cache provider that implements PfNodeCacheFetc...
Definition: yapf_costcache.hpp:21
DummyFollower
Definition: yapf_ship_regions.cpp:271
Ship
All ships have this type.
Definition: ship.h:24
CYapfDestinationRegionT::Tpf
Types::Tpf Tpf
The pathfinder class (derived from THIS class).
Definition: yapf_ship_regions.cpp:128
CYapfCostRegionT::Tpf
Types::Tpf Tpf
The pathfinder class (derived from THIS class).
Definition: yapf_ship_regions.cpp:240
IsDockingTile
bool IsDockingTile(Tile t)
Checks whether the tile is marked as a dockling tile.
Definition: water_map.h:374
CYapfOriginRegionT::Node
Types::NodeList::Titem Node
This will be our node type.
Definition: yapf_ship_regions.cpp:93
CYapfRegion_TypesT::PfOrigin
CYapfOriginRegionT< Types > PfOrigin
Origin provider.
Definition: yapf_ship_regions.cpp:289
abs
constexpr T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition: math_func.hpp:23
Map::Size
static debug_inline uint Size()
Get the size of the map.
Definition: map_func.h:288
CYapfRegion_TypesT::PfBase
CYapfBaseT< Types > PfBase
Pathfinder components (modules).
Definition: yapf_ship_regions.cpp:287
BaseStation
Base class for all station-ish types.
Definition: base_station_base.h:64
CYapfCostRegionT::PfCalcCost
bool PfCalcCost(Node &n, const TrackFollower *)
Called by YAPF to calculate the cost from the origin to the given node.
Definition: yapf_ship_regions.cpp:255
CYapfDestinationRegionT::Key
Node::Key Key
Key to hash tables.
Definition: yapf_ship_regions.cpp:130
CYapfOriginRegionT::Tpf
Types::Tpf Tpf
The pathfinder class (derived from THIS class).
Definition: yapf_ship_regions.cpp:92
CYapfRegion_TypesT::TrackFollower
DummyFollower TrackFollower
Track follower helper class.
Definition: yapf_ship_regions.cpp:282
YapfShipFindWaterRegionPath
std::vector< WaterRegionPatchDesc > YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length)
Finds a path at the water region level.
Definition: yapf_ship_regions.cpp:309
IsShipDestinationTile
bool IsShipDestinationTile(TileIndex tile, StationID station)
Test if a tile is a docking tile for the given station.
Definition: ship_cmd.cpp:664
CYapfBaseT
CYapfBaseT - A-star type path finder base class.
Definition: yapf_base.hpp:47
LinkGraph::BaseNode
Node of the link graph.
Definition: linkgraph.h:90
DIAGDIR_NE
@ DIAGDIR_NE
Northeast, upper right on your monitor.
Definition: direction_type.h:75
CYapfFollowRegionT::Key
Node::Key Key
Key to hash tables.
Definition: yapf_ship_regions.cpp:171
CFollowTrackT
Track follower helper template class (can serve pathfinders and vehicle controllers).
Definition: follow_track.hpp:28
WaterRegionPatchDesc::x
int x
The X coordinate of the water region, i.e. X=2 is the 3rd water region along the X-axis.
Definition: water_regions.h:27