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 | ||
50c6ac08 | 20 | -define(GEN_INTERVAL, 100). % In milliseconds |
d2a0e2f9 | 21 | |
1d652075 SK |
22 | -define(CHAR_DEAD, 32). % " " |
23 | -define(CHAR_ALIVE, 111). % "o" | |
24 | -define(CHAR_BAR, 45). % "-" | |
d2a0e2f9 SK |
25 | |
26 | ||
27 | -record(state, {x :: integer() | |
1aa9333c | 28 | ,y :: integer() |
3dade747 | 29 | ,cells :: [atom()] |
d2a0e2f9 | 30 | ,num_cells :: integer() |
1ffcb224 SK |
31 | ,num_dead :: integer() |
32 | ,num_alive :: integer() | |
3dade747 | 33 | ,state_pairs :: [{integer(), integer()}] |
d2a0e2f9 | 34 | ,replies_pending :: integer() |
0a9b8c17 | 35 | ,gen_id :: integer() |
c04c5832 | 36 | ,gen_began :: erlang:timestamp() |
d2a0e2f9 SK |
37 | }). |
38 | ||
39 | ||
40 | %% ============================================================================ | |
41 | %% API | |
42 | %% ============================================================================ | |
43 | ||
1aa9333c | 44 | start_link(X, Y, Cells) -> |
d2a0e2f9 | 45 | ServerName = {local, ?MODULE}, |
1aa9333c | 46 | Args = [X, Y, Cells], |
d2a0e2f9 SK |
47 | Opts = [], |
48 | gen_server:start_link(ServerName, ?MODULE, Args, Opts). | |
49 | ||
50 | ||
29199e7e SK |
51 | report_state(CellID, GenID, CellState) -> |
52 | gen_server:cast(?MODULE, {report_state, {CellID, GenID, CellState}}). | |
d2a0e2f9 SK |
53 | |
54 | ||
90456d08 SK |
55 | %% ============================================================================ |
56 | %% Callbacks (unused) | |
57 | %% ============================================================================ | |
58 | ||
59 | handle_call(_Msg, _From, State) -> {reply, ok, State}. | |
90456d08 SK |
60 | code_change(_Old, State, _Other) -> {ok, State}. |
61 | terminate(_Reason, State) -> {ok, State}. | |
62 | ||
63 | ||
d2a0e2f9 SK |
64 | %% ============================================================================ |
65 | %% Callbacks | |
66 | %% ============================================================================ | |
67 | ||
1aa9333c | 68 | init([X, Y, Cells]) -> |
0a9b8c17 SK |
69 | State = #state{x = X |
70 | ,y = Y | |
71 | ,cells = Cells | |
72 | ,num_cells = length(Cells) | |
73 | ,state_pairs = [] | |
74 | ,replies_pending = 0 | |
75 | ,gen_id = 0 | |
d2a0e2f9 | 76 | }, |
50c6ac08 SK |
77 | NextGenDelay = 0, |
78 | ok = schedule_next_gen(NextGenDelay), | |
d2a0e2f9 SK |
79 | {ok, State}. |
80 | ||
81 | ||
7319e378 | 82 | handle_info(next_gen, |
172421cb SK |
83 | #state{cells=Cells |
84 | ,num_cells=NumCells | |
85 | ,state_pairs=[] | |
0a9b8c17 | 86 | ,gen_id=GenID |
172421cb SK |
87 | }=State) -> |
88 | ||
c04c5832 | 89 | GenBegan = os:timestamp(), |
0a9b8c17 | 90 | NewGenID = GenID + 1, |
79a2bc14 | 91 | ok = life_lib:cast_one2all(Cells, {next_gen, NewGenID}), |
1ffcb224 SK |
92 | NewState = State#state{replies_pending=NumCells |
93 | ,gen_id=NewGenID | |
c04c5832 | 94 | ,gen_began=GenBegan |
1ffcb224 SK |
95 | ,num_dead=0 |
96 | ,num_alive=0 | |
97 | }, | |
98 | {noreply, NewState}; | |
d2a0e2f9 | 99 | |
7319e378 SK |
100 | handle_info(_Msg, State) -> |
101 | {noreply, State}. | |
102 | ||
103 | ||
29199e7e | 104 | handle_cast({report_state, {CellID, GenID, CellState}}, |
d2a0e2f9 | 105 | #state{x=X |
1aa9333c | 106 | ,y=Y |
1ffcb224 SK |
107 | ,num_dead=NDead |
108 | ,num_alive=NAlive | |
d2a0e2f9 SK |
109 | ,state_pairs=StatePairs |
110 | ,replies_pending=RepliesPending | |
0a9b8c17 | 111 | ,gen_id=GenID |
c04c5832 | 112 | ,gen_began=GenBegan |
812a14ea | 113 | ,num_cells=NumCells |
d2a0e2f9 SK |
114 | }=State) -> |
115 | ||
29199e7e | 116 | NewStatePairs = [{CellID, CellState} | StatePairs], |
d2a0e2f9 | 117 | NewRepliesPending = RepliesPending - 1, |
1ffcb224 SK |
118 | {NewNDead, NewNAlive} = increment_dead_or_alive(CellState, NDead, NAlive), |
119 | NewState = State#state{replies_pending=NewRepliesPending | |
120 | ,num_dead=NewNDead | |
121 | ,num_alive=NewNAlive | |
122 | }, | |
d2a0e2f9 SK |
123 | |
124 | case NewRepliesPending of | |
125 | 0 -> | |
3803d7be SK |
126 | SortedStatePairs = lists:sort( |
127 | fun({A, _}, {B, _}) -> A < B end, | |
128 | NewStatePairs | |
129 | ), | |
d2a0e2f9 | 130 | StateChars = [state_to_char(S) || {_, S} <- SortedStatePairs], |
eec4cd1f | 131 | |
50c6ac08 SK |
132 | GenDurationMic = timer:now_diff(os:timestamp(), GenBegan), |
133 | GenDurationMil = GenDurationMic / 1000, | |
134 | GenDurationSec = GenDurationMic / 1000000, | |
135 | NextGenDelay = round(?GEN_INTERVAL - GenDurationMil), | |
c04c5832 | 136 | |
50c6ac08 | 137 | ok = life_observer:log_generation(GenID, GenDurationSec, NewNDead, NewNAlive), |
c8c75648 | 138 | |
812a14ea | 139 | ok = io:format( |
c04c5832 | 140 | "X: ~b Y: ~b CELLS: ~b DEAD: ~b ALIVE: ~b GENERATION: ~b DURATION: ~f~n", |
50c6ac08 | 141 | [X, Y, NumCells, NewNDead, NewNAlive, GenID, GenDurationSec] |
812a14ea | 142 | ), |
f7349482 | 143 | ok = do_print_bar(X), |
d2a0e2f9 | 144 | ok = do_print_state_chars(X, StateChars), |
7319e378 | 145 | |
50c6ac08 | 146 | ok = schedule_next_gen(NextGenDelay), |
a88b2916 | 147 | {noreply, NewState#state{state_pairs=[]}}; |
d2a0e2f9 SK |
148 | |
149 | _N -> | |
150 | {noreply, NewState#state{state_pairs=NewStatePairs}} | |
151 | end; | |
152 | ||
153 | handle_cast(_Msg, State) -> | |
154 | {noreply, State}. | |
155 | ||
156 | ||
d2a0e2f9 SK |
157 | %% ============================================================================ |
158 | %% Internal | |
159 | %% ============================================================================ | |
160 | ||
1ffcb224 SK |
161 | increment_dead_or_alive(0, NDead, NAlive) -> {NDead + 1, NAlive}; |
162 | increment_dead_or_alive(1, NDead, NAlive) -> {NDead, NAlive + 1}. | |
163 | ||
164 | ||
50c6ac08 SK |
165 | schedule_next_gen(Delay) when Delay > 0 -> |
166 | erlang:send_after(Delay, self(), next_gen), | |
167 | ok; | |
168 | ||
169 | schedule_next_gen(_) -> | |
170 | erlang:send(self(), next_gen), | |
7319e378 | 171 | ok. |
172421cb SK |
172 | |
173 | ||
d2a0e2f9 SK |
174 | state_to_char(0) -> ?CHAR_DEAD; |
175 | state_to_char(1) -> ?CHAR_ALIVE. | |
176 | ||
177 | ||
178 | do_print_state_chars(_, []) -> ok; | |
179 | do_print_state_chars(X, Chars) -> | |
180 | {XChars, RestChars} = lists:split(X, Chars), | |
181 | ok = io:format([XChars, $\n]), | |
182 | do_print_state_chars(X, RestChars). | |
183 | ||
184 | ||
185 | do_print_bar(X) -> | |
186 | io:format("~s~n", [[?CHAR_BAR || _ <- lists:seq(1, X - 1)]]). |