Skip to content

Instantly share code, notes, and snippets.

@jlouis
Created November 3, 2010 16:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jlouis/661272 to your computer and use it in GitHub Desktop.
Save jlouis/661272 to your computer and use it in GitHub Desktop.
Make a text message into a brainfuck program
%%%-------------------------------------------------------------------
%%% File : bfize.erl
%%% Author : Jesper Louis Andersen <jesper.louis.andersen@gmail.com>
%%% Description : Brainfuckize a text message
%%%
%%% Created : 27 Oct 2010 by Jesper Louis Andersen <jesper.louis.andersen@gmail.com>
%%%-------------------------------------------------------------------
-module(bfize).
%% API, the single export from the module, taking one parameter
-export([bfize/1]).
%%====================================================================
%% API
%%====================================================================
bfize(FN) ->
%% Read in the file, assume it goes well. We now have the file as a binary in memory
{ok, Content} = file:read_file(FN),
%% Set up an IOlist containing the initialization vector and call process with 4 counters,
%% initialized by the IV at 70, 100, 30, and 10. We use multiple counters so we can choose the
%% counter closest to the value we wish to output and thus get a somewhat smaller brainfuck
%% program.
%%
%% IOlists are
%% datatype iolist = L of iolist list | S of string | B of binary | C of char
%% So we have O(1) concatenation. Many Erlang operations works on IOlists as well as they
%% do on strings (if only ML...).
Res = [initialization(), process(binary_to_list(Content),
0,
[{1, 70}, {2, 100}, {3, 30}, {4, 10}])],
%% Write out the resulting file.
file:write_file(FN ++ ".bf", format(lists:flatten(Res))).
%%====================================================================
%% Internal functions
%%====================================================================
%% Simple output formatter, 40 characters per line.
format(Lst) when length(Lst) < 40 -> Lst;
format(Lst) ->
{Front, Back} = lists:split(40, Lst),
[Front, $\n, format(Back)].
%% The initialization vector. A loop of 10 sets up 4 counters at positions 1,2,3,4
%% on the 0-indexed tape. Values are 7*10, 10*10,3*10 and 1*10.
initialization() ->
"++++++++++[>+++++++>++++++++++>+++>+<<<<-]".
%% If a counter is at Count and we want to print character Char, alter the counter so it
%% can print the character on the next output value by adjusting it with either + or -.
alter_counter(Char, Count) when Char == Count -> "";
alter_counter(Char, Count) when Char > Count -> string:copies("+", Char - Count);
alter_counter(Char, Count) when Char < Count -> string:copies("-", Count - Char).
%% Carry out move instructions on the tape to take us from an Old Position to a new one.
move_tape(OldPos, NewPos) when OldPos == NewPos -> "";
move_tape(OldPos, NewPos) when OldPos > NewPos -> string:copies("<", OldPos - NewPos);
move_tape(OldPos, NewPos) when OldPos < NewPos -> string:copies(">", NewPos - OldPos).
%% Helper: Update a property-lists key NK with a new value NV by searching for the element
%% to update.
update_proplist([{K, _V} | Rest], NK, NV) when K == NK ->
[{K, NV} | Rest];
update_proplist([{K, V} | Rest], NK, NV) when K =/= NK ->
[{K, V} | update_proplist(Rest, NK, NV)].
%% Find the nearest counter to use when you want to target output of N,
%% By searching all counters and returning the one that is closest at the
%% moment.
find_nearest(N, Cnt) ->
find_nearest(N, {0, 300}, Cnt).
find_nearest(_N, {K, V}, []) -> {K, V};
find_nearest(N, {K, V}, [{NK, NV} | R]) ->
case abs(N - V) < abs(N - NV) of
true ->
find_nearest(N, {K, V}, R);
false ->
find_nearest(N, {NK, NV}, R)
end.
%% Finally, the meat. We want to output character N.
process([], _Pos, _Counters) -> [];
process([N | R], Pos, Counters) ->
%% Find the nearest counter to N
{NPos, V} = find_nearest(N, Counters),
%% Move the tape to the counter, update the counter, print out the desired character
Res = [move_tape(Pos, NPos), alter_counter(N, V), $.],
%% Process next character in the input, update the counters so we keep track of them.
[Res | process(R, NPos, update_proplist(Counters, NPos, N))].
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment