Printing bar to visually mark the borders of the board.
[cellular-automata.git] / 001 / life.erl
CommitLineData
3a74f872
SK
1-module(life).
2
3-export([main/0
4 ,main/1
5 ]).
6
7
8-define(DIRECTIONS, ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']).
a3eea437 9
3a74f872 10-define(X, 230).
a3eea437
SK
11-define(Y, 58).
12
3a74f872 13-define(INTERVAL, 0). % In milliseconds
a3eea437
SK
14
15-define(CHAR_DEAD, $-).
16-define(CHAR_ALIVE, $O).
17-define(CHAR_BAR, $=).
3a74f872
SK
18
19
20main( ) -> bang(?X, ?Y).
21main([ ]) -> bang(?X, ?Y);
22main([X ]) -> bang(list_to_integer(X), list_to_integer(X));
23main([X, Y]) -> bang(list_to_integer(X), list_to_integer(Y)).
24
25
26%% ============================================================================
27%% Life processes
28%% ============================================================================
29
30%% ----------------------------------------------------------------------------
31%% Big bang
32%% ----------------------------------------------------------------------------
33
34bang(X, Y) ->
35 N = X * Y,
36 CellIDs = lists:seq(1, N),
37
38 Graph =
39 lists:foldl(
40 fun(ID, Pairs) ->
41 Neighbors = [
42 integer_to_atom(neighbor_id(D, X, ID))
43 || D <- ?DIRECTIONS
44 ],
45 [{integer_to_atom(ID), Neighbors} | Pairs]
46 end,
47 [],
48 CellIDs
49 ),
50
51 Parent = self(),
52
53 lists:foreach(
54 fun({ID, Neighbors}) ->
55 register(
56 ID,
57 spawn(fun() -> cell(ID, Parent, Neighbors) end)
58 )
59 end,
60 [{ID, filter_offsides(N, Neighbors)} || {ID, Neighbors} <- Graph]
61 ),
62
63 CellNames = [integer_to_atom(ID) || ID <- CellIDs],
64
65 tick(X, CellNames).
66
67
68%% ----------------------------------------------------------------------------
69%% Tick / tock
70%% ----------------------------------------------------------------------------
71
72tick(X, Cells) ->
73 ok = send_all(Cells, {tick, self()}),
74 All = Cells,
75 Pending = Cells,
76 StatePairs = [],
77 tock(X, All, Pending, StatePairs).
78
79
80tock(X, All, [], StatePairs) ->
81 States =
82 lists:foldl(
83 fun({_ID, State}, States) -> [State | States] end,
84 [],
85 lists:sort(StatePairs)
86 ),
a3eea437 87 ok = do_print_bar(X),
3a74f872 88 ok = do_print_states(X, States),
a3eea437 89 ok = do_print_bar(X),
3a74f872
SK
90 ok = timer:sleep(?INTERVAL),
91 tick(X, All);
92
93tock(X, All, Pending, StatePairs) ->
94 receive
95 {tock, {ID, State}} ->
96 NewPending = lists:delete(ID, Pending),
97 NewStatePairs = [{ID, State} | StatePairs],
98 tock(X, All, NewPending, NewStatePairs)
99 end.
100
101
102%% ----------------------------------------------------------------------------
103%% Cell
104%% ----------------------------------------------------------------------------
105
106% Init
107cell(MyID, MyParent, MyNeighbors) ->
108 MyState = crypto:rand_uniform(0, 2),
109 cell(MyID, MyParent, MyNeighbors, MyState).
110
111
112cell(MyID, MyParent, MyNeighbors, MyState) ->
113 receive
114 {tick, MyParent} ->
115 ok = send_all(MyNeighbors, {request_state, MyID}),
116 cell(MyID, MyParent, MyNeighbors, MyState, {MyNeighbors, []})
117 end.
118
119
120% All neighbors replied
121cell(MyID, MyParent, MyNeighbors, MyState, {[], States}) ->
122 LiveNeighbors = lists:sum(States),
123 MyNewState = new_state(MyState, LiveNeighbors),
124 MyParent ! {tock, {MyID, MyNewState}},
125 cell(MyID, MyParent, MyNeighbors, MyNewState);
126
127% Awaiting requests and replies
128cell(MyID, MyParent, MyNeighbors, MyState, {Pending, States}) ->
129 receive
130 {request_state, ID} ->
131 ID ! {response_state, MyID, MyState},
132 cell(MyID, MyParent, MyNeighbors, MyState, {Pending, States});
133
134 {response_state, ID, State} ->
135 NewPending = lists:delete(ID, Pending),
136 NewStates = [State | States],
137 cell(MyID, MyParent, MyNeighbors, MyState, {NewPending, NewStates})
138 end.
139
140
141%% ============================================================================
142%% Rules
143%% ============================================================================
144
145new_state(1, LiveNeighbors) when LiveNeighbors < 2 -> 0;
146new_state(1, LiveNeighbors) when LiveNeighbors < 4 -> 1;
147new_state(1, LiveNeighbors) when LiveNeighbors > 3 -> 0;
148new_state(0, LiveNeighbors) when LiveNeighbors =:= 3 -> 1;
149new_state(State, _LiveNeighbors) -> State.
150
151
152neighbor_id(Direction, X, I) ->
153 I + offset(Direction, X).
154
155
156offset('N' , X) -> -X;
157offset('NE', X) -> -(X - 1);
158offset('E' , _) -> 1;
159offset('SE', X) -> X + 1;
160offset('S' , X) -> X;
161offset('SW', X) -> X - 1;
162offset('W' , _) -> -1;
163offset('NW', X) -> -(X + 1).
164
165
166filter_offsides(N, IDs) ->
167 [ID || ID <- IDs, is_onside(N, atom_to_integer(ID))].
168
169
170is_onside(_, ID) when ID < 1 -> false;
171is_onside(N, ID) when ID > N -> false;
172is_onside(_, _) -> true.
173
174
175%% ============================================================================
176%% Plumbing
177%% ============================================================================
178
179atom_to_integer(Atom) ->
180 list_to_integer(atom_to_list(Atom)).
181
182
183integer_to_atom(Integer) ->
184 list_to_atom(integer_to_list(Integer)).
185
186
187send_all([], _) -> ok;
188send_all([PID | PIDs], Msg) ->
189 PID ! Msg,
190 send_all(PIDs, Msg).
191
192
193do_print_states(_, []) -> ok;
194do_print_states(X, States) ->
195 {XStates, RestStates} = lists:split(X, States),
196 ok = io:format([state_to_char(S) || S <- XStates] ++ "\n"),
197 do_print_states(X, RestStates).
198
199
a3eea437
SK
200do_print_bar(X) ->
201 Chars = [$+ | [?CHAR_BAR || _ <- lists:seq(1, X - 1)]],
202 io:format("~s~n", [Chars]).
203
204
3a74f872
SK
205state_to_char(0) -> ?CHAR_DEAD;
206state_to_char(1) -> ?CHAR_ALIVE.
This page took 0.03765 seconds and 4 git commands to generate.