(svn r25361) -Feature: distribute cargo according to plan given by linkgraph

This commit is contained in:
fonsinchen
2013-06-09 13:03:48 +00:00
parent a2ff96d682
commit 04e3eb6fab
16 changed files with 586 additions and 206 deletions

View File

@@ -10,7 +10,9 @@
/** @file cargopacket.cpp Implementation of the cargo packets. */
#include "stdafx.h"
#include "station_base.h"
#include "core/pool_func.hpp"
#include "core/random_func.hpp"
#include "economy_base.h"
#include "cargoaction.h"
#include "order_type.h"
@@ -151,8 +153,8 @@ void CargoPacket::Reduce(uint count)
/**
* Destroy the cargolist ("frees" all cargo packets).
*/
template <class Tinst>
CargoList<Tinst>::~CargoList()
template <class Tinst, class Tcont>
CargoList<Tinst, Tcont>::~CargoList()
{
for (Iterator it(this->packets.begin()); it != this->packets.end(); ++it) {
delete *it;
@@ -163,8 +165,8 @@ CargoList<Tinst>::~CargoList()
* Empty the cargo list, but don't free the cargo packets;
* the cargo packets are cleaned by CargoPacket's CleanPool.
*/
template <class Tinst>
void CargoList<Tinst>::OnCleanPool()
template <class Tinst, class Tcont>
void CargoList<Tinst, Tcont>::OnCleanPool()
{
this->packets.clear();
}
@@ -175,8 +177,8 @@ void CargoList<Tinst>::OnCleanPool()
* @param cp Packet to be removed from cache.
* @param count Amount of cargo from the given packet to be removed.
*/
template <class Tinst>
void CargoList<Tinst>::RemoveFromCache(const CargoPacket *cp, uint count)
template <class Tinst, class Tcont>
void CargoList<Tinst, Tcont>::RemoveFromCache(const CargoPacket *cp, uint count)
{
assert(count <= cp->count);
this->count -= count;
@@ -188,83 +190,16 @@ void CargoList<Tinst>::RemoveFromCache(const CargoPacket *cp, uint count)
* Increases count and days_in_transit.
* @param cp New packet to be inserted.
*/
template <class Tinst>
void CargoList<Tinst>::AddToCache(const CargoPacket *cp)
template <class Tinst, class Tcont>
void CargoList<Tinst, Tcont>::AddToCache(const CargoPacket *cp)
{
this->count += cp->count;
this->cargo_days_in_transit += cp->days_in_transit * cp->count;
}
/**
* Truncates the cargo in this list to the given amount. It leaves the
* first cargo entities and removes max_move from the back of the list.
* @param max_move Maximum amount of entities to be removed from the list.
* @return Amount of entities actually moved.
*/
template <class Tinst>
uint CargoList<Tinst>::Truncate(uint max_move)
{
max_move = min(this->count, max_move);
this->PopCargo(CargoRemoval<Tinst>(static_cast<Tinst *>(this), max_move));
return max_move;
}
/**
* Shifts cargo from the front of the packet list and applies some action to it.
* @tparam Taction Action class or function to be used. It should define
* "bool operator()(CargoPacket *)". If true is returned the
* cargo packet will be removed from the list. Otherwise it
* will be kept and the loop will be aborted.
* @param action Action instance to be applied.
*/
template <class Tinst>
template <class Taction>
void CargoList<Tinst>::ShiftCargo(Taction action)
{
Iterator it(this->packets.begin());
while (it != this->packets.end() && action.MaxMove() > 0) {
CargoPacket *cp = *it;
if (action(cp)) {
it = this->packets.erase(it);
} else {
break;
}
}
}
/**
* Pops cargo from the back of the packet list and applies some action to it.
* @tparam Taction Action class or function to be used. It should define
* "bool operator()(CargoPacket *)". If true is returned the
* cargo packet will be removed from the list. Otherwise it
* will be kept and the loop will be aborted.
* @param action Action instance to be applied.
*/
template <class Tinst>
template <class Taction>
void CargoList<Tinst>::PopCargo(Taction action)
{
if (this->packets.empty()) return;
Iterator it(--(this->packets.end()));
Iterator begin(this->packets.begin());
while (action.MaxMove() > 0) {
CargoPacket *cp = *it;
if (action(cp)) {
if (it != begin) {
this->packets.erase(it--);
} else {
this->packets.erase(it);
break;
}
} else {
break;
}
}
}
/** Invalidates the cached data and rebuilds it. */
template <class Tinst>
void CargoList<Tinst>::InvalidateCache()
template <class Tinst, class Tcont>
void CargoList<Tinst, Tcont>::InvalidateCache()
{
this->count = 0;
this->cargo_days_in_transit = 0;
@@ -281,11 +216,11 @@ void CargoList<Tinst>::InvalidateCache()
* @param cp Packet to be eliminated.
* @return If the packets could be merged.
*/
template <class Tinst>
/* static */ bool CargoList<Tinst>::TryMerge(CargoPacket *icp, CargoPacket *cp)
template <class Tinst, class Tcont>
/* static */ bool CargoList<Tinst, Tcont>::TryMerge(CargoPacket *icp, CargoPacket *cp)
{
if (Tinst::AreMergable(icp, cp) &&
icp->count + cp->count <= CargoPacket::MAX_COUNT) {
icp->count + cp->count <= CargoPacket::MAX_COUNT) {
icp->Merge(cp);
return true;
} else {
@@ -340,6 +275,57 @@ void VehicleCargoList::Append(CargoPacket *cp, MoveToAction action)
NOT_REACHED();
}
/**
* Shifts cargo from the front of the packet list and applies some action to it.
* @tparam Taction Action class or function to be used. It should define
* "bool operator()(CargoPacket *)". If true is returned the
* cargo packet will be removed from the list. Otherwise it
* will be kept and the loop will be aborted.
* @param action Action instance to be applied.
*/
template<class Taction>
void VehicleCargoList::ShiftCargo(Taction action)
{
Iterator it(this->packets.begin());
while (it != this->packets.end() && action.MaxMove() > 0) {
CargoPacket *cp = *it;
if (action(cp)) {
it = this->packets.erase(it);
} else {
break;
}
}
}
/**
* Pops cargo from the back of the packet list and applies some action to it.
* @tparam Taction Action class or function to be used. It should define
* "bool operator()(CargoPacket *)". If true is returned the
* cargo packet will be removed from the list. Otherwise it
* will be kept and the loop will be aborted.
* @param action Action instance to be applied.
*/
template<class Taction>
void VehicleCargoList::PopCargo(Taction action)
{
if (this->packets.empty()) return;
Iterator it(--(this->packets.end()));
Iterator begin(this->packets.begin());
while (action.MaxMove() > 0) {
CargoPacket *cp = *it;
if (action(cp)) {
if (it != begin) {
this->packets.erase(it--);
} else {
this->packets.erase(it);
break;
}
} else {
break;
}
}
}
/**
* Update the cached values to reflect the removal of this packet or part of it.
* Decreases count, feeder share and days_in_transit.
@@ -405,6 +391,23 @@ void VehicleCargoList::AgeCargo()
}
}
/**
* Sets loaded_at_xy to the current station for all cargo to be transfered.
* This is done when stopping or skipping while the vehicle is unloading. In
* that case the vehicle will get part of its transfer credits early and it may
* get more transfer credits than it's entitled to.
* @param xy New loaded_at_xy for the cargo.
*/
void VehicleCargoList::SetTransferLoadPlace(TileIndex xy)
{
uint sum = 0;
for (Iterator it = this->packets.begin(); sum < this->action_counts[MTA_TRANSFER]; ++it) {
CargoPacket *cp = *it;
cp->loaded_at_xy = xy;
sum += cp->count;
}
}
/**
* Stages cargo for unloading. The cargo is sorted so that packets to be
* transferred, delivered or kept are in consecutive chunks in the list. At the
@@ -412,10 +415,13 @@ void VehicleCargoList::AgeCargo()
* chunks.
* @param accepted If the cargo will be accepted at the station.
* @param current_station ID of the station.
* @param next_station ID of the station the vehicle will go to next.
* @param order_flags OrderUnloadFlags that will apply to the unload operation.
* @param ge GoodsEntry for getting the flows.
* @param payment Payment object for registering transfers.
* return If any cargo will be unloaded.
*/
bool VehicleCargoList::Stage(bool accepted, StationID current_station, uint8 order_flags)
bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationID next_station, uint8 order_flags, const GoodsEntry *ge, CargoPayment *payment)
{
this->AssertCountConsistency();
assert(this->action_counts[MTA_LOAD] == 0);
@@ -423,20 +429,59 @@ bool VehicleCargoList::Stage(bool accepted, StationID current_station, uint8 ord
Iterator deliver = this->packets.end();
Iterator it = this->packets.begin();
uint sum = 0;
bool force_keep = (order_flags & OUFB_NO_UNLOAD) != 0;
bool force_unload = (order_flags & OUFB_UNLOAD) != 0;
bool force_transfer = (order_flags & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0;
assert(this->count > 0 || it == this->packets.end());
while (sum < this->count) {
CargoPacket *cp = *it;
this->packets.erase(it++);
if ((order_flags & OUFB_TRANSFER) != 0 || (!accepted && (order_flags & OUFB_UNLOAD) != 0)) {
this->packets.push_front(cp);
this->action_counts[MTA_TRANSFER] += cp->count;
} else if (accepted && current_station != cp->source && (order_flags & OUFB_NO_UNLOAD) == 0) {
this->packets.insert(deliver, cp);
this->action_counts[MTA_DELIVER] += cp->count;
StationID cargo_next = INVALID_STATION;
MoveToAction action = MTA_LOAD;
if (force_keep) {
action = MTA_KEEP;
} else if (force_unload && accepted && cp->source != current_station) {
action = MTA_DELIVER;
} else if (force_transfer) {
action = MTA_TRANSFER;
cargo_next = ge->GetVia(cp->source, current_station, next_station);
assert((cargo_next != next_station || cargo_next == INVALID_STATION) &&
cargo_next != current_station);
} else {
this->packets.push_back(cp);
if (deliver == this->packets.end()) --deliver;
this->action_counts[MTA_KEEP] += cp->count;
cargo_next = ge->GetVia(cp->source);
if (cargo_next == INVALID_STATION) {
action = (accepted && cp->source != current_station) ? MTA_DELIVER : MTA_KEEP;
} else if (cargo_next == current_station) {
action = MTA_DELIVER;
} else if (cargo_next == next_station) {
action = MTA_KEEP;
} else {
action = MTA_TRANSFER;
}
}
Money share;
switch (action) {
case MTA_KEEP:
this->packets.push_back(cp);
if (deliver == this->packets.end()) --deliver;
break;
case MTA_DELIVER:
this->packets.insert(deliver, cp);
break;
case MTA_TRANSFER:
this->packets.push_front(cp);
/* Add feeder share here to allow reusing field for next station. */
share = payment->PayTransfer(cp, cp->count);
cp->AddFeederShare(share);
this->feeder_share += share;
cp->next_station = cargo_next;
break;
default:
NOT_REACHED();
}
this->action_counts[action] += cp->count;
sum += cp->count;
}
this->AssertCountConsistency();
@@ -468,14 +513,15 @@ uint VehicleCargoList::Reassign(uint max_move, MoveToAction from, MoveToAction t
/**
* Returns reserved cargo to the station and removes it from the cache.
* @param dest Station the cargo is returned to.
* @param max_move Maximum amount of cargo to move.
* @param dest Station the cargo is returned to.
* @param ID of next the station the cargo wants to go next.
* @return Amount of cargo actually returned.
*/
uint VehicleCargoList::Return(uint max_move, StationCargoList *dest)
uint VehicleCargoList::Return(uint max_move, StationCargoList *dest, StationID next)
{
max_move = min(this->action_counts[MTA_LOAD], max_move);
this->PopCargo(CargoReturn(this, dest, max_move));
this->PopCargo(CargoReturn(this, dest, max_move, next));
return max_move;
}
@@ -505,7 +551,7 @@ uint VehicleCargoList::Unload(uint max_move, StationCargoList *dest, CargoPaymen
uint moved = 0;
if (this->action_counts[MTA_TRANSFER] > 0) {
uint move = min(this->action_counts[MTA_TRANSFER], max_move);
this->ShiftCargo(CargoTransfer(this, dest, move, payment));
this->ShiftCargo(CargoTransfer(this, dest, move));
moved += move;
}
if (this->action_counts[MTA_TRANSFER] == 0 && this->action_counts[MTA_DELIVER] > 0 && moved < max_move) {
@@ -516,6 +562,19 @@ uint VehicleCargoList::Unload(uint max_move, StationCargoList *dest, CargoPaymen
return moved;
}
/**
* Truncates the cargo in this list to the given amount. It leaves the
* first cargo entities and removes max_move from the back of the list.
* @param max_move Maximum amount of entities to be removed from the list.
* @return Amount of entities actually moved.
*/
uint VehicleCargoList::Truncate(uint max_move)
{
max_move = min(this->count, max_move);
this->PopCargo(CargoRemoval<VehicleCargoList>(this, max_move));
return max_move;
}
/*
*
* Station cargo list implementation.
@@ -523,24 +582,112 @@ uint VehicleCargoList::Unload(uint max_move, StationCargoList *dest, CargoPaymen
*/
/**
* Appends the given cargo packet. Tries to merge it with another one in the
* packets list. If no fitting packet is found, appends it.
* Appends the given cargo packet to the range of packets with the same next station
* @warning After appending this packet may not exist anymore!
* @note Do not use the cargo packet anymore after it has been appended to this CargoList!
* @param cp Cargo packet to add.
* @param next the next hop
* @param cp the cargo packet to add
* @pre cp != NULL
*/
void StationCargoList::Append(CargoPacket *cp)
void StationCargoList::Append(CargoPacket *cp, StationID next)
{
assert(cp != NULL);
this->AddToCache(cp);
for (List::reverse_iterator it(this->packets.rbegin()); it != this->packets.rend(); it++) {
StationCargoPacketMap::List &list = this->packets[next];
for (StationCargoPacketMap::List::reverse_iterator it(list.rbegin());
it != list.rend(); it++) {
if (StationCargoList::TryMerge(*it, cp)) return;
}
/* The packet could not be merged with another one */
this->packets.push_back(cp);
list.push_back(cp);
}
/**
* Shifts cargo from the front of the packet list for a specific station and
* applies some action to it.
* @tparam Taction Action class or function to be used. It should define
* "bool operator()(CargoPacket *)". If true is returned the
* cargo packet will be removed from the list. Otherwise it
* will be kept and the loop will be aborted.
* @param action Action instance to be applied.
* @param next Next hop the cargo wants to visit.
* @return True if all packets with the given next hop have been removed,
* False otherwise.
*/
template <class Taction>
bool StationCargoList::ShiftCargo(Taction &action, StationID next)
{
std::pair<Iterator, Iterator> range(this->packets.equal_range(next));
for (Iterator it(range.first); it != range.second && it.GetKey() == next;) {
if (action.MaxMove() == 0) return false;
CargoPacket *cp = *it;
if (action(cp)) {
it = this->packets.erase(it);
} else {
return false;
}
}
return true;
}
/**
* Shifts cargo from the front of the packet list for a specific station and
* and optional also from the list for "any station", then applies some action
* to it.
* @tparam Taction Action class or function to be used. It should define
* "bool operator()(CargoPacket *)". If true is returned the
* cargo packet will be removed from the list. Otherwise it
* will be kept and the loop will be aborted.
* @param action Action instance to be applied.
* @param next Next hop the cargo wants to visit.
* @param include_invalid If cargo from the INVALID_STATION list should be
* used if necessary.
* @return Amount of cargo actually moved.
*/
template <class Taction>
uint StationCargoList::ShiftCargo(Taction action, StationID next, bool include_invalid)
{
uint max_move = action.MaxMove();
if (this->ShiftCargo(action, next) && include_invalid && action.MaxMove() > 0) {
this->ShiftCargo(action, INVALID_STATION);
}
return max_move - action.MaxMove();
}
/**
* Truncates where each destination loses roughly the same percentage of its
* cargo. This is done by randomizing the selection of packets to be removed.
* @param max_move Maximum amount of cargo to remove.
* @return Amount of cargo actually moved.
*/
uint StationCargoList::Truncate(uint max_move)
{
max_move = min(max_move, this->count);
uint prev_count = this->count;
uint moved = 0;
while (max_move > moved) {
for (Iterator it(this->packets.begin()); it != this->packets.end();) {
if (prev_count > max_move && RandomRange(prev_count) < prev_count - max_move) {
++it;
continue;
}
CargoPacket *cp = *it;
uint diff = max_move - moved;
if (cp->count > diff) {
this->RemoveFromCache(cp, diff);
cp->Reduce(diff);
return moved + diff;
} else {
it = this->packets.erase(it);
moved += cp->count;
this->RemoveFromCache(cp, cp->count);
delete cp;
}
}
}
return moved;
}
/**
@@ -550,10 +697,10 @@ void StationCargoList::Append(CargoPacket *cp)
* @param load_place Tile index of the current station.
* @return Amount of cargo actually reserved.
*/
uint StationCargoList::Reserve(uint max_move, VehicleCargoList *dest, TileIndex load_place)
uint StationCargoList::Reserve(uint max_move, VehicleCargoList *dest, TileIndex load_place, StationID next)
{
max_move = min(this->count, max_move);
this->ShiftCargo(CargoReservation(this, dest, max_move, load_place));
this->ShiftCargo(CargoReservation(this, dest, max_move, load_place), next, true);
return max_move;
}
@@ -564,21 +711,34 @@ uint StationCargoList::Reserve(uint max_move, VehicleCargoList *dest, TileIndex
* @param max_move Amount of cargo to load.
* @return Amount of cargo actually loaded.
*/
uint StationCargoList::Load(uint max_move, VehicleCargoList *dest, TileIndex load_place)
uint StationCargoList::Load(uint max_move, VehicleCargoList *dest, TileIndex load_place, StationID next_station)
{
uint move = min(dest->ActionCount(VehicleCargoList::MTA_LOAD), max_move);
if (move > 0) {
this->reserved_count -= move;
dest->Reassign(move, VehicleCargoList::MTA_LOAD, VehicleCargoList::MTA_KEEP);
return move;
} else {
move = min(this->count, max_move);
this->ShiftCargo(CargoLoad(this, dest, move, load_place));
return this->ShiftCargo(CargoLoad(this, dest, move, load_place), next_station, true);
}
return move;
}
/**
* Routes packets with station "avoid" as next hop to a different place.
* @param max_move Maximum amount of cargo to move.
* @param dest List to append the cargo to.
* @param avoid Station to exclude from routing and current next hop of packets to reroute.
* @param avoid2 Additional station to exclude from routing.
* @oaram ge GoodsEntry to get the routing info from.
*/
uint StationCargoList::Reroute(uint max_move, StationCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge)
{
return this->ShiftCargo(CargoReroute(this, dest, max_move, avoid, avoid2, ge), avoid, false);
}
/*
* We have to instantiate everything we want to be usable.
*/
template class CargoList<VehicleCargoList>;
template class CargoList<StationCargoList>;
template class CargoList<VehicleCargoList, CargoPacketList>;
template class CargoList<StationCargoList, StationCargoPacketMap>;