The International Simutrans Forum

Development => Technical Documentation => Topic started by: isidoro on December 16, 2011, 11:40:00 PM

Title: Documentation for road to two-lane unidirectional roads
Post by: isidoro on December 16, 2011, 11:40:00 PM
QuoteIn the beginning, there was nothing.  And Hajo said: "Let there be welt".  And welt there was.  And Hajo filled welt with planquadrat_ts.  And Hajo saw that it was good.

Since I have to delve into the internals of the code to make a proposal for a two-lane unidirectional road implementation, I thought it would be nice to write down my quest impressions while wandering through the code.  Please make suggestions, corrections...

Let's start.

Information applies to r5023.


CHAPTER 1: WORLD

welt (world) is the most important object in Simutrans.  It's of class karte_t (simworld.h).  It has a bidimensional array of planquadrat_t, named plan.  For each bidimensional coordinate, koord, (x,y), there is a planquadrat_t object.  You can retrieve it with the method planquadrat_t *lookup(koord).

karte_t is a singleton (only one world per invocation), but Hajo was not sure about that decision, as we can see in boden/grund.h:
Quote       /**
        * Pointer to the world of this ground. Static to conserve space.
        * Change to instance variable once more than one world is available.
        * @author Hj. Malthaner
        */
        static karte_t *welt;

planquadrat_t (simplan.h) objects are containers for ground elements (ground_t) that share the same position of the map, but have different heights.  You can get the element at a certain height with the method grund_t *get_boden_in_hoehe(sint16).  You can directly get the ground object from the welt with grund_t *karte_t::lookup(koord3d).

grund_t (boden/grund.h) objects contain a list (dinge) of all things (dings) that belong to a certain 3d position of the map.  grund_t objects may have ways (road, railway, ...), from 0 (no) to 2. They are always at index 0 and 1 in the object list dingliste_t.  (other objects follow at higher indexes).  grund_t objects also have an image (bild) that depends on the type of slope, climate, etc.  All other objects belonging to that grund_t also store their images themselves. The methods in connection with the ways the object has are (other types used in them are explained below):
grund_t objects may be of one of these types (virtual typ get_typ()):
grund_t objects may have none or several of these flags:
grund_t is an abstract class.  Known subclasses are: boden_t (boden/boden.h), wasser_t (boden/wasser.h), fundament_h (boden/fundament.h), brueckenboden_t (boden/brueckenboden.h), monorailboden_t (boden/monorailboden.h).  Tunnels (tunnelboden_t) are a subclass of boden_t.

Way types (waytype_t) are such fundamental data in Simutrans that they are defined in simtypes.h (together with integer sizes, for instance).  They are:
Things are that, things (abstract ding_t in simdings.h).  They have an owner (besitzer) and may or may not be physically located in a position of the welt.  Vehicles and ways are things, but not only.  There are a lot of types (smoke, buildings, tunnels, bridges, depots, roadsigns, ground objects, labels, all kinds of vehicles...).  Interestingly enough, a thing may be a vehicle and a way at the same time, which opens a huge world of possibilities...  They also have an associated image (bild).  If we know the index of a certain thing, we can retrieve it with ding_t *grund_t::obj_bei(uint8).

And what about time?  Well, let's hear the wise words of Prissi, the Magician:
Quote from: prissi on June 17, 2011, 05:30:47 PM
Each stuff [thing] can have three periodic calls: for very regulary and fast actions which require exclusive access on the map (moving cars, collision avoidance) there is sync_step, called before every frame. Slower actions like route_finding, way building etc. is done in step(), where the map can change in between slightly. Some objects (most notable trees) require seasonal changes, which are covered by the relatively new action method of check_season();

All objects that must be advanced in synchrony must therefore implement the interface sync_steppable (in ifc/sync_steppable.h).  welt keeps a list of sync_steppables (sync_list) and periodically calls the only method they have to implement to become a fully-fledged sync_steppable guy:
virtual bool sync_step(long delta_t).
Scheduled additions and deletions from the sync_list are done carefully through intermediate lists and also in sync.

And welt moves around in its sync_step method:
void karte_t::sync_step(long delta_t, bool sync, bool display),

CHAPTER 2: WAYS (AND ROADS)

One of the things that we can find on a grund_t are ways (abstract weg_t in boden/wege/weg.h).  In fact, ways are a subclass of a subclass of ding_t (abstract ding_no_info_t in simdings.h).  An example of a way is a tile of asphalt road.


A weg_t may have one or more of these flags (uint8 flags) activated:
Besides, a weg_t may belong to one of these system types:
To get the way type, use: virtual waytype_t get_waytype().  Although the type of the ding_t is always ding_t::way, of course:
Quotetyp get_typ() const { return ding_t::way; }

One can imagine that each way tile is like a city with four invisible walls: nothern, souther, eastern and western.  There may be a wall or a connection to the adjacents way tiles.  This is marked by the ribis (Richtungsbits) o bits of allowed directions.  weg_t::ribi is a 4-bit long bit field, each bit is 1 if that direction is allowed.  The order is WSEN.  To retrieve this information, use: ribi_t::ribi get_ribi_unmasked().

Even if there is a connection to an adjacent tile, it is possible that a certain direction is forbidden for vehicles.  This is marked with the ribi mask (ribi_maske).  It is another 4-bit long bit field.  This time, a 1 means that the corresponding direction is forbidden. To retrieve or set this information, use: void set_ribi_maske(ribi) or ribi_t::ribi get_ribi_maske().  To retrieve the combined final effect of ribi and ribi mask, use: ribi_t::ribi get_ribi().

Because of a weg_t is a ding_t, it has an associated image.  However, that image depends of the ribi, the slope, the climate, etc.  There is a method to calculate the image used (void calc_bild()).  This method is to be called each time we modify the ribi values with: void ribi_add(ribi_t::ribi), void ribi_rem(ribi_t::ribi ribi), void set_ribi(ribi_t::ribi ribi).

The weg_t has a description, Beschreibung, o simply besch when it is hanging out with friends (weg_besch_t in besch/weg_besch.h).  Those descriptions come from the paksets.  The information includes everything pak-dependent: introduction date, maximum speed,...  With these two methods, you can retrieve/set the description: void set_besch(weg_besch_t *) and weg_besch_t *get_besch(). The maximum speed is not taken directly from the description only, since it may be altered by other objects in the grund_t. For instance, a 80km/h tramway inside a city has a maximum speed of 50km/h.

Inside weg_besch_t, we only find these properties relevant to the problem in hands: uint8 wtyp (type of way), uint8 styp (system type).

Known subclasses of weg_t are: strasse_t, road, (boden/wege/strasse.h), kanal_t (boden/wege/kanal.h) and schiene_t, railway, (boden/wege/schiene.h).

Another objects that may appear in a grund_t in connection with ways are way objects (wayobj_t in dings/wayobj.h).  These are objects whose images change depending on the configuration (ribis) of the way tile.  The most typical of these are catenaries of the like.

In this point of our travel, we have to choose among the different subclasses of weg_tstrasse_t (in boden/wege/strasse.h) is chosen, since we are dealing with roads.

strasse_t is a boring class, though.  It only returns its type if asked, allows for the inclussion of sidewalks, or does domestic chores...  It has no known subclasses either (a bachelor, to say it so)

[[To be continued...]]
Title: Re: Documentation for road to two-lane unidirectional roads
Post by: Dwachs on December 17, 2011, 09:54:40 AM
Thank you very much !!!

I took the freedom to edit some incorrectnesses.
Title: Re: Documentation for road to two-lane unidirectional roads
Post by: isidoro on December 17, 2011, 10:59:48 AM
Please do.

I changed the wording of your corrections.  I have a question, though.  If there can only be one way per grund_t, what is the purpose of grund_t::has_way2 flag and associated methods: has_two_ways, etc.?  Maybe a leftover of a past implementation?

Title: Re: Documentation for road to two-lane unidirectional roads
Post by: Dwachs on December 17, 2011, 11:43:18 AM
Oh, I misunderstood you there! There can be 0..2 ways on one tile, the are at index 0 and 1 in the object list (dingliste_t). Edited it back.
Title: Re: Documentation for road to two-lane unidirectional roads
Post by: isidoro on December 17, 2011, 12:04:30 PM
Ups! We have been editing at the same time...   :o   Please, redo.
Title: Re: Documentation for road to two-lane unidirectional roads
Post by: Dwachs on December 17, 2011, 01:19:12 PM
Please post Chapter 3 in a new post.

Great idea to put documentation forward.
Title: Re: Documentation for road to two-lane unidirectional roads
Post by: Combuijs on December 17, 2011, 04:33:35 PM
Great documentation Isidoro, very well explained, keep up the good work!
Title: Re: Documentation for road to two-lane unidirectional roads
Post by: isidoro on December 18, 2011, 02:24:23 AM
Thanks both.  Nonetheless, things are getting complicated with next chapter...  (vehicles).

Edit: I've made some additions to Chapter 1 dealing with simulation time since they affect vehicles.  They are in green.  Please check.

These web editors are nice for a beginner, but they are a pain in the long run.  For instance, colors and lists don't marry well...
Title: Re: Documentation for road to two-lane unidirectional roads
Post by: prissi on December 18, 2011, 09:41:28 PM
Great, I wish I would have something like that when I started ... Some additions in blue from me.
Title: Re: Documentation for road to two-lane unidirectional roads
Post by: Isaac Eiland-Hall on December 18, 2011, 11:32:39 PM
Quote from: isidoro on December 18, 2011, 02:24:23 AM
These web editors are nice for a beginner, but they are a pain in the long run.  For instance, colors and lists don't marry well...

Click the "toggle view" button to change between GUI editor and old tag editor.
Title: Re: Documentation for road to two-lane unidirectional roads
Post by: isidoro on December 19, 2011, 12:32:25 AM
@prissi:  much appreciated.  With vehicles things are getting more complicated.  I will surely need more help from you guys, when that part is posted.

@Isaac: thanks, much better.  I'll keep a copy offline though, just in case I mess it all.

Title: Re: Documentation for road to two-lane unidirectional roads
Post by: Isaac Eiland-Hall on December 19, 2011, 09:42:11 AM
I'd say this should also go into the wiki - and yeah, an offline copy never hurts. hehe
Title: Re: Documentation for road to two-lane unidirectional roads
Post by: isidoro on December 23, 2011, 02:10:57 AM
The more I dig into the code, the more I see that the project is feasible.  My initial plan was to continue chapter 3 with vehicles and convoys but I think now that "Ribis and Slopes" is much better.  Any comments/corrections are welcome, but I would prefer you to cite the relevant part and allow me to correct the problem myself since now I keep a local copy and it is difficult to sync everything otherwise.

I think that this documentation about ribis and slopes would be a thousand times clearer with some clarifying pictures.  Maybe some of the pakset authors (and therefore artists) dare contribute for this and, this way, contribute to the code without even knowing a line about programming...


CHAPTER 3: MORE RIBIS, AND SLOPES TOO (r5023)

Slopes are implemented with the help of class hang_t in dataobj/ribi.h.  Although there is a provision in Simutrans for use the so called double grounds, we will deal here with normal behavior.

Slopes are special tiles that are not flat.  One or more that one of their corners may be raised one height unit.  If one or three corners are raised, we arrive to a shape formed by two triangles: one of them flat and the other, in slope.  If two corners are raised, we have two cases.  First happens when the raised corners are opposite corners and second case is when the raised corners lay on one side of the square of the tile.  In the former, a V shape happens.  In the latter, we have a flat slope, suitable to build an upwards road there, for instance.

A slope is thus expressed through a bit field indicating which corners are raised, following this diagram:

           ^
   [8] NW  |  NE [4]
          \|/
       <---+--->
          /|\
   [1] SW  |  SE [2]
           v


So, a flat slope going up from north to south (a north slope in game terminology) will have its SW and SE corners raised, and has therefore number 3 (1+2).

There are some flags too (and the corresponding static methods to check them (bool ist_einfach(uint8), etc.):

And some types of slopes, at least for flat slopes:

And static methods to get information regarding slopes:


The direction bits or ribis mark what is possible and not possible when moving along in Simutrans.  For a way tile, they are like doors that allow o deny communication with neighbor tiles.  The denial may be due to the real absence of a path to the neighbor tile (there is no road there), or because there are signs or other things forbidding to go there.

Ribis allow for all known railway graphic tiles.  But some notable possibilities are not accounted for.  For instance, there is a ribi that allow connection for all possibilities: from north to east, from north to south, etc.   But there is not a ribi to allow to go from north to west, and from south to east, but disallows to go from north to south at the same time.  To say it more concisely, once the vehicle is inside a tile and intends to move to a neighbor tile, to make a decision, it doesn't remember from what direction it came.  It only sees opened/closed doors to those tiles.

Ribis are made of a combination of the four basic directions in this bit order WSEN.  Thus, all possible directions are:

  0-ribi_t::_ribi::keine (no)      1-ribi_t::_ribi::nord
  2-ribi_t::_ribi::ost (east)      3-ribi_t::_ribi::nordost
  4-ribi_t::_ribi::sued (south)    5-ribi_t::_ribi::nordsued
  6-ribi_t::_ribi::suedost         7-ribi_t::_ribi::nordsuedost
  8-ribi_t::_ribi::west            9-ribi_t::_ribi::nordwest
10-ribi_t::_ribi::ostwest        11-ribi_t::_ribi::nordostwest
12-ribi_t::_ribi::suedwest       13-ribi_t::_ribi::nordsuedwest
14-ribi_t::_ribi::suedostwest    15-ribi_t::_ribi::alle (all)

One must be careful when using this.  The physical bit order is WSEN, with W the most significant bit, but the constants are expressed just the opposite way: NESW...  Here we will keep the constants' convention.

Some constants regarding ribis are defined, in order to group similar ribis:
   1-ribi_t::einfach (only one-exit, i.e. dead end): N, S, E, or W.
   2-ribi_t::gerade_ns (exit in N, S, or both): N, S, or NS
   4-ribi_t::gerace_ow (exit in E, W, or both): E, W, or EW
   8-ribi_t::kurve: NE, SE, NW, or SW, since they represent curves when in a way (except for diagonals, which are, in fact, curves in opposite directions: curve left-curve right-curve left-curve right,...  Diagonals are also considered kurve, when we look at them inside one tile)
  16-ribi_t::twoway (exactly two exits, straight or curve): NS, EW, NE, SE, NW, or SW
  32-ribi_t::threeway (three or more exits): NSE, NEW, NSW, SEW, NESW

And the corresponding static methods are defined:

Some other static methods serve ribi transformations:

These  methods are mainly based on precalculated tables, to gain some speed.  One of them is doppelr.  This is the content:
 
  All or nothing:  Dead-ends:  Straight:  Curves:  3-ways:
  0    -> 0        N->NS       NS->NS     NE->0    NSE->0
  NESW -> 0        E->EW       EW->NS     SE->0    NSW->0
                   S->NS                  NW->0    NEW->0
                   W->EW                  SW->0    SEW->0
 

So, this table gives the straight direction and opposite for dead-ends and straight lines.  The correspondig getter method is: ribi doppelt(ribi) (watch out the t).

We have some helper methods that returns ribis from a great variety of data:

There are two static methods that connect ribis with slopes and slopes with ribis:

Finally, when talking about one direction of the compass a vehicle is driving to, we have:

  ribi_t::_dir::dir_invalid=0, and

                         ribi_t::_dir::dir_nord=4
                                   N
ribi_t::_dir::dir_nordwest=7  NW  ^  NE ribi_t::_dir::dir_nordost=6
                                  \|/
     ribi_t::_dir::dir_west=1 W <--+--> E ribi_t::_dir::dir_ost=5
                                  /|\
ribi_t::_dir::dir_suedwest=2  SW  v  SE ribi_t::_dir::dir_suedost=3
                                   S
                         ribi_t::_dir::dir_sued=0

The same value is assigned to dir_invalid and dir_sued, is this a bug or a feature (those lazy southerners...)?

To be continued...

Title: Re: Documentation for road to two-lane unidirectional roads
Post by: prissi on December 23, 2011, 09:45:29 PM
Very good, you spotted already some errors I think.

The behaviour of ist_exakt_orthogonal() is an error and needs to be fixed. And ist_kreuzung can certainly go ...

You start to mix directions and ribis somewhere in the lower part. directions "dir" are only eight and they are used to indicate the direction of vehicles. They cannot be combined (or at least the should not, i.e. they should be always <=7). Thus vehicle return dir, but ways return ribi.

Insofar dir_invalid could be 255. I am not sure it will be change the behaviour at all.
Title: Re: Documentation for road to two-lane unidirectional roads
Post by: isidoro on December 24, 2011, 12:31:56 AM
Thanks, prissi.

I've changed the direction part to the end.  I didn't know it referred to different objects.  I've also added a tag with the revision in the title of the chapter.

Next is vehicles and convoys and, hopefully, my proposal.  Now I'm on vacation and have more time...

Title: Re: Documentation for road to two-lane unidirectional roads
Post by: IgorEliezer on December 24, 2011, 11:10:50 PM
I'm very happy that things here are going on very well. :D

Keep it up dudes.
Title: Re: Documentation for road to two-lane unidirectional roads
Post by: isidoro on December 29, 2011, 12:09:28 AM
Chapter 4 is short, but intense...


CHAPTER 4: SIGNS (r5023)

For Simutrans, all signs, traffic lights, and even signals in railway are roadsign_t (in dings/roadsign.h), even if they are not on roads at all.  The name is like that, probably for historical reasons.  roadsign_t is also sync_steppable because traffic lights (and now private barriers) want to chage configuration once in a while.

Signs affect the directions a vehicle may follow, since some of those directions may be prohibited by the sign.

Ways have flags (HAS_SIGN and HAS_SIGNAL) and when set, the roadsign is searched and the information is retrieved.  This is done due to performance.

Note that the roadsign_t destructor resets the ribi-mask, and that means that only one sign must be allowed per tile.

To set the direction of the roadsign and the tile ribi mask accordingly, the void roadsign_t::set_dir(ribi_t::ribi) method is used.  These ribi_mask are considered when finding a route or when a vehicle hops from one tile to the following one in its route.  This double checking is needed because something may have changed (a road is destructed, a new sign appears, etc.) since the time the route was calculated.

We can get information about sings in their description class (roadsign_besch_t in besch/roadsign.h).  Their type can be one of these:

A sign also holds the following information: flags (types, as seen previously), wtyp (way type), and min_speed

We also find two useful methods: bool is_traffic_light() (if on road and we have more than 4 images, it is a traffic light, since normal signs have only 4 images, one for each direction), and bool is_signal_type() (if it is a railway signal: normal, pre or, longblock).

To be continued...


Title: Re: Documentation for road to two-lane unidirectional roads
Post by: isidoro on December 30, 2011, 12:45:56 AM
And chapter 5 is long, long, road, with many a winding turn...


CHAPTER 5: VEHICLES AND CONVOYS (r5023)

Vehicles are other things (ding_t). In fact, they are moving things.  Their base class is vehicle_basis_t (in vehicle/simvehikel.h). A fahrer_t (driver), in ifc/fahrer.h, is what makes a vehicle a full vehicle, i.e. a vehikel_t, also in vehicle/simvehikel.h.  This class inherits from both previous classes:  vehikel_basis_t and fahrer_t.  No other class implements the fahrer_t interface.

vehikel_basis_t, however, is the superclass of other moving objects (movingobj_t in vehicle/movingobj.h) and verkehrsteilnehmer_t, or users of the road, in (vehicle/simverkehr.h).  The latter will give rise to the private city cars (stadtauto_t) randomly moving in the streets of the game and, not less important, pedestrians (fussgaenger_t), both in the same file as their parent.

If we want a vehikel_t or several of them to move, we have to form a group of them, i.e. a convoy (convoi_t in simconvoi.h).  Convoys are sync_steppable (movable, thus) and overtakers (if on roads).  In the convoy code, there is an interesting remark by Hajo wondering if it is better to use pointers (they are faster) or handles (one can know if the object pointed to is still alive or not).  Some objects are dealt with pointers, some with handles, in the game.

Let's have a look at convoys:
When moving, convoys and, of course, vehicles, move one or several integer space units called steps, although in screen representation, finesse is more.

And these are interesting methods:
A convoy is a finite automaton and may be in one of these states:
There is a lot of information in a vehicle pertinent to our matters.  We must pay attention to these variables, since vehicle information is kept in them and usually the methods involved in managing a vehicle have few parameters, if any:
As said above, a vehicle must implement the fahrer_t (driver, person who drives) interface.  This is the contract (all methods are abstract):
To be continued...
Title: Re: Documentation for road to two-lane unidirectional roads
Post by: missingpiece on December 30, 2011, 12:11:04 PM
WOW ! You are actually documenting before implementing. That is better than in the multi-million-dollar SW workshop I'm working in...  :P
And a fairly entertaining author's style. Nice.
Title: Re: Documentation for road to two-lane unidirectional roads
Post by: prissi on December 30, 2011, 09:42:36 PM
I did some edit in blue.

And this interface exists more ore less (minus overtaker) since 2002. Thus documentation before implementation is not really adaquate ...
Title: Re: Documentation for road to two-lane unidirectional roads
Post by: isidoro on December 30, 2011, 11:39:14 PM
Thanks, missingpiece and prissi.

Here's the next chapter:

CHAPTER 6: SCHEDULES AND ROUTES (r5023)

A schedule (German fahrplan) is, in Simutrans terminology, a set of destinations a convoy must follow in its daily routine.

A route is a set of tiles a convoy must visit in order from its present position to the next destination.  Routes are expensive to calculate (specially for open ways like the sea or air, where there are more possibilities) and this calculation is done as less frequently as possible and it is cached by the convoy.  This means that if changes are made to the map, the route of the convoy may be stale and needs recalculation.

The convoi_t::drive_to method is used to (re)calculate the route of a convoy from its present position to the next destination.  If calls the bool vehikel_t::calc_route method of its first vehicle and, if successful, it calls convoi_t::vorfahren (advance).

The bool vehikel_t::calc_route method just calls the route_t::calc_route method with the same parameters.  And this latter method just calls route_t::intern_calc_route to do the hard work.  Well, to be frank, the poor route_t::calc_route method also helps parking.

bool route_t::intern_calc_route returns true if it is able to find a route and false, otherwise.  The method follows a A* sort-of algorithm (not sure of this) with a limit of MAX_STEPS steps.  The cost function depends on the type of road, maximum speed, elements on a tile, upwards slopes (those set in fahrer_t::get_kosten), but also curves and sharp turns increment that value so that they are in fact discouraged.

Guess 1:  Maybe, a kind of heuristic should be implemented for route calculation for ships and aircrafts so that nearer to destination tiles are tried first.

Guess 2:  When there is only one possibility (and this happens a lot in roads and railways between crossroads, some calculations may be saved if we continue to the next crossroad).  If fact, a single cost may be given to the entire path up to the new crossroad, since the only decision we can make is at crossroads (those are the important tiles to find a route).

To be continued...

Title: Re: Documentation for road to two-lane unidirectional roads
Post by: Combuijs on December 31, 2011, 09:36:52 AM
Brilliant documentation, keep up the good work!

A question:

QuoteRoutes are expensive to calculate (specially for open ways like the sea or air, where there are more possibilities)

I can see why sea routes are expensive to calculate, but why is an air-route expensive? There are not any obstacles, are there? It is just getting the plane in the air, getting the plane on the ground again, and connect those two points with at most one diagonal and one straight line?

Am I overlooking something?
Title: Re: Documentation for road to two-lane unidirectional roads
Post by: IgorEliezer on December 31, 2011, 10:23:46 AM
Quote from: Combuijs on December 31, 2011, 09:36:52 AMAm I overlooking something?
I'm not sure about it. Perhaps, mountains and buildings are considered as obstacles. And airplanes don't go through a straight line from an airport to another. Like ways and vehicles, they must obey the grid too, then they move along the N-S and E-W axises and in diagonals (NW-SE and SW-NE). Therefore, if the airports aren't aligned, the airplane will need to make at least a 45º turn in mid-air, 2 point for take-off and more 2 for landing, 5 points in total.
Title: Re: Documentation for road to two-lane unidirectional roads
Post by: prissi on December 31, 2011, 09:10:32 PM
Air routes are dirt cheap to calculate, since nothing is calculated at all. If a max height for planes is mposed, then this might be another problem, but for now these are cheap (but long, du to take off circling, parking etc.)

Very expensive are boat routes. The worst case is the way around a large island. I really optimised very very much of this. The only thin "cheaper" would be a hierachical pathfinder, which exist as a patch. However, thoses routes were not very convincing and it was not much faster.

And finally, route search is also invoked by choose signs/signals, using the find_route function. Thus the behaviour of fahrer_t will change when target_halt is set.
Title: Re: Documentation for road to two-lane unidirectional roads
Post by: isidoro on January 01, 2012, 01:43:39 AM
Happy new year (we will need it) and thanks all for your kind comments.

Don't take the documentation too literal.  Some things come from code inspection, some are guesses, some are things I remember from the forums (for example, prissi saying that sea routes is expensive), etc.

It seems to me that, at least in sea, there are a lot of possibilities (a lot of tiles) the algorithm has to check before reaching destination.  In this kind of algorithms, an heuristic (that is to say, a guess) may be chosen.  That consists of choosing, among all possibilities for the next tile that with more chances to belong to the correct solution.  In our case, tiles physically nearer to the destination should be tried first.  It is only a chance.  It may happen that in that direction there is only land and we must later go round that land...

When we have roads, there are much fewer tiles to check.

Going back to topic.  Here comes another chapter.  I'm near the end...


CHAPTER 7: MOVEMENT OF CONVOYS (r5023)

The nature of convoys as sync_steppable makes them suitable candidates for moving.  Everything starts at the bool sync_step(long delta_t) method of convoys.

In that method, the property wait_lock is updated and, if still possitive, nothing is done.

Convoys are finite automata and, thus, their life cycle consists of jumping from one state to another.  The only states that are urgent and, therefore, updated in a sync_step are LEAVING_DEPOT and DRIVING.  Let's see the latter.

In that state, the first thing that is done is to update the actual values of acceleration, speed (akt_speed), and accumulated distance (sp_soll).  Next, the convoy is moved the accumulated distance by the vehikel_basis_t::fahre_basis(distance_to_move) method.  This method returns the actual distance the vehicle was able to move.  If lucky, that distance will be the distance we asked it to move, but may be less.  The rest of vehicles of the convoy are also moved and the smoke is generated.

The core work of all this is done by the fahre_basis method.  As it has just been said, it receives the distance we want the vehicle to move and returns how much of that distance the vehicle was able to move.  The following is done in that method:

For road vehicles (automobil_t), a road is passable if:

The vehikel_t::hop method does the following:

One of the most complicated methods in this part of Simutrans is bool ist_weg_frei (is the way free?).  There is a specialitation of this method for each kind of vehicles.  In fact, the schiff_t::ist_weg_frei method, used by ships, is so simple that it ignores all other ships in the tile, and that allows several of them to occupy the same tile at the same time.

That method deserves a special chapter in itself for our purposes.

The rest of states are dealt with in the convoi_t::step method, which is called with much less frequency.  This method is appropriate for things that doesn't alter the position of the vehicle or are too CPU intensive: calculating routes and checking schedules, mainly.  The frequency a vehicle tries to do this (for instance, finding a route after being unsuccessful) is also set in this method.

To be continued...

Title: Re: Documentation for road to two-lane unidirectional roads
Post by: Fabio on January 01, 2012, 12:11:43 PM
Maybe in a far future the wisdom collected here could lead to real double tracks as well...
Title: Re: Documentation for road to two-lane unidirectional roads
Post by: isidoro on January 04, 2012, 11:41:56 AM
This last chapter things, I don't understand it fully, but since I think I have enough information to make my proposal, here it is.  The proposal will be done shortly.

CHAPTER 8: IST_WEG_FREI and friends (r5023)

The full prototype of this method is: bool ist_weg_frei(int &restart_speed, bool second_check).  It is called each time we want to know if a way tile is free and the vehicle can go there.  It returns two values: a boolean, indicating whether the way is free or not and, if not, the speed at which the vehicle should restart when eventually the way is free.

Sometimes, the method calls itself a second time to check, for instance, if the vehicle in front of us can or cannot move.  These checks are more relaxed and are indicated by a value of true in the second parameter of the method.

In order to analyse this method, we will refer to the number of the lines as in r5023. bool automobil_t::ist_weg_frei(int &,bool) method goes from line 1938 to 2235.  These are its main tasks:

So the most important part happens in lines 2041-2229: let's visit them:

The vehikel_basis_t *no_cars_blocking(grund_t *,convoi_t *,uint8 current_fahrtrichtung,uint8 next_fahrtrichtung,uint8 next_90fahrtrichtung) method really checks if there is at least one vehicle stopping us.  We will have a look at it:


Finally, overtaking is dealt in more or less a simplistic way.  A normal or a street car may be in one of three states:

When the vehicle is drawn, it is drawn with an offset depending on the state.  When in ist_weg_frei, if there is a blocking car ahead, conditions are checked in convoi_t::can_overtake, and if possible, the aforementioned numbers of tiles are calculated and the states are set.

Each time a vehicle enters a new tile, the number of tiles (overtaking/overtaken) is decremented and, if zero is reached, the state is reset to normal.


To be continued... or not?
Title: Re: Documentation for road to two-lane unidirectional roads
Post by: prissi on January 04, 2012, 03:13:12 PM
The checks at crossroads and way crossings (railroad, channels etc.) are to may sure a car does neither have to stop directly on them to prevent traffic jams. For this, it even has to take into account the directions of driving (e.g. right turn is always possible, if next tile is empty, while left trun has to wait for block traffic for driving on right side).
Title: Re: Documentation for road to two-lane unidirectional roads
Post by: TurfIt on January 04, 2012, 08:18:46 PM
Quote from: isidoro on January 04, 2012, 11:41:56 AM
   2055-2193:  something in connection with crossroads and a 4 tiles check that I don't understand
Don't understand???  :o  Why it's a thing of beauty!  ;D   Drove me nuts getting it to work. Seems so simple yet...
As prissi mentioned, we don't want a vehicle stopping in an intersection (or on a crossing) potentially blocking traffic; And ultimately potentially causing a deadlock. So this routine simply scans ahead along the vehicles route making sure every tile is free until it finds one where it's safe to stop. The 4 tiles is the routine giving up after 4 consecutive intersections and allowing the vehicle to proceed anyways.  Possibility of a deadlock, but otherwise the overall traffic flow is slowed too much in certain circumstances.


Quote from: isidoro on January 04, 2012, 11:41:56 AM
   589-614: very complicated calculations dealing with what happens in crossroads when traffic is turning, etc. checking if we have free way or not
These calculations cover every possible route through the intersection, ensuring as many vehicles as possible can all be in the intersection, at the same time, all taking non conflicting routes; For standard two way roads (left and right driving sides) only. This routine is the very reason I was considering only controlled access highways with no intersections (except the access ramps which would have their own specialized logic). The extra logic to handle multilane one way roads, and all the possible intersection types that come from joining one way and two way, and one lane and two lane, and etc.. in all combinations is mind bogglingly humongous.
Title: Re: Documentation for road to two-lane unidirectional roads
Post by: isidoro on January 04, 2012, 10:03:53 PM
Thanks both.  Now it is much clearer for me.

Quote from: TurfIt on January 04, 2012, 08:18:46 PM
These calculations cover every possible route through the intersection, ensuring as many vehicles as possible can all be in the intersection, at the same time, all taking non conflicting routes;
[...]

Nice architectural work...   ;D   Wouldn't you mind loosing it too much?   :P
The method I'm devising would take that off to accommodate for one way intersections and transitions (going from one-direction to two-direction and vice versa) too...

Tomorrow afternoon I think I will have enough time to finish the technical aspects of my proposal.  It seems simple to me, but I may have overlooked something....
Title: Re: Documentation for road to two-lane unidirectional roads
Post by: Spike on January 04, 2012, 10:23:04 PM
Isidoro, this is absolutely amazing work to read all this from the code and document it so detailed and precisely :)

It's been very interesting to me to read and compare it to my memories, to see what changed and what stayed the same. It seems the team has improved the class hierarchies a bit, to make responsibilities more clear, and also added a few more details to the ribi-related data.

Good luck with the two lane road project! After this analysis of the code I believe that you can do it  :D

Title: Re: Documentation for road to two-lane unidirectional roads
Post by: isidoro on January 05, 2012, 06:32:16 PM
Thank you, Hajo.  You are very kind.

In fact, code as prissi says is pretty well documented itself.  Problem for me is German, though...  And I also have played a lot and took contact with it  when developing the overtaking patch.

I have already made my proposal.  I hope some of you, guys, are not too deceived by it, but it is what it seems to me feasible enough:
http://forum.simutrans.com/index.php?topic=8906.msg82865;topicseen#msg82865

I plan to add all the corrections to my visit to the code and put it together and, if you want, publish it in the wiki or somewhere else.