(For an overview of the original implementation see: here)
This works by attaching programs to signals, which the pathfinder will run when determining the cost of a path through that signal for a particular train.
A program is a list of instructions which are executed in order. Instructions can be either conditionals (test something) or actions (do something).
The program can test various properties of the train, and the direction through the signal that we are going.
Programs can be shared between multiple signals, or copied from one signal to another.
Restrictions on a set of signals apply in both directions. For the case of path signals, restrictions apply in both directions even though there isn't a physical signal in one direction (this is in fact quite useful).
Tiles with two sets of signals (i.e. up to 4 physical signal heads) can have separate programs for each of the two sets.
Restricted signals are visually indicated by having a blue signal post, when using electric signals from the default set or OpenGFX GRF, or when the setting to show restricted electric signals using the default sprites is enabled.
Outputs that programs can have are:
- Deny
The pathfinder will treat this signal as a dead end. (There is also an option to cancel deny commands earlier in the program). - Add penalty
The pathfinder will add the penalty to the "cost" of paths through the signal - Reserve through (as of patch 6/commit 73b69c55)
If a PBS signal uses this command, PBS reservations which would otherwise stop at this signal instead continue through it to the next signal/waiting point. In effect this allows the 'safe waiting point' property of a PBS signal to be conditionally turned off. (There is also an option to cancel reserve through commands earlier in the program). - Long reserve (as of patch 7/commit 82cab7f3) (by KeldorKatarn)
If a PBS signal uses this command, trains which make reservations which end at the signal will try to reserve through to the next signal where possible. If no further reservation is possible the reservation ends here as normal. (There is also an option to cancel long reserve commands earlier in the program).
- Train length
- Train maximum speed
- Train current order (station, waypoint or depot)
- Train next order (station, waypoint or depot)
- Train last visited station
- Can train carry a particular cargo
- Which direction is the path entering the signal from (front, back, or the compass directions of the tile edge: north-west, north-east, south-west or south-east)
- Entered tile of PBS signal block (as of patch 2/commit a0520b8)
Conditions can be in the form of "if", "else if", "or if" or "else".
Conditions can be nested.
The idea of using linear programs with nestable conditionals and commands is based on the Programmable Signals patch.
The GUI for this patch is very strongly based on that from the Programmable Signals patch. (It's not completely plagiarised, but the base layout and much of the boilerplate is copied from there). The non-GUI code however, is completely different.
Caveats:
- This requires the use of YAPF as the train pathfinder
- Programs are only evaluated within the YAPF lookahead distance (this defaults to about 10 signals away), this is to avoid damaging pathfinder performance and/or require changing the pathfinder too much
- This is not a replacement for waypoints
This is in part a result of the previous caveat. Trying to use this patch for long distance routing may not have the desired effect and will probably be more difficult to manage than waypoints. - This patch does not actually stop trains passing signals where a Deny actions is in force. If no route at all can be found the pathfinder may choose to use a path that ends at the signal anyway, like it would any other dead end.
- This is not the same as the programmable signals or logic signals patches, though this path can happily co-exist with those
Most of the specific use cases that I've thought about so far have involved station platform allocation without needing waypoints at the ends of platforms or awkward track layouts, though it's generally applicable to a number of scenarios. For example:
- Handling mixed bay/though platform stations, based on next order
- Platform allocation based on train type (e.g. cargo type, speed, etc.)
- Finer grained prioritisation of which platform to use when more than one is unoccupied, by using pathfinder penalties, for example to reduce conflicting moves when the train later leaves the station
- Access control, e.g. to stop slow trains going onto high-speed lines
- Preventing long trains from stopping at places where they overhang and block junctions
- In the more general case: to stop trains going to places where they should not, and encourage trains to go where they should
The two signals in front of the bay platforms share the program in the upper window.
The signal in front of the north/west bound through platform has the program in the lower window.
The bay platform signals deny access if the train is entering from the back of the signal and the next order (the order after the call at New Bedtown) *is not* Southerly Station. This has the effect of stopping trains which need to continue north/west from using the bay platforms. The "train is entering from the back of the signal" test is to avoid restricting trains trying to leave the platforms.
The through platform signal adds a pathfinder penalty if the train is entering from the back of the signal and the next order (the order after the call at New Bedtown) *is* Southerly Station. This has the effect of adding a penalty for trains which could use the bay platforms, such that those trains will always use a bay platforms if one is available. However if all the bay platforms are full or otherwise unreachable, it can still use the through platform, and reverse out again afterwards.
This is a contrived example to show as many program features in one screen-shot as possible. It is possible to create quite complicated programs, however for almost all use cases something much simpler will be sufficient.
Notable changes from the original TTDPatch implementation:
- Using a linear program instead of a binary operator tree
User feedback at the time suggested that the binary operator tree was not very intuitive. It also doesn't work very well when you have more than just a binary output.
A linear program is more flexible. - "Train weight" and "train horsepower" tests are not implemented
I'm not sure how useful these actually were in real life, they didn't seem to get used much, as far as I can tell - "Number of tiles from signals" test is not implemented
This was somewhat unintuitive to use and probably isn't necessary, given the overall better state of pathfinding in YAPF relative to that in TTDPatch - "Signal Status" test is not implemented
People at the time found this very confusing, and it didn't seem to get used much - Binary XOR is not implemented
Nobody seemed to use this, and there aren't any use cases that I'm aware of - "Number of Days Since Last Service" and "Train Currently Searching For Depot" tests are not implemented
I'm not sure how useful these are in OpenTTD, given the improved handling of depots/servicing. - Programmable signals is not implemented
Use the Programmable Signals patch instead
See this post for most recent Windows build.
Source:
The source is on GitHub. This is currently based on r27386.
Alternatively an SVN-style diff is attached below, and the required GRF can be found here. Patch boilerplate:
This is not an official OpenTTD release, therefore bug reports, suggestions, questions and so on should be posted in this thread.
As currently implemented, savegames created using the patched source will not be loadable on trunk versions or other patched versions except where indicated otherwise.
I've had somewhat of a break from working on TTD, but it's been nice to get back into things a little.
Post edit changelog:
- Initial post: 2015-07-30
Upload patch 1: from r27346 to commit 2c5bfc5 - Edit 1: 2015-08-04
Upload patch 2: from r27346 to commit a0520b8
This adds "Entered tile of PBS signal block" test. post - Edit 2: 2015-08-04
Upload patch 3: from r27346 to commit 76707fb
This changes the GUI for pathfinder penalty values and adds preset values - Edit 3: 2015-08-15
Upload patch 4: from r27386 to commit 29c4f244
Add link to post with win32 binary - Edit 4: 2015-08-20
Upload patch 5: from r27386 to commit 4681799
Implement graphical display of restricted signals by recolouring the signal post to blue (except semaphores and custom signal GRFs).
Add/update links to post with Windows binaries and GRF. - Edit 5: 2015-09-03
Upload patch 6: from r27386 to commit 73b69c55- Program GUI changes to make 'or if' conditions easier to add, remove and use.
- Add a 'reserve through' program command.
If a restricted PBS signal uses this command, PBS reservations which would otherwise stop at this signal instead continue through it to the next signal/waiting point. In effect this allows the 'safe waiting point' property of a PBS signal to be conditionally turned off. - Improvements to the correctness and thoroughness of the program validator.
- Update links, documentation text and example image 1
- Edit 6: 2016-02-13
Upload patch 7: from r27386 to commit 82cab7f3- Add 'Long Reserve' feature (by KeldorKatarn)
- Add setting to show restricted electric signals using the default sprites.
- Fix attempt to recolour signal sprites from GRFs (except OpenGFX).
- Fix minor issue when deleting stations/depots/waypoints.
- Fix newly copied or unshared programs not being activated until the next edit action.
- Fix copying an unrestricted signal making an empty program.
- Fix GUI issue with picker button raise/lower states: When a picker button was selected, clicking on a different picker button would cause both buttons to be raised, instead of just the previous.
- Fix compilation on legacy compilers (pre C++11).