Feature: [Linkgraph] Prioritize faster routes for passengers, mail and express cargo

Passengers usually prefer fast paths to short paths.
Average travel times of links are updated in real-time for use in Dijkstra's algorithm,
and newer travel times weigh more, just like capacities.

(cherry picked from commit 977604ef08)
pull/451/head
Nicolas Chappe 3 years ago committed by Jonathan G Rennison
parent 1518c2fa1e
commit a43a1902bb

@ -39,6 +39,7 @@ inline void LinkGraph::BaseEdge::Init()
{
this->capacity = 0;
this->usage = 0;
this->travel_time_sum = 0;
this->last_unrestricted_update = INVALID_DATE;
this->last_restricted_update = INVALID_DATE;
this->last_aircraft_update = INVALID_DATE;
@ -75,6 +76,7 @@ void LinkGraph::Compress()
if (edge.capacity > 0) {
edge.capacity = std::max(1U, edge.capacity / 2);
edge.usage /= 2;
edge.travel_time_sum = std::max(1ULL, edge.travel_time_sum / 2);
}
}
}
@ -102,9 +104,11 @@ void LinkGraph::Merge(LinkGraph *other)
backward = other->edges[node2][node1];
forward.capacity = LinkGraph::Scale(forward.capacity, age, other_age);
forward.usage = LinkGraph::Scale(forward.usage, age, other_age);
forward.travel_time_sum = LinkGraph::Scale(forward.travel_time_sum, age, other_age);
if (forward.next_edge != INVALID_NODE) forward.next_edge += first;
backward.capacity = LinkGraph::Scale(backward.capacity, age, other_age);
backward.usage = LinkGraph::Scale(backward.usage, age, other_age);
backward.travel_time_sum = LinkGraph::Scale(backward.travel_time_sum, age, other_age);
if (backward.next_edge != INVALID_NODE) backward.next_edge += first;
}
BaseEdge &new_start = this->edges[new_node][new_node];
@ -190,13 +194,14 @@ NodeID LinkGraph::AddNode(const Station *st)
* @param usage Usage to be added.
* @param mode Update mode to be used.
*/
void LinkGraph::Node::AddEdge(NodeID to, uint capacity, uint usage, EdgeUpdateMode mode)
void LinkGraph::Node::AddEdge(NodeID to, uint capacity, uint usage, uint32 travel_time, EdgeUpdateMode mode)
{
assert(this->index != to);
BaseEdge &edge = this->edges[to];
BaseEdge &first = this->edges[this->index];
edge.capacity = capacity;
edge.usage = usage;
edge.travel_time_sum = travel_time * capacity;
edge.next_edge = first.next_edge;
first.next_edge = to;
if (mode & EUM_UNRESTRICTED) edge.last_unrestricted_update = _date;
@ -211,14 +216,14 @@ void LinkGraph::Node::AddEdge(NodeID to, uint capacity, uint usage, EdgeUpdateMo
* @param usage Usage to be added.
* @param mode Update mode to be used.
*/
void LinkGraph::Node::UpdateEdge(NodeID to, uint capacity, uint usage, EdgeUpdateMode mode)
void LinkGraph::Node::UpdateEdge(NodeID to, uint capacity, uint usage, uint32 travel_time, EdgeUpdateMode mode)
{
assert(capacity > 0);
assert(usage <= capacity);
if (this->edges[to].capacity == 0) {
this->AddEdge(to, capacity, usage, mode);
this->AddEdge(to, capacity, usage, travel_time, mode);
} else {
(*this)[to].Update(capacity, usage, mode);
(*this)[to].Update(capacity, usage, travel_time, mode);
}
}
@ -235,6 +240,7 @@ void LinkGraph::Node::RemoveEdge(NodeID to)
edge.last_restricted_update = INVALID_DATE;
edge.last_aircraft_update = INVALID_DATE;
edge.usage = 0;
edge.travel_time_sum = 0;
NodeID prev = this->index;
NodeID next = this->edges[this->index].next_edge;
@ -253,23 +259,39 @@ void LinkGraph::Node::RemoveEdge(NodeID to)
/**
* Update an edge. If mode contains UM_REFRESH refresh the edge to have at
* least the given capacity and usage, otherwise add the capacity and usage.
* least the given capacity and usage, otherwise add the capacity, usage and travel time.
* In any case set the respective update timestamp(s), according to the given
* mode.
* @param capacity Capacity to be added/updated.
* @param usage Usage to be added.
* @param travel_time Travel time to be added, in ticks.
* @param mode Update mode to be applied.
*/
void LinkGraph::Edge::Update(uint capacity, uint usage, EdgeUpdateMode mode)
void LinkGraph::Edge::Update(uint capacity, uint usage, uint32 travel_time, EdgeUpdateMode mode)
{
assert(this->edge.capacity > 0);
assert(capacity >= usage);
if (mode & EUM_INCREASE) {
if (this->edge.travel_time_sum == 0) {
this->edge.travel_time_sum = (this->edge.capacity + capacity) * travel_time;
} else if (travel_time == 0) {
this->edge.travel_time_sum += this->edge.travel_time_sum / this->edge.capacity * capacity;
} else {
this->edge.travel_time_sum += travel_time * capacity;
}
this->edge.capacity += capacity;
this->edge.usage += usage;
} else if (mode & EUM_REFRESH) {
this->edge.capacity = std::max(this->edge.capacity, capacity);
/* If travel time is not provided, we scale the stored time based on
* the capacity increase. */
if (capacity > this->edge.capacity && travel_time == 0) {
this->edge.travel_time_sum = this->edge.travel_time_sum / this->edge.capacity * capacity;
this->edge.capacity = capacity;
} else {
this->edge.capacity = std::max(this->edge.capacity, capacity);
this->edge.travel_time_sum = std::max<uint64>(this->edge.travel_time_sum, travel_time * capacity);
}
this->edge.usage = std::max(this->edge.usage, usage);
}
if (mode & EUM_UNRESTRICTED) this->edge.last_unrestricted_update = _date;

@ -70,6 +70,7 @@ public:
struct BaseEdge {
uint capacity; ///< Capacity of the link.
uint usage; ///< Usage of the link.
uint64 travel_time_sum; ///< Sum of the travel times of the link, in ticks.
Date last_unrestricted_update; ///< When the unrestricted part of the link was last updated.
Date last_restricted_update; ///< When the restricted part of the link was last updated.
Date last_aircraft_update; ///< When aircraft capacity of the link was last updated.
@ -106,6 +107,12 @@ public:
*/
uint Usage() const { return this->edge.usage; }
/**
* Get edge's average travel time.
* @return Travel time, in ticks.
*/
uint32 TravelTime() const { return this->edge.travel_time_sum / this->edge.capacity; }
/**
* Get the date of the last update to the edge's unrestricted capacity.
* @return Last update.
@ -311,7 +318,7 @@ public:
* @param edge Edge to be wrapped.
*/
Edge(BaseEdge &edge) : EdgeWrapper<BaseEdge>(edge) {}
void Update(uint capacity, uint usage, EdgeUpdateMode mode);
void Update(uint capacity, uint usage, uint32 time, EdgeUpdateMode mode);
void Restrict() { this->edge.last_unrestricted_update = INVALID_DATE; }
void Release() { this->edge.last_restricted_update = INVALID_DATE; }
void ClearAircraft() { this->edge.last_aircraft_update = INVALID_DATE; }
@ -445,8 +452,8 @@ public:
this->node.demand = demand;
}
void AddEdge(NodeID to, uint capacity, uint usage, EdgeUpdateMode mode);
void UpdateEdge(NodeID to, uint capacity, uint usage, EdgeUpdateMode mode);
void AddEdge(NodeID to, uint capacity, uint usage, uint32 time, EdgeUpdateMode mode);
void UpdateEdge(NodeID to, uint capacity, uint usage, uint32 time, EdgeUpdateMode mode);
void RemoveEdge(NodeID to);
};

@ -310,16 +310,33 @@ void MultiCommodityFlow::Dijkstra(NodeID source_node, PathVector &paths)
capacity /= 100;
if (capacity == 0) capacity = 1;
}
/* punish in-between stops a little */
uint distance = DistanceMaxPlusManhattan(this->job[from].XY(), this->job[to].XY()) + 1;
/* Prioritize the fastest route for passengers, mail and express cargo,
* and the shortest route for other classes of cargo.
* In-between stops are punished with a 1 tile or 1 day penalty. */
bool express = IsCargoInClass(this->job.Cargo(), CC_PASSENGERS) ||
IsCargoInClass(this->job.Cargo(), CC_MAIL) ||
IsCargoInClass(this->job.Cargo(), CC_EXPRESS);
auto calculate_distance = [&]() {
return DistanceMaxPlusManhattan(this->job[from].XY(), this->job[to].XY()) + 1;
};
uint distance_anno;
if (express) {
/* Compute a default travel time from the distance and an average speed of 1 tile/day. */
distance_anno = (edge.TravelTime() != 0) ? edge.TravelTime() + DAY_TICKS : calculate_distance() * DAY_TICKS;
} else {
distance_anno = calculate_distance();
}
if (edge.LastAircraftUpdate() != INVALID_DATE && aircraft_link_scale > 100) {
distance *= aircraft_link_scale;
distance /= 100;
distance_anno *= aircraft_link_scale;
distance_anno /= 100;
}
Tannotation *dest = static_cast<Tannotation *>(paths[to]);
if (dest->IsBetter(source, capacity, capacity - edge.Flow(), distance)) {
if (dest->IsBetter(source, capacity, capacity - edge.Flow(), distance_anno)) {
if (dest->GetAnnosSetFlag()) annos.erase(AnnoSetItem<Tannotation>(dest));
dest->Fork(source, capacity, capacity - edge.Flow(), distance);
dest->Fork(source, capacity, capacity - edge.Flow(), distance_anno);
dest->UpdateAnnotation();
annos.insert(AnnoSetItem<Tannotation>(dest));
dest->SetAnnosSetFlag(true);

@ -249,6 +249,12 @@ void LinkRefresher::RefreshStats(const Order *cur, const Order *next, uint8 flag
/* A link is at least partly restricted if a vehicle can't load at its source. */
EdgeUpdateMode restricted_mode = (cur->GetCargoLoadType(c) & OLFB_NO_LOAD) == 0 ?
EUM_UNRESTRICTED : EUM_RESTRICTED;
Station *st_to = Station::GetIfValid(next_station);
/* This estimates the travel time of the link as the time needed
* to travel between the stations at half the max speed of the consist.
* The result is in tiles/tick (= 2048 km-ish/h). */
uint32 time_estimate = (st_to != nullptr) ?
DistanceManhattan(st->xy, st_to->xy) * 4096U / this->vehicle->GetDisplayMaxSpeed() : 0;
if (HasBit(flags, AIRCRAFT)) restricted_mode |= EUM_AIRCRAFT;
@ -264,15 +270,15 @@ void LinkRefresher::RefreshStats(const Order *cur, const Order *next, uint8 flag
uint effective_capacity = cargo_quantity * this->vehicle->load_unload_ticks;
if (effective_capacity > (uint)this->vehicle->orders->GetTotalDuration()) {
IncreaseStats(st, c, next_station, effective_capacity /
this->vehicle->orders->GetTotalDuration(), 0,
this->vehicle->orders->GetTotalDuration(), 0, 0,
EUM_INCREASE | restricted_mode);
} else if (RandomRange(this->vehicle->orders->GetTotalDuration()) < effective_capacity) {
IncreaseStats(st, c, next_station, 1, 0, EUM_INCREASE | restricted_mode);
IncreaseStats(st, c, next_station, 1, 0, 0, EUM_INCREASE | restricted_mode);
} else {
IncreaseStats(st, c, next_station, cargo_quantity, 0, EUM_REFRESH | restricted_mode);
IncreaseStats(st, c, next_station, cargo_quantity, 0, time_estimate, EUM_REFRESH | restricted_mode);
}
} else {
IncreaseStats(st, c, next_station, cargo_quantity, 0, EUM_REFRESH | restricted_mode);
IncreaseStats(st, c, next_station, cargo_quantity, 0, time_estimate, EUM_REFRESH | restricted_mode);
}
}
}

@ -178,6 +178,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_TOWN_SETTING_OVERRIDE, XSCF_NULL, 1, 1, "town_setting_override", nullptr, nullptr, nullptr },
{ XSLFI_SCRIPT_INT64, XSCF_NULL, 1, 1, "script_int64", nullptr, nullptr, nullptr },
{ XSLFI_U64_TICK_COUNTER, XSCF_NULL, 1, 1, "u64_tick_counter", nullptr, nullptr, nullptr },
{ XSLFI_LINKGRAPH_TRAVEL_TIME, XSCF_NULL, 1, 1, "linkgraph_travel_time", nullptr, nullptr, nullptr },
{ XSLFI_NULL, XSCF_NULL, 0, 0, nullptr, nullptr, nullptr, nullptr },// This is the end marker
};

@ -132,6 +132,7 @@ enum SlXvFeatureIndex {
XSLFI_SCRIPT_INT64, ///< See: SLV_SCRIPT_INT64
XSLFI_U64_TICK_COUNTER, ///< See: SLV_U64_TICK_COUNTER
XSLFI_LINKGRAPH_TRAVEL_TIME, ///< See: SLV_LINKGRAPH_TRAVEL_TIME
XSLFI_RIFF_HEADER_60_BIT, ///< Size field in RIFF chunk header is 60 bit
XSLFI_HEIGHT_8_BIT, ///< Map tile height is 8 bit instead of 4 bit, but savegame version may be before this became true in trunk

@ -126,6 +126,7 @@ static const SaveLoad _edge_desc[] = {
SLE_CONDNULL(4, SL_MIN_VERSION, SLV_191), // distance
SLE_VAR(Edge, capacity, SLE_UINT32),
SLE_VAR(Edge, usage, SLE_UINT32),
SLE_CONDVAR_X(Edge, travel_time_sum, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_LINKGRAPH_TRAVEL_TIME)),
SLE_VAR(Edge, last_unrestricted_update, SLE_INT32),
SLE_CONDVAR(Edge, last_restricted_update, SLE_INT32, SLV_187, SL_MAX_VERSION),
SLE_CONDVAR_X(Edge, last_aircraft_update, SLE_INT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_LINKGRAPH_AIRCRAFT)),

@ -33,7 +33,7 @@ public:
inline static const SaveLoad description[] = {
SLE_VAR(Edge, capacity, SLE_UINT32),
SLE_VAR(Edge, usage, SLE_UINT32),
//SLE_CONDVAR(Edge, travel_time_sum, SLE_UINT64, SLV_LINKGRAPH_TRAVEL_TIME, SL_MAX_VERSION),
SLE_CONDVAR(Edge, travel_time_sum, SLE_UINT64, SLV_LINKGRAPH_TRAVEL_TIME, SL_MAX_VERSION),
SLE_VAR(Edge, last_unrestricted_update, SLE_INT32),
SLE_CONDVAR(Edge, last_restricted_update, SLE_INT32, SLV_187, SL_MAX_VERSION),
SLE_VAR(Edge, next_edge, SLE_UINT16),

@ -4499,7 +4499,7 @@ void DeleteStaleLinks(Station *from)
* @param usage Usage to add to link stat.
* @param mode Update mode to be applied.
*/
void IncreaseStats(Station *st, CargoID cargo, StationID next_station_id, uint capacity, uint usage, EdgeUpdateMode mode)
void IncreaseStats(Station *st, CargoID cargo, StationID next_station_id, uint capacity, uint usage, uint32 time, EdgeUpdateMode mode)
{
GoodsEntry &ge1 = st->goods[cargo];
Station *st2 = Station::Get(next_station_id);
@ -4541,7 +4541,7 @@ void IncreaseStats(Station *st, CargoID cargo, StationID next_station_id, uint c
}
}
if (lg != nullptr) {
(*lg)[ge1.node].UpdateEdge(ge2.node, capacity, usage, mode);
(*lg)[ge1.node].UpdateEdge(ge2.node, capacity, usage, time, mode);
}
}

@ -52,8 +52,8 @@ void UpdateAirportsNoise();
bool SplitGroundSpriteForOverlay(const TileInfo *ti, SpriteID *ground, RailTrackOffset *overlay_offset);
void IncreaseStats(Station *st, const Vehicle *v, StationID next_station_id);
void IncreaseStats(Station *st, CargoID cargo, StationID next_station_id, uint capacity, uint usage, EdgeUpdateMode mode);
void IncreaseStats(Station *st, const Vehicle *v, StationID next_station_id, uint32 time);
void IncreaseStats(Station *st, CargoID cargo, StationID next_station_id, uint capacity, uint usage, uint32 time, EdgeUpdateMode mode);
void RerouteCargo(Station *st, CargoID c, StationID avoid, StationID avoid2);
void RerouteCargoFromSource(Station *st, CargoID c, StationID source, StationID avoid, StationID avoid2);

@ -3140,6 +3140,7 @@ void Vehicle::DeleteUnreachedImplicitOrders()
*/
static void VehicleIncreaseStats(const Vehicle *front)
{
uint32 travel_time = front->current_order_time;
for (const Vehicle *v = front; v != nullptr; v = v->Next()) {
StationID last_loading_station = HasBit(front->vehicle_flags, VF_LAST_LOAD_ST_SEP) ? v->last_loading_station : front->last_loading_station;
if (v->refit_cap > 0 &&
@ -3156,7 +3157,7 @@ static void VehicleIncreaseStats(const Vehicle *front)
EdgeUpdateMode restricted_mode = EUM_INCREASE;
if (v->type == VEH_AIRCRAFT) restricted_mode |= EUM_AIRCRAFT;
IncreaseStats(Station::Get(last_loading_station), v->cargo_type, front->last_station_visited, v->refit_cap,
std::min<uint>(v->refit_cap, v->cargo.StoredCount()), restricted_mode);
std::min<uint>(v->refit_cap, v->cargo.StoredCount()), travel_time, restricted_mode);
}
}
}

Loading…
Cancel
Save