We are currently working on new rules for what content should and shouldn't be allowed on this website, and are looking for feedback! See Esolang:2026 topicality proposal to view and give feedback on the current draft.

The Exasperation Machine

From Esolang
Jump to navigation Jump to search

The Exasperation Machine (TEM) is an esoteric programming language and a computational model created by User:Rainwave in 2026. The language can be seen as a brainfuck dialect that ditches explicit control flow in favor of an implicit loop and fallible movement instructions. It can also be seen as 1-dimensional A Painter Ant, but it has both conditional and unconditional move instructions.

The language provides an easy reduction target for other minimal languages, especially ones that have an implicit loop. In fact, it was originally created to serve as an intermediate step in A Painter Ant's Turing completeness proof.

Language Semantics

The Exasperation Machine operates on a bidirectionally unbounded tape. Each cell in the tape stores a single bit (either 0 or 1). Initially, every cell is set to 0.

A tape head is initially positioned on the first cell. The tape head can be controlled to move left/right and read/write the value of its current cell. The Exasperation Machine provides the following instruction set:

Instruction Description
> Move the tape head right to the next cell.
< Move the tape head left to the previous cell.
/ Move the tape head right if the value of the current cell is 1. Otherwise, this instruction has no effect.
\ Move the tape head left if the value of the current cell is 1. Otherwise, this instruction has no effect.
1 Set the value of the cell under the tape head to 1.
0 Set the value of the cell under the tape head to 0.

The entire program is wrapped in an endless implicit loop. What this means is that the execution wraps around to the first instruction after the last instruction is executed.

A TEM program is written as a single string consisting of instructions. Implementations may ignore symbols that are not listed as instructions.

Computational class

The Exasperation Machine is Turing complete as any binary Turing machine can be compiled into it. One possible compilation strategy follows.

Turing machine

A Turing machine has a transition table where each row of the table contains a transition lookup rule when a particular state is active. Each rule usually reads like "For state , if read then write , move , and change state to . If read 1, then write , move , and change state to ." These rules can be broken down into three simpler primitive rules:

  1. If state is , then write and change state to .
  2. If state is and read , then change state to .
  3. If state is , then move and change state to .

Like the standard Turing machine, we can construct these rules so that only one rule's condition is met when all rules are evaluated simultaneously. It can be shown that we need no more than 5 microstates for each TM transition rule. Therefore, we need microstates in total to simulate a Turing machine with states.

The idea now is to compile each of these primitives into a reusable template of TEM instructions. We will use the following layout to encode the Turing machine. Each TM cell will be mapped to a contiguous block of TEM cells starting from [b][_][_] up to [b'][_][_].

...[b][_][_][s1][_][_][s2][_][_]...[s5n][_][_][b'][_][_][b][_][_][s1][_][_][s2][_][_]...[s5n][_][_][b'][_][_]...

Note:

  • [b] (bit) stores the actual value of the TM cells. We want to normalize the tape head position to this cell at the start of each TM cycle.
  • [s1] through [s5n] (states) encode a hot-bit for the current active microstate. Across the tape, only one of these can equal 1 at the start of each TM cycle.
  • [_] are buffer cells. They should all equal 0 at the start of each TM cycle.
  • [b'] stores a temporary copy of the value of [b]. This should be 0 when unused.

Rule 1

The first rule is the easiest to construct. The idea is that \> will shift the head alignment when the state is not active, causing subsequent writes to happen to buffer cells. After writing to [b] and [sT] (or their buffers), we then fix the alignment to +1 while simultaneously writing 0 to [sS] using /<0>. At the end, we use the +1 alignment to clear [b] and [sT]'s buffers.

>>>>>>...>>>               S times
\>
<<<<<<...<<<               S times
1                          1 or 0 depending on B
>>>>>>...>>>               T times
1
<<<<<<...<<<               T-S times. Use >>> if T is less than S
/<0>
<<<<<<...<<<               S times
0
>>>>>>...>>>               T times
0<
<<<<<<...<<<               T times

Rule 2

Rule 2 is conceptually similar to rule 1. The difference is that there are two conditions that need to be satisfied, resulting in three possible alignment offsets.

>>>>>>...>>>                S times
\>
<<<<<<...<<<                S times
\>                          If B=1. Use / if B=0
>>>>>>...>>>                T times
1
<<<<<<...<<<                T times
</>                         If B=1. Use / if B=0
>>>>>>...>>>                S times
/</                         If B=1. Use / if B=0
>>>>>>...>>>                T-S times. Use <<< if T is less than S
0>0<<\>
<<<<<<...<<<                T-S times. Use >>> if T is less than S
0
>>>>>>...>>>                T-S times. Use <<< if T is less than S
/<
<<<<<<...<<<                T times

Rule 3

Rule 3 is the trickiest one to construct. For now, let's consider the case where .

We need to move [b] to [b'] and [sS] to the previous block's [sT]. If state was active, the result is that the cells between [sS] and the previous block's [sT] will be devoid of 1's. We can then use a sequence of \> instructions to produce a (a full block length) cell discrepancy between the cases where state ends up active and inactive. At the end, we restore the value of [b].

\>
>>>>>>...>>>                5n+1 times
1
<<<<<<...<<<                5n+1 times
/<0>
>>>>>>...>>>                5n+1 times
0<
<<<<<<...<<<                5n+1-S times
\>
<<<<<<...<<<                5n+2+S-T times
1
>>>>>>...>>>                5n+2+S-T times
/<0>
<<<<<<...<<<                5n+2+S-T times
0<
\>\>\>...\>                 15n+6 times
>>>>>>...>>>                10n+3-T times
\>
<<<<<<...<<<                5n+1 times
1
>>>>>>...>>>                5n+1 times
/<0>
<<<<<<...<<<                5n+1 times
0<<<<\>
<<<<<<...<<<                5n+1 times
1
>>>>>>...>>>                5n+1 times
/<0>
<<<<<<...<<<                5n+1 times
0<

The case where can be constructed in a similar manner. It is therefore left as an exercise for the reader.

Implementations

Python

import itertools, collections
tape = collections.defaultdict(int)
p = 0
for c in itertools.cycle(input()):
    if c in '/\\' and tape[p] or c in '><':
        p += {'>': 1, '<': -1, '/': 1, '\\': -1}[c]
    elif c in '10':
        tape[p] = int(c)

JavaScript

This implementation can be run inside a JS runtime or a web console

const code = "" // write your code here
let tape = {}
let i = 0, p = 0
while (true) {
    const c = code[i++ % code.length]
    if ("/\\".includes(c) && tape[p] || "><".includes(c))
        p += { '>': 1, '<': -1, '/': 1, '\\': -1 }[c]
    else if ("10".includes(c))
        tape[p] = Number.parseInt(c)
}

Rust

fn main() {
    let mut code = String::new();
    std::io::stdin().read_line(&mut code).unwrap();
    let mut p: i32 = 0;
    let mut tape = std::collections::HashSet::<i32>::new();
    for c in code.chars().cycle() {
        match c {
            '>' => p += 1,
            '<' => p -= 1,
            '/' => if tape.contains(&p) {
                p += 1
            }
            '\\' => if tape.contains(&p) {
                p -= 1
            }
            '1' => {
                tape.insert(p);
            }
            '0' => {
                tape.remove(&p);
            }
            _ => (),
        }
    }
}

Further simplifications

The Exasperation Machine is Turing complete even when restricted to a unidirectionally unbounded tape. However, this causes < instructions and successful \ instructions to result in undefined behavior if executed when the tape head is at the leftmost cell.

Variants

General Exasperation Machine

The General Exasperation Machine (GEM) is a superset of TEM. Instead of storing a single bit, each cell can store arbitrarily many bits. The machine should be able to inspect and mutate any individual bit, which means the instructions need to take an argument to determine which bit to inspect/mutate. One upside of this generalization is that the > and < instructions become redundant, reducing the instruction count to just 4. It's easy to see that 2 bits per cell is the minimum requirement for a GEM without > and < to be Turing complete.

Alternating Exasperation Machine

The Alternating Exasperation Machine (AEM) is a Turing complete variant of TEM which uses % to toggle a bit instead of using 1 and 0 to idempotently set the current bit's value. This variant is very useful for proving certain certain languages where toggling a bit is a more natural operation than setting a bit idempotently, like for example GNAW and Kak.

We'll map a standard TEM cell into a block of AEM cells as follows

0 = 01?010
1 = 11?010

Where ? is a sacrificial bit that we flip when the current bit already possessed the value that we want to write.

We can map each TEM instruction to a sequence of AEM instructions as follows

TEM AEM
> >>>>>>
< <<<<<<
/ / >>>/ <<\ <<</ >>>>\ </>
\ \ <\ >\ >>>\ <</ <<\
1 //% <\\>
0 \<\>>>% <\>\

Then what remains is to make sure that the head stays in a valid block before each TEM instruction. At the beginning of the execution, we need to establish the first valid block. To do that, we add the following code at the start of the program

>>>>>> >\>>>>\<%>>/</ <<<</%<\ 

and the following code at the end of the program to cancel the effect of the initialization code if we're currently not in the first cycle

<<<<<<

Then, we also need to dynamically expand the tape when we're at the rightmost cell so that > and / won't reach an invalid block. To do that, we add the following code before each TEM instruction

>>>>>> >\>>>>\<%>>/</ <<<</%<\ <<<<<<

Two-Way Exasperation Machine

The Two-Way Exasperation Machine (TWEM) is a Turing complete variant of TEM that replaces / and \ with ^, which moves the tape head right if the value of the current cell is 1, and moves the tape head left otherwise. Standard TEM can be compiled into TWEM using the following block layout

0 = 011001
1 = 101001

Assuming the head is normalized at the first cell in its current block, we can map each TEM instruction as follows

TEM TWEM
> >>>>>>
< <<<<<<
/ ^>^>>>>>^<<<
\ >^>>>^<<<<<^<<
1 1>0<
0 0>1<

Unlike the previous construction where we expand the tape right, here we'll expand the tape left when we're at the leftmost block. To do that we need to add the following code before each TEM instruction

<<<1<^>>>>>>>>^<<<<^<1>>>1<<^<<<1>>>>^>>^<<<<<<0>>>

Lastly, we also need to initialize the first valid block, which we can do quite easily.

>1>1>>>1<<<<<

The Exasperation Machine to brainfuck

There's an almost trivial way to reduce brainfuck to TEM, proving that brainfuck with unbounded tape length is Turing complete.

Instruction Mapping
> >>
< <<
/ [>]<[>]>
\ [<]>[<]<
1 [-]+
0 [-]

See also