Orthogonal Income Patch

Forum for technical discussions regarding development. If you have a general suggestion, problem or comment, please use one of the other forums.

Moderator: OpenTTD Developers

Wahazar
Tycoon
Tycoon
Posts: 1451
Joined: 18 Jan 2014 18:10

Re: Orthogonal Income Patch

Post by Wahazar »

Alberth wrote:Can't you make an approximation? People are not going to notice it's not a prefect exp function.
Of course, expanding into truncated Taylor series is a kind of aproximation.
How large integers are in use in openttd? dwords?
Formerly known as: McZapkie
Projects: Reproducible Map Generation patch, NewGRFs: Manpower industries, PolTrams, Polroad, 600mm narrow gauge, wired, ECS industry extension, V4 CEE train set, HotHut.
Another favorite games: freeciv longturn, OHOL/2HOL.
Eddi
Tycoon
Tycoon
Posts: 8267
Joined: 17 Jan 2007 00:14

Re: Orthogonal Income Patch

Post by Eddi »

Money is a 64-bit integer.

but wouldn't it be easier to use an exponential function based on 2^-x (aka shift-right) instead of e^-x?
Wahazar
Tycoon
Tycoon
Posts: 1451
Joined: 18 Jan 2014 18:10

Limited Growth Income Patch

Post by Wahazar »

Eddi wrote: but wouldn't it be easier to use an exponential function based on 2^-x (aka shift-right) instead of e^-x?
Thank you, it bring me closer to transform my model to integers - but I must refit my model parameters (I must to refit them anyway,
because I messed up a little with recalculating from km to tile and back, or days/tick etc).

I will continue within this thread, because in my opinion, my new payment model is also the answer to the payment overflow problem,
because a limited growth function is used, instead of linear tail.

Current GetTransportedGoodsIncome, as I mentioned in other threads:
http://www.tt-forums.net/viewtopic.php? ... 3#p1112770
works well for small maps, and all cargo parameters were balanced for original transport set.
In case of big maps, original formula is no longer valid. You may call it feature, but it is nearly a bug
- overflow of timecounter stopped by the magic 31 number.
As a result, for large maps, we have 50k income at 200 tile distance, 100k at 500, again 50k at 1500, and again 100k at 4000 tiles,
the last result doesn't depend on engine used - for example Asia Star Electric (red dashed line) and Kirby Paul Tank (red solid thin line),
results for 40 passengers and for 40 cubic meters of oil, respectively:
ImageImage
The blue lines indicate total running cost of both engines (including amortisation of purchase cost),
surprisingly that both cost are nearly the same, because ASE is more expensive but faster
(by the way, running cost should be balanced proportional to square of velocity, not velocity itself, to reproduce costs of kinetic energy).
Please notice wide open scissors between red income and and blue costs.

The best solution is to slow down time, some kind of Daylength hack, but I have no idea,
how much it would interfere with other parts of the game including external scripts etc.
My proposition much less obstructive - rewrite cargo payment model and use average speed (calculated from distance and transit time),
instead of transit time itself.
To maintain backward compatibility with original TTD maps and cargoes,
Days1 and Days2 cargo parameters are used to define reciprocal of threshold speed:
1/Vthreshold = Days/200 tiles.
For example, pax have Days1=0, i.e. there there is no finite speed to satisfy first threshold limit, and Days2=25
- above this speed, no negative ban is applied.
The bigger Days number is, slower speed is more tolerable.
Green lines on above plot indicate my preliminary model - superposition of exponential decay and linear function.
Slope of linear function vary with speed, depending cargo type, and as described above, can be negative or positive.
I made attempt to fit my model to keep minimal as possible spread between new and old income within a range of 256 tiles,
for wide range of speeds and most of standard cargoes (I didn't check it for newgrfs such Firs).

Finally, I replaced linear growth by elongated inverted exponent, therefore current model equations are:
eq3a.png
eq3a.png (47.87 KiB) Viewed 494 times
where E1 slope depend on first threshold velocity, E2 slope and sign depend on second threshold velocity,
whole model can be tested here in comparison to the old one:
https://docs.google.com/spreadsheet/ccc ... sp=sharing
- red fields are cargo parameters and average velocity - can be altered to see other results.
Because there is no linear growth of income, scissors between income and running cost should be always close, for enough high distance.

My programming skills stopped at the XX century stage, I have no idea how to create patch file
(any link with readme would be appreciated),
and implementation of my model is a dirty piece of floating redundant constants, however for testing purposes,
below if a body of GetTransportedGoodsIncome function, to be replaced in economy.cpp file, from line 962,
until end of this procedure (calculating by callback is untouched):

Code: Select all

 //replace Money GetTransportedGoodsIncome from MIN_TIME_FACTOR declaration 
	static const int DECAY1 = 10;
	static const int DECAY2 = 40;
	static const float INV_VA = 1/330;
	static const int EXP2_AMPL = 80;
	static const int INCOME_DIVIDER = 900;
	static const int LOW_SPEED_DIVIDER = 200;
	static const int LOW_SPEED_OFFSET = 50;
	static const float TILE2KM = 28.66;
        const float d = dist;
        float transitdays = transit_days;
        transitdays = 2.5*maxf(transitdays, 1.0);
	const int cargo_payment = cs->current_payment;
	const int days1 = cs->transit_days[0];
	const int days2 = cs->transit_days[1];
	const float inv_vt1 =days1/(200*TILE2KM);  // reciprocal of first threshold velocity
	const float inv_vt2 =days2/(200*TILE2KM);  // reciprocal of second threshold velocity
	const float v_avg = d*TILE2KM/(transitdays); //average transit velocity
	const float inv_v_avg = 1/maxf(v_avg,0.1); //reciprocal of average transit velocity
	const float max_1v_1vt1 = maxf(inv_v_avg,inv_vt1); //threshold 1 
	const float max_1v_1vt2 = maxf(inv_v_avg,inv_vt2); //threshold 2 
	/*
	 * The income factor is calculated based on the average velocity
	 * compared to two cargo-depending threshold velocities. 
	 * Formula is divided into three parts:
	 *
	 *  - fast exponential growth limited by 1st threshold velocity
	 *  - slow exponential growth which can be negative or positive, depending on 2nd threshold
	 *  - residual correction for very slow local transit
	 *
	 */
	const float exp1 = (1-exp(d*(-max_1v_1vt1-INV_VA)/DECAY1))/(max_1v_1vt2);
	const float exp2 = (inv_vt2-inv_v_avg)*EXP2_AMPL*(1-exp(d*(-max_1v_1vt2-INV_VA)/DECAY2))/max_1v_1vt2;
	const int32 test1 = 
trunc(cargo_payment*num_pieces*maxf(exp1+exp2,minf(d/LOW_SPEED_DIVIDER,v_avg/LOW_SPEED_OFFSET))/INCOME_DIVIDER);
  return test1;
}
Temporarily, a maxf and minf function need to be defined in core/math.hpp

Code: Select all

static inline float maxf(const float a, const float b)
{
	return max<float>(a, b);
}
static inline int min(const int a, const int b)
{
	return min<int>(a, b);
}
The payment graph no longer produce correct plots, because velocity calculated along 20 tiles drops to zero very fast.
Probably the better would be to plot payment vs velocity instead of time, and calculate it for 1 unit per 200 tiles.

If above model is considered as useful, I would proceed with converting floats to int and code optimalisation.
Last edited by Wahazar on 03 Mar 2014 13:57, edited 1 time in total.
Formerly known as: McZapkie
Projects: Reproducible Map Generation patch, NewGRFs: Manpower industries, PolTrams, Polroad, 600mm narrow gauge, wired, ECS industry extension, V4 CEE train set, HotHut.
Another favorite games: freeciv longturn, OHOL/2HOL.
Eddi
Tycoon
Tycoon
Posts: 8267
Joined: 17 Jan 2007 00:14

Re: Limited Growth Income Patch

Post by Eddi »

McZapkie wrote:My programming skills stopped at the XX century stage, I have no idea how to create patch file
If you used TortoiseSVN/TortoiseHG to check out the source code, there is a right-click context menu entry to create a patch.
If you used command line svn/hg, type "svn diff" or "hg diff" to show the patch and use "... > file.diff" to write it to a file
if you use git, there should be a similar command, but it will be called totally differently and unintuitively.
if you downloaded a source tarball, get a version control system first, and use one of the above steps as applicable :)
Wahazar
Tycoon
Tycoon
Posts: 1451
Joined: 18 Jan 2014 18:10

Re: Limited Growth Income Patch

Post by Wahazar »

Eddi wrote:If you used command line hg, type or "hg diff" to show the patch and use "... > file.diff" to write it to a file
It is my case, but I have no idea, which sources should be considered to apply a diff - stable, trunc, most recent?

Another question - I need a global variable, which would be updated every year (for optional change of payment curve, to reflect change of life style across centuries and also decreasing tolerance for slow transit).
Where are all those variables declared, and how to update it properly?
Formerly known as: McZapkie
Projects: Reproducible Map Generation patch, NewGRFs: Manpower industries, PolTrams, Polroad, 600mm narrow gauge, wired, ECS industry extension, V4 CEE train set, HotHut.
Another favorite games: freeciv longturn, OHOL/2HOL.
Eddi
Tycoon
Tycoon
Posts: 8267
Joined: 17 Jan 2007 00:14

Re: Orthogonal Income Patch

Post by Eddi »

i'd say in economy.* (where inflation etc. is handled)
User avatar
PikkaBird
Graphics Moderator
Graphics Moderator
Posts: 5602
Joined: 13 Sep 2004 13:21
Location: The Moon

Re: Limited Growth Income Patch

Post by PikkaBird »

McZapkie wrote:Another question - I need a global variable, which would be updated every year (for optional change of payment curve, to reflect change of life style across centuries and also decreasing tolerance for slow transit).
Achieving any kind of "balance" in OpenTTD is very difficult. It's even more difficult when people start swinging sledgehammers around in the source code. :roll:

But in this case, the "optional change of payment curve" already exists; it's in the TTD inflation model, which increases running costs by 1% more each year than payments. It's also badly broken at the moment, so if you really want to create another speed bump for newgrf authors, that would be the best place to start.
Wahazar
Tycoon
Tycoon
Posts: 1451
Joined: 18 Jan 2014 18:10

Re: Limited Growth Income Patch

Post by Wahazar »

PikkaBird wrote: But in this case, the "optional change of payment curve" already exists; it's in the TTD inflation model, which increases running costs by 1% more each year than payments.
In my model, I would rather change the shape of payment vs distance curve according to speed and date.
By now, negative bias from low speed is constant during centuries, which was tolerable for original game beginning in 1950,
but is not very well balanced, if we start from 1800, thanks to the, for example, eGRVTS.

Before hey day of railways, horse coaches with their 10-20 km/h speed were considered as very fast and were very expensive
(French name of coach was diligence - haste).
In XIX century, 100 km/h train was very fast.
Now, nobody want to pay for travelling 2 weeks by coach or ship, for purposes other than leisure,
and at long distances most of peoples prefer airplane than a train.
It can be implemented in my model by increasing weight of second "elongated" exponent with time:
income in 1800, different average speeds
income in 1800, different average speeds
paxpay1800.png (23.25 KiB) Viewed 2578 times
income in 1950, different average speeds
income in 1950, different average speeds
paxpay1950.png (22.49 KiB) Viewed 2576 times
Please note increasing spread between payments from different speeds. Middle of this spread depend on second days parameter of cargo type.

Unlike inflation model, it would be always calculated from absolute year value, therefore all newgrf sets,
if historically accurate, should works fine.

BTW, my personal feelings are, that you suggested me to write own patch, and now you are writing about sledgehammering of the code.
I just tapped a body of one (ok, most important) function.
Formerly known as: McZapkie
Projects: Reproducible Map Generation patch, NewGRFs: Manpower industries, PolTrams, Polroad, 600mm narrow gauge, wired, ECS industry extension, V4 CEE train set, HotHut.
Another favorite games: freeciv longturn, OHOL/2HOL.
User avatar
PikkaBird
Graphics Moderator
Graphics Moderator
Posts: 5602
Joined: 13 Sep 2004 13:21
Location: The Moon

Re: Limited Growth Income Patch

Post by PikkaBird »

McZapkie wrote:BTW, my personal feelings are, that you suggested me to write own patch, and now you are writing about sledgehammering of the code. I just tapped a body of one (ok, most important) function.
I really like the idea of fixing the hump in the income/distance curves, and I hope that your intention is to see that patch included in trunk some day - but if that is your intention, all I'm saying is keep it simple.

Edit: Regarding "change of life style across centuries and also decreasing tolerance for slow transit", early vehicles' slowness can be compensated for in newgrfs by cheaper costs or reduced cargo decay. It doesn't require a hard-coded solution which assumes vehicles get faster over time.

For "realistic" gameplay, the earning power of a train which travels at 30km/h in 1950 is irrelevant, because such a train won't exist. On the other hand, if I have an unrealistic vehicle set with (for some reason) very slow futuristic vehicles, I'm going to see an income divergence of several hundred percent depending on whether or not the player has your patch active. How am I supposed to balance for that?
Wahazar
Tycoon
Tycoon
Posts: 1451
Joined: 18 Jan 2014 18:10

Re: Limited Growth Income Patch

Post by Wahazar »

PikkaBird wrote: early vehicles' slowness can be compensated for in newgrfs by cheaper costs or reduced cargo decay
cargo_decay is a static parameter, AFAIK, and is not displayed in purchase menu.
But I understand your overall point of view with integrity of newgrf balance.
As you suggested, I will just do the basic framework of my patch.

BTW, I created patch files, hope that it is correct.
Still floats, I will fix it next week. Some constant need to be tweaked also, because spread of income for very low or very high speeds is to high in comparison to old model (in the range of classic TTD map).
I need to put a switch between old and new model, to compare easily, instead running two instances.
Payment graph shows now payment of 1 piece at 200 tiles distance in function of average velocity, instead of time.
I need to change description string - I think the new string should be created in english.txt?
Attachments
economy.diff
(2.94 KiB) Downloaded 61 times
graph_gui.diff
(642 Bytes) Downloaded 57 times
math_func.diff
(447 Bytes) Downloaded 62 times
Formerly known as: McZapkie
Projects: Reproducible Map Generation patch, NewGRFs: Manpower industries, PolTrams, Polroad, 600mm narrow gauge, wired, ECS industry extension, V4 CEE train set, HotHut.
Another favorite games: freeciv longturn, OHOL/2HOL.
Wahazar
Tycoon
Tycoon
Posts: 1451
Joined: 18 Jan 2014 18:10

Re: Orthogonal Income Patch

Post by Wahazar »

One simple question:
I need to send max vehicle speed to PayFinalDelivery and PayTransfer function.
There is *first pointer in Cargo Payment struct, but I need max speed of train, not a first engine only.
May I use GetDisplayMaxSpeed() ?
Rubidium
OpenTTD Developer
OpenTTD Developer
Posts: 3815
Joined: 09 Feb 2006 19:15

Re: Orthogonal Income Patch

Post by Rubidium »

McZapkie wrote:One simple question:
I need to send max vehicle speed to PayFinalDelivery and PayTransfer function.
There is *first pointer in Cargo Payment struct, but I need max speed of train, not a first engine only.
May I use GetDisplayMaxSpeed() ?
What is the max vehicle speed going to accomplish? I'd simply let the final delivery be done by transporting cargo one tile by the slowest possible vehicle, then I'd get loads more money than delivering directly with the faster vehicle.

GetDisplayMaxSpeed() seems to be a reasonable function to use for getting the speed. However, for NewGRF ships the maximum speed might increase significantly once the vehicle has been unloaded (but before payment), and for NewGRF aircraft the maximum speed might be very low because the NewGRF is limiting the taxiing speed. Arguably similar cases can exist for trains and road vehicles (NUTS?). In either case, the speed check might have some significant unwanted side effects.
Wahazar
Tycoon
Tycoon
Posts: 1451
Joined: 18 Jan 2014 18:10

Re: Orthogonal Income Patch

Post by Wahazar »

Rubidium wrote:
McZapkie wrote: GetDisplayMaxSpeed() seems to be a reasonable function to use for getting the speed. However, for NewGRF ships the maximum speed might increase significantly once the vehicle has been unloaded (but before payment), and for NewGRF aircraft the maximum speed might be very low because the NewGRF is limiting the taxiing speed.
My idea is to count late delivery penalty as difference between
expected delivery time (distance/max_speed+some offset) and actual transfer time.
But I don't need actual maximal speed (which can depend, for example for aircraft, on current vehicle state - approaching/taxiing etc),
just a maximum of the maximal speed :)

On the other side, features such track speed limits (bridges, NUTRACK), would give high spread between actual and expected transfer time.

Maybe some kind of accumulated record of expected time, based on GetDisplayMaxSpeed, or even GetCurrentMaxSpeed?
Formerly known as: McZapkie
Projects: Reproducible Map Generation patch, NewGRFs: Manpower industries, PolTrams, Polroad, 600mm narrow gauge, wired, ECS industry extension, V4 CEE train set, HotHut.
Another favorite games: freeciv longturn, OHOL/2HOL.
Arie-
Director
Director
Posts: 593
Joined: 20 Jan 2009 16:07

Re: Orthogonal Income Patch

Post by Arie- »

To me link capacity comes to mind when thinking of actual speed in transportation and expected. Currently I have no idea on how to calculate some actual usable number for this.
Eddi
Tycoon
Tycoon
Posts: 8267
Joined: 17 Jan 2007 00:14

Re: Orthogonal Income Patch

Post by Eddi »

i'd not use the speed of the vehicle doing the delivery, but the (average or highest) speed of the available engines.


e.g.

Code: Select all

W = list of all available vehicles capable of loading the cargo
E = list of all available vehicles carrying no (normal) cargo

considered_speed = min(max(W->speed), max(E->speed))
[recalculate this on new month, when prototypes become available]
this would consider wagon speed limits and stuff, and doing this for every vehicle type individually means fast planes available won't make ship delivery useless. something might be necessary to separate the transfer legs for intermodal transport, to prevent abuse like making the last tile delivery by ship, or so. like record cargo age for each transport type individually (or finally resolve the long-standing issues with leg payment by recording a list of all transfer legs, including which company and which vehicle type made this transfer)
Wahazar
Tycoon
Tycoon
Posts: 1451
Joined: 18 Jan 2014 18:10

Re: Orthogonal Income Patch

Post by Wahazar »

Eddi wrote:i'd not use the speed of the vehicle doing the delivery, but the (average or highest) speed of the available engines.
this would consider wagon speed limits and stuff, and doing this for every vehicle type individually means fast planes available won't make ship delivery useless.
My idea was just opposite, to calculate expected delivery time for a given vehicle (I want to split calculations of revenue based on distance and penalty based on time).
If I understand correctly Eddi suggestion, expected delivery time would be shortened, as new faster vehicle appears, making old vehicle useless.
This is a very interesting idea, similar to my previous "life style modifier", but I worry about newgrf creators complains.
Eddi wrote: something might be necessary to separate the transfer legs for intermodal transport, to prevent abuse like making the last tile delivery by ship, or so. like record cargo age for each transport type individually (or finally resolve the long-standing issues with leg payment by recording a list of all transfer legs, including which company and which vehicle type made this transfer)
CargoPacket has already position of last loaded point, so it should be sufficient to send (for example via CargoAge called in RunVehicleDayProc)
current tile position and nominal max velocity, to allow cp estimate expected time increment.
After delivery, if sum of expected time increments is greater than actual days_in_transit plus transit_days[1], penalty would be substracted form payment base, until it reach 0.
I think, that it should also fix problems with unfair transit shares.

But I still don't know, which velocity should I use.
For example, I don't understand these multipliers ans dividers used in ship.h:

Code: Select all

 int GetDisplayMaxSpeed() const { return this->vcache.cached_max_speed / 2; }
int GetCurrentMaxSpeed() const { return min(this->vcache.cached_max_speed, this->current_order.max_speed * 2); }
?
Eddi
Tycoon
Tycoon
Posts: 8267
Joined: 17 Jan 2007 00:14

Re: Orthogonal Income Patch

Post by Eddi »

McZapkie wrote:
Eddi wrote: something might be necessary to separate the transfer legs for intermodal transport, to prevent abuse like making the last tile delivery by ship, or so. like record cargo age for each transport type individually (or finally resolve the long-standing issues with leg payment by recording a list of all transfer legs, including which company and which vehicle type made this transfer)
CargoPacket has already position of last loaded point, so it should be sufficient to send (for example via CargoAge called in RunVehicleDayProc)
current tile position and nominal max velocity, to allow cp estimate expected time increment.
After delivery, if sum of expected time increments is greater than actual days_in_transit plus transit_days[1], penalty would be substracted form payment base, until it reach 0.
I think, that it should also fix problems with unfair transit shares.
i don't think this works as you think it does.
But I still don't know, which velocity should I use.
For example, I don't understand these multipliers ans dividers used in ship.h:

Code: Select all

 int GetDisplayMaxSpeed() const { return this->vcache.cached_max_speed / 2; }
int GetCurrentMaxSpeed() const { return min(this->vcache.cached_max_speed, this->current_order.max_speed * 2); }
?
the internal speed units are different for various vehicle types, otherwise things like airplane speed easily overflow. this is a remnant of the old TTD way of doing things. you should not worry about these, just GetDisplayMaxSpeed should be fine.
Wahazar
Tycoon
Tycoon
Posts: 1451
Joined: 18 Jan 2014 18:10

Re: Orthogonal Income Patch

Post by Wahazar »

Eddi wrote:the internal speed units are different for various vehicle types, otherwise things like airplane speed easily overflow. this is a remnant of the old TTD way of doing things. you should not worry about these, just GetDisplayMaxSpeed should be fine.
OK, it is clear now, I noticed it in engine_type.h file reference.

I realised, that average max speed you mentioned before, can be calculated, for each cargo type,
by rolling average from all max speeds of any matching vehicles belonging to each company vehicle pools.
Is a GenerateVehicleSortList function good for such query?
Initial average value can be calculated from newgrf Engine Pool.

Now, strategy depend on what other competitors are doing - if only some of them using faster means of transport, no worry,
but if most of them, run fast or die :)

PS. I see one flaw with vehicles without speed limit, or with wagons faster than any available engine.
Locomotives (non DMU/EMU) are not taken into account. Avg. max speed is calculated for each cargo separately and stored in cargo struct.
Formerly known as: McZapkie
Projects: Reproducible Map Generation patch, NewGRFs: Manpower industries, PolTrams, Polroad, 600mm narrow gauge, wired, ECS industry extension, V4 CEE train set, HotHut.
Another favorite games: freeciv longturn, OHOL/2HOL.
User avatar
PikkaBird
Graphics Moderator
Graphics Moderator
Posts: 5602
Joined: 13 Sep 2004 13:21
Location: The Moon

Re: Orthogonal Income Patch

Post by PikkaBird »

Sooo when you start your new airline, suddenly my buses on the other side of the map become unprofitable? This is silly - you're looking for solutions to a problem which doesn't exist.
Wahazar
Tycoon
Tycoon
Posts: 1451
Joined: 18 Jan 2014 18:10

Re: Orthogonal Income Patch

Post by Wahazar »

PikkaBird wrote:Sooo when you start your new airline, suddenly my buses on the other side of the map become unprofitable?
Not suddenly, if there is rolling average max speed taken for every vehicles.
One airline give very small impact on average.
But yes, if many players start many passenger airlines, your slow passenger vehicles become unprofitable on large distances.
You call it silly?
I call it competition.
It is also a natural way to remove abandoned companies - issue on servers without engine breaking
- instead of forcing removal - let them bankrupt.

Another idea is to calculate average on base of available to purchase engines.
In this case, long distance profit depend on vehicle set and its introduction dates.
Change of profitability related strictly to the vehicle set is, in my opinion, the better and more predictable option
than a "floating date" inflation?
Of course, mixing different newgrf sets could change balance of given set, but even now mixing sets often produce unbalanced prices etc.
Last edited by Wahazar on 15 Mar 2014 13:43, edited 1 time in total.
Formerly known as: McZapkie
Projects: Reproducible Map Generation patch, NewGRFs: Manpower industries, PolTrams, Polroad, 600mm narrow gauge, wired, ECS industry extension, V4 CEE train set, HotHut.
Another favorite games: freeciv longturn, OHOL/2HOL.
Post Reply

Return to “OpenTTD Development”

Who is online

Users browsing this forum: No registered users and 17 guests