108
\$\begingroup\$

Inspired a bit by a previous Brainfuck question and the recent invasion, I decided to make FizzBuzz in Brainfuck.

The code is entirely my own, except for the inclusion of the Printing a number algorithm that I found on StackOverflow.

My code is divided into several parts.

  • Setup Construct the tape to contain some data
  • Loop through all FizzBuzzes
  • Print number if no FizzBuzz was found and Print new line

It was quite a challenge to do this, I started playing around with the basic Brainfuck operators to create the basic tools that I could use. After figuring out how to "search" for a value on the tape, copying/moving a value to another location, and some other useful things, I decided to go with this approach.


The Tape

After the setup section, this is what my tape looks like:

Brainfuck tape

From top to bottom this represent:

  • 100 = The number of times to loop
  • 10 = New Line character
  • 255 = Marker to find the start
  • 0 = Counter, what number we're currently at.
  • 0 = Boolean to indicate if a FizzBuzz has been found or not (to know if we should print the number)
  • Empty space for converting number to string algorithm

Then there is a configurable sequence of:

  • 254 = Marker for a FizzBuzz section
  • 0 = How many iterations it has been since last print
  • 3 / 5 = How many iterations until we will print this text
  • Text
  • Empty space to indicate end of text

And finally:

  • 254 = Marker for a FizzBuzz section
  • 253 = End of FizzBuzz sequences

The Approach in regular code

The code does not do a "classic" modulo operation each iteration, instead it can be considered as doing something like this:

int fizz = 3;
int fizzUp = 0;
int buzz = 5;
int buzzUp = 0;
boolean printed = false;
for (int i = 1; i <= 100; i++) {
    printed = false;

    fizz--;
    fizzUp++;
    if (fizz == 0) {
        print("Fizz");
        printed = true;
        while (fizzUp > 0) {
            fizzUp--;
            fizz++;
        }
    }

    // Same for buzz as for fizz

    if (!printed) {
        print(i);
    }
    print("\n");
}

Main questions

  • Is my Brainfuck code somewhat readable? (I know, I know, it is Brainfuck...) I have tried to add plenty of comments describing what the code does (for once I actually think the what is more important than the why).
  • Did I make a good decision to structure the tape in this way?
  • Can the setup of the "Fizz" and "Buzz" texts be more efficient?
  • Can something else be done more efficient?
  • Any other comments appreciated
++++++++++[>++++++++++<-]> Initialize 100 (number of times to perform FizzBuzz)

TAPE MEANINGS
255 Start
254 A Fizz or Buzz text to print
253 End of Fizzes and Buzzes
252 Currently processed FizzBuzz calculation

TAPE OVERVIEW
Remaining Iterations
10 for Line Break
255 Start Marker
Counter
Boolean 1 or 0 for whether or not a fizzbuzz matches current counter
Some empty space for converting counter to string
  Any Number of Sequences of the following
    254 Indicator for FizzBuzz sequence
    Counter
    Countdown until next text output
    Text any number of characters
    Zero
    Zero
254 and 253 marker to indicate the end of sequences


>++++++++++                   Line break
>-                            Start marker
>>>>>>>>>>>>>>>>              Empty space for counter to string conversion


SETUP Create the Fizz and Buzz sequences on the tape

FIZZ
-->                               Create indicator
+++++++[->++++++++++<]>           Create F
[->+>+>+>+<<<<]                   Copy 4x F
+++                               Set modulo operator to 3
>>+++>>>                          Adjust second letter to I
++++++++[-<++++<++++<++++>>>]     Make the last three lowercase to Fiff
+++++[-<++++<++++>>]              Modify the last two F to Z by adding 20
>>                                Leave two zeros at the end

BUZZ
-->                               Create indicator
++++++[->+++++++++++<]>           Create B
[->+>+>+>+<<<<]                   Copy 4x B
+++++                             Set modulo operator to 5
>>+>++++++>++++++>                Adjust BBBB to BCHH
++++++++[-<++++<++++<++++>>>]     Make lower case
++++++[-<+++<+++<+++>>>]          Adjust Bchh to Buzz
>>                                Leave two zeros at the end



-->---          Mark the ending with 254 and 253

END OF SETUP

ALGORITHM START

+[-<+]-         Go backwards to the 255 mark
<

[
 +[->+]-        Go forward to the start position 255 marker
 <<->>          Decrease countdown
 >+             Increase counter
 >[-]           Reset boolean for if we have found a match or not

 ++[-->++]-->   Find next 254 and go one step beyond it

                Loop through all 254s
    +++[---         Make sure that we are not at 253 (end)
     ++[--<++]--    Find last value 254
     --             Change marker to 252 to indicate that we're processing it
     >>             Go to the countdown
     -<             Decrease fizzbuzz countdown
     +>             Increase fizzbuzz counter

     If current marker is NOT zero
         [
             ++++[----<++++]--   Find value 252 and change to 254
             [>]                 Position to a place that is zero to avoid repeat
             ++[-->++]-- <       Find NEXT 254 marker and stop right before it
         ]

     >++
         Check if we are positioned on a 254 already then if skip this
         [--

             We have a match so find start position and mark match

             +[-<+]-  >>         Find 255 marker and go to the boolean
             [-]+                Set boolean to 1 whatever the previous value is
             ++++[---->++++]--   Find value 252 and change to 254
             >[->+<]             Reset the current FizzBuzz countdown
             >>[.>]              Print the text
             ++[-->++]           Go to next 254 change to 256 to break loop
         ]
     -->

     +++ # Detect if we are at the 253 end
     ]
  ---

ALL FIZZBUZZES PROCESSED
Use the boolean to check whether or not to print the number


  +[-<+]-     Go back to the 255 marker
  >>          Go to boolean
  -[          If boolean is zero then print the number
         +>>>
         +[-<+]-    Go back to the 255 marker
         >>          We are positioned after the counter
         Code taken from StackOverflow below for printing a number
>++++++++++<<[->+>-[>+>>]>[+[-<+>]>+>>]<<<<<<]>>[-]>>>++++++++++<[->-[>+>>]>[+[-
<+>]>+>>]<<<<<]>[-]>>[>++++++[-<++++++++>]<.<<+>+>[-]]<[<[->-<]++++++[->++++++++
<]>.[-]]<<++++++[-<++++++++>]<.[-]<<[-<+>]
         End of StackOverflow code

         +[-<+]->>    Locate the 255 marker and goto the boolean
  ]+

  Boolean is always one here so print new line
  [-]<<<.>>>     Print new line

  +[-<+]-    Go back to the 255 marker
  <<         Go to the countdown to find out if we should go another round
]

Compressed typical Brainfuckish-version of the code:

++++++++++[>++++++++++<-]>>++++++++++>->>>>>>>>>>>>>>>>-->+++++++[->++
++++++++<]>[->+>+>+>+<<<<]+++>>+++>>>++++++++[-<++++<++++<++++>>>]++++
+[-<++++<++++>>]>>-->++++++[->+++++++++++<]>[->+>+>+>+<<<<]+++++>>+>++
++++>++++++>++++++++[-<++++<++++<++++>>>]++++++[-<+++<+++<+++>>>]>>-->
---+[-<+]-<[+[->+]-<<->>>+>[-]++[-->++]-->+++[---++[--<++]---->>-<+>[+
+++[----<++++]--[>]++[-->++]--<]>++[--+[-<+]->>[-]+++++[---->++++]-->[
->+<]>>[.>]++[-->++]]-->+++]---+[-<+]->>-[+>>>+[-<+]->>>++++++++++<<[-
>+>-[>+>>]>[+[-<+>]>+>>]<<<<<<]>>[-]>>>++++++++++<[->-[>+>>]>[+[-<+>]>
+>>]<<<<<]>[-]>>[>++++++[-<++++++++>]<.<<+>+>[-]]<[<[->-<]++++++[->+++
+++++<]>.[-]]<<++++++[-<++++++++>]<.[-]<<[-<+>]+[-<+]->>]+[-]<<<.>>>+[
-<+]-<<]
\$\endgroup\$
11
  • 39
    \$\begingroup\$ Oh god, something serious was made in BF? \$\endgroup\$
    – Kayla
    Commented Jul 19, 2014 at 6:03
  • 11
    \$\begingroup\$ Is "+++++++[->++++++++++<]> Create F" correct? Surely you're incrementing then moving when you should be doing the opposite. (Yes, I'm taking this seriously, brainfuck is an educational language that I highly approve of.) \$\endgroup\$
    – Pharap
    Commented Jul 19, 2014 at 10:34
  • 15
    \$\begingroup\$ @Kayla If you call FizzBuzz serious, yes. \$\endgroup\$ Commented Jul 19, 2014 at 18:34
  • 4
    \$\begingroup\$ I don't suppose "Don't use bf." counts as a review here. \$\endgroup\$
    – RubberDuck
    Commented Sep 1, 2014 at 19:13
  • 5
    \$\begingroup\$ @RubberDuck As I wrote this for a learning experience and to see what I could do in BF, sadly no. \$\endgroup\$ Commented Sep 1, 2014 at 19:16

2 Answers 2

53
\$\begingroup\$

Simplifications

Thanks to implementing some analysis of while loops in my Brainduck project (which I worked on thanks to reviewing "Hello, Brainfuck"), I managed to find a bunch of loops that were always performed x times. By expanding these loops (copy-paste their content x times and remove the loop) several simplifications are possible.

Boolean peek-a-boo. Wheeeere's the boolean?

+[-<+]->>    Locate the 255 marker and goto the boolean

This loop is always being performed exactly twice, which means that the loop can be expanded to +-<+-<+->> which in turn can be simplified to <<>> which of course can be totally removed. So it can be replaced with a comment:

cursor is now located on the boolean

Boolean is always one, no shit Sherlock!

]+

Boolean is always one here so print new line
[-]<<<.>>>     Print new line

No wonder that the boolean is always one when you did + right after the ]. As the value is always 1, the loop is always performed once, which can simplify this code to:

]
Boolean is now zero so just print the new line
<<<.>>>     Print new line

Boolean, marker, boolean, Dumbledore!

-[          If boolean is zero then print the number
     +>>>
     +[-<+]-    Go back to the 255 marker
     >>          We are positioned after the counter

"We are positioned after the counter" is the same as "we are positioned at the boolean" (which now has the value zero). This code starts at the boolean, which is positioned two steps to the right of the 255 marker, and then you move three steps more to the right only to go back to the 255 marker... as this loop to go backwards is always performed five times, this can be simplified. First, expand the loop:

     +>>>
     +-<+-<+-<+-<+-<+-    Go back to the 255 marker
     >>          We are positioned after the counter

Then remove the +- that cancel each other out:

     +>>>
     <<<<<       Go back to the 255 marker
     >>          We are positioned after the counter

Then remove >< that cancel each other out and here is what remains:

+

Finding 252 and changing to 254

++++[----<++++]--   Find value 252 and change to 254

This loop is done exactly twice, which can be simplified to:

<<++                Go left to value 252 and change to 254

Finding 254 when we're almost there already

+++[---         Make sure that we are not at 253 (end)
   ++[--<++]--  Find last value 254

   --           Change marker to 252 to indicate that we're processing it

Guess what!? Your 253 is located exactly one step after the 254 value, which means that the "Find last value 254" loop is only done once, which means it is simplification time:

 +++[---         Make sure that we are not at 253 (end)
     <--         Go to 254 marker and change to 252 to indicate that we are processing it

Go directly to countdown, do not change it, do not collect $200

>>             Go to the countdown
-<             Decrease fizzbuzz countdown
+>             Increase fizzbuzz counter

The BF code >>-<+> is the same thing as >+>- so change this to:

>+             Increase fizzbuzz counter
>-             Decrease fizzbuzz countdown

Resetting the boolean

>+              Increase counter
>[-]            Reset boolean for if we have found a match or not

The boolean is reset at the end of every loop, no need to reset it at the beginning as well, change to:

>+>             Increase counter

Finding the 255 marker and the countdown

ALGORITHM START

+[-<+]-         Go backwards to the 255 mark
<

[
    +[->+]-     Go forward to the start position 255 marker

My analysis found something interesting here, the first time "Go forward to the start position 255 marker" loop was done, it was only performed once, but all other times it was performed exactly twice. This gave me a clue that there was a possibility of refactoring here, starting with simply replacing the single < with << to make the loop always be performed exactly twice.

+[-<+]-         Go backwards to the 255 mark
<<              Go to the countdown

[
    +[->+]-     Go forward to the start position 255 marker

Then it occurred to me that at the end of the loop, at this code:

<<<.>>>     Print new line

+[-<+]-    Go back to the 255 marker
<<         Go to the countdown to find out if we should go another round

This "Go back to the 255 marker" was always performed twice, which means that we once again expand and simplify:

<<<.>>>     Print new line

+-<+-<+-   Go back to the 255 marker
<<         Go to the countdown to find out if we should go another round

and then:

<<<.      Print new line
<         Go to the countdown to find out if we should go another round

Now, going back to the start of the big loop, let's take a look at:

[
  +[->+]-        Go forward to the start position 255 marker
  <<->>          Decrease countdown

Considering the first [ is always at the countdown, we can just do ->>, i.e. we don't need to first fine the 255 marker just to go to the countdown when we are already at the countdown. So replace that with:

[
  ->>            Decrease countdown

Clarifying comments

[>]                 Position to a place that is zero to avoid repeat
++[-->++]-- <       Find NEXT 254 marker and stop right before it

The 'avoid repeat' comment is not very good, because of the loop afterwards, it is enough to only do the > once. However, if the string itself would contain the magical 254 value, then we would have a problem. So clarify this comment:

[>]                 Go to a zero to avoid repeat in case there is a 254 value in the string

Memory utilization

The memory utilization seems to be quite good, only 39 memory cells are being used overall. However, this:

>>>>>>>>>>>>>>>>              Empty space for counter to string conversion

Is unnecessary extra space. That can be reduced to:

>>>>> >>>>> >

Also note how spaces every 5 instructions makes it more readable.

Additional simplifications, that reduces the flexibility

This simplification can only be done thanks to the fact that the FizzBuzz ends at 100. If you want to FizzBuzz more, you cannot simplify this part.

The SO code for printing a number can be simplified to:

     Code taken from StackOverflow below for printing a number
>++++++++++<<[->+>-[>+>>]>[+[-<+>]>+>>]<<<<<<]>>[-]>>>++++++++++<
[->->+<<]>[-]>[<++++++[->++++++++<]
>.[-]]<<++++++[-<++++++++>]<.[-]<<[-<+>]
     End of StackOverflow code

With this simplification, one extra memory cell can be freed by changing to:

>>>>> >>>>>                   Empty space for counter to string conversion

2 become 1 (two zeros become one)

Currently there are always two zeros after a Fizz or Buzz string. This can easily be changed to only one zero, which would allow a bit of simplification in this code:

 If current marker is NOT zero
     [
         <<++                Go left to value 252 and change to 254
         [>]                 Go to a zero to avoid repeat in case there is a 254 value in the string
     ]

This also allows a simplification at the end of the "We have a match" case:

>++                 Go to next 254 change to 256 to break loop

It allows more flexibility though to have it as it used to be, to scan for the next 254 value.

Resulting code

Non-minimized version of the resulting code, without the additional flexibility-reducing simplifications, 631 Brainfuck instructions in the source code, 152267 being performed at run-time:

TAPE MEANINGS
255 Start
254 A Fizz or Buzz text to print
253 End of Fizzes and Buzzes
252 Currently processed FizzBuzz calculation

TAPE OVERVIEW
Remaining Iterations
10 for Line Break
255 Start Marker
Counter
Boolean 1 or 0 for whether or not a fizzbuzz matches current counter
Some empty space for converting counter to string
  Any Number of Sequences of the following
    254 Indicator for FizzBuzz sequence
    Counter
    Countdown until next text output
    Text any number of characters
    Zero
    Zero
254 and 253 marker to indicate the end of sequences

++++++++++[>++++++++++<-]> Initialize 100 (number of times to perform FizzBuzz)


>++++++++++                   Line break
>-                            Start marker
>>>>> >>>>> >                 Empty space for counter to string conversion


SETUP Create the Fizz and Buzz sequences on the tape
  without having to write plus more than 65 times for every character

FIZZ
-->                               Create indicator
+++++++[->++++++++++<]>           Create F
[->+>+>+>+<<<<]                   Copy 4x F
+++                               Set modulo operator to 3
>>+++>>>                          Adjust second letter to I
++++++++[-<++++<++++<++++>>>]     Make the last three lowercase to Fiff
+++++[-<++++<++++>>]              Modify the last two F to Z by adding 20
>>                                Leave two zeros at the end

BUZZ
-->                               Create indicator
++++++[->+++++++++++<]>           Create B
[->+>+>+>+<<<<]                   Copy 4x B
+++++                             Set modulo operator to 5
>>+>++++++>++++++>                Adjust BBBB to BCHH
++++++++[-<++++<++++<++++>>>]     Make lower case
++++++[-<+++<+++<+++>>>]          Adjust Bchh to Buzz
>>                                Leave two zeros at the end



-->---          Mark the ending with 254 and 253

END OF SETUP

ALGORITHM START

+[-<+]-         Go backwards to the 255 mark
<<              Go to the countdown

[
 ->>            Decrease countdown
 >+>            Increase counter

 ++[-->++]-->   Find next 254 and go one step beyond it

                Loop through all 254s
    +++[---         Make sure that we are not at 253 (end)
     <--            Go to 254 marker and change to 252 to indicate that we are processing it
     >+             Increase fizzbuzz counter
     >-             Decrease fizzbuzz countdown

     If current marker is NOT zero
         [
             <<++                Go left to value 252 and change to 254
             [>]                 Go to a zero to avoid repeat in case there is a 254 value in the string
             ++[-->++]-- <       Find NEXT 254 marker and stop right before it
         ]

     >++
         Check if we are positioned on a 254 already then if skip this
         [--

             We have a match so find start position and mark match

             +[-<+]-  >>         Find 255 marker and go to the boolean
             [-]+                Set boolean to 1 whatever the previous value is
             ++++[---->++++]--   Find value 252 and change to 254
             >[->+<]             Reset the current FizzBuzz countdown
             >>[.>]              Print the text
             ++[-->++]           Go to next 254 change to 256 to break loop
         ]
     -->

     +++ Detect if we are at the 253 end
     ]
  ---

ALL FIZZBUZZES PROCESSED
Use the boolean to check whether or not to print the number


  +[-<+]-     Go back to the 255 marker
  >>          Go to boolean
  -[+         If boolean is zero then print the number

         Code taken from StackOverflow below for printing a number
>++++++++++<<[->+>-[>+>>]>[+[-<+>]>+>>]<<<<<<]>>[-]>>>++++++++++<[->-[>+>>]>[+[-
<+>]>+>>]<<<<<]>[-]>>[>++++++[-<++++++++>]<.<<+>+>[-]]<[<[->-<]++++++[->++++++++
<]>.[-]]<<++++++[-<++++++++>]<.[-]<<[-<+>]
         End of StackOverflow code

         cursor is now located on the boolean
  ]

  Boolean is now zero so just print the new line
  <<<.      Print new line
  <         Go to the countdown to find out if we should go another round
]

The minimized version of the resulting code with the additional flexibility-reducing simplifications, 548 characters: (run-time instructions being performed is 143808)

++++++++++[>++++++++++<-]>>++++++++++>->>>>>>>>>>-->+++++++[->++++++++
++<]>[->+>+>+>+<<<<]+++>>+++>>>++++++++[-<++++<++++<++++>>>]+++++[-<++
++<++++>>]>-->++++++[->+++++++++++<]>[->+>+>+>+<<<<]+++++>>+>++++++>++
++++>++++++++[-<++++<++++<++++>>>]++++++[-<+++<+++<+++>>>]>-->---+[-<+
]-<<[->>>+>++[-->++]-->+++[---<-->+>-[<<++[>]]>++[--+[-<+]->>[-]+++++[
---->++++]-->[->+<]>>[.>]>++]-->+++]---+[-<+]->>-[+>++++++++++<<[->+>-
[>+>>]>[+[-<+>]>+>>]<<<<<<]>>[-]>>>++++++++++<[->->+<<]>[-]>[<++++++[-
>++++++++<]>.[-]]<<++++++[-<++++++++>]<.[-]<<[-<+>]]<<<.<]
\$\endgroup\$
1
  • 8
    \$\begingroup\$ I don't mind losing the checky to this answer one bit. Phenomenal. ++ \$\endgroup\$
    – RubberDuck
    Commented Sep 7, 2015 at 13:46
42
+150
\$\begingroup\$
  • You should "initialize 100" after the "Tape meanings". I consider it to be part of the actual code and the explanation to be a kind of "header" explaining the code.

  • I agree that commenting "what" becomes more important in bf, but you should still explain "why" every now and then. The reasoning behind why you build the "Fizz" and "Buzz" strings the way you do is not obvious. A simple "This is more efficient than directly creating the letters." comment would go a long way. There's also no indication as to why you leave two zeros at the end of each string.

  • I can't figure out what this hash mark is doing here. It's a little confusing. Anything in bf that's not a valid operator is a comment. You don't use hashes for comments elsewhere, don't do it here either.

+++ # Detect if we are at the 253 end

All in all it's the most readable bf I've laid my eyes on though. However, don't take my omission of commenting on the actual code as an indication that it was done right. Truthfully, I've no idea. I'd have to brush up on Turing machines and spend some time writing something simple just to get into the right mindset to decipher this. I mostly wanted to post an answer so that maybe it would prod someone else into taking a serious look at it.

\$\endgroup\$
3
  • 4
    \$\begingroup\$ P.S. Don't do it in brainfuck. =P \$\endgroup\$
    – RubberDuck
    Commented Sep 1, 2014 at 19:31
  • 11
    \$\begingroup\$ right, do it in lolcode! \$\endgroup\$ Commented Sep 1, 2014 at 19:32
  • 7
    \$\begingroup\$ I think if someone else is going to take a look at it, that will probably be me... but it will take some time... gotta make my Brainfuck Sudoku Solver first! \$\endgroup\$ Commented Nov 17, 2014 at 22:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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