This articles describes the creation and motivation behind the Brainfuck Fontifier, a toy I made which generates a brainfuck program that prints text similar to the shape of the generated program.
Essentially you give it a piece of text such as "Hello" and it generates a brainfuck program like:
++ ++ +++++[>+++++++ +< -] >.>++++++++[>+
++ ++ ++ +< -] >+ ++
++ .> ++ ++ ++ ++ +[
>++++++++<-]>+ +++.>+++++ ++ ++ [> ++
++ ++ ++ <- ]> ++ ++
.> ++ ++ ++ ++ +[ >+
++ ++ +++<-]>+++++++ .>--+>+<+<>+<+ <<+++++-+<-++> <-++++<+-++<++
Running this program in a brainfuck interpreter outputs:
HELLO
I found the mostly finished code for this toy while looking thru an old harddrive. If memory serves me correct, I wrote this program while I was taking a compilers course during my final year of college. Looking back, compilers was one of the most important CS classes I took. Having an understand of what's going on beneath the "surface" of ones code is extremely helpful. I also found that once understanding compilers I was able to learn new programming languages faster, because I had an understanding of how programming languages were designed. This shifted my mindset and made it easier to figure where to start when thinking, "I know how to do this in Y, but how would I do it in X?"
If you have studied compilers at all you have most certainly stumbled across the concept of esoteric programming languages. Often turing complete, these languages tend to value novelty over every thing else.
The most famous these is the vulgar and appropriately named, Brainfuck. A language with only 8 commands and an instruction pointer.
To print a character, let's say "J" in Brainfuck you could do somethingl like:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.
+
is repeated 74 times to increase the value of the currently selected register. 74 is the ascii value of "J". .
is called to print the output of the current register. Thus we get "J".
With this in mind I could print my entire name by increment a register to the appropriate ascii value, printing it, moving to the next register and repeating the process for the next character.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.
>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.
>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.
>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.
>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.
As I gazed at my code I thought, "this can be more beautiful" and so I rearranged it. Brainfuck doesn't care about whitespace so why not?
++++++++++++++++ ++ ++++++++++++++ ++++++++++++++++ ++++++++++++++
++ ++++ ++ ++ ++ .> ++
++ ++ ++ ++ ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++ +.
>+ ++ ++ ++ ++ ++ ++ ++
++ ++++++++++++++++ ++++++++++++++ ++++++++ ++ ++
++ ++ ++ ++ ++ ++ ++ ++
++ ++ ++ +. >+ ++ ++ ++
++ ++ ++ ++ ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++ ++
++ ++ ++ ++ ++ ++ ++ .>
+++++++++ ++ ++ ++ ++ ++++++++++++++++ +++++++++++++++++++++++++++++++++++.
Wow look at this. Perfect for the message boards of the past. A signature that would truly show off my status as a 1337 hacker
.
Actually, the ascii art I handmade above was awful. The letters are huge and font feels inconsistent. My journey into learning brainfuck took a sudden turn. Now all I wanted to do was make a program that generated brainfuck that looked like the output the brainfuck code produced.
As I was moving the pieces of code around arranging my name I realized there was a lot of code to work with. I didn't want the generated letters to be huge.
Looking at the Brainfuck ecosystem I discovered that a common way to print letters is with a loop. For example to print 'J" you could:
+++++++++[>++++++++<-]>++.
This is signicifantly less code than:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.
This looping technique utlizes two registers. The chunk of pluses in the middle are added to the second register a number of times equal to the first chunk of pluses. Once the first register reaches a value of 0, the last chunk of plues is added to the second register and then .
is called, printing the ascii value corresponding with the value of the second register.
You can get a better idea of what I am saying by watching the gif below:
This gif was generated with a slighly mangled version of the Brainfuck Visualizer. I would recommend playing around with it to get a better understanding of how brainfuck code is executed.
The font used above was assembled by hand and it shows. The final letter 'D' has a bunch of extra code hanging off the bottom of it. I need to insure that my generated code fits into the the letters it is a part of. Using the technique above I am able to get a more consistent range of length for each letter. In the source I have:
function getBrainfuckToPrintCharacter(ascii_value, characters_per_loop) {
var brainfuckCode = [];
var remainder = ascii_value % characters_per_loop;
var loop_count = Math.floor(ascii_value / characters_per_loop);
for (var i = 0; i < loop_count; i++) {
brainfuckCode.push("+");
}
brainfuckCode.push("[>");
for (var i = 0; i < characters_per_loop; i++) {
brainfuckCode.push("+");
}
brainfuckCode.push("<-]>");
for (var i = 0; i < remainder; i++) {
brainfuckCode.push("+");
}
brainfuckCode.push(".>");
return brainfuckCode.join('');
}
The character_per_loop
parameter indicates the number of characters to go into the inner loop of the print character code. I wanted to determine which character_per_loop
value generated the most consistently sized code for each letter A-Z. I wrote this test function to find out:
function test() {
var MIN_ASCII = 65;
var MAX_ASCII = 90;
var ASCII_RANGE = MAX_ASCII - MIN_ASCII;
for (var i = 1; i <= 15; i++) {
var min_length = null;
var max_length = null;
var sum_of_all_character_lengths = 0;
for (var ascii = MIN_ASCII; ascii <= MAX_ASCII; ascii++) {
var code_length = (getBrainfuckToPrintCharacter(ascii, i)).length;
sum_of_all_character_lengths += code_length;
if (min_length == null || code_length < min_length) {
min_length = code_length
} else if (max_length == null || code_length > max_length) {
max_length = code_length
}
}
avg = sum_of_all_character_lengths / ASCII_RANGE;
console.log("character_per_loop: " + i + ", min_length: " + min_length + ", max_length: " + max_length + ", avg: " + avg);
}
}
If you go to the Brainfuck Fontifier and open the javascript console you can run this test on your own. Here are the results:
chaaracter_per_loop: 1, min_length: 74, max_length: 99, avg: 89.96
character_per_loop: 2, min_length: 43, max_length: 55, avg: 50.96
character_per_loop: 3, min_length: 33, max_length: 42, avg: 39
character_per_loop: 4, min_length: 29, max_length: 36, avg: 33.8
character_per_loop: 5, min_length: 26, max_length: 34, avg: 31.24
character_per_loop: 6, min_length: 25, max_length: 33, avg: 30.16
character_per_loop: 7, min_length: 25, max_length: 33, avg: 29.96
character_per_loop: 8, min_length: 25, max_length: 33, avg: 29.76
character_per_loop: 9, min_length: 25, max_length: 34, avg: 30.44
character_per_loop: 10, min_length: 25, max_length: 35, avg: 31.28
character_per_loop: 11, min_length: 25, max_length: 36, avg: 31.56
character_per_loop: 12, min_length: 26, max_length: 37, avg: 32.76
character_per_loop: 13, min_length: 26, max_length: 39, avg: 33.8
character_per_loop: 14, min_length: 27, max_length: 40, avg: 34.84
character_per_loop: 15, min_length: 28, max_length: 42, avg: 36.76
6, 7 and 8 have the narrowest ranges. I decided to use 6 because it has the highest average. This means if I have at least 33 charactesr per letter I know for sure that all my code will fit. To fill in the empty space at the end I generate random +
, -
, <
and >
commands.
I ended out making a character set for and
A-Z
. Here is what 'A' looks like:
[" ## "],
[" ## ## "],
[" ## ## "],
[" ## ## "],
[" ############## "],
[" ## ## "],
[" ## ## "]
Each character is stored as a list. In the code for generating the ascii presentation the #
character is substituted with the correct Brainfuck command. This is done by the fontify
function:
function fontify() {
var input = document.getElementById("input").value;
var characters = [];
var code_to_print_characters = "";
for (var i = 0; i < input.length; i++) {
var character = input.charAt(i)
ascii_value = getCharacterAsciiValue(character);
if (ascii_value) {
characters.push(getCharacterTemplate(character));
code_to_print_characters += getBrainfuckToPrintCharacter(ascii_value, 6);
}
}
var templates = templatesToString(characters);
for (var i = 0; i < code_to_print_characters.length; i++) {
templates = templates.replace("#", code_to_print_characters[i]);
}
while (templates.includes("#")) {
var noise = "+++++++><-";
templates = templates.replace("#", noise.charAt(Math.floor(Math.random() * noise.length)));
}
document.getElementById("output").value = templates;
}
And that is basically how the Brainfuck Fontifier came to be.
+++++++++++[>+ +++++<-]>++++. >+ ++
++ ++ ++++ +[
>+ ++ ++ +< -]
>+.>++++++ ++ ++ ++ +[
>+ ++ ++ +< -]
>. >- ++ +++-
+- +-++++++++<-<+ +- ++