Created
November 3, 2010 16:03
-
-
Save jlouis/661272 to your computer and use it in GitHub Desktop.
Make a text message into a brainfuck program
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%%%------------------------------------------------------------------- | |
%%% 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