News:

Simutrans Tools
Know our tools that can help you to create add-ons, install and customize Simutrans.

Average comfort for lines is computed wrong....

Started by neroden, January 09, 2011, 05:48:18 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

neroden

Comfort levels, as displayed for lines, average non-passenger convoys with passenger convoys, which isn't right.

In void convoi_t::laden() :

   // Recalculate comfort
   book(get_comfort(), CONVOI_COMFORT);

This needs to be done only for passenger-carrying convoys.  Is there a quick way to determine whether this was a convoy carrying passengers?

One other point.  Average speed isn't computed right for lines either.  The average speed for a journey is computed right (so the payments are right), but for line display the average speed is then averaged over the number of journeys (so one 5 mph trip and one 35 mph trip average out to a 20 mph "average"), even if the 35 mph trip is twenty times longer than the 5 mph trip.  Makes it hard to get meaningful numbers out of the stats window.

This may require specialized book_average_speed and book_comfort routines.  Actually, we should have those anyway, checking inside the book() routine is gross.

FOLLOWUP THOUGHT.
Things could be simplified somewhat if we "changed the rules" to make a comfort of 0 mandatory for freight and impossible for passengers; that is, if we forced all passenger vehicles to have comfort values of at least 1, even overcrowded vehicles.  This would allow for an easy test, where anything with a comfort of 0 was to be excluded from the average.  Empty passenger vehicles shouldn't count into the averages either, and they also have a comfort of 0.  We can't currently do this because it's theoretically possible to get a comfort of zero with a carriage with a very low specified comfort value, either specified as 0, or specified as 1 with zero ordinary capacity and a positive overcrowded capacity.  :-P

jamespetts

Thank you for that, that's helpful - I shall look into amending the comfort when I have a moment (although I had changed it at one point to ignore non-passenger vehicles when computing average comfort - is that mechanism no longer present?).

As to the average speed, things aren't quite so simple. The line's average speed is what is used for the calculation of revenue, not the actual average speed of a particular journey (as the price in real life does not depend on how fast that the journey was that day, but the general average over a period of time). Averaging between different journeys makes sense in this context, since a passenger journey is just as likely to be over a short distance as over a long distance, so the system seems correct.
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.

jamespetts

I've now pushed a fix for the comfort issue to the 9.x branch.
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.

neroden

#3
Quote from: jamespetts on January 09, 2011, 12:21:20 PM
Thank you for that, that's helpful - I shall look into amending the comfort when I have a moment (although I had changed it at one point to ignore non-passenger vehicles when computing average comfort - is that mechanism no longer present?).
That's still there -- it computes it correctly for each *convoy*.  Unfortunately for a *line* it averages (for example) an all-mail convoy with an all-passenger convoy.

QuoteAs to the average speed, things aren't quite so simple. The line's average speed is what is used for the calculation of revenue, not the actual average speed of a particular journey
Oh, awful.  You need to change that, and here's why.  It's quite typical to have a route which runs London Victoria - Clapham Junction - Brighton.  The way you're averaging, you're punishing the player for doing this, because you're averaging the low-speed Victoria-Clapham section with the Clapham-Brighton and Victoria-Brighton routes.  This is neither reasonable nor acceptable.  If it were weighted by number of passengers (very few are travelling Victoria-Clapham), it would be OK -- but it's not weighted by number of passengers, as far as I can tell.  We want the speed *experienced by the average passenger*.

Another important point: is revenue still credited for passengers at intermediate points, or only once the whole trip is finished?  (Did you fix that bit?)  More importantly, is average speed computed separately for Victoria-Brighton, or (as I suspect) is it figured only on Victoria-Clapham and Clapham-Brighton?

I know we are *correctly* computing the journey time from Victoria to Brighton.  The payment should be based on *that* -- distance divided by average journey time.  Isn't it?   It should most certainly not be based on the average line speed.

Quote(as the price in real life does not depend on how fast that the journey was that day, but the general average over a period of time). Averaging between different journeys makes sense in this context,
No, it doesn't.  See above.
Quotesince a passenger journey is just as likely to be over a short distance as over a long distance, so the system seems correct.
Wrong.  See above.  You haven't thought this through.  Maybe the system is correct, but only if it's quite different from the way you described it.

EDIT: I reviewed the code in convoi_t::calc_revenue.  You're right, it's all being done horribly wrong.  I despair of fixing it without a complete rewrite.  Indeed, we are not computing journey times correctly either.  There are some really horrible exploits which I can think of off the top of my head -- throw some very fast-moving vehicles with miniscule passenger capacity into a line consisting of very slow vehicles, and watch your revenues rise as your miscomputed "average speed" rises.

I was wondering, incidentally, why my average speed had hit 64 on a line with no vehicles which can go faster than 55, and now I'm suspecting that the average speed computation is just complete garbage from beginning to end.  Ugh.  I wish I had the time and energy to rewrite it.

jamespetts

Quote from: neroden on January 09, 2011, 08:53:36 PM
That's still there -- it computes it correctly for each *convoy*.  Unfortunately for a *line* it averages (for example) an all-mail convoy with an all-passenger convoy.

I think that I've fixed this now - have a look at the 9.x branch.

QuoteOh, awful.  You need to change that, and here's why.  It's quite typical to have a route which runs London Victoria - Clapham Junction - Brighton.  The way you're averaging, you're punishing the player for doing this, because you're averaging the low-speed Victoria-Clapham section with the Clapham-Brighton and Victoria-Brighton routes.  This is neither reasonable nor acceptable.  If it were weighted by number of passengers (very few are travelling Victoria-Clapham), it would be OK -- but it's not weighted by number of passengers, as far as I can tell.  We want the speed *experienced by the average passenger*.

The difficulty with weighting by number of passengers is that there's no way of telling when averaging by what numbers to divide the speeds as we do not know at the time of booking the speeds how many passengers (etc.) will travel in the next stages. If we can't do it by number of passengers in this way, the present system is no worse than a system that computes speed averaged by distance.

QuoteAnother important point: is revenue still credited for passengers at intermediate points, or only once the whole trip is finished?  (Did you fix that bit?)  More importantly, is average speed computed separately for Victoria-Brighton, or (as I suspect) is it figured only on Victoria-Clapham and Clapham-Brighton?

Revenue is computed for passengers when they finish their entire journey leg: see sint64 convoi_t::calc_revenue(ware_t& ware).

QuoteI know we are *correctly* computing the journey time from Victoria to Brighton.  The payment should be based on *that* -- distance divided by average journey time.  Isn't it?   It should most certainly not be based on the average line speed.

The problem is that we don't know the average journey time except by computing it from the distance (which is fixed) and the average line speed (which is averaged). There isn't somewhere to store lots of point to point average journey times - I'd be worried about memory consumption if we did it that way; it would in any event be quite a significant piece of coding to do that.
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.

neroden

#5
Quote from: jamespetts on January 09, 2011, 09:03:05 PM
Revenue is computed for passengers when they finish their entire journey leg: see sint64 convoi_t::calc_revenue(ware_t& ware).
Whew, I thought that was correct.  (Sorry to be so negative on the last posting, the situation is not as bad as I feared.)

QuoteThe problem is that we don't know the average journey time except by computing it from the distance (which is fixed) and the average line speed (which is averaged). There isn't somewhere to store lots of point to point average journey times

OK, so that is the problem.  I think this is solvable in a reasonable manner.  First, note that the halts are already storing this information.  Now, how can we compute it correctly?

Each packet of passengers knows where it departed from.  It knows where it arrived.  It knows *when* it arrived.  All it is missing is *when it left*.

Quote- I'd be worried about memory consumption if we did it that way; it would in any event be quite a significant piece of coding to do that.

Experimental has already increased memory usage by adding arrival_time and accumulated_distance to ware_t.  I propose adding departure_time.   I'm hoping accumulated_distance can be eliminated eventually, but I'll have to think about that.

Hmm.  My next project, clearly.  An experimental branch of experimental.   I wonder what to call it.  :-P  double-experimental?

This is my plan:
(1) ware_t will track its time of departure.  Upon arrival, it will therefore know how long the trip took.
(2) In the first pass, average line speed will be computed by booking it individually for each ware_t, so that it can be averaged over the passengers; the passenger denominator will be totalled up during each month as well as the speed-for-that-passenger numerator.  This still causes distortions, but it's a start.

EDIT: I've gone horribly off topic.  Your fix to the comfort looks good.

jamespetts

Ahh, the halts don't store a complete list of point to point timings, however: they only store the journey times for the best convoy or line to a certain destination (and the total journey time to that destination, not to all intermediate points). Passengers may not be using the best convoy or line, as they might have waited too long, or have calculated that the second best will get them there more quickly than waiting for the best.

Also, we need to be careful to use averages not the actual journey time in question, as it is unrealistic for passengers to pay more because their particular journey happened to be quicker than expected, or less if their particular journey happened to take longer than expected.

However, I'd be very interested to see a branch!
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.

neroden

#7
Quote from: jamespetts on January 09, 2011, 09:51:21 PM
However, I'd be very interested to see a branch!
You could help me with one thing.  The arrival_time number marks when the ware_t shows up at the halt.  But *when is it read*?  I've been having trouble figuring this out.

EDIT: I think I've found out, it's always checked prior to exiting the halt.  This means I can *reuse it* to save memory.  :-)

jamespetts

arrival_time is used to calculate waiting time at stations: see haltestelle_t::hole_ab:


// compatible car and right target stop?
if(next_transfer == plan_halt)
{
const uint16 speed_bonus = tmp.get_besch()->get_speed_bonus();
uint16 waiting_minutes = get_waiting_minutes(welt->get_zeit_ms() - tmp.arrival_time);
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.

neroden

I have an almost-working branch already.  The problem I've encountered is the completely insane arbitrary constants which everything is being multiplied by.  I'm going to have to go back and clean up the time handling in standard like I was planning to before I can make a routine where the numbers make sense to me.  :-(

sdog

#10
as the price in real life does not depend on how fast that the journey was that day, but the general average over a period of time[/quote]
that doesn't matter, you can easily charge the money based on that journey. Since you are summing the whole costs up. When speed bonus is a linear function of speed both should be equal: N f(a_1 + a_2 + ... a_N)/N = f(a_1) + f(a_2) + ... f(a_N); where a are the average speeds of convoys 1 to N in a line and f(x)=b x + c is the speed bonus function.
You would perhaps overcharge for a particular fast train and undercharge for a particular slow train.
But this does not matter for the revenue over the course of a month. (it would be unfair for the simpeople, but we don't care for them)


edit accidentally cut of a line at the end, amending it now;

neroden

#11
Quote from: sdog on January 10, 2011, 02:21:38 AM
as the price in real life does not depend on how fast that the journey was that day, but the general average over a period of time
that doesn't matter, you can easily charge the money based on that journey. Since you are summing the whole costs up. When speed bonus is a linear function of speed both should be equal: N f(a_1 + a_2 + ... a_N)/N = f(a_1) + f(a_2) + ... f(a_N); where a are the average speeds of convoys 1 to N in a line and f(x)=b x + c is the speed bonus function.
You would perhaps overcharge for a particular fast train and undercharge for a particular slow train.

:grin:
This would simplify the code even more.  Unfortunately, I'm delayed by, as I said, trying to straighten out the unit conversions.  But if you're curious, check out the departure_time branch; it compiles.  I don't think it's ready to submit to experimental yet though, because I want to move the code around more; I think I'm going to push the average speed calculation down into the revenue calculation, and try to pull as much of the units cleanup from my standard branch work as I can.

EDIT: upon testing version 1 here, it works, but there are serious, serious unit conversion problems.  To get the numbers to "look right" I have to multiply by 4 and I really don't understand why, although I do see mysterious multiplications by 4 floating around elsewhere.

EDIT: Sigh.  Found another stupid bug in my code.  Now I really have it working, but I still don't believe the unit conversions are right -- I feel like there should be an adjustment for km_per_tile, but there wasn't in the existing code and there isn't in my code....

jamespetts

#12
Nathaneal,

I've finally had time to look over your code on this: that looks like a lot of work! Just to check that I've understood what you've done correctly: you change the way that a convoy's average speed is calculated to take into account the number of passengers who travel between a particular pair of stops, so that, for example, if 100 passengers travel between A and B and only ten between B and C (and, say, 20 between A and C), the average speed between A and B will have ten times more influence on the average speed of the line than the average speed between B and C (and five times more influence than that between A and C)?

Testing it on a very small map with a single 'bus route seems to produce figures broadly consistent with the previous approach, but testing it on a large map (Sdog's 162) seems to result in greatly elevated journey times and, consequently, much reduced ridership. I am not quite sure whether this is correct yet, but there seemed to be some somewhat odd outcomes, with a short 'bus ride in the 1960s from one end of a small town to another taking an hour.

As to the numbers: were you aware that minutes were stored in tenths so as to increase accuracy?

Edit: Testing further, it seems that the travelling times in 9.2 were equally long for SDog's 162.sve game, and comparing the two after running them for some months gives broadly similar results, so it seems that the speeds/times are within a sensible range.
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.

neroden

#13
Quote from: jamespetts on January 22, 2011, 11:45:37 PM
Nathaneal,

I've finally had time to look over your code on this: that looks like a lot of work!
You looked at it just as I was preparing to go off on a trip... I'm back now.
QuoteJust to check that I've understood what you've done correctly: you change the way that a convoy's average speed is calculated to take into account the number of passengers who travel between a particular pair of stops, so that, for example, if 100 passengers travel between A and B and only ten between B and C (and, say, 20 between A and C), the average speed between A and B will have ten times more influence on the average speed of the line than the average speed between B and C (and five times more influence than that between A and C)?
Correct.  :-)  Essentially, if each passenger submitted a review of their train trip and listed the speed he experienced, and the marketing office computed the "average speed" by averaging the speeds reported by each passenger -- that's the speed I'm computing.  That's the logic behind it.  Each unit of freight reports as well, of course.

QuoteTesting it on a very small map with a single 'bus route seems to produce figures broadly consistent with the previous approach, but testing it on a large map (Sdog's 162) seems to result in greatly elevated journey times and, consequently, much reduced ridership. I am not quite sure whether this is correct yet, but there seemed to be some somewhat odd outcomes, with a short 'bus ride in the 1960s from one end of a small town to another taking an hour.

As to the numbers: were you aware that minutes were stored in tenths so as to increase accuracy?
Yes.  I haven't actually gotten deep into that....

QuoteEdit: Testing further, it seems that the travelling times in 9.2 were equally long for SDog's 162.sve game, and comparing the two after running them for some months gives broadly similar results, so it seems that the speeds/times are within a sensible range.

Oh good.  :-)  

You may have been seeing the "transition problem" by loading a game on my branch which was saved with the old code -- I didn't put in much of any transition code so it can cause screwy (i.e. low ridership) results for the first month or so before a new average is computed.  I'm not quite sure how to fix that, I was OK with just accepting it.

I merged the branch back up to the current 9.x head.  I'm going to work on further improvements.  But first I must ask a question.  I am currently using a single field in ware_t both for the last time of arrival and the last time of departure; this requires careful sequencing of procedure calls.  I could instead have separate fields, but I worry about memory bloat, because ware_t is used a LOT.  Should I sort the procedure calls or use separate fields?

(EDIT 1): commit 6f685f61d2745cda1c87dafc304f03e9456ea83b is the version adapted to the current 9.x branch.  This simply changes computation of averages.  It *will* require a version number increment for experimental, but I'd like you to try it out and see if you're willing to go with it for version 10.

Subsequent commits on the departure_time branch will change the computation of revenue to be based on individual trip speed rather than average speed.  (Average speed will continue to be booked, of course, and used for many purposes including passenger determination of which line is "fastest" from a point.)  I think this will work out just fine, but for reference, commit 6f685f6... is the last one on the branch which uses average speed for revenue.

(EDIT 2): Here's the thing I'm currently having trouble with.  Upon arrival of a convoy, I want to hack into the connexions database for the origin halt, and do an incremental update; if this is the line registered as being the "best" for this origin-destination pair, I want to change the travel time specified to match this convoy's travel time (whether up or down); if this is not the line registered as being the "best", and its time is better than the one currently in the database, I want to replace the connexion entry with this one.  I *have* the right point in the code to do the database edit, I *have* the travel time, I *have* the origin and destination halts, but it appears that the connexions database is currently never incrementally updated, but instead totally rebuilt periodically.

(Yes, this might result in unstable pathing, since I'm using a "most recent bus" time rather than a long-term average, but I'd like to try it this way and see how it plays; it might actually work out just fine, and if it did it would be elegant.)

I'm not sure how to change the connexions db to allow for an incremental update.  James? Knightly?  To make this work right I'd also want to discourage total rebuilds of the connexions db; because I see no way of getting an actual point-to-point travel time during an arbitrary total rebuild.  (Total rebuilds of the PATH db are fine, it's just the CONNEXIONS db I want to update incrementally.)  Tell me what problems I might run into....

jamespetts

Nathaneal,

apoplogies most sincere for the delay in reverting to you on this: I have been very busy at work recently, and what little time that I have had for Simutrans I have spent on bugfixing and one or two shorter term projects. As you identified, the sorts of changes that you are looking at would need to be made with a major version increment. Currently, my priorities are to finish fixing some bugs (the failure of the system that Bernd Gabriel wrote some time ago to notify players when a "no route" is because of a weight limit and the duplication of vehicles in the depot window when a vehicle is purchased, and, perhaps of greatest concern, the failure of Linux clients to interface with Windows clients reliably over a network, which will probably have to wait until 9.5 to be fixed and will likely require a gargantuan amount of work), as well as implement one or two smaller feature upgrades, mainly related to industries, such as the demand of consumer industries in towns being based on the relative size of those towns. That being noted, however, these longer-term things do need planning, so it is worthwhile discussing them now to go with the other things that I am planning for 10.0.

As to the details, firstly the departure times system. Having thought about the matter with some care, I do have a reservation about the average speed calculation in your branch, which is this: the current system's problem is that the average speed for a convoy's entire route may not be the average speed for point-to-point journeys on the convoy's route, so that some trips are generated incorrectly. Your solution is to weight the average speed by the number of passengers (or units of freight, etc.) so that one gets, in effect, an average speed per unit of goods transported, rather than an average convoy speed. I see three potential difficulties with this approach. The first, and most significant, of those difficulties appears to be that, whilst it makes the average speed more accurate for a greater number of passengers, for those passengers who are in the minority, it makes the average speed far less accurate such that the overall accuracy is unchanged. For example, it might make the accuracy of the average speed for 100 passengers 10% better, but make the accuracy of the average speed for 20 passengers 50% worse: the net accuracy remains unchanged, but there would actually be greater routing anomalies for the minority of passengers. In the original Victoria, Clapham Junction, Brighton example suggested: whilst the many Clapham to Victoria passengers might well have slightly more accurate times, the few Clapham to Brighton passengers would have much, much less accurate times.

Secondly, it seems counter-intuitive to have a graph that says "average speed" that does not, in fact, represent the average speed of anything: players are likely to find it confusing if changes in ridership alone can have a very large effect on the "average speed" of a convoy.

Thirdly, using a per unit computation is likely to cause particular anomalies with mixed cargo types. Some cargo types (in Pak128.Britain, most notably cars) are much heavier and bulkier than others, and carry a higher per unit price to compensate such that, for example, one car might be the equivalent of ten units of piece goods. If there was a convoy carrying both cars and piece goods, the piece goods' averages would rank ten times more weightily than the cars' even though the same weight, bulk and economic value of each was being carried.

I think that all this requires careful consideration of the alternatives. One is to leave things as they are, which is workable, but creates the anomalies identified and incentivises calling patterns that are simpler and less efficient than in reality, which is undesirable. Another option is to look at whetehr it really would consume too much memory to keep a point to point table of connexions in each halt. This would not require any significant changes to the pathing code (except a few non-functional changes where it interfaces with the connexions code), but would require rewriting a significant amount of connexions code. Consideration would have to be given to just how much impact that this would have on memory consumption. Essentially, each halt would be required to store a list of actual journey and waiting times for each line or lineless convoy logged since the last recalculation of the connexions table. Then, whenever the connexions table is recalculated, each entry would be averaged to reach (1) a list of average journey and waiting times for each line/lineless convoy from the halt to each destination directly served; and (2) the single figures representing the best journey/waiting time pair, which could then be used in the same way as the current figures in the pathing calculations.  I'd be interested in any calculations that you might be able to make as to just how much more memory that this would really consume (I suspect that it can be calculated quite easily for any given size of game by looking at the number and size of data that would need to be stored for each halt/number of lines, the average number of lines per halt and the number of halts in a large game).

A system such as this would very effectively eliminate the anomalies seen with the average speed based calculations at present. (In the case of insufficient data of actual journies, the current averaeg speed system should continue to be used). It does, however, raise somewhat vexing questions about the interaction between travelling and waiting times. Currently, all waiting times are divided by two to represent the fact that Simutrans does not simulate timetables, and, if there were timetables, passengers would be able to time their journeis more efficiently (and connexions would be timed to  meet each other efficiently) such that the amount of waiting would be reduced significantly. Whether this aspect would be affected I do not know, but that should be borne in mind for the discussion that follows. The real issue is: how are waiting times to be computed for particular lines? Suppose that a particular station is served by a very fast, but very infrequent express, and a slower, but much more frequent, stopping service. The aim is to find the lowest total journey time (that is, combination of travel and waiting times). We do not know at any given moment how long that it will be before the next express train arrives. Passengers arrive at the station. A local train arrives. Some algorithm must determine (as it does now) whether passengers would be better off boarding that local train or waiting for the express. Inevitably, a significant number of passengers who would have taken the express if they arrived at the right time end up taking the stopping service. When the express comes, the total waiting times of all the passengers who board the express represents the waiting times only of those passengers who arrived at an optimal time to catch the express rather than the local train (or else they would not have caught the express, but the local). That is a lower figure than it would be if all of the passengers who could take the express all waited for it whether it was optimal to do so or not. That means that the waiting time of the express is artificailly low. The waiting time for the local is also low, but not artificially: but its travelling time is higher. However, the simple system of taking the "best" travel/waiting combination would take the travel/waiting combination of the express and ignore the local, which would give an artificailly low journey time, as many passengers who turn up at the station end up having to catch the slower local instead of the faster express. In a situation with competition between players in particular, this anomaly could cause real problems. One solution might be somehow to combine travel/waiting times into a global journey time for passengers between one station and all others directly connected to it, but we would still need to retain separate travel/waiting times for passenger informational purposes, and also to use when we need to fall back to the current system when there are insufficient data to get average actual journey times. This would then end up using more memory, and possibly introducinga additional compexity that seems undesirable. Any thoughts as to an elegant way of managing this issue (especially that maximises code readability and maintainability and minimises memory use and compuational load) would be very welcome.

The next issue is of computing journey revenues based on the actual journey times rather than average journey times. This is less problematic, I think, as the net result is likely to be the same, and the advantage is that players can clealry see the revenue benefit of faster convoys in a way not possible currently. However, some pause for thought should be given even to this: I do tend to prefer a long-term average system, as it is more realistic: your standard class return from London to Birmingham on the West Coast Main Line is based on the time that it generally takes to travel on that line, not the time that your particular journey that day happened to take. A long-term average does give a certain additional element of predictability. If, however, such a system as is mentioned above were implemented, an average might be difficult to calculate as one would have to use the point-to-point average, which would then have to be retrieved from the origin halt for the calculation of the revenue of each journey (and would be less transparent than using the convoy average speed). If such a system was implemented, I can see that there would be some benefit to an actual journey time based approach to the computation of revenue. One query that remains is whether the same approach should also be applied to comfort.

As to changing the way in which the connexions databse is filled, I am very wary of meddling with the mechanics of the pathing system in that way, as Knightly's system is very intricately designed, and I do not fully understand all the inner workings of it. At preesnt, it works well, so I am not sure of any particularly compelling reason to change it in any event. The point to point table suggested above could very likely be done without any non-trivial changes to the pathing system, but changing the intervals at which the connexions database is rebuilt is likely to have a more significant impact that is hard to assess.

Thank you for putting so much effort into trying to improve Experimantal, and apologies again for the delay in replying to this topic: you will appreciate, hopefully, from the length and detail of this reply why I was not able to do so immediately.
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.

jamespetts

Further thought has given rise to a potentially simpler solution to all of this, albeit still one which will consume more memory than at present: if each line (or lineless convoy) stored an average speed for each origin-destination pair and if that average speed was used for computing the journey time using that line or convoy between that origin and destination pair, this would avoid anomalies such as the Victoria-Clapham-Brighton example given above without requiring a major reconceptualisation of how journey times are calculated or encountering any additional complexities with respect to waiting times. The general average speed of the convoy/line would continue to be calculated for use in the graph, and also as a fallback if the point to point average is not available for some reason.

The averages would be recorded by recording one departure time for each stop on the schedule. At every arrival, the convoy would check the departure time for each other stop on the schedule. For departure times of zero (the default), it would skip the average calculation, as it would not previously have stopped at that point. If the same stop appears more than once in the schedule, the most recent departure time from that stop would be considered. There would have to be some mechanism for accounting for mirrored schedules (perhaps having two departure times per stop for each mirrored schedule and simply taking the latest of the two).

Each arrival/departure station pair would be stored as a struct comprising a pair of koord3d objects. Those would be stored as keys in a hashtable. A new type of hashtable (derived from the current koordhashtable) would have to be written for the purpose. The values in the hashtable would be structs comprising (1) the total sum speed for that origin-destination pair, and (2) the number of times that the average speed has been recorded since the last reset. Every time that a new average speed is recorded, it would simply be added to the first number in the pair and the second number incremented by one. When rebuild_connexions() is called (which checks for the purposes of the pathfinding system and the display of journey times at stations), the average would be computed (by dividing the first number by the second), and both values reset. The pathfinding system would therefore be based on the average speed between two particular points rather than a generalised average of all of the convoy or line's activities.

This need not entail any changes to passenger waiting behaviour or waiting time computation. It might, however, be possible to refine waiting behaviour better on the basis of the further information available as a result of this method, although I have not yet considered in detail how. One possibility that occurs to me is to record the frequency of any given line or lineless convoy calling at any given station/stop in any given month period, and use that plus the existing passengers' acumulated waiting time to deduce the likely remaining waiting time for the faster convoy to be able to compare that with some accuracy to the difference in journey time for the better convoy than the one that has presently arrived (whilst maintaining the frustration factor behaviour of passengers boarding the first convoy that will get them to their destination if sufficient time relative to the expected waiting time has elapsed). This would require additionally recording the frequency of each line/convoy calling at each stop.

This additional information gives rise to new opportunities to display this information in a diagnostically useful way, althgouh finding and implementing an effective and easily understandable way of doing this is likely to be time-consuming and  if implemented at all, is likely to be substantially later than the principal feature(s) themselves.

I should be interested in any feedback on these suggestions.
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.

Carl

#16
Hi jamespetts,

These proposals sound interesting and promising! I want to make sure I'm understanding the proposal correctly. First, I'll describe a scenario which the current average-speed/journey-time framework in Experimental treats badly, and ask whether it is this kind of flaw which the new feature is designed to solve. Second, I'll outline a further proposal for altering passenger waiting behaviour.

Consider the following two lines:

A------------------C--D--E--F--G--H (Line 1)
                      B--C--D--E--F--G--H (Line 2)

Suppose that the dashes denote a measure of distance here (say 1km), and suppose that both lines are operated with the same kind of vehicle. Line 1 is likely to end up with a higher overall average speed than Line 2 because of the long run between A and C. Under the current framework, this will result in the game mistakenly calculating that Line 1 will provide a quicker journey time between C and H. As such, no passengers will board Line 2 in order to travel from C to H -- despite the fact that, in actual fact, the lines should take exactly the same time to get from C to H.

Now I take it that one aspect of this problem will be solved by the new proposals. That is: the journey time calculation between C and H will be based on the average speed between C and H only. This should mean that Line 1 and Line 2 provide very similar journey times between C and H. Is this correct?

A second aspect of the problem invovles passenger waiting behaviour, and you address this in your penultimate paragraph. That is, even with these changes made, passengers from C to H will still have to favour one line over another. Random variation makes it likely that either Line 1 or Line 2 will be marginally quicker between C and H -- even if just by 1kph -- and if this happens then all passengers from C to H will try to take the "quicker" train, even if it is only quicker by 1 minute. A more ideal behaviour, given the similarity in journey times, would be that passengers take whichever Line arrives first. The challenge, I take it, is how to implement this in a satisfactory fashion -- and in my view this challenge is just as important as resolving the journey time calculation.

Here's a quick sketch of a solution. Whenever a convoy bound for H arrives at C, waiting passengers should make the following two-step calculation.

Step 1. Is this convoy on the fastest line between C and H? If yes, board. If not, go to step 2.

Step 2.
A. Calculate (F-W). (that is, F minus W)*
B. If this value is greater than or equal to S, board the slower convoy.
C. If this value is smaller than S, wait for next convoy and return to Step 1.

*Legend:
F = Average waiting time for faster convoy
W = Number of minutes passenger has been waiting so far
S = Number of minutes passenger would save by taking faster convoy (i.e.: Slower convoy journey time minus faster convoy journey time)

The F-W value thus represents how long I should expect to wait for a faster convoy, given how long I've been waiting already.

Let me explain this with an example. I'm waiting for a convoy between C and H. Suppose that the average wait time for the faster convoy is 20 minutes, and suppose that a slower convoy shows up after I've been waiting for 10 minutes. I should expect a faster convoy to be along in 10 minutes -- so should I board the slower convoy which is here now? Well, it depends on just how quick the faster convoy is. If the faster convoy is more than 10 minutes quicker between C and H, then I should wait for that. But if it is less than 10 minutes quicker, then I should take the slower convoy. This decision process is enshrined in step 2 above.

I take it that passengers already undertake a calculation something like this -- if they've been waiting long enough, they'll take *any* convoy to their destination. This proposal is in a similar vein, but treads a more moderate ground. I think this solution does more or less what you outlined at the end of your post, but note that it doesn't require any extra recording of frequencies for lines.

I'd be very interested to hear what you think about this!


-------------------------
Further thoughts
Step 2 would have to be merged with the current behaviour for this to work properly. Consider the case where the F-value is 30 minutes, but I've already been waiting for 90 Since the F-W value here will be -60, it will always be smaller than the S-value, and thus I will never board a slower convoy. But having waited this long, I should presumably board any convoy going to my destination, no matter how slow. We could thus add a further step:

2D. If F-W < 0, then board the slower convoy.

Perhaps this is too extreme, given that F is the *average* waiting time. A better version of this might be:

2D. If (F-W) + F < 0, then board the slower convoy.

That is, the nuclear option (boarding whatever shows up) will not come into effect until the passenger has been waiting for *twice* the average waiting time of the faster convoy.


Carl

I realise that the above is getting somewhat off topic, and I'd be happy to start a new topic for discussion of passenger waiting behaviour if you'd rather keep to the original points here.

neroden

#18
Quote from: jamespetts on April 26, 2011, 10:10:27 AM
Further thought has given rise to a potentially simpler solution to all of this, albeit still one which will consume more memory than at present: if each line (or lineless convoy) stored an average speed for each origin-destination pair and if that average speed was used for computing the journey time using that line or convoy between that origin and destination pair, this would avoid anomalies such as the Victoria-Clapham-Brighton example given above without requiring a major reconceptualisation of how journey times are calculated or encountering any additional complexities with respect to waiting times.
Sorry to take so long to get back to you, I've been unable to work on this for a while.  This was my original conception, *and my branch does 3/4 of the work of this already*.

Specifically, it tracks individual trip speeds.  When each packet of passengers gets off the train, the data of the trip time, trip distance, origin, and of course destination, is available, the way I'm doing it.  

Keeping things simple, the revenue is immediately based on these numbers.  (Yes, it's slightly less "realistic", but the average comes out the same, players see more immediate revenue response to improving or reducing line speed, including big hits for delayed trains, and it's significantly more straightforward code -- you should definitely pay according to actual trip speed.)

Furthermore, I proceed to accumulate an appropriate type of average -- the code I use to compute the average speed is necessary in order to this right, I figure.  On my branch it's easy to compute a separate average (computed in my new way) for the individual origin-destination pair via this line, which represents a fair description of people's perception of the line speed.  So the key here is having the origin-destination database and a place to *store* the average speed.  That was where I got stuck.

The rest of your message involves a proposed database structure, but mind you, I think you're reinventing the wheel, badly, and duplicating data structures.

We already have a fairly elegant structure for a database of CONNEXIONS.  We already have a structure for booking averages and tracking them over time.  I used the latter one, rather than reinventing the computation entirely.  You could easily have a mini-CONNEXIONS db for each line (it would have relatively few entries).  Or, perhaps better, enhance the existing CONNEXIONS db to contain multiple entries for each station pair (one for each line running between the station pair), and to allow for incremental updates.  That puts all the data you need in the place you want to look for it when trying to work out passenger behavior.  

In particular, if this is done, each origin can successfully track the average waiting time data for each destination via each line *and* successfully track the average journey time data for each destination via each line (using the enhanced connexions database), which will allow you to implement whatever passenger line choice behavior you want in a nice self-contained module of code which can be tweaked easily without running hither and yon across the codebase.

EDIT:  I guess the key here is having a place to store the origin-to-destination trip time averages, and deciding where to store them.  (I do think a generic "average computation" mechanism should be used and that the "book()" methods should probably be leveraged for this purpose, unless there's some reason why that will get messy).  The connexions database was what I immediately eyed, because it's already handling origin-destination pairs along individual lines.  Its sole problem is that it doesn't allow for incremental updates.  If it did it would be the natural place to store the data.

Once the data is available and stored in an appropriate location, masses of goo can be ripped out of the code and the passenger behavior can be isolated to a few tightly-written modules, making the code much easier to follow.  (A lot of the work I did already is fundamentally code restructuring, of the sort Simutrans *badly, badly needs*, being a very ugly codebase at this point.  James, I notice you have an aversion to restructuring the code.  While if you're bad at it perhaps you shouldn't do it personally, the code *needs* restructuring and I'm good at that, so I urge you to accept structural cleanups which will simplify all our work in the long run.)

jamespetts

Nathaneal,

thank you for your reply; this is a complex issue indeed. As you may have noticed from other threads, I am rather tied up, in Simutrans terms, with fixing the physics engine after having to strip all the floating point code from it in order to make it work in network mode when different binaries are compiled with different compilers; that is a rather enormous job (particularly as I did not write the physics engine in the first place), and somewhat urgent (as it will not be possible to play Simutrans-Experimental online until it is done). I then have to go back and implement all of the changes from Standard (including some major changes to the way in which industries work, which impact on parts of the code heavily modified by Experimental, such as passenger generation); that will take us to version 9.7. That will all take a long time (in particular, I have no idea how long that it will take to fix the physics ready for the release of 9.6). However, when that is done, this is one of the next things to look at ready for 10.0 (as additional data will have to be saved, requiring an increment in the saved game number). Consequently, movement on this will not be fast at this stage, although it will be useful to know where we are going.

We need to separate discussion, I think, of (1) changes to the structure of the code; and (2) changes to the behaviour of the code. The former is discussed in terms of maintenance and, perhaps to some extent, execution efficiency, and the latter in terms of the desirability of the behaviour in question. In so far as it is practicable, the structure should be determined by the desirable behaviour, not the other way around.

In terms of desirable behaviour, the necessary thing to avoid anomalies is to calculate any given journey time by reference to the average speed of lines/convoys between the particular start and end points of that journey, rather than on the overall general average of that line/convoy's entire diagram. This will be sufficient to avoid the significant anomalies discussed in this thread. The other changes (the calculation of revenue per trip rather than on the basis of the existing average and the computation of journey times based on the number of passengers who use the convoys for those journeys) are not necessary in order to eliminate those anomalies. In some respects, those changes are sub-optimal in that they both reduce the accuracy of the simulation: fares are not based on the actual journey time of the particular journey but on how long that journeys, on average, tend to take by the particular method in question, and how long that it takes any given passenger to get from A - B does not depend to any extent on the proportion of passengers using that particular line that in fact travel from A - B rather than, say, form B - C.

As to the structure, I do not currently have time to remind myself of the details of the code in enough detail to respond fully (but we probably need to deal with desirable behaviour first in any event). My concern about altering structure significantly is that I know that, whenever major alterations are made, an enormous amount of time and effort is consumed, and it tends to be a very long time before all the bugs are chased out of the system, during which time the game-play can significantly be impacted. I am trying to make Experimental as stable as possible, and I am concerned at anything that will unnecessarily increase the number of possible bugs in the code. Furthermore, the connexions/routing system was some time ago now comprehensively rewritten by Knightly to maximise the speed of routing, something which had significantly impacted the performance. As a result of that, I am not intimately familiar with that code, and am reluctant to make more than minimal changes to code which (1) I know works, and (2) I do not know how to change significantly and yet keep it working. My time is extremely limited and the number of things necessary to do in order to make Experimental work and balance well are very large, and I am very, very keen to make sure that the time between now and when the code is such that Experimental works and balances well is as small as possible. There are a number of significant economic changes required as distilled from the discussions with Moblet when he participated in discussions here at the beginning of the year, and it is important that these be implemented before pakset balancing takes place.

It is only when the code is such as to allow good pakset balancing (and is reliable enough to allow long-term online play) that balancing can take place, and it is only when the pakset(s) is/are balanced that Experimental can be more widely publicised. Once Experimental can more widely be publicised (and when it is in any event more playable), there will be a good chance that it will attract considerably more players and potential developers (both code and pakset). At that juncture, things other than those which are important to get Experimental to that position can be contemplated. However, the important thing is to arrive at that position as soon as possible, or else the long-term time efficiency of the project is likely to be compromised (there is always the danger that the things necessary to make the game actually work well are put off indefinitely by concern with structure, etc.). I hope, therefore, that you understand the reasons that I am somewhat reluctant to work on very major structural changes to code that largely works at present.

If you were to write the code for the structural changes (and such effort is always much appreciated), that might alter the balance somewhat, but a number of things would have to be considered (on the assumption that the restructuring did not to any extent alter the behaviour; otherwise there would also be desirable behaviour considerations, as discussed above): firstly, it would have to be tested very thoroughly before implementation to ensure that it would not significantly degrade Experimental's reliability; secondly, it'd have to be something that I'd understand well enough to be able to maintain as much as the existing code; and thirdly, it'd have to make sure that it did not undermine the performance and stability of Knightly's existing very good code relating to routing.

Thank you again for your input on this important topic; it is appreciated.
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.

neroden

#20
Thanks for the reply.

I think it's become clear that we need to maintain a point-to-point time/distance database of some sort.  I guess all I'm *really* pushing for is to do it as cleanly as possible.  I am certainly happy to try to write it myself, but as you probably realize, help from Knightly would be ideal for this, since he wrote the existing code which maintains databases of this sort.  :-P

I actually believe that the changes I'm trying to make should make the routing code run faster; incremental updates to the database should be preferred over recurring total rebuilds, which are currently done.

I understand that you've got a lot of work to do right now.  Eliminating floating-point code is wise in any case.  This is actually something I'm good at.  If you'd like me to work on replacing the floating-point code in the physics engine with fixed-point code, let me know.  I actually understand the physics engine already, if that helps.  :-)

EDIT: I'm going to make a controversial statement.  I notice that you are defending minimizing the changes in the code on the grounds of code stability, for the purposes of behavioral stability.  

My concern about altering structure significantly is that I know that, whenever major alterations are made, an enormous amount of time and effort is consumed, and it tends to be a very long time before all the bugs are chased out of the system, during which time the game-play can significantly be impacted.

I'm going to tell you bluntly that you're partly wrong.  It does, certainly, take a lot of time and effort.  But whether it "tends to be a very long time before all the bugs are chased out of the system" depends on *who wrote the structure altering patches*.  When *I* write them, it generally doesn't take that long to chase out all the significant bugs, because I get it very close to right before I merge it with the trunk.  Don't take this as arrogance; there's a reason I'm authorized to rewrite the entire build architecture of GCC whenever I want to.

EDIT 2: I suspect that the recent changes to standard to use halt_id's over the network should be merged before attempting to improve the database, as it's going to be a lot easier to maintain a database indexed by origin halt and destination halt using halt_id's rather than koords.

jamespetts

Nathaneal,

apologies for the delay in replying: I have been rather preoccupied with other Simutrans issues and the small matter of going on holiday. Your input is, however, appreciated, as always.

Events have rather overtaken us with the physics engine, as you will see from other threads on the subject: Bernd Gabriel has written a C++ floating point class, which, apart from what are now minor anomalies in some unusual cases, works without difficulty.

As to the connexions database, there is an issue, I think, of structure: what the path explorer needs when it looks at all of the connexions nodes is a single pair of figures: the travelling and waiting time between that node and a putative destination node. Fetching those data is an operation performed a great many times in the path explorer code. It is important that a single best travelling time figure is accessible directly to the path explorer code such that there is no need either to calculate the figure or even retrieve it from a hashtable or the like.

The connexions structures as they currently stand are designed particularly to perform this specific task. It seems to me that the structure required for average speeds (that is, a pair of figures (running total of speeds and number of entries), which have to be computed to produce a sensible figure (running total divided by number of entries) at each point of access) is incompatible with that objective; moreover, having this information per convoy/line would require both a collection of some kind (most likely a hashtable) in which these data can be stored, and a computational means of determining which convoy or line's figures should be used, which, if the data were updated on the fly, would need to be recalculated with every access. Conversely, if the average speeds were stored in a separate structure in each convoy or line, the average would only have to be computed once per rebuilding of the connexions database, and likewise the choice of which line or convoy is the fastest. It would have the added advantage that few major changes would be required to the connexions database code (although I do take your point about it taking less time to chase out the bugs if you write the code).

Thank you again for your input on this topic, however; I should be interested in any further comments on the subject.
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.

jamespetts

Update: There is now a test implementation of the method described in this post above on my point-to-point-average-speeds branch of Github - I should be very grateful for any pre-release testing. Note that I have not yet implemented saving of these data, which will have to be done before release.
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.

inkelyad

#23
1)

koord_pair_hashtable_tpl<koord_pair, average_tpl<uint32> > * average_speeds;

Don't use pointer. We can afford store full hashtable variable inside convoy_t/simline_t.
It will reduce memory fragmentation add remove extra dereference.

koord_pair_hashtable_tpl<koord_pair, average_tpl<uint32> > average_speeds;

2) rewrite average_tpl so it will use fixed number of last samples. (like I to in my last patch for round-trip time) Using whole history is not right. One wrong path/stuck time and your convoy will have bad average speed for looong time.

3)
Consider more major rewrite. We don't need _speed_.  We need journey time from station A to station B.
a) make
last_time_at_stop[<stop koord>] array/hash.
b) use it to populate
average_tpl journey_time[< A to B koord_pair>]
c) use it in path_explorer instead average speed (haltestelle_t::hole_ab calculate journey time from speed/distance. Now it can use already prepared value)

Dwachs

Using coordinates of halts as key in the arrays is a bad idea too. Position of halts may change if de/con-struction happens, so you may lose all the recorded travel time history.
Parsley, sage, rosemary, and maggikraut.

jamespetts

Thank you both for your feedback. Inkelyad - I tried using hashtables as local objects not pointers, but ended up with bizarre access violations when trying to use them for the first time, which I could only fix by converting them to pointers.

As to using a fixed number of samples, that would require more memory. What I do instead is to reset the averages every time that they are checked by the path explorer, such that every check gets the average speed since the last check.

I have now changed this code to use journey times instead of average speed: the amended version is available on my Github branch.

Dwachs,

instead of using co-ordinates, would it be better, then, to use pairs of quickstones? How would I best go about hashing a pair of 32-bit numbers?
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.

inkelyad

Uhh. We really need do to something with.

const sint64 journey_time = ((welt->get_zeit_ms() - last_departure_time) * 100) / 4096;
And then
average.add(journey_time / 13);

Can we agree to some reasonable time unit and write tick_to_min function?

Calculate minutes as if show_month=1 and store value in tenth of minute looks good.
It will make

unsigned int tenth_of_minutes = (ticks * 24 * 60*10) >> wl->ticks_per_world_month_shift;




jamespetts

Inkelyad,

the reason that I use the formula that I do is that this is the exact format required for path_explorer.cc. I am not sure that the formula that you propose would have the same effect?
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.

inkelyad

That why i proposed more radical changes. path explorer don't need speed. It needs journey time in some integer time unit.
It was not available, so approximation from distance/speed was used.

If we remove all speed related variables from goods routing code, it will make things so much less confusing.

jamespetts

Inkelyad,

have you seen the changes that I have most recently made to the path explorer, which do indeed remove speed other than as a fallback? The particular formulation that I use (with the division by 13) is still required for this code.
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.

inkelyad

Quote from: jamespetts on July 08, 2011, 04:10:19 PM
have you seen the changes that I have most recently made to the path explorer, which do indeed remove speed other than as a fallback?
You left it in haltestelle_t::add_connexion

accumulated_journey_time += ((accurate_distance(current_halt->get_basis_pos(), previous_halt->get_basis_pos())
/ average_speed) *welt->get_settings().get_meters_per_tile() * 60);
and then
new_connexion->journey_time = accumulated_journey_time;


Quote
The particular formulation that I use (with the division by 13) is still required for this code.
And that i don't understand. Why?

jamespetts

Inkelyad,

haltestelle_t::add_connexion is from the old distributed pathfinding system. I plan on removing that entirely by the next version, so I do not plan on updating that for this feature.

As to the second question: the division by 13 is needed to get numbers that are equivalent to what was previously used in path_explorer.cc.
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.

inkelyad

Quote from: jamespetts on July 08, 2011, 04:28:37 PM
haltestelle_t::add_connexion is from the old distributed pathfinding system.
Oops. Wrong copy-paste. Should be haltestelle_t::hole_ab.

Quote
As to the second question: the division by 13 is needed to get numbers that are equivalent to what was previously used in path_explorer.cc.
Do we need to do it? We can convert them for old versions at save/loading time.

jamespetts

Inkelyad,

thank you for spotting that: haltestelle::hole_ab now updated.

I am not sure that I fully understand the second point - perhaps we are at cross-purposes? The number generated by dividing by 13 is the journey time in tenths of minutes, a format that is assumed all over the code: it would be harder to change everywhere else to accommodate something 13 times the number of journey minutes than it would be not to divide it by 13.
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.

inkelyad

Quote from: jamespetts on July 08, 2011, 05:23:52 PM
I am not sure that I fully understand the second point - perhaps we are at cross-purposes? The number generated by dividing by 13 is the journey time in tenths of minutes, a format that is assumed all over the code
Yes, but what is not clear: "how long is minute and what is relationship between it and all others time-related numbers in GUI".

If

sint64 journey_time = ((welt->get_zeit_ms() - last_departure_time) * 100) / 4096;

is right, then path-explorer minute is constant length - it not depends on bits_per_month.
but GUI minute depends on it.
I can change it, but how to display it without confusion?

jamespetts

#35
Inkelyad,

the time for the purposes of journey calculation is indeed independent of bits_per_month, and this is intentional, since both distance and speed in km/h remain the same at any given number of bits_per_month, as does the distance scale and the number of tiles per real second that any given in-game km/h setting represents. Given that GUI minutes can be represented in many different ways depending on the local user's preferences, it does not seem as though synchronising that would work effectively.

Update: I have now taken up Dwachs' suggestion and used the halts' Quickstone IDs instead of co-ordinates for storage.
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.

inkelyad

Quote from: jamespetts on July 08, 2011, 07:36:39 PM
Given that GUI minutes can be represented in many different ways depending on the local user's preferences, it does not seem as though synchronising that would work effectively.
Well, I will try.

Quote
Update: I have now taken up Dwachs' suggestion and used the halts' Quickstone IDs instead of co-ordinates for storage.
You should do same for haltestelle_t:: waiting_times

And you need clean-up code. When station is deleted, all records about it still be present in average_journey_times.


jamespetts

Inkelyad,

see the latest commits to the point-to-point-average-speeds branch for an implementation of those most recent suggestions.
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.

inkelyad

#38
Is this code in path_explorer.cc is clean-up code for average_journey_times?

if(!halt_list[i].is_bound() || ! halt_list[(i+1)%entry_count].is_bound())
{
   current_linkage.line->average_journey_times->remove(pair);
   continue;
}
<And some code just later>

Can it be moved to convoi_t::unregister_stops and simline_t::unregister_stops?

And(maybe) blank journey_times can be filled in convoi_t::register_stops and simline_t:register_stops.
It (maybe) allow skip existence checking in path_explorer.

Edit: using average_tpl::reset in path_explorer seems too excessive. Something like this:

if (count > 1) {
    count /=2;
    total /=2;
}

should provide fade-out effect for old data.

jamespetts

Inkelyad,

thank you for your thoughts. The unregister stops methods, however, appear to be invoked only when a schedule changes such as to cause the present convoy or line no longer to call at those particular stops. This does not occur when a halt is deleted, as the convoys that stop there will continue to have the entry in their schedules, but as a waypoint instead of a halt. This cannot, therefore, account for deleting halts.

As to the fading of data: this does not seem optimum to me, as the remnants of very old data would linger for a long time, being ever reduced but never eliminated; this would be significant in cases of large discrepancies between the data (as where a convoy gets stuck on one occasion, for example). At present, it is designed to ensure that each set of data is used exactly once in the calculations. Additionally, a divide operation would be more computationally expensive than an assignment operation.
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.

inkelyad

Quote from: jamespetts on July 10, 2011, 10:23:17 AM
This does not occur when a halt is deleted, as the convoys that stop there will continue to have the entry in their schedules, but as a waypoint instead of a halt. This cannot, therefore, account for deleting halts.
That can't be right.
1) We have line through multi-tile stop
2) Remove said tile from stop
3) It became waypoint in schedule.
4) Line should be unregistered from stop and stop must be excluded from path-finding for that line somewhere. (In haltestelle_t::add_grund/haltestelle_t::rem_grund? registered_lines  used/changed here, but no calls to register/unregister functions.)