Seeing that the current language for Learn You a Lang for Great Good is Piet, and that we don't seem to have a tips page for Piet yet, I decided to make one.

Piet is a stack-based esoteric programming language in which the programs are meant to resemble abstract drawings.

What are some general tips for golfing in Piet?

As usual, please keep the tips somewhat specific to Piet, and one tip per answer.


3 Answers 3


Compact encoding and tools

The default scoring method for code golf is in bytes. It gives Piet a significant disadvantage, since every Piet program must be a valid image file (which can be in PNG, GIF, or PPM format, but all of them costs well over 10 bytes per pixel). Also, TIO has npiet as the Piet interpreter (Try it online!), but it is unwieldy to use because it expects the hexdump of an image file.

In this regard, DLosc has made a tool called ascii-piet, which gives a printable-ASCII-based roughly-1-byte-per-pixel encoding for valid Piet programs.

The color table for ascii-piet looks like this:

Red Yellow Green Cyan Blue Magenta
Light t v r s q u
Medium l n j k i m
Dark d f b c a e
Black (space) White ?

Using only these chars gives a plain 2D-lang-like scoring, where blanks at the end of each line can be omitted and a newline counts as 1 byte. In addition, you can change the last character of each line to uppercase (_ for white) and omit all newlines.

As an example, the program

can be written like an ordinary 2D language as


which can be written in one line as


which has the score of 12 bytes = 12 codels.

Self-contained TIO setup: Try it online! (Protip: you can pass -t to npiet to get step-by-step information.)

If you use this setup and scoring, you should specify that ascii-piet is used for the encoding, like this:

# [Piet](https://github.com/cincodenada/bertnase_npiet) + [ascii-piet](https://github.com/dloscutoff/ascii-piet), 12 bytes

MasterPiets has a nice color chooser interface and a step-by-step debugger (click "Debugger" on the right side to open it). It has a few drawbacks though: you lose your work when you resize the grid, and some users have reported that Export to PNG doesn't work.

  • 1
    \$\begingroup\$ If I'm not mistaken, aren't Piet programs scored based on codels, not the byte size of the image? I'm not sure though. \$\endgroup\$
    – Aiden Chow
    Mar 31 at 4:51
  • \$\begingroup\$ Huh, actually, I found this meta post, the top answers (count score as image size and count score as number of codels) both have the same net vote, so I'm not sure about this. \$\endgroup\$
    – Aiden Chow
    Mar 31 at 4:56
  • 1
    \$\begingroup\$ @AidenChow That discussion is quite old and has accumulated lots of negative votes for both options. But the recent general consensus is that EVERY language must be scored in bytes (as in the size of the file in disk). One relatively recent example. \$\endgroup\$
    – Bubbler
    Mar 31 at 5:18

Basic routing and halting

Routing and halting are two of the hardest walls when starting to write nontrivial Piet programs. The pointer moves in the unit of connected areas of the same color, and it chooses the next cell using the following variables:

  • DP: Direction pointer. One of right, down, left, or up. Initially right.
  • CC: Codel Chooser. One of left or right. Initially left.

Given the current area, DP, and CC, the next cell to move into is chosen as follows (hopefully more intuitive than the official doc):

  • Imagine you are facing in the direction of DP.
  • In the area you're stepping on, choose the cell(s) that are farthest from you.
  • If there are multiple, choose the rightmost or leftmost cell out of them (in your view) based on CC.
  • Walk to the chosen cell and move front once.

For example, if your current area looks like x in the following and DP=right and CC=left,

 h g

Then the next cell is a. The cells marked a to h are the cells you might enter next, depending on the current DP and CC.

If the next cell is black, or out of bounds, then the pointer tries the next combination of DP and CC - first swap CC, then rotate DP once clockwise, repeat. If all 8 combinations are tried and fails, it halts. (See the previous example again: the cells marked a to h are the cells that are tried in order.) Note that, by the spec, invalid commands (stack underflow and division by zero) are simply ignored and execution continues, so the only way to halt a program is via a trapped region.

So how to construct such a trapped region? The simplest ones I found are:




Can you see why each of these works? (It is impossible to trap in a 1-row program, or using areas of size 1 or 2.) And since these are the only trapping configuration of size 3, you will likely use one of these in every halting golfed program.

The turn-right-when-blocked behavior can be used to route in interesting ways. Some examples I found are:

# 2-row infinite loop

# 3-row finite loop with tail
# use conditional DP+ on `de` to turn right to move towards exit
# also can be used for a linear program; put `Push 1` on `cd` and `DP+` on `de`
abcabc z
dcbacb z

# 2-row with small loops; use conditional DP+ on `de`
 pool   pool   zz

# more rows with few black cells to eventually lead to infinite loop
# note that seemingly unused cells can be used to push constants
>>>>v .v

A note on DP+ and CC+ commands (looping and if-else)

There is a command that can change the direction of movement (essentially DP). It is called "pointer" (at hue +3, darkness +1) in the official doc, but I like to call it DP+, as it directly adds the top value to the current DP (modulo 4). If you have a condition that evaluates to 0 or 1, you can use DP+ on that to turn right once based on a condition. This creates a branch in your program, and you can mainly use it to create a conditional loop. (If you somehow need to turn left instead, applying *3 to the condition is the shortest method I can think of, which costs 4 more cells.)

There is CC+ ("switch" in official doc, hue +3, darkness +2) command too, which adds the top value to CC (modulo 2). This can be useful in creating if-else constructs: if you place CC+ at ab in the following code


then the pointer follows the cde path or the uvw path based on the resulting CC. Then the x block acts as the merging point: since CC=right path is blocked, CC is toggled and the execution continues through xyz... with CC=left in both cases.

  • \$\begingroup\$ This is interesting, didn't know you could make conditional loops in Piet. Could you elaborate more on what DP+ is, how it can be used for conditionals? thanks \$\endgroup\$
    – Aiden Chow
    Mar 31 at 6:20
  • \$\begingroup\$ @AidenChow Added. \$\endgroup\$
    – Bubbler
    Mar 31 at 6:59


If you need to create control flow that splits and merges quickly, you might want to use this construction:


where xA is CC+ and bcd.. and fgh.. are two if-else paths. w must be surrounded by three blacks (or outside of the program). It is easiest to use when there's nothing to do for bcd.., in which case you can just fill that row with white codels. Otherwise, you need to check what bcd.. does when run backwards.

An example where it prints 10 (newline) based on a condition.

The bounce part can be used without an if-else too, since it has an effect of fixing CC after it bounces back. primo's Hello World uses it for the 3-row layout to minimize wasted codels.

  • \$\begingroup\$ I don't get this at all :( \$\endgroup\$
    – Aiden Chow
    Jul 7 at 20:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.