News:

Simutrans Wiki Manual
The official on-line manual for Simutrans. Read and contribute.

[11.35] Bridleways & unsurfaced roads have the same maintenance cost

Started by kazarmy, December 24, 2014, 02:51:03 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

kazarmy

In 1750 with Simutrans Exp 11.35, bridleways & unsurfaced roads have the same maintenance cost (1.28c per 8 tiles), and neither of them has the advertised maintenance cost (i.e. 0.80c per 8 tiles for bridleways and 1.60c per 8 tiles for unsurfaced roads).

jamespetts

Thank you for your report. May I ask how you are computing the actual maintenance cost, and whether this applies only to this type of road or whether it applies to any other sort of way?
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.

kazarmy

Ok sure. The computation process is as follows:

       
  • Noted that the simuconf.tab of Pak128.Britain-Ex-0.9.1 states that meters_per_tile = 125, which works out to 1000/125 = 8 tiles per km.
  • Start a new game (256x256, timeline on from 1750).
  • Switch on the grid ('#' key).
  • Start a road in a large, flat region of the map.
  • End the road exactly 7 tiles away in a straight line NE (thus, the length of the road is 8 including the start tile).
  • Note the maintenance cost in the Finances window.
  • Delete road, and repeat steps 4 to 7 for other ways.
The maintenance costs for ways in 1750 are as follows, with comparisons done between the actual cost and the advertised cost:

       
  • Bridleway: 1.28c (instead of 0.80c).
  • Unsurfaced road: 1.28c (instead of 1.60c).
  • Wooden trestle elevated road: 96.00c (as advertised).

       
  • Plateway: 96.00c (as advertised).
  • Wooden trestle elevated track: 103.68c (instead of 104.00c).

       
  • Tub boat Canal: 15.36c (instead of 16.00c).
  • Narrowboat Canal: 23.04c (instead of 24.00c).
  • Barge Canal: 64.00c (as advertised).
  • Ship Canal: 47.36c (instead of 48.00c).
Aqueducts' advertised costs are correct, per single tile and they behave like bridges.

So only the wooden trestle elevated road, plateway and barge canal have their advertised cost. It certainly appears that 32 is a significant number here since it's a common factor of 64 and 96 but not 104, 16, 24 and 48. I forgot to mention that I'm using the 64-bit version of Simutrans Exp 11.35 but the bug affects the 32-bit version as well.

This bug was only noticed after I converted a long-distance unsurfaced road into a bridleway and realized that the maintenance costs stayed the same.

DrSuperGood

Possible integer precision error? 32 rings alarm bells as the sort of number you can get from improper fixed point computation of fractions.

kazarmy

I wouldn't mind keeping the current maintenance costs though. I currently have a 24-wagon midrange planks line that appears (in attached screenshot) to be sustainable in profit, even after maintenance costs have been taken into account, and I certainly would like to keep it that way.

jamespetts

Yes, this is an integer precision error, and not an easy one to solve. The current development version of the pakset has different maintenance costs (0.10c for the unsurfaced road and 0.05c for the bridleway), but these are rendered as 0.16c and 0.00c in the game. They are, in the way-improvements branch, shown correctly in the tooltip, but not accurately translated from the pakset file.

The trouble is that all numbers need to be stored in integers because floating point values are not consistent between client and server on the network, and we need to divide the maintenance cost by 8 when there are 8 meters per tile, so anything not divisible by 8 will not work well. (I should add that the bits per month then causes the values to be multiplied by 2 afterwards, but this is done in a separate part of the code, so it is not easy simply to integrate the two to double the available precision). Setting the unsurfaced road to 16 and the bridleway to 8 in the pakset seems to work, however, yielding an in-game cost of 32 and 16 respectively.
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.

DrSuperGood

Quotebecause floating point values are not consistent between client and server on the network
Not entirely true. If the build is the same and the processor FPUs behave the same then they will work. Games like Warcraft III had few issues using floating points for everything as long as only the Windows version was used for all clients in a multiplayer session. The issue is that different builds and different processors in a network will behave differently. Warcraft III had a good example of this where the Mac and Windows versions would desynchronize in multiplayer if terrain deformations were used due to difference in the floating point results.

Due to the cross platform nature of Simutrans (Mac, Linux and Windows which can even run on non-x86 platforms like ARM) it is not worth the effort of using floats and fighting an endless battle with compilers to produce deterministic results. Fixed point numbers are often faster to compute anyway, even with FPU support due to the complexities of floats.

Quoteand we need to divide the maintenance cost by 8 when there are 8 meters per tile, so anything not divisible by 8 will not work well. (I should add that the bits per month then causes the values to be multiplied by 2 afterwards, but this is done in a separate part of the code, so it is not easy simply to integrate the two to double the available precision). Setting the unsurfaced road to 16 and the bridleway to 8 in the pakset seems to work, however, yielding an in-game cost of 32 and 16 respectively.
This is not a good solution as you are letting the game engine dictate your balance.

It might be possible with some fixed point hackery to fix it, however I need to see the code involved.

jamespetts

Ahh, yes, I should have been more precise about the floating point situation: the problems only occurred when clients were on Windows and the server on Linux (and presumably vice versa, but that was never tested). In any event, Bernd Gabriel spend months working on a special floating point class using integers which is much slower than real floating points, but is deterministic accross the network. It is currently used only for physics.

If you can think of any workable fixed point hackery, that would be most welcome. The relevant code is in void karte_t::set_scale() and the functions called thereby.

Edit: The actual work is done in the template function set_scale_generic:


// @author: jamespetts, April 2011
template<class T> static T set_scale_generic(T value, uint16 scale_factor) { return (value * (T)scale_factor) / (T)1000; }
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.

DrSuperGood

QuoteIn any event, Bernd Gabriel spend months working on a special floating point class using integers which is much slower than real floating points, but is deterministic accross the network. It is currently used only for physics.
I assume 64bit fixed point was not suitable due to numeric range?

Strange that he would need to spend months on it as software floating point implementations should be rather wide spread since not all processors have a FPU.

QuoteEdit: The actual work is done in the template function set_scale_generic:
Why is it dividing by 1000? That is neither a percentage or a fixed point scale factor. scale_factor should be some fixed point value and not some decimal fraction. Not only is this faster (since shifts are faster than division) but also allows you to adjust the precision easily. For example a 10 bit precision fixed point should use "/1024" which should optimize to ">>10". Raising precision could and should be made as simple as altering a constant.

jamespetts

I do not know why Bernd implemented it the way that he did: the physics stuff is rather over my head, I am afraid.

As to why it is dividing by 1,000: the "scale factor" is the number of meters per tile, so, to get a per km value, one must multiply by the number of meters per tile and divide by 1,000.
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.

DrSuperGood

And what is output? Un-scaled (not month length adjusted) cost in what units?

Adding an extra 10 bits of precision to the output would solve this mostly however it then depends what happens with the output.

kazarmy

Since the scale factor does not change during a game session, why not you all precompute the maintenance cost per tile using floating point? I do think a standard C++ double has enough precision to calculate the maintenance cost to 4 decimal places accurately, and I would amazed if the calculation results to 4 decimal places are different on Windows and Linux (or on ARM for that matter).


Secondly, I suggest that the maintenance cost shown to players be always per tile rather than per km (perhaps only up to 2 decimal places, unless the maintenance cost is less then 0.01 in which up to 4 decimal places could be shown). The computed maintenance cost should be rounded according to the shown maintenance cost. That way, players don't need to divide than add; they can just add. Also if I'm not mistaken, there is no loss of precision when adding 2 fixed-point numbers.


Thirdly, it might be possible to set the meters per tile value such that the computed maintenance cost per tile after rounding becomes 0.0000. However, I don't see the need to accommodate such extreme values.

DrSuperGood

QuoteSince the scale factor does not change during a game session, why not you all precompute the maintenance cost per tile using floating point? I do think a standard C++ double has enough precision to calculate the maintenance cost to 4 decimal places accurately, and I would amazed if the calculation results to 4 decimal places are different on Windows and Linux (or on ARM for that matter).
Until you find one random value and then everyone starts complaining how they are being kicked from the server. Also doubles are very slow to compute due to them being 64 bit types.

QuoteSecondly, I suggest that the maintenance cost shown to players be always per tile rather than per km (perhaps only up to 2 decimal places, unless the maintenance cost is less then 0.01 in which up to 4 decimal places could be shown). The computed maintenance cost should be rounded according to the shown maintenance cost. That way, players don't need to divide than add; they can just add. Also if I'm not mistaken, there is no loss of precision when adding 2 fixed-point numbers.
It is for dynamic scaling. Experimental specifically does it over standard simutrans which is per tile.

What actually needs to be done is that maintenance is shifted 1000 units to allow for fractional units to accommodate any possible meters per tile value (even 1). This might add overhead when adding normal financial units however as they need to be scaled by 1000 but that can be considered trivial.

Alternatively maintenance could just be given a fractional accumulator which holds all the units normally lost to rounding error which takes them in "money meters" unit format. The accumulator need only be a sint16 as it should only be in the range of 0 and 999. This would add overhead only for methods that add fractional parts.

jamespetts

Quote from: DrSuperGood on December 26, 2014, 01:32:09 AM
And what is output? Un-scaled (not month length adjusted) cost in what units?

Adding an extra 10 bits of precision to the output would solve this mostly however it then depends what happens with the output.

The set_scale_factor_generic function's output is, as the name indicates, generic: it is used in many places for different units. With scaling way maintenance costs, however, the output is a sint32 value representing Simucents, the currency of Simutrans.

As for using floating point to pre-calculate, that would not work without a drastic redesign of the basic code, as paksets are always loaded before the game itself is loaded.
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.

DrSuperGood

QuoteThe set_scale_factor_generic function's output is, as the name indicates, generic: it is used in many places for different units. With scaling way maintenance costs, however, the output is a sint32 value representing Simucents, the currency of Simutrans.
In which case I would recommend adding some sort of fractional accumulator to the player balance. If it is set to 1/1000 cent units you could get away without the 1,000 division and so lose no precision and this could be implemented without altering any of the existing maintenance code (would be a new method called to add/remove maintenance leaving existing one un-altered). For an extra 2 odd bytes of class size (ignoring padding) and a slightly more complex method for adding/removing way maintenance it seems worth it.

jamespetts

Quote from: DrSuperGood on December 26, 2014, 02:12:18 PM
In which case I would recommend adding some sort of fractional accumulator to the player balance. If it is set to 1/1000 cent units you could get away without the 1,000 division and so lose no precision and this could be implemented without altering any of the existing maintenance code (would be a new method called to add/remove maintenance leaving existing one un-altered). For an extra 2 odd bytes of class size (ignoring padding) and a slightly more complex method for adding/removing way maintenance it seems worth it.

Hmm, that would require changing all the maintenance cost code everywhere, which is no small task.
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.

DrSuperGood

QuoteHmm, that would require changing all the maintenance cost code everywhere, which is no small task.
I would imagine you just need a new method which is only used for ways. Since the accumulator is sub-cent units you can be nice to players and give it to them for free. The maintenance system should not need to be mechanically changed, only the adding of certain maintenances to it changed (using another method).

The accumulator is there to prevent loss of information from the division by 1000. The process of adding would go something like this.

Total = add + accumulator
Maintainance+= Total / 1000
accumulator = total % 1000

The key here is to not get it so that every tile costs the correct amount (impossible due to rounding) but the total is very close and errors do not stack. Even if you have a tile which costs 1c per KM at 1,000 tiles per km this would work since it would still add 1 to the accumulator per tile. After 1,000 tiles the player would correctly get booked for 1c. Removing 999 tiles would leave the accumulator with 1 in it which might not add anything to maintenance, but it will add to accumulation from other way types. Sure for very small numbers of very cheap tiles there could be a significant difference (in favour of the player mind you) however as your network expands the difference will become insignificant as the base maintenance value will be at most off by under 1 unit.

If month scaling is applied after total maintainace then it is good that the error scales as well since it makes maintenance more deterministic.

jamespetts

The maintenance cost for ways works by adding and subtracting a value in simucents to a single figure per player every time that ways are built or removed. I am not quite sure how this would work with an accumulator of the sort that you describe.
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.

kazarmy

Quote from: DrSuperGood on December 26, 2014, 05:28:45 AM
Until you find one random value and then everyone starts complaining how they are being kicked from the server.


After reading through the bridgewater-brunel server thread, I do see how a desync is something to be avoided at all costs. That a network game is at all possible is impressive; it does appear that for all clients to remain connected, the simulation has to run at the speed of the slowest computer which is no small feat.


Alas, while I'm sorely tempted to look at the source code to resolve any issues I have with the game, RL time constraints currently do not allow me to do so. Perhaps I should focus more; graphs/statistics that provide more detailed or more accurate information regarding a line, a stop or a convoy could come in useful. For example, it would be useful if the Profit graph for a line can be shifted downwards by a number representing the fixed maintenance cost per month of that line. It would spare me the mental calculations needed.


Thanks for the performance work you all have put in, and for the game itself. It's quite thrilling when after setting fast_forward to a high value, the Fast forward button pushes the simulation along at around x1000! And with wagons plodding along at 6km/h, such speed is very helpful when determining whether a line will be profitable.

jamespetts

Quote from: kazarmy on January 04, 2015, 07:26:50 AM
Alas, while I'm sorely tempted to look at the source code to resolve any issues I have with the game, RL time constraints currently do not allow me to do so. Perhaps I should focus more; graphs/statistics that provide more detailed or more accurate information regarding a line, a stop or a convoy could come in useful. For example, it would be useful if the Profit graph for a line can be shifted downwards by a number representing the fixed maintenance cost per month of that line. It would spare me the mental calculations needed.

How would you work out what the fixed (I presume infrastructure) maintenance of a specific line is, especially in cases where a line shares some or all of its infrastructure with other lines or even other players?
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.

kazarmy

I define the fixed (infrastructure) maintenance cost of a line to be the difference between the Fixed Costs values (as stated in the Finances window) immediately before groundwork (roads, stops, depot etc.) for that line and immediately after the line starts operation. This means that a line that piggybacks on existing infrastructure will have a lower (sometimes much lower or even zero) maintenance cost compared to if the line needs to be laid down from scratch. I am aware that expensive infrastructure like a canal might need to be used by multiple lines for it to be economically viable.

Even if I have to set the maintenance cost manually, it would be helpful. Then again, I probably could use the mental exercise.

I don't have experience with multiplayer and thus, apart from city roads, no experience on how revenue sharing works. I do think any access charge can be determined from the Finances window in the same manner as above though.

EDIT: Multiplayer.

jamespetts

I am not sure that I understand your definition here: imagine, for example, a railway route 10km of which is used by 5 lines, 15km of which is used by 3 lines, and a remaining 20km of which is used by 1 line each. Exactly how would each of those 5 lines work out its fixed cost?
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.

kazarmy

From the definition, the fixed cost for each infrastructure tile will be borne out by a single line only. This is the line that laid down the infrastructure tile in the first place.

Referring to your example, the 10km portion of the railway route will be maintained by only 1 out of those 5 lines. The other lines, assuming that they are from the same company, get to ride that 10km for free. Ditto for the 15km and 20km portions.

I'm curious, if each line was run by a different company, how would the fixed costs be distributed? Would a same or similar method be used?

jamespetts

If that were so, that would be a somewhat unsatisfactory and inauthentic way of keeping accounts, and not likely to be worth the effort to implement the feature.

As to charging players for using other players' lines, it is based on a share of the revenue set in simuconf.tab using the toll_revenue_percentage. Suppose that toll revenue percentage were set to 33 and suppose further that a convoy spends 50% of its time on a native way, and 50% of the time on another player's way: the player over whose ways it has travelled will earn 33% x 50% (16.5%) of the revenue.
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.

kazarmy

I suppose a more satisfactory and authentic way would be to distribute infrastructure fixed costs equally over all lines using that infrastructure. That would probably require a linked list for every infrastructure tile, with usage for stops and ways determined by a line's schedule. For depots, usage would probably need to be determined by the depot(s) found using the "Go to depot" function, along the line's entire schedule. Every time infrastructure or a line's schedule changes, infrastructure usage will need to be re-determined.

Sounds like fun but a lot of coding effort. Not sure about the computational and memory requirements for this.

jamespetts

Even this would not be satisfactory, because the actual use of the infrastructure by the line should be taken into account; however, I do not think that this is how things are actually accounted for: running extra traffic over certain infrastructure does not increase its fixed maintenance cost, and so, as long as the infrastructure justifies its maintenance and construction costs, extra traffic that would not be profitable if the fixed maintenance costs had to be taken into account but is profitable if they are not should not show as loss making.
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.

kazarmy

Quote from: jamespetts on January 05, 2015, 12:49:38 PM
Even this would not be satisfactory, because the actual use of the infrastructure by the line should be taken into account;

Lines that do not have convoys should be disregarded. The convoy routefinding system should be able to tell what infrastructure is used by a line.

Quote from: jamespetts on January 05, 2015, 12:49:38 PM
however, I do not think that this is how things are actually accounted for: running extra traffic over certain infrastructure does not increase its fixed maintenance cost, and so, as long as the infrastructure justifies its maintenance and construction costs, extra traffic that would not be profitable if the fixed maintenance costs had to be taken into account but is profitable if they are not should not show as loss making.

This might require the capability of merging several lines into one composite line for accounting and charting purposes. For example, all coal lines that use canal A could be combined into one composite line and the fixed costs for canal A be assigned to that composite line. It would be easier to read one chart instead of several. Passenger lines using that canal could be exempt from the fixed costs.

The details (e.g. the UI, multiplayer access charges) could be tricky to work out but this problem does appear to be soluble. Whether it's going to be to everyone's liking is another matter.

jamespetts

I was not referring to lines without convoys, but rather the situation in which some lines make heavier use of a given piece of infrastructure than others. In fact, the satisfactory way of doing this in accounting terms would be to allow players to create customisable cost centres for accounting purposes, but that would require a lot of work in the code, and there are lots of higher priority projects that impact on the actual balance of gameplay.
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.