A few tips here:
Constants:
The Esolangs' constants page has an extremely useful list of the shortest ways to create specific values. I find myself consulting this page at least twice per program.
The start of everything:
+++[[<+>>++<-]>]
This sets up the tape in the format 3*n^2, which looks like
3 6 12 24 48 96 192 128 0 0'
Why is this so important?
Let's go down the list:
- 3 and 6 are boring
- 12: Close to 10 (newline) or 13 (carriage return). Can also be used for the counter for 0-9
- 24: Close to 26, the number of letters in the alphabet
- 48: ASCII for
0
- 96: Close to 97, ASCII for
a
- 196 and 128: 196-128=64, close to 65, the ASCII for
A
.
From this one algorithm, we're at the start of practically every sequence in the ASCII range, along with a counter for each and a newline in easy reach.
A practical example:
Printing all uppercase and lowercase letters and digits.
With algorithm:
+++[[<+>>++<-]>]<<[-<->]<<<<++[->>+.>+.<<<]<--[>>.+<<-]
Without:
+++++++++++++[->+++++++>++>+++++>++++>+<<<<<]>+++++>[-<+.>>.+<]>>---->---[-<.+>]
We spend most of the bytes just initialising the tape in the second example. Some of this it offset by the extra movements in the first example, but this method clearly has the advantage.
A couple of other interesting algorithms in the same vein:
3*2^n+1:
+++[[<+>>++<-]+>]
Tape: 4 7 13 25 49 65 197 129 1 0'
This offsets the values by 1, which accomplishes a few things. It makes the 12 a carriage return, the 64 the actual start of the uppercase alphabet, and the 24 one closer to 26.
2^n:
+[[<+>>++<-]>]
Tape: 1 2 4 8 16 32 64 128
Because 64 is good for uppercase letters, 32 is the ASCII for space, and 128 can be used as the counter for 26 (130/5 = 26). This can save bytes in certain situations where digits and lowercase letters aren't needed.
Choose the implementation that suits the question:
- Negative cells are almost always useful, and there's no reason to avoid them (unless it doesn't change your bytecount)
- Almost the exact same thing with wrapping cells, even more so because many constants use wrapping.
- Arbitrary cell sizes are useful for infinite math sequences, such as calculating the Fibonacci sequence infinitely (
+[[-<+>>+>+<<]>]
), or processing larger/negative numbers. The downside is that some common methods, such as [-]
and [->+<]
can't be relied upon, just in case the number is negative.
- EOF as 0, -1 or no change. 0 is usually preferable, as you can loop over an entire input without extra checks. -1 is useful when looping over array structures. I haven't yet found a use for no change :(.
Keep track of what the frick is going on:
At all times you should have comments on where the pointer should be in relation to the data around it, and make sure that you know the range of possible values of each cell. This is especially important when you've split the pointer before a loop, because you'll want to join the two possibilities back together afterwards.
At any point, my code is littered with comments on every other line that look like this:
*0 *dat a_1 ? 0' !0 0*
or
*0 *dat 0' ap1 0 !0 0*
Some extra advice is to assign symbols special meanings. In the above example, '
is where the pointer is, *
means repetition in that direction, ?
means a cell with unknown value, !0
means a non-zero cell, _
is a substitute for -
and p
is a substitute for +
. or
implies that the tape could look like either of representations, and needs to be handled as such.
Your symbol scheme doesn't necessarily have to be the same as mine (which has a few flaws), it just has to be consistent. This is also extremely useful when debugging, because you can run it up to that point and compare the actual tape to what you should have, which can point out potential flaws in your code.
Holy shit, thought I recognised your name. Big fan of your brainfuck programs, especially your super-short self-interpreter! – Jo King – 2018-02-20T11:22:25.893
"you may occasionally notice ... that there are small portions that could be deleted entirely, nothing added, and the thing would, by happenstance, still run flawlessly." This is so true, especially for a beginner. I've only ever written one answer in BF as far as I can remember, yet its revision history contains at least 3 instances where I was playing with the program and randomly went, "hey, the program still works if I remove this bit!"
– ETHproductions – 2018-02-21T04:05:14.510"Try out as many variations of your layout as you have patience for," or can brute-force :P
– Esolanging Fruit – 2018-02-21T04:37:02.217Really nice answer – RGS – 2020-02-06T00:12:32.440
3Reading this makes me want to try programming in Brainfuck now.. – Claudiu – 2014-09-26T17:44:28.927