Commit | Line | Data |
---|---|---|
982ec720 | 1 | -module(life_time). |
d2a0e2f9 SK |
2 | -behaviour(gen_server). |
3 | ||
4 | ||
5 | %% API | |
1aa9333c | 6 | -export([start_link/3 |
29199e7e | 7 | ,report_state/3 |
d2a0e2f9 SK |
8 | ]). |
9 | ||
10 | %% Callbacks | |
11 | -export([init/1 | |
12 | ,handle_call/3 | |
13 | ,handle_cast/2 | |
14 | ,handle_info/2 | |
15 | ,terminate/2 | |
16 | ,code_change/3 | |
17 | ]). | |
18 | ||
19 | ||
a43ebd2e | 20 | -define(INTERVAL, 100). % In milliseconds |
d2a0e2f9 SK |
21 | |
22 | -define(CHAR_DEAD, 32). % Space | |
23 | -define(CHAR_ALIVE, 111). % o | |
eec4cd1f | 24 | -define(CHAR_BAR, 45). % - |
d2a0e2f9 SK |
25 | |
26 | ||
27 | -record(state, {x :: integer() | |
1aa9333c | 28 | ,y :: integer() |
d2a0e2f9 SK |
29 | ,cells :: list(atom()) |
30 | ,num_cells :: integer() | |
1ffcb224 SK |
31 | ,num_dead :: integer() |
32 | ,num_alive :: integer() | |
d2a0e2f9 SK |
33 | ,state_pairs :: list(tuple(integer(), integer())) | [] |
34 | ,replies_pending :: integer() | |
0a9b8c17 | 35 | ,gen_id :: integer() |
d2a0e2f9 SK |
36 | }). |
37 | ||
38 | ||
39 | %% ============================================================================ | |
40 | %% API | |
41 | %% ============================================================================ | |
42 | ||
1aa9333c | 43 | start_link(X, Y, Cells) -> |
d2a0e2f9 | 44 | ServerName = {local, ?MODULE}, |
1aa9333c | 45 | Args = [X, Y, Cells], |
d2a0e2f9 SK |
46 | Opts = [], |
47 | gen_server:start_link(ServerName, ?MODULE, Args, Opts). | |
48 | ||
49 | ||
29199e7e SK |
50 | report_state(CellID, GenID, CellState) -> |
51 | gen_server:cast(?MODULE, {report_state, {CellID, GenID, CellState}}). | |
d2a0e2f9 SK |
52 | |
53 | ||
54 | %% ============================================================================ | |
55 | %% Callbacks | |
56 | %% ============================================================================ | |
57 | ||
1aa9333c | 58 | init([X, Y, Cells]) -> |
0a9b8c17 SK |
59 | State = #state{x = X |
60 | ,y = Y | |
61 | ,cells = Cells | |
62 | ,num_cells = length(Cells) | |
63 | ,state_pairs = [] | |
64 | ,replies_pending = 0 | |
65 | ,gen_id = 0 | |
d2a0e2f9 | 66 | }, |
1d121bcf | 67 | ok = schedule_next_gen(), |
d2a0e2f9 SK |
68 | {ok, State}. |
69 | ||
70 | ||
71 | terminate(_Reason, State) -> | |
72 | {ok, State}. | |
73 | ||
74 | ||
75 | code_change(_Old, State, _Other) -> | |
76 | {ok, State}. | |
77 | ||
78 | ||
79 | handle_call(_Msg, _From, State) -> | |
80 | {reply, ok, State}. | |
81 | ||
82 | ||
0a9b8c17 | 83 | handle_cast(next_gen, |
172421cb SK |
84 | #state{cells=Cells |
85 | ,num_cells=NumCells | |
86 | ,state_pairs=[] | |
0a9b8c17 | 87 | ,gen_id=GenID |
172421cb SK |
88 | }=State) -> |
89 | ||
0a9b8c17 SK |
90 | NewGenID = GenID + 1, |
91 | ok = cast_all(Cells, {next_gen, NewGenID}), | |
1ffcb224 SK |
92 | NewState = State#state{replies_pending=NumCells |
93 | ,gen_id=NewGenID | |
94 | ,num_dead=0 | |
95 | ,num_alive=0 | |
96 | }, | |
97 | {noreply, NewState}; | |
d2a0e2f9 | 98 | |
29199e7e | 99 | handle_cast({report_state, {CellID, GenID, CellState}}, |
d2a0e2f9 | 100 | #state{x=X |
1aa9333c | 101 | ,y=Y |
1ffcb224 SK |
102 | ,num_dead=NDead |
103 | ,num_alive=NAlive | |
d2a0e2f9 SK |
104 | ,state_pairs=StatePairs |
105 | ,replies_pending=RepliesPending | |
0a9b8c17 | 106 | ,gen_id=GenID |
812a14ea | 107 | ,num_cells=NumCells |
d2a0e2f9 SK |
108 | }=State) -> |
109 | ||
29199e7e | 110 | NewStatePairs = [{CellID, CellState} | StatePairs], |
d2a0e2f9 | 111 | NewRepliesPending = RepliesPending - 1, |
1ffcb224 SK |
112 | {NewNDead, NewNAlive} = increment_dead_or_alive(CellState, NDead, NAlive), |
113 | NewState = State#state{replies_pending=NewRepliesPending | |
114 | ,num_dead=NewNDead | |
115 | ,num_alive=NewNAlive | |
116 | }, | |
d2a0e2f9 SK |
117 | |
118 | case NewRepliesPending of | |
119 | 0 -> | |
3803d7be SK |
120 | SortedStatePairs = lists:sort( |
121 | fun({A, _}, {B, _}) -> A < B end, | |
122 | NewStatePairs | |
123 | ), | |
d2a0e2f9 | 124 | StateChars = [state_to_char(S) || {_, S} <- SortedStatePairs], |
eec4cd1f | 125 | |
812a14ea | 126 | ok = io:format( |
1ffcb224 SK |
127 | "X: ~b Y: ~b CELLS: ~b DEAD: ~b ALIVE: ~b GENERATION: ~b~n", |
128 | [X, Y, NumCells, NewNDead, NewNAlive, GenID] | |
812a14ea | 129 | ), |
f7349482 | 130 | ok = do_print_bar(X), |
d2a0e2f9 | 131 | ok = do_print_state_chars(X, StateChars), |
d2a0e2f9 | 132 | ok = timer:sleep(?INTERVAL), |
1d121bcf | 133 | ok = schedule_next_gen(), |
a88b2916 | 134 | {noreply, NewState#state{state_pairs=[]}}; |
d2a0e2f9 SK |
135 | |
136 | _N -> | |
137 | {noreply, NewState#state{state_pairs=NewStatePairs}} | |
138 | end; | |
139 | ||
140 | handle_cast(_Msg, State) -> | |
141 | {noreply, State}. | |
142 | ||
143 | ||
144 | handle_info(_Msg, State) -> | |
145 | {noreply, State}. | |
146 | ||
147 | ||
148 | %% ============================================================================ | |
149 | %% Internal | |
150 | %% ============================================================================ | |
151 | ||
1ffcb224 SK |
152 | increment_dead_or_alive(0, NDead, NAlive) -> {NDead + 1, NAlive}; |
153 | increment_dead_or_alive(1, NDead, NAlive) -> {NDead, NAlive + 1}. | |
154 | ||
155 | ||
0a9b8c17 | 156 | schedule_next_gen() -> |
1d121bcf | 157 | ok = gen_server:cast(?MODULE, next_gen). |
172421cb SK |
158 | |
159 | ||
160 | cast_all([], _) -> ok; | |
161 | cast_all([Server | Servers], Msg) -> | |
162 | ok = gen_server:cast(Server, Msg), | |
163 | cast_all(Servers, Msg). | |
d2a0e2f9 SK |
164 | |
165 | ||
166 | state_to_char(0) -> ?CHAR_DEAD; | |
167 | state_to_char(1) -> ?CHAR_ALIVE. | |
168 | ||
169 | ||
170 | do_print_state_chars(_, []) -> ok; | |
171 | do_print_state_chars(X, Chars) -> | |
172 | {XChars, RestChars} = lists:split(X, Chars), | |
173 | ok = io:format([XChars, $\n]), | |
174 | do_print_state_chars(X, RestChars). | |
175 | ||
176 | ||
177 | do_print_bar(X) -> | |
178 | io:format("~s~n", [[?CHAR_BAR || _ <- lists:seq(1, X - 1)]]). |