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