Trianguish
A 2d "programming language" based around triangles
Install / Use
/learn @Radvylf/TrianguishREADME
Trianguish
Trianguish is a 2d "programming language" designed by Radvylf Programs.
Basics
Trianguish uses a triangular grid of cells, called "ops" (short for "operator"). Each of these has three inputs or outputs, called "pins", which can be of various types, including inputs and outputs. Ops pass around information via their pins, performing operations such as addition, comparison, and conditonal switching. This occurs in "ticks", the smallest meaningful unit of time in Trianguish, during which all ops simultaneously observe their inputs and compute their outputs. Trianguish features some operators, called builders, which can change the ops around them, and others which can perform I/O operations.
Data
Trianguish has two data types: NIL and numbers. NILs are used to indicate that no input could be taken, no output could be produced, or that no op or pin corresponds to an input pin. "Vanilla" Trianguish's number type is a three digit base-6 integer, which can represent values from 0 to 215, but this "Mod 216" behavior can be turned on or off, and when off Trianguish will allow numbers to be arbitrary signed integers. As Trianguish is self-modifying, all ops have IDs which are also numbers. There are only 216 meaningful IDs for an op, many of which are redundant (when Mod 216 is off, op IDs can be outside the range of 0 to 215, but they will behave as if their IDs are modulo'd to be within this range). In this documentation, numbers will be represented with trailing ns.
Ops
Ops are stored using numeric IDs, typically ranging from 0n to 215n. This is broken into three fields: op type, variant, and orientation. Op type ranges from 0n to 11n, variant from 0n to 2n, and orientation from 0n to 5n. To find these values, the formulas op_type = floor(id / 18n), variant = floor(id / 6n) % 3n, and orientation = id % 6n may be used.
The op type determines which operation an op will perform:
0n:nil1n:constant2n:2-wire3n:splitter4n:t-switch5n:s-switch6n:n-switch7n:add8n:mul9n:cmp10n:i/o11n:builder
The variant field is used only for constants and builders:
0n:0ori-builder1n:1oro-builder2n:-1ors-builder
The orientation contains two subfields, rotation and chirality. Rotation is 0n by default, 1n for a single pin rotation left, and 2n for a single pin rotation right ("left" and "right" referring to which side the up/down facing pin rotates toward), and chirality determines what order pins go in, or the "handedness" of an op (you can think of it as drawing the op on a sheet of transparent paper, and flipping the sheet over). Rotation and chirality can be found with the formulas rotation = floor(orientation / 2n) and chirality = orientation % 2n.
Op types
NIL:

NILs (0n) are the most basic op, simply outputting NIL to each of their pins.
Constant:

Constants come in three varieties: 0 (18n), 1 (24n), and -1 (30n). Each of these variants will output their value on all pins. If Mod 216 is on, any -1 constants will wrap around to 215.
2-Wire:

2-Wires (36n) are the most basic way to move values. They have one input pin and one output pin, and will copy their input to their output, with their third pin being NILs.
Splitter:

Splitters (54n) are similar to 2-wires, but output on both of their non-input pins.
T-Switch:

T-Switches (72n) act as conditional wires. If non-NIL input is provided to their third pin (differentiated by color), the input and output pins act as a 2-wire, and otherwise the t-switch acts as a NIL.
S-Switch:

S-Switches (90n) act like two-input wires. They will behave as standard 2-wires, unless their input is NIL, in which case any input to their third pin (differentiated by color) will be outputted. Only if both inputs are NIL will an s-switch output NIL.
N-Switch:

N-Switches (108n) are used for conditionally switching between NIL and non-NIL outputs based on comparison. If the n-switch's two inputs are different, the primary input will be outputted, and otherwise the n-switch's output will be NIL.
Add:

Adds (126n) add their inputs. Unlike switches, both inputs to an add are identical. If both inputs are non-NIL, the add will output the sum of its inputs (only wrapping if Mod 216 is on), and otherwise it will output NIL.
Mul:

Muls (144n) multiply their inputs. Like adds, both inputs to a mul are identical. If both inputs are non-NIL, the mul will output the product of its inputs (only wrapping if Mod 216 is on), and otherwise it will output NIL.
Cmp:

Cmps (162n) compare their two inputs, outputting one of three values to indicate less than, equal to, or greater than. If either input is NIL, the cmp's output will also be NIL. If the primary input to the cmp is less than the secondary input (differentiated by color), the cmp's output is -1 (or 215 if Mod 216 is on), if they are equal it is 0, and if the primary input is greater the output is 1. This is equivalent to taking the sign of the primary input minus the secondary input. Cmps are most useful when chained with n-switches.
I/o:

I/os (180n) can perform user input and output operations. Their primary input pin is used for output, with any non-NIL input being outputted, and their secondary input pin is used to take input (so that all input is not consumed before it can be properly handled). If the secondary input pin is given a non-NIL value, one value will be taken from user input. This will be outputted on the i/o's output pin, and will always be non-NIL, unless input is exhausted. Once NIL is returned from an i/o's input, all future input operations will return NIL.
Builders:



Builders come in three varieties: i-builders (198n), o-builders (204n), and s-builders (210n). Builders are one of the most important ops, as they allow for self-modification, in turn permitting complex timing operations, more efficient storage, randomization or undefined behavior, and short non-NIL pulses. Builders are the only ops to have a build input pin, which when given non-NIL input, will treat their input as a numeric representation of an op to "build" in an adjacent cell before the next tick. Builders will always build one pin clockwise of their build input pin, and the pin adjacent to this cell can have varying purposes. An i-builder or o-builder will double as wires, with i- or o- indicating which end is used as the build cell. Possibly the most important builder type is the s-builder, which has a NIL pin adjecent to its build cell, and an output pin. S-Builders will output the numeric representation of the op in their build cell. This will always be non-NIL. If Mod 216 is off, builders can place ops with IDs outside of the typical range (0n to 215n), and s-builders can read these unchanged.
Fun fact: The original idea for Trianguish, years ago, was for it to simulate living organisms, or other complex macro-scale automata. This would not work, since the density of builders to support ops is likely far too low for self-moving structures to be built.
Timing
During a tick, all ops will update simultaneously. All ops will observe the states of pins around them before any of that tick's changes take place, ensuring that programs work identically when rotated or moved. This has two exceptions, however. First, building will occur after all ops update their pins. This means that the program pictures below, with a 0 constant on the build input pin of a builder, with its build cell being a 1 constant, any ops inputting from the 1 constant will input a 1n, in the same tick that the constant is replaced by a NIL.

This timing quirk can be used to propagate signals faster or slower than wires, which is essential as the strict structure of the triangular grid makes precise timing difficult or impossible otherwise. The second exception to simultaneous updating is a necessary consequence of i/os and builders existing, that being handling conflicting operations. If two or more builders build in the same cell on the same turn, or two or more i/os take input or produ
