Despite all the possible innuendos that could be suggested with a title like that, no, I’m not trying to be dirty. I’m talking about brainfuck: an esolang created by Urban Müller designed to be extremely minimalist while still remaining Turing-complete. Meant to be both challenging and amusing, brainfuck has naturally attracted some of the most witty and clever hacks.
The behavior of a brainfuck program mimics that of a Turing machine. Generally speaking, a Turing machine is a hypothetical device that manipulates symbols on an infinitely long tape according to a fixed set of rules. Though such a device is inconceivable since there’s simply no way to implement something infinite, it’s used as a model to simulate the logic of those amazing little CPU’s inside all of our neat-o gadgets.
For something more tangible, here’s a video of an actual Turing machine created by Mike Davey. Note that it’s not a definitive Turing machine since the strip of tape is not infinitely long. Regardless, it’s still incredible.
To say that brainfuck is “extremely minimalist” is really quite an understatement. Perhaps “uniquely cryptic” is a little more accurate. The entire brainfuck language consists of merely eight instructions. Furthermore, each one of them is only represented with a single character.
||Increment the data pointer to point to the next cell.|
||Decrement the data pointer to point to the previous cell.|
||Increment the byte value at the data pointer.|
||Decrement the byte value at the data pointer.|
||Output the byte value at the data pointer.|
||Input one byte and store its value at the data pointer.|
||If the byte value at the data pointer is zero, jump to the instruction following the matching
||Unconditionally jump back to the matching
A brainfuck program consists of a sequence of these odd instructions, a byte or "cell" array (usually 30,000 bytes and initialized to zero, although not required), a data pointer which starts at the first byte of the array, and two standard streams for input and output.
The brainfuck Interpreter
Before moving on, you're obviously going to need a brainfuck interpreter. The Internet is brimming with different brainfuck compilers and interpreters so you shouldn't have a hard time finding one. In fact, I'll make it easy for you; just use mine! It's called Egghead. I wrote Egghead with the goal of making it a full GNU package and not just some amusing 30 line Perl script. It also has the advantage of including a debugger; although it's still in a development branch at the moment.
As much as I would love for people to use Egghead, I'd also encourage you to write your own brainfuck interpreter. The funny thing about brainfuck is that even though the language itself is obscure, a brainfuck interpreter is incredibly simple; an assertion that is usually the opposite for most languages. In fact, Müller's brainfuck-2 interpreter was only 240 bytes!
Here's the simplest way that it's generally done. Starting out with:
char array; char *ptr = array;
The brainfuck instruction set can be roughly translated into the following C statements:
Plop that in a switch statement and you're done. It's that simple.
But I'm not here to just explain the brainfuck interpreter. If I was, I'd be done already and we'd all be sad. It's time to shed some light on an obscure language that, admittedly, even took myself a while to comprehend.
So you know what brainfuck is and you know it has eight commands. You would think that the next appropriate step would be to demonstrate the classic "Hello World!" example. Not with brainfuck. A brainfuck "Hello World!" program looks like a complete nightmare at first glance. Let's just do some basic input/output first.
, // Read one byte into first cell + // Increment value at first cell . // Display value at first cell
Simple. Whatever is input gets incremented and displayed back out. If you entered 'a', it will display 'b' and if you entered 'x', it will display 'y', and so on.
You're probably thinking, "What the brainfuck? You never said anything about inline comments." That's because those aren't really comments. By default, brainfuck will ignore any invalid characters. You're free to insert whatever you want. I chose to use C++ style comments just for the sake of familiarity.
Mimicking toupper() and tolower()
We can expand upon this example and perform something similar to C's
toupper() function. The difference between an ASCII lowercase and uppercase letter is 32. Therefore, we could write:
, ---------------- // Decrement value at first cell ---------------- // thirty-two times .
- command decrements the value at the data pointer by one, using it 32 times accomplishes the same thing as subtracting the initial value by 32. Therefore, whatever is input gets displayed as its uppercase equivalent. So 'a' becomes 'A' and 'x' becomes 'X'.
These two code snippets only manipulate the first cell of the byte array though. There's 29,999 more at our disposal. Enter the
< commands which advance and backtrack the data pointer respectively.
Let's revamp that last example a little bit and combine both
tolower()'s behavior. We can subtract 32 from the first cell, move to the second cell, ask for another value, add 32, then display both values. Just like this:
, // Input first value ---------------- // Convert to uppercase ---------------- > // Move data pointer to second cell , // Input another byte into second cell ++++++++++++++++ // Convert to lowercase ++++++++++++++++ < // Move data pointer back to first cell .>. // Display first and second cell
See how cryptic this can become? All we did was convert uppercase and lowercase letters and yet if you were to look at this briefly, you'd have no idea that's all it was doing. It takes a lot of patience.
You may be starting to feel just a little brainfucked but there's two more commands left:
]. These commands are used to form loops. They basically say "while the current value being pointed to is not zero, keep on looping between the
] until it is." That's it.
++++++ // Start with 6 [ // Loop while first cell is non-zero - // Decrement first cell ] // Jump back to matching [
Here we set the first cell to 6. When the [ is encountered, the interpreter checks to see if the current value being pointed to is greater than zero. Since it is, the loop is entered. The loop simply decrements the cell value by one. When the ] is encountered, execution jumps back to the matching [ bracket and the conditional is evaluated again. Now that it's 5, the loop continues. This goes on and on until the cell finally contains zero.
Simulating Multiplication and Division
I know, I know...that's still kinda lame. The [ and ] commands don't really introduce anything new or special but they give us a lot of power. Given everything that's been explained up until now, can you think of a way to perform multiplication or division?
Here's 7 × 6:
+++++++ // Loop 7 times [ >++++++ // Add 6 to second cell <- // Decrement first cell ] >. // Display second cell's value
For each interval in the first cell's value, a given amount gets added to the second cell. In this example, 6 gets added to the second cell 7 times, giving 42. It took Deep Thought 7.5 million years to calculate this yet all it took for us was a 22-character brainfuck script. :P
Division can be done using the same technique for 12 ÷ 4:
++++++++++++ // Loop 12 times [ >+ // Increment second cell <---- // Subtract 4 from first cell ] >. // Display second cell's value
Building upon this technique, the ever-loved "Hello World!" program finally becomes possible:
++++++++[>+++++++++<-]>. // 8 x 9 = 72 (H) <+++++[>++++++<-]>-. // 72 + (6 x 5) - 1 = 101 (e) +++++++.. // 101 + 7 = 108 (l) +++. // 108 + 3 = 111 (o) <++++++++[>>++++<<-]>>. // 8 x 4 = 32 (SPACE) <<++++[>------<-]>. // 111 - 24 = 87 (W) <++++[>++++++<-]>. // 87 + 24 = 111 (o) +++. // 111 + 3 = 114 (r) ------. // 114 - 6 = 108 (l) --------. // 108 - 8 = 100 (d) >+. // 32 + 1 = 33 (!)
It's ugly - there's no doubt about that - but if you take it step by step, it makes complete sense. For the most part, it simply uses the multiplication technique from earlier, then it applies it over and over again to get from the previous number to the next. However, there are a few cases where it uses a third cell. That's why I'll explain it again except, this time, I'll list the value of each cell. I'll use the notation Cn:x where n represents the cell number and x is the decimal value of the respective cell. For example, if the first cell contained the value 23, it'd be written C1:23 and if the second cell contained the value 145, it'd be written C2:145.
++++++++[>+++++++++<-]>. // C1:0 C2:72 C3:0 <+++++[>++++++<-]>-. // C1:0 C2:101 C3:0 +++++++.. // C1:0 C2:108 C3:0 +++. // C1:0 C2:111 C3:0 <++++++++[>>++++<<-]>>. // C1:0 C2:111 C3:32 <<++++[>------<-]>. // C1:0 C2:87 C3:32 <++++[>++++++<-]>. // C1:0 C2:111 C3:32 +++. // C1:0 C2:114 C3:32 ------. // C1:0 C2:108 C3:32 --------. // C1:0 C2:100 C3:32 >+. // C1:0 C2:100 C3:33
One of the neat things about brainfuck is its adherence to Perl's TIMTOWTDI philosophy: "there's more than one way to do it". Consider the program that reverses an input string:
This could equally have been written as:
Knocking it up a notch, here's one way to compute the Fibonacci sequence:
+++++++++++ >+>>>>++++++++++++++++++++++++++++++++++++++++++++ >++++++++++++++++++++++++++++++++<<<<<<[>[>>>>>>+> +<<<<<<<-]>>>>>>>[<<<<<<<+>>>>>>>-]<[>++++++++++[- <-[>>+>+<<<-]>>>[<<<+>>>-]+<[>[-]<[-]]>[<<[>>>+<<< -]>>[-]]<<]>>>[>>+>+<<<-]>>>[<<<+>>>-]+<[>[-]<[-]] >[<<+>>[-]]<<<<<<<]>>>>>[+++++++++++++++++++++++++ +++++++++++++++++++++++.[-]]++++++++++<[->-<]>++++ ++++++++++++++++++++++++++++++++++++++++++++.[-]<< <<<<<<<<<<[>>>+>+<<<<-]>>>>[<<<<+>>>>-]<-[>>.>.<<< [-]]<<[>>+>+<<<-]>>>[<<<+>>>-]<<[<+>-]>[<+>-]<<<-]
>++++++++++>+>+[[+++++ [>++++++++<-]>.<++++++[>--------<-]+<<<]>.>>[[-]<[ >+<-]>>[<<+>+>-]<[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+ <-[>+<-[>+<-[>[-]>+>+<<<-[>+<-]]]]]]]]]]]+>>>]<<<]
But wait, there's more!
And just when you thought things couldn't possibly get any worse, then someone comes along and completely shatters your worldview. How? Oh, you know, just by writing another brainfuck interpreter...in brainfuck. Behold BF interpreter written in BF. Self-hosting compilers are tough to comprehend as it is but somehow Frans Faase pulled it off. Believe it or not, self-hosted brainfuck interpreters are not an uncommon site. The Internet is full of them. Two more of them are linked to from his homepage, both of which are even shorter.
It doesn't stop there though. Another example of the horrifying things brainfuck has inspired is Thomas Wright and his brainfuck ASCII art message concealer. Give it some text to display and an image to upload and it'll spit out an ASCII art presentation of your image with the necessary brainfuck code required to display your text discretely embedded within it. It's pure genius. It also makes for a great forum signature.
There's also CherryBlossom. Written by Vivin Paliath, it maps the brainfuck instruction set to words that convey a similar meaning so that they can be written as a haiku. Consider the
, instruction which is used to input a value. Therefore, it's meaning can be represented with words like absorb, receive, and learn.
Remember I said how awful the brainfuck "Hello World" example can look? Well take a look at the equivalent CherryBlossom code:
your lovely fragrance heals me
dreaming of your lovely smile,
when will you come here?
sunshine and summer flowers
a lovely morning
on a fragrant summer day
I can remember
my happy dreams of summer
it was beautiful
flying doves, sunrays
beauty flying in sunshine
rain in the valley.
snow falls in moonlight,
returns to the mountainside.
view from mountaintop
is a beautiful painting,
in summer sunshine.
the fragrant flowers
and the pretty butterflies
spring by singing creek.
butterflies by riverside
floating in sunshine.
such a lovely sight,
the valley waterfall is
in the spring sunshine.
sunrays and sunshine,
the butterflies and flowers
loving the new spring.
the pretty flowers
are dreaming of a summer
with the smiling sun.
music from heaven,
is melodious and sweet,
dreamy and happy.
the river is cold
and misty in the moonlight,
in the autumn chill.
lonely, icy, and chilly
the lonely winter,
barren riverside ahead
a dreaming poet
Has code ever looked so beautiful?
It's not hard to see now why the name "brainfuck" is just about the only appropriate name for such a language. When there's something that has zero practical purpose yet has motivated people to create inexplicable anomalies such as these, how else could you possibly describe the state of their brain!? Now get out there and have fun...err...frying your brain.