TRAIN!
- Not to be confused with Train.
TRAIN! is an esoteric programming language designed by Jonathan Brouwer, Jonathan Dönszelmann, Julius de Jeu and Noah Jadoenathmisier created as part of the first LangJam. In TRAIN!, computation is based on trains driving around tracks between stations. Stations represent operations applied to a train or pair of trains which enter the station. Data is contained within trains as a list of passengers holding a number. Generally operations apply to the top number in this list working like a stack.
Instructions
Instructions are encoded as stations. Stations can have either one or two input and one or two output tracks. As an example: add has two input tracks and two output tracks. The add station waits until two trains have entered the station (one on each input track). The first number of the train on track 1 is added to the first number of the train on track 2, and the result replaces the top number of the train on track 1. The train on track 2 is not modified (and keeps its number) (note: all binary operations work like this). Now both trains leave their respective track again to another (different or the same) station. Traveling from one station to another takes 1 timestep.
It is possible that a station expecting two trains for its operation, only receives one train that timestep. This train now waits on the station until the station receives a second train on the other track. However, if in that time another train arrives on the same track this new train is queued but not deleted. It is unspecified behaviour to send two trains to the same track of the same station in the same timestep.
Stations can apply one of the following operations to trains entering it:
Name | Use |
---|---|
nothing | Do nothing. |
print string | Prints the contents of a train as a string. Each number in the list passengers in the train is converted into an ascii character. |
print number | Prints the contents of a train as a list of decimal numbers. |
Input | Ask for a single integer of input, which is added to the top of a train |
switch eq | Trains come in on track 1. If their first number is equal to zero, they leave on track 1. Otherwise they leave on track 2. |
switch gt | Trains come in on track 1. If their first number is equal to zero or positive, they leave on track 1. Otherwise they leave on track 2. |
switch empty | Trains come in on track 1. If the train has no passengers, leave on track 1. Otherwise they leave on track 2. |
transfer | Two trains are accepted. The first passenger in train 1 is transferred to the train on track 2. This passenger now becomes the first passenger in train 2. |
rotate | Trains come in on track 1 and leave again on track 1. The first passenger is rotated and becomes the last passenger on that train. The second becomes the first, the third the second, etc. |
duplicate | Trains come in on track 1. A copy of the train is outputted on track 1 and track 2 (yes, trains can magically clone). |
delete top | Deletes the first number from the train on track 1. The train is sent out again on track 1. |
add | Binary operation adding the value of the first person in train 1 to the value of the first person in train 2. Both trains continue out of the station, train 2 unmodified and train 1 with the top value modified to be the sum. |
sub | Binary operation subtracting the value of the first person in train 1 to the value of the first person in train 2. Both trains continue out of the station, train 2 unmodified and train 1 with the top value modified to be the difference. |
div | Binary operation dividing the value of the first person in train 1 to the value of the first person in train 2. Both trains continue out of the station, train 2 unmodified and train 1 with the top value modified to be the quotient. |
mul | Binary operation multiplying the value of the first person in train 1 to the value of the first person in train 2. Both trains continue out of the station, train 2 unmodified and train 1 with the top value modified to be the product. |
mod | Binary operation taking the modulo of value of the first person in train 1 and the value of the first person in train 2. Both trains continue out of the station, train 2 unmodified and train 1 with the top value modified to be the remainder. |
delete | All trains entering this station (even in the same timestep) are deleted from the world. |
Programs
Programs in TRAIN! have a very specific structure:
First come the trains. There can be an infinite number of trains. Trains are specified as follows:
[LOCOMOTIVE] start at Eindhoven track 1 [FIRST CLASS] Gerrit: This train has a very pretty yellow colour! Robbin: I like the blue detail. Sara: The train is very short though... [SECOND CLASS] Marijke: 102 Peter: 105 Layla: 122 Tom: 122 Jonathan: 98 Laura: 117 Julius: 122 Jasmine: 122
Trains start with the word [LOCOMOTIVE] after which the starting station and track is specified. Folliowing that are the first and second class passengers. First class passengers are comments and can contain (after a name and colon) arbitrary text. However, the text is interpreted (looking for the words "long", "medium" or "short" and colours in the format "<colourname> color", "<colourname> locomotive", "<colourname> colour", "<colourname> accent", "<colourname> detail", "<colourname> line" or "<colourname> stripe" where the colourname can be one of:
- "light red"
- "dark red"
- "light green"
- "dark green"
- "light blue"
- "dark blue"
- "water blue"
- "brown"
- "yellow"
- "orange"
Comments are in first class as per the theme of the LangJam which was "First class comments". After the first class, the second class is defined with people holding integer (64 bit signed) data. There can be an arbitrary number of passengers.
The train definitions are followed by a number of station definitions:
{Utrecht} operation: add track 1: go to Utrecht track 2 track 2: go to Amsterdam track 1 {Amsterdam} operation: print number track 1: go to Utrecht track 1
(the above is a simple Fibonacci program, given the right train setup). Utrecht has the add operation. The add operation has two tracks. Numbers on trains on track 1 are added to numbers on trains on track 2. After addition, the train on track 1 moves to Utrecht again, but now on track 2. While the train which was on track 2 now goes to Amsterdam. The train in Utrecht waits until the train is back from Amsterdam after the contents of the Amsterdam train were printed, continuing the cycle.
Considerations:
- Station names can not contain spaces
- Operation names are to be written as in the table with instruction names above
- Trains and stations may not contain empty lines within their definition
- Stations may not define more or fewer tracks than the operation dictates
Implementation
The reference implementation of TRAIN! can be found here: https://github.com/jonay2000/jam0001/tree/main/Boole. The reference implementation also has a visual execution which simulates the trains moving from station to station. Train stations and tracks are automatically layed out using a combination of pathfinding and physics simulation.
Sample programs
Apart from the samples which can be found on the reference implementation's github repository, the following is an example of a full fibonacci program in TRAIN!
[LOCOMOTIVE] start at Amsterdam track 1 [FIRST CLASS] Jan: this is a nice long lime green train! Pieter: It has some blue accents as well! [SECOND CLASS] Julius: 1 [LOCOMOTIVE] start at Utrecht track 2 [FIRST CLASS] Gerrit: This train has a very pretty yellow colour! Marijke: I like the blue detail. Sara: The train is very short though... [SECOND CLASS] Jonathan: 1 {Utrecht} operation: add track 1: go to Utrecht track 2 track 2: go to Amsterdam track 1 {Amsterdam} operation: print number track 1: go to Utrecht track 1