Suggestions for intermediate brainfuck programmers.

Design early and often. Several things about brainfuck make it more difficult, and maybe more necessary, to maintain conceptual control. In other languages, you can often look at some code in the middle of a program and see what it does; but brainfuck code is almost unintelligible unless you already have a clear idea of what the current state is. Planning your program, and especially its data representation, in advance will prevent a lot of confusion.

At the same time, don't hesitate to rethink and redesign every aspect of the program throughout the coding process, even long after the program is working. The first working draft of a brainfuck program is usually too long or too slow by at least a factor of two, and often both. Fortunately, as you gain familiarity with the language and with a given program, you get a better handle on it and it becomes easier and easier to make even large changes without breaking anything. So don't stop too soon.

Pay particular attention to designing a data representation that will make your job easy. It's hard to generalize about this, but one useful way to move the pointer around is the breadcrumb technique--arrange a sequence of equally spaced nonzero cells leading to a zero near your destination, then use code like "[>>>]" to get there. If you can make it work, this is much more efficient than using a cell to store the distance to move the pointer and using code like "[-[>>>+<<<-]>>>]", even assuming the distance will fit in a cell--incidentally, it's not a good idea to store a number in a single cell if it may be large.

Use nondestructive flow control where appropriate. To deal with variable-sized data it is clearly necessary to use loops that produce net pointer movement. Less obviously, this is often useful even for small fixed-size data. For example, say we have three cells set to ...[A][0][B]... and we want to subtract A from B, but without reducing B below zero. We could do something like [>>[-[<+>-]]<[>+<-]<-] or we could save time and code by doing [>>[-<]<[>]<-]. The idea is that if you need a zero, it is cheaper to move the pointer than to move data. Be careful not to lose track of the pointer's location permanently. Incidentally, these techniques are very dependent on data layout, and often require the creation of dummy nonzeroes.

Remember that you're programming in brainfuck. It's tempting to avoid confusion and tedium by thinking in terms of higher-level abstractions as much as possible, rather than thinking in brainfuck. This can buy enough control to make a working program, usually at the cost of such inefficiencies as:
-excessive pointer movement, sometimes as simple as ">+<->" and sometimes less apparent; often a result of doing things in an intuitively appealing order.
-using six cells to store six values of which no more than four will ever exist at the same time
-storing information in a cell that could profitably be "stored" in the pointer or the instruction pointer, or not stored at all
-including code to deal with cases that will provably not occur, or which existing code would handle correctly though not explicitly designed to do so; or other forms of overly general code
-things like this:

>++++++++++[<++++++++++>-]<+++++.[-]
>++++++++++[<++++++++++>-]<+++++++++.[-]

Naturally, all this applies a fortiori to the use of macro languages in connection with brainfuck. Whether it's a problem depends on your goals. If you just want a working program, it may be fine to deal with the language itself as little as possible (though having a tenuous grasp on the low-level events is likely to make debugging harder). But if you want your program to be graceful, you'll need to immerse yourself in the details.

And now for a full-scale case study (or rather a case to study, since I'm not going to provide detailed commentary): the first working version of random.b. This is, if anything, better than average--carefully designed, with a well-chosen data layout and no grossly wasteful code. But it's clunky. It's too general, the pattern of loops is ill-conceived, and destructive flow control leads to way too much copying. It needed more work, and got it.

>++++++++>>>>>++>+<[
    >>+>>+[
        -[->>+<[<<<[<<]<<+>>>>[>>]+>-]<[>+<-]]
        <<<[>>>++++<<<-]>>[>++<-]>>[<+>-]
        <[>+<-[<+>-[-[<-<<+>>>-[>-<-[<+>-[-]]]]]]]+<<
    ]<+<<-[>>-<<[<+>-]]<[>+<-]
   >>>[<.[-]<++++++++>>-]<[>+<-]>[<++>-]>>>[>>]<<
]

back to "some brainfuck fluff"