Home > Programming > Why you should learn brainfuck (or: learn you a brainfuck for great good!)

Why you should learn brainfuck (or: learn you a brainfuck for great good!)

Before I begin, a little disclaimer: there’s no way to write about brainfuck without offending someone. Some people will be upset about the swearing, others about the censorship. I’ve come up with a partial solution: Decensor this article (decensor plugin available on github) If you’re reading this in a feed reader then sorry for the swearing.

What is brainfuck?

Brainfuck is close to being the simplest programming language possible, with only 8 instructions:

> < + - , . [ ]

These instructions move an internal data pointer, increment and decrement the value at the data pointer, input and output data, and provide simple looping.

As an aside: brainfuck was written with the intent of having a language with the smallest-possible compiler. Many compilers for brainfuck are smaller than 200 bytes! See Wikipedia for more details.

Why would I want to learn such a stupid language?

It’s been suggested that you should learn at least 6 programming languages to be a good programmer. The more programming languages you know, the more perspectives you’ll have on problems. Brainfuck is such a simple language to learn, there’s almost no reason not to learn it. If you read the whole of this blog post now, and try out all the code samples in the interpreter, in half-an-hour to an hour you’ll have a new programming language under your belt. Tell me that’s a waste of time :)

Another good reason to learn brainfuck is to understand how basic a Turing-complete programming language can be. A common argument when programmers compare languages is “well they’re all Turing-complete”, meaning that anything you can do in one language you can do in another. Once you’ve learnt brainfuck, you’ll understand just how difficult it can be to use a Turing-complete language, and how that argument holds no water.

Ok, you’ve convinced me. Teach me!

Good. I knew you were one of the smart ones.

Brainfuck doesn’t have variable names – just one long array of cells, each of which contains a number:

address: 0  1  2  3  4  5  6  ...
value:  [0][0][0][0][0][0][0][...]

Each cell is initialized to zero. A data pointer starts off pointing to the first cell:

pointer: v
address: 0  1  2  3  4  5  6  ...
value:  [0][0][0][0][0][0][0][...]

Moving the data pointer and modifying the data

A brainfuck program is a stream of the 8 commands (other characters are allowed in this stream but are ignored). Here’s a basic brainfuck program:

>>>>++

The greater-than command moves the data pointer one cell to the right. The plus command increases the value of the cell under the data pointer by 1. Here’s what the data looks like after we run the above program:

pointer:             v
address: 0  1  2  3  4  5  6  ...
value:  [0][0][0][0][2][0][0][...]

The data pointer moved four cells to the right, and the value of that cell was increased by 2. Pretty simple.

You can probably guess what the less-than and minus commands do:

program: >>>>++<<++-

pointer:       v
address: 0  1  2  3  4  5  6  ...
value:  [0][0][1][0][2][0][0][...]

So, move four cells right, increment twice, move two cells left, increment twice and decrement once.

Well done, you’ve already learnt half of the syntax of brainfuck.

I/O

A programming language isn’t much use without the ability to input and output data. Brainfuck has two commands for I/O – , (comma) and . (full-stop/period). The comma inputs a character from the input into the current cell, and the period outputs the character in the current cell. The ASCII code of the character is used on input and output. You may not be used to interchanging characters and integers, so it’s worth having a look at this chart to see how they map.

Here’s a simple program that inputs a character, increments it once, then outputs it:

program: ,+.
input:   a

pointer:  v     
address:  0  1  2  3  4  5  6  ...
value:  [98][0][0][0][0][0][0][...]

The program takes a character from the input, in this case the letter ‘a’, and puts it into the first data cell. It then increments it, and outputs the value of the cell. A lowercase ‘a’ has the ASCII code 97, and ‘b’ has the ASCII code 98.

Try it out. Go to my brainfuck interpreter, put the string ,+. into the “Code” box and the letter ‘a’ into the “Input” box (or use this link), then press “Run”. Try it with different input, then try using more pluses or minuses.

Every time you use the comma command you remove a character from the input stream:

program: ,>,>,.<.<.
input:   abc

pointer:  v     
address:  0   1   2  3  4  5  6  ...
value:  [97][98][99][0][0][0][0][...]

This program puts the first character of input in the first cell, the second character in the second cell and the third character in the third cell (although it looks like an evil dragon smiley). Try it out to see what happens.

Looping

Ok, that’s three-quarters of the language covered now. All that’s left is looping. This is a bit trickier, but fairly simple once you’ve got the hang of it.

[ and ] (left and right brackets) are used for looping. Anything in between pairs of brackets will loop (you can have nested loops – the pairs match like parentheses (i.e. this is the equivalent of an inner loop) and this is the outer loop (did you see what I did there? (inner inner!))). Of course there’s no point looping forever, so brainfuck needs a way of knowing when to stop. It does this by checking the value of the current data cell to see if it is zero. If it is, execution will skip to after the end of the loop.

To illustrate this I’m going to need to show the position of the current instruction. This is called the instruction pointer, or i_ptr in the figure below.

To start with, the ‘z’ is read into the first cell:

i_ptr:   v
program: ,[.-]
input:   z

pointer:  v     
address:  0   1  2  3  4  5  6  ...
value:  [122][0][0][0][0][0][0][...]

The instruction pointer moves to the next instruction in the program. It checks to see if the value in the current data cell is zero. It isn’t, so the loop is entered.

i_ptr:    v
program: ,[.-]
input:   z

pointer:  v     
address:  0   1  2  3  4  5  6  ...
value:  [122][0][0][0][0][0][0][...]

The value in the current data cell is output (‘z’), then decremented:

i_ptr:      v
program: ,[.-]
input:   z

pointer:  v     
address:  0   1  2  3  4  5  6  ...
value:  [121][0][0][0][0][0][0][...]

The instruction pointer reaches the end of the loop and jumps back to the beginning:

i_ptr:    v
program: ,[.-]
input:   z

pointer:  v     
address:  0   1  2  3  4  5  6  ...
value:  [121][0][0][0][0][0][0][...]

The current cell is still not zero, so the loop is entered again, the new value is output (‘y’), and decremented again. This keeps on going until the value of the first data cell is zero. At this point, the instruction pointer jumps to after the end of the loop, which also happens to be the end of the program:

i_ptr:        v
program: ,[.-]
input:   z

pointer: v     
address: 0  1  2  3  4  5  6  ...
value:  [0][0][0][0][0][0][0][...]

Try it out to see what happens.

Refresher

And that’s the entire language. Here’s a quick recap:

  • > Move the data pointer one cell to the right
  • < Move the data pointer one cell to the left
  • + Increment the value of the cell at the data pointer
  • - Decrement the value of the cell at the data pointer
  • , Take a character from the input and place its value into the current data cell
  • . Output the value of the current data cell as a character
  • [ If the current data cell is zero, skip to after the closing bracket, otherwise continue
  • ] Skip back to the matching opening bracket (a common optimization is to skip over this instruction if the current cell is zero, rather than going back to the opening bracket and checking)

The end

If you’ve read the whole blog post and tried out all the code, you now know brainfuck. Well done, that’s another item for your CV! If you try making anything relatively complex you’ll realise the true value of the abstractions we build software on. All languages have different levels of abstraction, and you should be working at the highest level you can afford. It’s worth dropping down to the level of brainfuck occasionally so you can really appreciate the abstractions you’re given.

Further reading

Categories: Programming Tags: ,
  1. Dude
    February 21st, 2011 at 18:14 | #1

    You’re a fucking moron. Why would anyone waste their time with this when they can learn something that provides them with some kind of benefit?

  2. February 21st, 2011 at 19:48 | #2

    @Dude Thanks for the constructive comment, “Dude”. Yeah, I’m a fucking moron – I like knowledge for knowledge’s sake. It would be much better if I just learnt things I knew I’d need – like a wise man once said: “Every time I learn something new it pushes some old stuff out of my brain!”

    I think it’s funny that you ask “why would anyone waste their time” when I’d already answered the question “Why would I want to learn such a stupid language?” But I guess learning to read was one of those things you didn’t think would provide you with any benefit. I mean, who wants to waste their life with their head in a book?

  3. Robb
    February 21st, 2011 at 20:15 | #3

    Thanks. I thought that was an excellent explanation and I quite enjoyed trying out the examples on your very handy interpreter. Learning more about “bf” (another common censored name for the language) is something I’ve been meaning to do for a long time.

    To me there is a lot of similarity between bf and the simple 4-instruction Unlimited Register Machine language used to by Nigel Cutland in his book Computability: An Introduction to Recursive Function Theory, which he also shows is Turing-complete. This is another reason for thinking long and hard about bf.

  4. February 21st, 2011 at 23:02 | #4

    @Robb Great, glad you found it useful :)

    I’m not a computer scientist, but I’m trying to fill the gaps in my knowledge. That Cutland book sounds really interesting (a bit like a CS textbook version of Godel, Escher, Bach).

  5. February 21st, 2011 at 23:43 | #5

    Great article. I knew BrainF*ck, and I like learning things for fun. Norvig said that you should learn 6 languages from different paradigms ;). If you want to learn functional programming languages (OCaml, Lisp, Haskell, …), I would recommend you to start with an introduction to lambda-calculus (http://en.wikipedia.org/wiki/Lambda_calculus ). The simplest language (3 constructs). It helped me to understand the main concept.

  6. February 22nd, 2011 at 00:13 | #6

    @Gregory Cheers! Yeah, I wouldn’t count bf as one of Norvig’s minimum of 6 languages :) It was more the idea that the more languages you know the better, even if you’re never going to program anything in them.

    I’m currently working my way through SICP, so I’m picking up some of the lambda-calculus stuff, but it would be good to properly dive into it at some point – it does sound like a fascinating concept.

  7. February 22nd, 2011 at 12:49 | #7

    @dude you’re the one wasting time

    @skildlrick nice little intro, now I know Brainfuck, ta :)

  8. February 22nd, 2011 at 12:55 | #8

    @maht Awesome – thanks for the kind words :)

  9. June 4th, 2012 at 10:27 | #9

    Thanks for this. The Java brainfuck interpreter is cool! I am keen to have a play with this language, this is really helpful.

  10. Andrew
    March 9th, 2013 at 21:28 | #10

    Is it possible to have the user enter an integer into the input, instead of a char?

  11. Bruce Ironstaunch
    April 18th, 2013 at 14:03 | #11

    Well done, this is probably the easiest to follow lesson on how to use brainfuck on the entire internet.

  12. Jasper Catthoor
    May 11th, 2013 at 12:21 | #12

    Like Bruce said, this really is the easiest to follow tutorial you can find about brainfuck (: Great job on this!

    @Andrew: I don’t think this is possible in a traditional brainfuck interpreter or compiler, maybe you can do this in a dialect of the language or in an interpeter/compiler that supports that extra feature? (Correct me if I’m wrong)

  13. Richard
    September 25th, 2013 at 16:19 | #13

    Very useful thanks for providing a decent blog post on it.

  14. October 5th, 2013 at 10:59 | #14

    Why not just learn about turing machines?

  15. notDude
    October 24th, 2013 at 09:38 | #15

    I found it interesting and good written to understand, but I think Dude has a point…what kind of actually usable program would be easier to write in brainf*ck than in something like java?

  16. Nisarg Shah
    November 2nd, 2013 at 16:34 | #16

    Thanks for the tutorial man! Really good!

  17. January 8th, 2014 at 13:15 | #17

    I just started learning IT and I know batch, a little php, js, html, css, just basic, a little python and now brainf*ck! I already had heard about it, and I thought it would be hard to learn it. Oh I’m going to add this in my professional skills on fb.

  18. Drew McDermott
    March 18th, 2014 at 15:48 | #18

    @Robb: There’s a huge difference between Brainfuck and register machines (although I’m not familiar with Cutland’s version). Each location in Brainfuck can store an 8-bit byte; in a register machine the integers are of indefinite length and there are only a finite number of them. To get Turing-completeness, the register machine must simulate a tape (or half-tape) as an integer. Brainfuck’s memory is a half-tape, and its alphabet is the 256 possible bytes. Some of these bytes correspond to Ascii characters, which suggests the I/O model.

    It’s rather obvious how to convert a BF program into a TM table, but you have to assume a slightly nonstandard TM with an input tape and output tape each square of which can be read or written just once. There’s one state for each position in the program. I/O commands transfer bytes from the input tape to the main tape or from the main type to the output tape. Most states are unconditional. The ‘[' command is the only exception. It does one thing if it's looking at a 0, another if it's looking at something else. Almost all the other instructions, no matter what byte they are looking at on the main tape, move the read head on the main tape, read, or write, and then go to the following state. The ']‘ is also unconditional, although it branches to an earlier state, the one corresponding to the matching ‘[‘.

    Generating a BF program from an arbitrary TM description (again assuming a TM with dedicated input and output tapes) is more difficult. it’s analogous to transformations from programs with GOTOs to structured programs. BF is the structured side; TMs allow convoluted spaghetti control networks that are not easy to translate into the elegance and economy of a BF program. :)

  19. Iplodman
    October 27th, 2014 at 13:29 | #19

    @notDude It’s not really about being able to write the programs, but to have a different perspective on it.

  1. February 21st, 2011 at 04:50 | #1

Comments parsed as Markdown.