Commit | Line | Data |
---|---|---|
982ec720 | 1 | -module(life_time). |
d2a0e2f9 SK |
2 | -behaviour(gen_server). |
3 | ||
4 | ||
5 | %% API | |
1aa9333c | 6 | -export([start_link/3 |
172421cb | 7 | ,tock/2 |
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() | |
31 | ,state_pairs :: list(tuple(integer(), integer())) | [] | |
32 | ,replies_pending :: integer() | |
f7349482 | 33 | ,gen_count = 0 :: integer() |
d2a0e2f9 SK |
34 | }). |
35 | ||
36 | ||
37 | %% ============================================================================ | |
38 | %% API | |
39 | %% ============================================================================ | |
40 | ||
1aa9333c | 41 | start_link(X, Y, Cells) -> |
d2a0e2f9 | 42 | ServerName = {local, ?MODULE}, |
1aa9333c | 43 | Args = [X, Y, Cells], |
d2a0e2f9 SK |
44 | Opts = [], |
45 | gen_server:start_link(ServerName, ?MODULE, Args, Opts). | |
46 | ||
47 | ||
172421cb SK |
48 | tock(CellID, CellState) -> |
49 | gen_server:cast(?MODULE, {tock, {CellID, CellState}}). | |
d2a0e2f9 SK |
50 | |
51 | ||
52 | %% ============================================================================ | |
53 | %% Callbacks | |
54 | %% ============================================================================ | |
55 | ||
1aa9333c | 56 | init([X, Y, Cells]) -> |
d2a0e2f9 | 57 | State = #state{x=X |
1aa9333c | 58 | ,y=Y |
d2a0e2f9 SK |
59 | ,cells=Cells |
60 | ,num_cells=length(Cells) | |
61 | ,state_pairs=[] | |
62 | ,replies_pending=0 | |
63 | }, | |
172421cb | 64 | schedule_next_tick(), |
d2a0e2f9 SK |
65 | {ok, State}. |
66 | ||
67 | ||
68 | terminate(_Reason, State) -> | |
69 | {ok, State}. | |
70 | ||
71 | ||
72 | code_change(_Old, State, _Other) -> | |
73 | {ok, State}. | |
74 | ||
75 | ||
76 | handle_call(_Msg, _From, State) -> | |
77 | {reply, ok, State}. | |
78 | ||
79 | ||
172421cb SK |
80 | handle_cast(next_tick, |
81 | #state{cells=Cells | |
82 | ,num_cells=NumCells | |
83 | ,state_pairs=[] | |
84 | }=State) -> | |
85 | ||
86 | ok = cast_all(Cells, tick), | |
d2a0e2f9 SK |
87 | {noreply, State#state{replies_pending=NumCells}}; |
88 | ||
89 | handle_cast({tock, {ID, CellState}}, | |
90 | #state{x=X | |
1aa9333c | 91 | ,y=Y |
d2a0e2f9 SK |
92 | ,state_pairs=StatePairs |
93 | ,replies_pending=RepliesPending | |
f7349482 | 94 | ,gen_count=GenCount |
812a14ea | 95 | ,num_cells=NumCells |
d2a0e2f9 SK |
96 | }=State) -> |
97 | ||
98 | NewStatePairs = [{ID, CellState} | StatePairs], | |
99 | NewRepliesPending = RepliesPending - 1, | |
100 | NewState = State#state{replies_pending=NewRepliesPending}, | |
101 | ||
102 | case NewRepliesPending of | |
103 | 0 -> | |
f7349482 | 104 | NewGenCount = GenCount + 1, |
3803d7be SK |
105 | SortedStatePairs = lists:sort( |
106 | fun({A, _}, {B, _}) -> A < B end, | |
107 | NewStatePairs | |
108 | ), | |
d2a0e2f9 | 109 | StateChars = [state_to_char(S) || {_, S} <- SortedStatePairs], |
eec4cd1f | 110 | |
812a14ea | 111 | ok = io:format( |
1aa9333c SK |
112 | "X: ~b Y: ~b CELLS: ~b GENERATIONS: ~b~n", |
113 | [X, Y, NumCells, NewGenCount] | |
812a14ea | 114 | ), |
f7349482 | 115 | ok = do_print_bar(X), |
eec4cd1f | 116 | |
d2a0e2f9 | 117 | ok = do_print_state_chars(X, StateChars), |
eec4cd1f | 118 | |
d2a0e2f9 | 119 | ok = timer:sleep(?INTERVAL), |
172421cb | 120 | schedule_next_tick(), |
f7349482 | 121 | {noreply, NewState#state{state_pairs=[], gen_count=NewGenCount}}; |
d2a0e2f9 SK |
122 | |
123 | _N -> | |
124 | {noreply, NewState#state{state_pairs=NewStatePairs}} | |
125 | end; | |
126 | ||
127 | handle_cast(_Msg, State) -> | |
128 | {noreply, State}. | |
129 | ||
130 | ||
131 | handle_info(_Msg, State) -> | |
132 | {noreply, State}. | |
133 | ||
134 | ||
135 | %% ============================================================================ | |
136 | %% Internal | |
137 | %% ============================================================================ | |
138 | ||
172421cb SK |
139 | schedule_next_tick() -> |
140 | gen_server:cast(?MODULE, next_tick). | |
141 | ||
142 | ||
143 | cast_all([], _) -> ok; | |
144 | cast_all([Server | Servers], Msg) -> | |
145 | ok = gen_server:cast(Server, Msg), | |
146 | cast_all(Servers, Msg). | |
d2a0e2f9 SK |
147 | |
148 | ||
149 | state_to_char(0) -> ?CHAR_DEAD; | |
150 | state_to_char(1) -> ?CHAR_ALIVE. | |
151 | ||
152 | ||
153 | do_print_state_chars(_, []) -> ok; | |
154 | do_print_state_chars(X, Chars) -> | |
155 | {XChars, RestChars} = lists:split(X, Chars), | |
156 | ok = io:format([XChars, $\n]), | |
157 | do_print_state_chars(X, RestChars). | |
158 | ||
159 | ||
160 | do_print_bar(X) -> | |
161 | io:format("~s~n", [[?CHAR_BAR || _ <- lists:seq(1, X - 1)]]). |