/* * This file is part of OpenTTD. * 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. * 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. * 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 . */ /** @file linkgraph.h Declaration of link graph classes used for cargo distribution. */ #ifndef LINKGRAPH_H #define LINKGRAPH_H #include "../core/pool_type.hpp" #include "../station_base.h" #include "../cargotype.h" #include "../timer/timer_game_economy.h" #include "../saveload/saveload.h" #include "linkgraph_type.h" #include class LinkGraph; /** * Type of the pool for link graph components. Each station can be in at up to * 32 link graphs. So we allow for plenty of them to be created. */ typedef Pool LinkGraphPool; /** The actual pool with link graphs. */ extern LinkGraphPool _link_graph_pool; /** * A connected component of a link graph. Contains a complete set of stations * connected by links as nodes and edges. Each component also holds a copy of * the link graph settings at the time of its creation. The global settings * might change between the creation and join time so we can't rely on them. */ class LinkGraph : public LinkGraphPool::PoolItem<&_link_graph_pool> { public: /** * An edge in the link graph. Corresponds to a link between two stations. */ struct BaseEdge { uint capacity; ///< Capacity of the link. uint usage; ///< Usage of the link. uint64_t travel_time_sum; ///< Sum of the travel times of the link, in ticks. TimerGameEconomy::Date last_unrestricted_update; ///< When the unrestricted part of the link was last updated. TimerGameEconomy::Date last_restricted_update; ///< When the restricted part of the link was last updated. NodeID dest_node; ///< Destination of the edge. BaseEdge(NodeID dest_node = INVALID_NODE); /** * Get edge's average travel time. * @return Travel time, in ticks. */ uint32_t TravelTime() const { return this->travel_time_sum / this->capacity; } /** * Get the date of the last update to any part of the edge's capacity. * @return Last update. */ TimerGameEconomy::Date LastUpdate() const { return std::max(this->last_unrestricted_update, this->last_restricted_update); } void Update(uint capacity, uint usage, uint32_t time, EdgeUpdateMode mode); void Restrict() { this->last_unrestricted_update = EconomyTime::INVALID_DATE; } void Release() { this->last_restricted_update = EconomyTime::INVALID_DATE; } /** Comparison operator based on \c dest_node. */ bool operator <(const BaseEdge &rhs) const { return this->dest_node < rhs.dest_node; } bool operator <(NodeID rhs) const { return this->dest_node < rhs; } friend inline bool operator <(NodeID lhs, const LinkGraph::BaseEdge &rhs) { return lhs < rhs.dest_node; } }; /** * Node of the link graph. contains all relevant information from the associated * station. It's copied so that the link graph job can work on its own data set * in a separate thread. */ struct BaseNode { uint supply; ///< Supply at the station. uint demand; ///< Acceptance at the station. StationID station; ///< Station ID. TileIndex xy; ///< Location of the station referred to by the node. TimerGameEconomy::Date last_update; ///< When the supply was last updated. std::vector edges; ///< Sorted list of outgoing edges from this node. BaseNode(TileIndex xy = INVALID_TILE, StationID st = INVALID_STATION, uint demand = 0); /** * Update the node's supply and set last_update to the current date. * @param supply Supply to be added. */ void UpdateSupply(uint supply) { this->supply += supply; this->last_update = TimerGameEconomy::date; } /** * Update the node's location on the map. * @param xy New location. */ void UpdateLocation(TileIndex xy) { this->xy = xy; } /** * Set the node's demand. * @param demand New demand for the node. */ void SetDemand(uint demand) { this->demand = demand; } void AddEdge(NodeID to, uint capacity, uint usage, uint32_t time, EdgeUpdateMode mode); void UpdateEdge(NodeID to, uint capacity, uint usage, uint32_t time, EdgeUpdateMode mode); void RemoveEdge(NodeID to); /** * Check if an edge to a destination is present. * @param dest Wanted edge destination. * @return True if an edge is present. */ bool HasEdgeTo(NodeID dest) const { return std::binary_search(this->edges.begin(), this->edges.end(), dest); } BaseEdge &operator[](NodeID to) { assert(this->HasEdgeTo(to)); return *GetEdge(to); } const BaseEdge &operator[](NodeID to) const { assert(this->HasEdgeTo(to)); return *GetEdge(to); } private: std::vector::iterator GetEdge(NodeID dest) { return std::lower_bound(this->edges.begin(), this->edges.end(), dest); } std::vector::const_iterator GetEdge(NodeID dest) const { return std::lower_bound(this->edges.begin(), this->edges.end(), dest); } }; typedef std::vector NodeVector; /** Minimum effective distance for timeout calculation. */ static const uint MIN_TIMEOUT_DISTANCE = 32; /** Number of days before deleting links served only by vehicles stopped in depot. */ static constexpr TimerGameEconomy::Date STALE_LINK_DEPOT_TIMEOUT = 1024; /** Minimum number of days between subsequent compressions of a LG. */ static constexpr TimerGameEconomy::Date COMPRESSION_INTERVAL = 256; /** * Scale a value from a link graph of age orig_age for usage in one of age * target_age. Make sure that the value stays > 0 if it was > 0 before. * @param val Value to be scaled. * @param target_age Age of the target link graph. * @param orig_age Age of the original link graph. * @return scaled value. */ inline static uint Scale(uint val, TimerGameEconomy::Date target_age, TimerGameEconomy::Date orig_age) { return val > 0 ? std::max(1U, val * target_age.base() / orig_age.base()) : 0; } /** Bare constructor, only for save/load. */ LinkGraph() : cargo(INVALID_CARGO), last_compression(0) {} /** * Real constructor. * @param cargo Cargo the link graph is about. */ LinkGraph(CargoID cargo) : cargo(cargo), last_compression(TimerGameEconomy::date) {} void Init(uint size); void ShiftDates(TimerGameEconomy::Date interval); void Compress(); void Merge(LinkGraph *other); /* Splitting link graphs is intentionally not implemented. * The overhead in determining connectedness would probably outweigh the * benefit of having to deal with smaller graphs. In real world examples * networks generally grow. Only rarely a network is permanently split. * Reacting to temporary splits here would obviously create performance * problems and detecting the temporary or permanent nature of splits isn't * trivial. */ /** * Get a node with the specified id. * @param num ID of the node. * @return the Requested node. */ inline BaseNode &operator[](NodeID num) { return this->nodes[num]; } /** * Get a const reference to a node with the specified id. * @param num ID of the node. * @return the Requested node. */ inline const BaseNode &operator[](NodeID num) const { return this->nodes[num]; } /** * Get the current size of the component. * @return Size. */ inline NodeID Size() const { return (NodeID)this->nodes.size(); } /** * Get date of last compression. * @return Date of last compression. */ inline TimerGameEconomy::Date LastCompression() const { return this->last_compression; } /** * Get the cargo ID this component's link graph refers to. * @return Cargo ID. */ inline CargoID Cargo() const { return this->cargo; } /** * Scale a value to its monthly equivalent, based on last compression. * @param base Value to be scaled. * @return Scaled value. */ inline uint Monthly(uint base) const { return base * 30 / (TimerGameEconomy::date - this->last_compression + 1).base(); } NodeID AddNode(const Station *st); void RemoveNode(NodeID id); protected: friend SaveLoadTable GetLinkGraphDesc(); friend SaveLoadTable GetLinkGraphJobDesc(); friend class SlLinkgraphNode; friend class SlLinkgraphEdge; friend class LinkGraphJob; CargoID cargo; ///< Cargo of this component's link graph. TimerGameEconomy::Date last_compression; ///< Last time the capacities and supplies were compressed. NodeVector nodes; ///< Nodes in the component. }; #endif /* LINKGRAPH_H */