News:

Simutrans Forum Archive
A complete record of the old Simutrans Forum.

Musings on revenue

Started by fbfree, July 03, 2011, 10:20:46 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

fbfree

I've looked over the code for calculating revenue, and I'm surprisingly impressed by it.  I was motivated by the apparent difficulties in balancing marine revenues and by an apparent loss of revenue for early long-distance travel (c. 1840) in pak-brit-exp.

First, I'll go through the features I really like.

The same calculation should be able to calculate the revenue from the service provided, not caring about the mode.  The speed bonus parameters are the only variable that is mode dependent, and these parameters have all been set equal (except for boats.)  Passengers care about waiting time, comfort, and travel time, regardless of the mode of transport.

I like how speedbonus is calculated based on speed and distance rather than on time saved.  While one would naively think that time=money for passengers, the premium paid for passengers to save time in reality is not proportional to the time saved.  Basing the speed bonus on distance might not be a completely accurate model, but it provides a simple non-linear relationship between time saved and ticket price.

The parameters used to calculate comfort are exactly as I imagined them.  i.e. for a given amount of time, you're willing to ride with substandard amenities.  After 30 minutes, you're going to want A/C a smooth enough ride to read a book, comfort = 60.  After 2 hours, you're going to want a snack or a coffee bar, comfort = 100.  After 5 hours, you need a decent meal and a chance to stretch out, comfort = 160.  At comfort = 220, you're in a full service sleeper ready to cross an ocean.  Also, comfort doesn't matter as much on trips shorter than a couple of hours.  Spot on.

I like how revenue can actually decline as distance increases when comfort is intolerable.  While passengers calculate their expected journey time before deciding to leave, they don't calculate the expected comfort along the route.  For long distance travel at a low speed bonus, the customer can still be expected to pay for the trip if there weren't other faster options to take.  However, the operator will have to stop for meals, bathroom breaks, and lodging along the way if these are not provided on-board.  Otherwise, the customer will not pay.

At present, a base revenue is calculated, then the comfort and speed bonus are corrected in parallel.  i.e., if the speed bonus decreases revenue by 50% and the comfort increases revenue by 50%, the final revenue is 1 - 0.5 + 0.5 = 1 instead of 1 * 0.5 * 1.5 = 0.75.  I was initially skeptical of this feature. However, given that increasing the comfort level of a vehicle generally introduces a set price in operations cost, a speed bonus independent method of adding the revenue back in makes it easier to balance the game.

Now for some concerns:

While comfort should be able to produce a negative marginal revenue vs. distance, speedbonus should not.  With configurations at present, with large negative speed bonuses ((100 average_speed/ref_speed)-100 less than -30) and distances between 150 and 300 tiles, negative marginal revenues are possible.  To solve this problem, and to remove some need for fine tuning speedbonus parameters, I would suggest a revenue model based similarly to how income tax is calculated.
i.e. for trips under 2 km, revenue is given strictly by a base fare.
for trips between 2 and 10 km, revenue is given by base fare + (dist - 2) * (A * (speed / ref speed) + B),
for trips between 10km and 50km, revenue is given by base fare + 8 * (A * (speed /ref speed) + B) + (dist-10) * (C * (speed/refspeed)+D)
for trips ...
This would be conceptually simple and easier to tune than the existing calculation.

I think it would be a good idea to be able to define a stop as being a refueling stop.  At these stops, revenue would be calculated and the trip distance reset.  These stops can only be made at stations that are equipped to handle resting passengers (with a restaurant, or a staging inn.)  These will add a delay the trip and add an extra overhead cost, but they will allow early long-distance trips on uncomfortable vehicles, or even modern long-distance trips by coach bus.

It's pretty harrowing for a player to see that a convoy can actually lose money by traveling further.  An introduction to the player stating that this is intentionally due to poor vehicle comfort is required.

jamespetts

fbfree,

thank you for your thoughts - most appreciated. However, the speed bonus code is designed to preclude the possibility of negative revenues with these lines:

Quote
        const ware_besch_t* goods = ware.get_besch();
   const sint64 price = (sint64)goods->get_preis();
   const sint64 min_price = price / 10ll;
   const uint16 speed_bonus_rating = calc_adjusted_speed_bonus(goods->get_speed_bonus(), distance);
   const sint64 ref_speed = welt->get_average_speed( fahr[0]->get_besch()->get_waytype() );
   const sint64 speed_base = (100ll * average_speed) / ref_speed - 100ll;
   const sint64 base_bonus = (price * (1000ll + speed_base * speed_bonus_rating));
   const sint64 min_revenue = min_price > base_bonus ? min_price : base_bonus;
   const sint64 revenue = min_revenue * (sint64)revenue_distance * (sint64)ware.menge;
   sint64 final_revenue = revenue;

Much of the speed bonus code is adapted from Standard, but with the substitution of the actual speed for the convoy's theoretical maximum speed. As for refuelling stops, that is a very interesting idea, but I worry that it might make the game too fiddly for players: I had previously contemplated the idea of giving vehicles a range (being the maximum distance that they can travel between stops), to simulate the need for vehicles to refuel, but rejected it on the basis that it would make it too difficult for the player in comparison to the relatively small additional element of vehicle differentiation and network planning that it would entail. I rather suspect that the same applies here.
Download Simutrans-Extended.

Want to help with development? See here for things to do for coding, and here for information on how to make graphics/objects.

Follow Simutrans-Extended on Facebook.

fbfree

A couple of proofreading notes for the revenue calculation:

> const uint16 journey_minutes = (((distance * 100) / average_speed) *  welt->get_einstellungen()->get_meters_per_tile()) / 1667;
include a comment showing that 100/1667 = 60min/hr / 1000 m/km.  I was confused on this for quite a bit.

> min_price = price/10ll
this effectively sets min_price as price/10000 as another factor of 1000 shows up later in the calculation.  This effect should be commented.

> //Multiply by a factor (default: 0.3) to ensure that it fits the scale properly. Journey times can easily appear too long.
this comment seems vestigial.

> if(speed_bonus_rating > 0 && happy_percentage > 0)
I'm not sure it's worth having this if statement as it doesn't actually affect the outcome of the following calculation, it's computationally inefficient, and it confuses the reader.  Note, both variables are uint16, and thus cannot be less than 0 to begin with.

The calc_tolerable_comfort calculation introduces some truncation jitter into the revenue calculation.  This jitter is noticeable when calculation low comfort revenue at long distance.

fbfree

Hi James,

The code does prevent negative revenues, but they don't protect against negative marginal revenues vs. distance.  A convoy that travels 150 tiles can lose money over the next 150 tiles before earning money again after passing max_bonus_min_distance = 300 tiles.

I also believe that
const sint64 min_price = price / 10ll;
should be set to
const sint64 min_price = price * 1000ll / 10ll;
to reproduce the behaviour I think you were expecting.

jamespetts

fbfree,

thank you for your suggestions: I have updated the comments as suggested, save for the last suggestion: this calculation is very similar to Standard, save that, in Standard, the operation is << 7 instead of /10, the latter of which I introduced to make things clearer. It might be that this is incorrect in Standard from which I have copied it, but the intention in Standard is to ensure that the base price is no less than 1/128 of the original price.

As to "if(speed_bonus_rating > 0 && happy_percentage > 0)", these may be unsigned integers and cannot go below zero, but they can be exactly zero, the absence of which is the condition for which I am testing. If the speed bonus rating is zero, the goods simply do not care about their transport conditions at all.

As to the jitter, this may have been introduced recently when I removed all of the floating point calculation in order to make the game work over the internet (as floating point truncation is not identical between different platforms, resulting in desynchronisation). I am not sure what can easily be done to remedy this.
Download Simutrans-Extended.

Want to help with development? See here for things to do for coding, and here for information on how to make graphics/objects.

Follow Simutrans-Extended on Facebook.