Converted to OTP app.
authorSiraaj Khandkar <siraaj@khandkar.net>
Mon, 23 Jul 2012 08:13:23 +0000 (04:13 -0400)
committerSiraaj Khandkar <siraaj@khandkar.net>
Mon, 23 Jul 2012 08:13:23 +0000 (04:13 -0400)
001/.gitignore [new file with mode: 0644]
001/bin/life [moved from 001/life with 55% similarity]
001/life.erl [deleted file]
001/src/cell.erl [new file with mode: 0644]
001/src/god.erl [new file with mode: 0644]
001/src/life.app.src [new file with mode: 0644]
001/src/life.erl [new file with mode: 0644]
001/src/time.erl [new file with mode: 0644]

diff --git a/001/.gitignore b/001/.gitignore
new file mode 100644 (file)
index 0000000..cfb2329
--- /dev/null
@@ -0,0 +1 @@
+ebin
similarity index 55%
rename from 001/life
rename to 001/bin/life
index d653da0..87edae8 100755 (executable)
--- a/001/life
@@ -5,5 +5,8 @@ Y=`expr \`stty size | awk '{print $1}'\` - 3`
 X=`stty size | awk '{print $2}'`
 
 
-erlc life.erl
-erl -noshell -s life bang $X $Y
+erl \
+    -noshell \
+    -pa ebin \
+    -life x $X y $Y \
+    -s life bang
diff --git a/001/life.erl b/001/life.erl
deleted file mode 100644 (file)
index c44674d..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
--module(life).
-
--export([bang/1]).
-
-
--define(DIRECTIONS, ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']).
-
--define(INTERVAL, 0).  % In milliseconds
-
--define(CHAR_DEAD,   32).  % Space
--define(CHAR_ALIVE, 111).  % o
--define(CHAR_BAR,    61).  % =
-
-
-%% ============================================================================
-%% Life processes
-%% ============================================================================
-
-%% ----------------------------------------------------------------------------
-%% Big bang
-%% ----------------------------------------------------------------------------
-
-bang([X, Y]) ->
-    bang(atom_to_integer(X), atom_to_integer(Y)).
-
-
-bang(X, Y) ->
-    N = X * Y,
-    CellIDs = lists:seq(1, N),
-
-    Graph =
-        lists:foldl(
-            fun(ID, Pairs) ->
-                Neighbors = [
-                    integer_to_atom(neighbor_id(D, X, ID))
-                    || D <- ?DIRECTIONS
-                ],
-                [{integer_to_atom(ID), Neighbors} | Pairs]
-            end,
-            [],
-            CellIDs
-        ),
-
-    Parent = self(),
-
-    lists:foreach(
-        fun({ID, Neighbors}) ->
-            register(
-                ID,
-                spawn(fun() -> cell(ID, Parent, Neighbors) end)
-            )
-        end,
-        [{ID, filter_offsides(N, Neighbors)} || {ID, Neighbors} <- Graph]
-    ),
-
-    CellNames = [integer_to_atom(ID) || ID <- CellIDs],
-
-    tick(X, CellNames).
-
-
-%% ----------------------------------------------------------------------------
-%% Tick / tock
-%% ----------------------------------------------------------------------------
-
-tick(X, Cells) ->
-    ok = send_all(Cells, {tick, self()}),
-    All = Cells,
-    Pending = Cells,
-    StatePairs = [],
-    tock(X, All, Pending, StatePairs).
-
-
-tock(X, All, [], StatePairs) ->
-    States =
-        lists:foldl(
-            fun({_ID, State}, States) -> [State | States] end,
-            [],
-            lists:sort(
-                fun({A, _}, {B, _}) ->
-                    atom_to_integer(A) < atom_to_integer(B)
-                end,
-                StatePairs
-            )
-        ),
-    ok = do_print_bar(X),
-    ok = do_print_states(X, States),
-    ok = do_print_bar(X),
-    ok = timer:sleep(?INTERVAL),
-    tick(X, All);
-
-tock(X, All, Pending, StatePairs) ->
-    receive
-        {tock, {ID, State}} ->
-            NewPending = lists:delete(ID, Pending),
-            NewStatePairs = [{ID, State} | StatePairs],
-            tock(X, All, NewPending, NewStatePairs)
-    end.
-
-
-%% ----------------------------------------------------------------------------
-%% Cell
-%% ----------------------------------------------------------------------------
-
-% Init
-cell(MyID, MyParent, MyNeighbors) ->
-    MyState = crypto:rand_uniform(0, 2),
-    cell(MyID, MyParent, MyNeighbors, MyState).
-
-
-cell(MyID, MyParent, MyNeighbors, MyState) ->
-    receive
-        {tick, MyParent} ->
-            ok = send_all(MyNeighbors, {request_state, MyID}),
-            cell(MyID, MyParent, MyNeighbors, MyState, {MyNeighbors, []})
-    end.
-
-
-% All neighbors replied
-cell(MyID, MyParent, MyNeighbors, MyState, {[], States}) ->
-    LiveNeighbors = lists:sum(States),
-    MyNewState = new_state(MyState, LiveNeighbors),
-    MyParent ! {tock, {MyID, MyState}},
-    cell(MyID, MyParent, MyNeighbors, MyNewState);
-
-% Awaiting requests and replies
-cell(MyID, MyParent, MyNeighbors, MyState, {Pending, States}) ->
-    receive
-        {request_state, ID} ->
-            ID ! {response_state, MyID, MyState},
-            cell(MyID, MyParent, MyNeighbors, MyState, {Pending, States});
-
-        {response_state, ID, State} ->
-            NewPending = lists:delete(ID, Pending),
-            NewStates = [State | States],
-            cell(MyID, MyParent, MyNeighbors, MyState, {NewPending, NewStates})
-    end.
-
-
-%% ============================================================================
-%% Rules
-%% ============================================================================
-
-new_state(1, LiveNeighbors) when LiveNeighbors  <  2 -> 0;
-new_state(1, LiveNeighbors) when LiveNeighbors  <  4 -> 1;
-new_state(1, LiveNeighbors) when LiveNeighbors  >  3 -> 0;
-new_state(0, LiveNeighbors) when LiveNeighbors =:= 3 -> 1;
-new_state(State, _LiveNeighbors) -> State.
-
-
-neighbor_id(Direction, X, ID) ->
-    ID + offset(Direction, X).
-
-
-offset('N' , X) -> ensure_negative(X);
-offset('NE', X) -> ensure_negative(X - 1);
-offset('E' , _) ->                     1;
-offset('SE', X) ->                 X + 1;
-offset('S' , X) ->                 X;
-offset('SW', X) ->                 X - 1;
-offset('W' , _) -> ensure_negative(    1);
-offset('NW', X) -> ensure_negative(X + 1).
-
-
-filter_offsides(N, IDs) ->
-    [ID || ID <- IDs, is_onside(N, atom_to_integer(ID))].
-
-
-is_onside(_, ID) when ID < 1 -> false;
-is_onside(N, ID) when ID > N -> false;
-is_onside(_, _) -> true.
-
-
-%% ============================================================================
-%% Plumbing
-%% ============================================================================
-
-ensure_negative(N) when N < 0 -> N;
-ensure_negative(N) -> -(N).
-
-
-atom_to_integer(Atom) ->
-    list_to_integer(atom_to_list(Atom)).
-
-
-integer_to_atom(Integer) ->
-    list_to_atom(integer_to_list(Integer)).
-
-
-send_all([], _) -> ok;
-send_all([PID | PIDs], Msg) ->
-    PID ! Msg,
-    send_all(PIDs, Msg).
-
-
-do_print_states(_, []) -> ok;
-do_print_states(X, States) ->
-    {XStates, RestStates} = lists:split(X, States),
-    ok = io:format([state_to_char(S) || S <- XStates] ++ "\n"),
-    do_print_states(X, RestStates).
-
-
-do_print_bar(X) ->
-    io:format("~s~n", [[?CHAR_BAR || _ <- lists:seq(1, X - 1)]]).
-
-
-state_to_char(0) -> ?CHAR_DEAD;
-state_to_char(1) -> ?CHAR_ALIVE.
diff --git a/001/src/cell.erl b/001/src/cell.erl
new file mode 100644 (file)
index 0000000..6090fa0
--- /dev/null
@@ -0,0 +1,136 @@
+-module(cell).
+-behaviour(gen_server).
+
+
+%% API
+-export([start_link/1]).
+
+%% Callbacks
+-export([init/1
+        ,handle_call/3
+        ,handle_cast/2
+        ,handle_info/2
+        ,terminate/2
+        ,code_change/3
+        ]).
+
+
+-record(state, {id              :: integer()
+               ,name            :: string()
+               ,cell_state      :: 0 | 1
+               ,neighbors       :: list(atom())
+               ,live_neighbors  :: integer()
+               ,num_neighbors   :: integer()
+               ,replies_pending :: integer()
+               }).
+
+
+%% ============================================================================
+%% API
+%% ============================================================================
+
+start_link({_ID, Name, _NeighborNames}=Datum) ->
+    ServerName = {local, Name},
+    Args = [Datum],
+    Opts = [],
+    gen_server:start_link(ServerName, ?MODULE, Args, Opts).
+
+
+%% ============================================================================
+%% Callbacks
+%% ============================================================================
+
+init([{ID, Name, NeighborNames}]) ->
+    State = #state{id=ID
+                  ,name=Name
+                  ,cell_state=crypto:rand_uniform(0, 2)
+                  ,neighbors=NeighborNames
+                  ,num_neighbors=length(NeighborNames)
+                  ,live_neighbors=0
+                  ,replies_pending=0
+                  },
+    {ok, State}.
+
+
+terminate(_Reason, State) ->
+    {ok, State}.
+
+
+code_change(_Old, State, _Other) ->
+    {ok, State}.
+
+
+handle_call(_Msg, _From, State) ->
+    {reply, ok, State}.
+
+
+handle_cast(_Msg, State) ->
+    {noreply, State}.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+handle_info(tick,
+    #state{name=Name
+          ,neighbors=Neighbors
+          ,num_neighbors=NumNeighbors
+          }=State) ->
+
+    ok = send_all(Neighbors, {request_state, Name}),
+    {noreply, State#state{replies_pending=NumNeighbors}};
+
+
+handle_info({request_state, Requester}, State) ->
+    Requester ! {response_state, State#state.cell_state},
+    {noreply, State};
+
+
+handle_info({response_state, NeighborState},
+    #state{id=ID
+          ,replies_pending=Pending
+          ,cell_state=CellState
+          ,live_neighbors=LiveNeighbors
+          }=State) ->
+
+    NewPending = Pending - 1,
+    NewLiveNeighbors = LiveNeighbors + NeighborState,
+
+    NewState = State#state{replies_pending=NewPending
+                          ,live_neighbors=NewLiveNeighbors
+                          },
+
+    case NewPending of
+        0 ->
+            NewCellState = new_state(CellState, NewLiveNeighbors),
+            ok = time:cast({tock, {ID, NewCellState}}),
+
+            {noreply, NewState#state{live_neighbors=0
+                                    ,cell_state=NewCellState
+                                    }};
+
+        _N ->
+            {noreply, NewState}
+    end;
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+handle_info(_Msg, State) ->
+    {noreply, State}.
+
+
+%% ============================================================================
+%% Internal
+%% ============================================================================
+
+send_all([], _) -> ok;
+send_all([PID | PIDs], Msg) ->
+    PID ! Msg,
+    send_all(PIDs, Msg).
+
+
+new_state(1, LiveNeighbors) when LiveNeighbors  <  2 -> 0;
+new_state(1, LiveNeighbors) when LiveNeighbors  <  4 -> 1;
+new_state(1, LiveNeighbors) when LiveNeighbors  >  3 -> 0;
+new_state(0, LiveNeighbors) when LiveNeighbors =:= 3 -> 1;
+new_state(State, _LiveNeighbors) -> State.
diff --git a/001/src/god.erl b/001/src/god.erl
new file mode 100644 (file)
index 0000000..3922680
--- /dev/null
@@ -0,0 +1,47 @@
+-module(god).
+-behaviour(supervisor).
+
+
+%% API
+-export([start_link/2]).
+
+%% Callbacks
+-export([init/1]).
+
+
+%% Helper macro for declaring children of supervisor
+-define(CHILD(Type, I, Args), {I, {I, start_link, Args}, permanent, 5000, Type, [I]}).
+
+
+%% ============================================================================
+%% API
+%% ============================================================================
+
+start_link(X, CellData) ->
+    supervisor:start_link({local, ?MODULE}, ?MODULE, [X, CellData]).
+
+
+%% ============================================================================
+%% Callbacks
+%% ============================================================================
+
+init([X, CellData]) ->
+    CellNames = [Name || {_, Name, _} <- CellData],
+    RestartStrategy = {one_for_one, 5, 10},
+    Cells = [spec_cell(Datum) || Datum <- CellData],
+    Time = ?CHILD(worker, time, [X, CellNames]),
+    Children = Cells ++ [Time],
+    {ok, {RestartStrategy, Children}}.
+
+
+spec_cell({_, Name, _}=Datum) ->
+    M = cell,
+    F = start_link,
+    A = [Datum],
+    {Name, {M, F, A}, permanent, 5000, worker, [M]}.
+
+    %{ID, {ID, start_link, Args}, permanent, 5000, Type, [ID]}.
+
+
+
+
diff --git a/001/src/life.app.src b/001/src/life.app.src
new file mode 100644 (file)
index 0000000..7514f82
--- /dev/null
@@ -0,0 +1,12 @@
+{application, life,
+ [
+  {description, ""},
+  {vsn, "1"},
+  {registered, []},
+  {applications, [
+                  kernel,
+                  stdlib
+                 ]},
+  {mod, { life, []}},
+  {env, []}
+ ]}.
diff --git a/001/src/life.erl b/001/src/life.erl
new file mode 100644 (file)
index 0000000..c59b98b
--- /dev/null
@@ -0,0 +1,86 @@
+-module(life).
+-behaviour(application).
+
+
+%% API
+-export([bang/0]).
+
+%% Callbacks
+-export([start/2, stop/1]).
+
+
+-define(DIRECTIONS, ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']).
+
+
+%% ============================================================================
+%% API
+%% ============================================================================
+
+bang() ->
+    application:start(?MODULE).
+
+
+%% ============================================================================
+%% Callbacks
+%% ============================================================================
+
+start(_StartType, _StartArgs) ->
+    {ok, X} = application:get_env(?MODULE, x),
+    {ok, Y} = application:get_env(?MODULE, y),
+    CellData = cell_data(X, Y),
+    god:start_link(X, CellData).
+
+
+stop(_State) ->
+    ok.
+
+
+%% ============================================================================
+%% Internal
+%% ============================================================================
+
+cell_data(X, Y) ->
+    N = X * Y,
+    [cell_datum(X, N, ID) || ID <- lists:seq(1, N)].
+
+
+cell_datum(X, N, ID) ->
+    Name = integer_to_atom(ID),
+    NeighborNames = filter_offsides(N,
+        [integer_to_atom(neighbor_id(Dir, X, ID)) || Dir <- ?DIRECTIONS]
+    ),
+    {ID, Name, NeighborNames}.
+
+
+neighbor_id(Direction, X, ID) -> ID + offset(Direction, X).
+
+
+offset('N' , X) -> ensure_negative(X);
+offset('NE', X) -> ensure_negative(X - 1);
+offset('E' , _) ->                     1;
+offset('SE', X) ->                 X + 1;
+offset('S' , X) ->                 X;
+offset('SW', X) ->                 X - 1;
+offset('W' , _) -> ensure_negative(    1);
+offset('NW', X) -> ensure_negative(X + 1).
+
+
+ensure_negative(N) when N < 0 -> N;
+ensure_negative(N) -> -(N).
+
+
+filter_offsides(N, IDs) ->
+    [ID || ID <- IDs, is_onside(N, atom_to_integer(ID))].
+
+
+is_onside(_, ID) when ID < 1 -> false;
+is_onside(N, ID) when ID > N -> false;
+is_onside(_, _) -> true.
+
+
+atom_to_integer(Atom) ->
+    list_to_integer(atom_to_list(Atom)).
+
+
+integer_to_atom(Integer) ->
+    list_to_atom(integer_to_list(Integer)).
diff --git a/001/src/time.erl b/001/src/time.erl
new file mode 100644 (file)
index 0000000..d710926
--- /dev/null
@@ -0,0 +1,136 @@
+-module(time).
+-behaviour(gen_server).
+
+
+%% API
+-export([start_link/2
+        ,cast/1
+        ]).
+
+%% Callbacks
+-export([init/1
+        ,handle_call/3
+        ,handle_cast/2
+        ,handle_info/2
+        ,terminate/2
+        ,code_change/3
+        ]).
+
+
+-define(INTERVAL, 0).  % In milliseconds
+
+-define(CHAR_DEAD,   32).  % Space
+-define(CHAR_ALIVE, 111).  % o
+-define(CHAR_BAR,    61).  % =
+
+
+-record(state, {x               :: integer()
+               ,cells           :: list(atom())
+               ,num_cells       :: integer()
+               ,state_pairs     :: list(tuple(integer(), integer())) | []
+               ,replies_pending :: integer()
+               }).
+
+
+%% ============================================================================
+%% API
+%% ============================================================================
+
+start_link(X, Cells) ->
+    ServerName = {local, ?MODULE},
+    Args = [X, Cells],
+    Opts = [],
+    gen_server:start_link(ServerName, ?MODULE, Args, Opts).
+
+
+cast(Msg) ->
+    gen_server:cast(?MODULE, Msg).
+
+
+%% ============================================================================
+%% Callbacks
+%% ============================================================================
+
+init([X, Cells]) ->
+    State = #state{x=X
+                  ,cells=Cells
+                  ,num_cells=length(Cells)
+                  ,state_pairs=[]
+                  ,replies_pending=0
+                  },
+    cast(next_tick),
+    {ok, State}.
+
+
+terminate(_Reason, State) ->
+    {ok, State}.
+
+
+code_change(_Old, State, _Other) ->
+    {ok, State}.
+
+
+handle_call(_Msg, _From, State) ->
+    {reply, ok, State}.
+
+
+handle_cast(next_tick, #state{cells=Cells, num_cells=NumCells, state_pairs=[]}=State) ->
+    ok = send_all(Cells, tick),
+    {noreply, State#state{replies_pending=NumCells}};
+
+handle_cast({tock, {ID, CellState}},
+    #state{x=X
+          ,state_pairs=StatePairs
+          ,replies_pending=RepliesPending
+          }=State) ->
+
+    NewStatePairs = [{ID, CellState} | StatePairs],
+    NewRepliesPending = RepliesPending - 1,
+    NewState = State#state{replies_pending=NewRepliesPending},
+
+    case NewRepliesPending of
+        0 ->
+            SortedStatePairs = lists:sort(NewStatePairs),
+            StateChars = [state_to_char(S) || {_, S} <- SortedStatePairs],
+            ok = do_print_bar(X),
+            ok = do_print_state_chars(X, StateChars),
+            ok = do_print_bar(X),
+            ok = timer:sleep(?INTERVAL),
+            cast(next_tick),
+            {noreply, NewState#state{state_pairs=[]}};
+
+        _N ->
+            {noreply, NewState#state{state_pairs=NewStatePairs}}
+    end;
+
+handle_cast(_Msg, State) ->
+    {noreply, State}.
+
+
+handle_info(_Msg, State) ->
+    {noreply, State}.
+
+
+%% ============================================================================
+%% Internal
+%% ============================================================================
+
+send_all([], _) -> ok;
+send_all([PID | PIDs], Msg) ->
+    PID ! Msg,
+    send_all(PIDs, Msg).
+
+
+state_to_char(0) -> ?CHAR_DEAD;
+state_to_char(1) -> ?CHAR_ALIVE.
+
+
+do_print_state_chars(_, []) -> ok;
+do_print_state_chars(X, Chars) ->
+    {XChars, RestChars} = lists:split(X, Chars),
+    ok = io:format([XChars, $\n]),
+    do_print_state_chars(X, RestChars).
+
+
+do_print_bar(X) ->
+    io:format("~s~n", [[?CHAR_BAR || _ <- lists:seq(1, X - 1)]]).
This page took 0.052719 seconds and 4 git commands to generate.