Commit | Line | Data |
---|---|---|
d2a0e2f9 SK |
1 | -module(time). |
2 | -behaviour(gen_server). | |
3 | ||
4 | ||
5 | %% API | |
6 | -export([start_link/2 | |
7 | ,cast/1 | |
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 | ||
20 | -define(INTERVAL, 0). % In milliseconds | |
21 | ||
22 | -define(CHAR_DEAD, 32). % Space | |
23 | -define(CHAR_ALIVE, 111). % o | |
24 | -define(CHAR_BAR, 61). % = | |
25 | ||
26 | ||
27 | -record(state, {x :: integer() | |
28 | ,cells :: list(atom()) | |
29 | ,num_cells :: integer() | |
30 | ,state_pairs :: list(tuple(integer(), integer())) | [] | |
31 | ,replies_pending :: integer() | |
32 | }). | |
33 | ||
34 | ||
35 | %% ============================================================================ | |
36 | %% API | |
37 | %% ============================================================================ | |
38 | ||
39 | start_link(X, Cells) -> | |
40 | ServerName = {local, ?MODULE}, | |
41 | Args = [X, Cells], | |
42 | Opts = [], | |
43 | gen_server:start_link(ServerName, ?MODULE, Args, Opts). | |
44 | ||
45 | ||
46 | cast(Msg) -> | |
47 | gen_server:cast(?MODULE, Msg). | |
48 | ||
49 | ||
50 | %% ============================================================================ | |
51 | %% Callbacks | |
52 | %% ============================================================================ | |
53 | ||
54 | init([X, Cells]) -> | |
55 | State = #state{x=X | |
56 | ,cells=Cells | |
57 | ,num_cells=length(Cells) | |
58 | ,state_pairs=[] | |
59 | ,replies_pending=0 | |
60 | }, | |
61 | cast(next_tick), | |
62 | {ok, State}. | |
63 | ||
64 | ||
65 | terminate(_Reason, State) -> | |
66 | {ok, State}. | |
67 | ||
68 | ||
69 | code_change(_Old, State, _Other) -> | |
70 | {ok, State}. | |
71 | ||
72 | ||
73 | handle_call(_Msg, _From, State) -> | |
74 | {reply, ok, State}. | |
75 | ||
76 | ||
77 | handle_cast(next_tick, #state{cells=Cells, num_cells=NumCells, state_pairs=[]}=State) -> | |
78 | ok = send_all(Cells, tick), | |
79 | {noreply, State#state{replies_pending=NumCells}}; | |
80 | ||
81 | handle_cast({tock, {ID, CellState}}, | |
82 | #state{x=X | |
83 | ,state_pairs=StatePairs | |
84 | ,replies_pending=RepliesPending | |
85 | }=State) -> | |
86 | ||
87 | NewStatePairs = [{ID, CellState} | StatePairs], | |
88 | NewRepliesPending = RepliesPending - 1, | |
89 | NewState = State#state{replies_pending=NewRepliesPending}, | |
90 | ||
91 | case NewRepliesPending of | |
92 | 0 -> | |
93 | SortedStatePairs = lists:sort(NewStatePairs), | |
94 | StateChars = [state_to_char(S) || {_, S} <- SortedStatePairs], | |
95 | ok = do_print_bar(X), | |
96 | ok = do_print_state_chars(X, StateChars), | |
97 | ok = do_print_bar(X), | |
98 | ok = timer:sleep(?INTERVAL), | |
99 | cast(next_tick), | |
100 | {noreply, NewState#state{state_pairs=[]}}; | |
101 | ||
102 | _N -> | |
103 | {noreply, NewState#state{state_pairs=NewStatePairs}} | |
104 | end; | |
105 | ||
106 | handle_cast(_Msg, State) -> | |
107 | {noreply, State}. | |
108 | ||
109 | ||
110 | handle_info(_Msg, State) -> | |
111 | {noreply, State}. | |
112 | ||
113 | ||
114 | %% ============================================================================ | |
115 | %% Internal | |
116 | %% ============================================================================ | |
117 | ||
118 | send_all([], _) -> ok; | |
119 | send_all([PID | PIDs], Msg) -> | |
120 | PID ! Msg, | |
121 | send_all(PIDs, Msg). | |
122 | ||
123 | ||
124 | state_to_char(0) -> ?CHAR_DEAD; | |
125 | state_to_char(1) -> ?CHAR_ALIVE. | |
126 | ||
127 | ||
128 | do_print_state_chars(_, []) -> ok; | |
129 | do_print_state_chars(X, Chars) -> | |
130 | {XChars, RestChars} = lists:split(X, Chars), | |
131 | ok = io:format([XChars, $\n]), | |
132 | do_print_state_chars(X, RestChars). | |
133 | ||
134 | ||
135 | do_print_bar(X) -> | |
136 | io:format("~s~n", [[?CHAR_BAR || _ <- lists:seq(1, X - 1)]]). |