Piet

Paradigm:
Typing discipline:
File extensions:
.png
Versions and implementations (Collapse all | Expand all):
Programming language

Piet (named after painter Piet Mondrian) is one of the most known esoteric programming languages, which uses images as source code. The language uses 20 colors, and the commands are encoded as changes of color between adjacent pixels.

Piet was created by David Morgan-Mar, who aimed for a language which would have its code look like abstract art. The language hasn’t spawned any dialects; there are several implementations, which differ slightly, mainly in the way the colors are processed. Turing-completeness of the language hasn’t been proven.

Piet uses the following machine model:

  • the program is a two-dimensional image which consists of separate pixels. Pixels can be of any color, but only 20 colors are considered when the program is interpreted: black, white and 18 colors, arranged in a tale (see image) by hue and lightness. Contiguous blocks of pixels of the same color are called blocks and are minimal units of the program. Note that Piet programs are often enlarged for better view of them; in this case a group of pixels which corresponds to single pixel of original program is called a codel.
  • the memory is a stack. Piet operates only on integer numbers. Characters can be processed as their ASCII-values.
  • instruction pointer has complex structure and consists of a pointer to the current color block (there is no concept of current pixel in Piet, though color block can consist of a single pixel), Direction Pointer (can point left, right, up or down) and Codel Chooser (can point left or right, direction is relative to the value of Direction Pointer).

The program is interpreted in the following way. On each step The interpreter finds the furthest edge of the current colour block in the direction of the DP. For blocks of complex shape this edge might be disjoint. From all the pixels that form this edge, the interpreter finds the furthest one to the CC’s direction (if you face DP’s direction). From this pixel the pointer moves into the next colour block in the direction of the DP from that edge pixel. After the movement the command which corresponds to this change of color (see table) is executed.

In general case movement between blocks doesn’t change the directions of pointers; but attempt to move into a black block does. Black blocks can’t be entered, so if current directions of CC and DP lead to a black block, their directions change — first CC, then DP, then CC again etc., until the block to move in is colored. Besides, commands pointer and switch change direction of DP and CC.

The table of commands as defined by color changes follows:

  Hue             Lightness change
change  None            -1          -2
  0                 push        pop
  1     add         subtract    multiply
  2     divide      mod         not
  3     greater     pointer     switch
  4     duplicate   roll        in(int)
  5     in(char)    out(int)    out(char)

The commands of the language are:

  • push: pushes the number of pixels in the colour block exited on to the stack. Note that negative and zero values can get into the stack only as a result of arithmetic operations.
  • pop: pops the top value off the stack and discards it.
  • add, subtract, multiply, divide, mod: pops the top two values off the stack, performs the corresponding operation on them (first operand is the second-top element), and pushes the result back on the stack.
  • not: replaces the top value of the stack with 0 if it is non-zero, and 1 if it is zero.
  • greater: pops the top two values off the stack, and pushes 1 on to the stack if the second top value is greater than the top value, and pushes 0 if it is not greater.
  • pointer: pops the top value off the stack and rotates the DP clockwise that many times (anticlockwise if negative).
  • switch: pops the top value off the stack and toggles the CC that many times.
  • duplicate: зushes a copy of the top value on the stack on to the stack.
  • roll: pops the top two values off the stack and “rolls” the remaining stack entries to a depth equal to the second value popped, by a number of rolls equal to the first value popped. A single roll to depth n is defined as burying the top value on the stack n deep and bringing all values above it up by 1 place. Negative number of rolls rolls the stack in the opposite direction. Negative depth is an error.
  • in: reads a value from STDIN as a number or a character, depending on the type of the command, and pushes the result on to the stack.
  • out: pops the top value off the stack and prints it to STDOUT as a number or a character, depending on the type of the command.

Commands which can’t be executed (have invalid arguments or are applied in invalid conditions) are simply ignored.

Piet table of colors
Piet table of colors

Examples:

Hello, World!:

Example for versions npiet 1.2

This example uses only 2 commands — push and out (char). To print one character, one has to create a block of any color; the number of pixels in it should be equal to ASCII-code of this character. After this, one should create a block of color one shade darker of arbitrary size immediately to the right of that block (this will perform push command). Finally, a block of color one hue to the left of the original one of arbitrary size will perform out(char) command. After this, this sequence repeats for the next character.

To end program execution, we use a loop — a single-color block surrounded with black pixels. When the instruction pointer gets inside this block, it can’t leave in any direction.

The decorative version of the example shows that blocks can have any shape, even with pixels of other colors inside.

"Hello, World!" in Piet (basic)
"Hello, World!" in Piet (basic)

"Hello, World!" in Piet (basic, 5x scale)
"Hello, World!" in Piet (basic, 5x scale)

"Hello, World!" in Piet (decorative)
"Hello, World!" in Piet (decorative)

"Hello, World!" in Piet (decorative, 5x scale)
"Hello, World!" in Piet (decorative, 5x scale)

Hello, World!:

Example for versions npiet 1.2

This example was generated using a translator by Sergei Lewis. It provides two tools — a translator from a simple C-like language into an assembler and from the assembler into the Piet image. In this case the original program looked like this:

main()
{
asm{ @"Hello, World!\r\n" }
}

Hello, World! in Piet (autogenerated)
Hello, World! in Piet (autogenerated)

Hello, World! in Piet (autogenerated, 5x scale)
Hello, World! in Piet (autogenerated, 5x scale)

Factorial:

Example for versions npiet 1.2

This example was generated automatically. The original program translated into the image follows; it uses iterative factorial calculation. Note that values of 13! and larger are wrong due to overflow.

main()
{
  f = 1;
  for ( i = 0; i <= 16; i++ )
  {
    __outn(i);
    asm{ @"! = " }
    __outn(f);
    __out(10);
    f = f * (i+1);
  }
}

Factorial in Piet (autogenerated)
Factorial in Piet (autogenerated)

Factorial in Piet (autogenerated, 4x scale)
Factorial in Piet (autogenerated, 4x scale)

Fibonacci numbers:

Example for versions npiet 1.2

This example was generated automatically. The original program translated into the image follows; it uses iterative calculation of Fibonacci numbers.

main()
{
  f1 = 0;
  f2 = 1;
  for ( i = 1; i <= 16; i++ )
  {
    __outn(f2);
    __out(44);
    __out(32);
    f2 = f1 + f2;
    f1 = f2 - f1;
  }
  __out(46);
  __out(46);
  __out(46);
  __out(10);
}

Fibonacci numbers in Piet (autogenerated)
Fibonacci numbers in Piet (autogenerated)

Fibonacci numbers in Piet (autogenerated, 4x scale)
Fibonacci numbers in Piet (autogenerated, 4x scale)