Commit | Line | Data |
---|---|---|
982ec720 | 1 | -module(life_cell). |
d2a0e2f9 SK |
2 | -behaviour(gen_server). |
3 | ||
4 | ||
5 | %% API | |
6 | -export([start_link/1]). | |
7 | ||
8 | %% Callbacks | |
9 | -export([init/1 | |
10 | ,handle_call/3 | |
11 | ,handle_cast/2 | |
12 | ,handle_info/2 | |
13 | ,terminate/2 | |
14 | ,code_change/3 | |
15 | ]). | |
16 | ||
17 | ||
29199e7e | 18 | -record(state, {cell_id :: integer() |
d2a0e2f9 SK |
19 | ,name :: string() |
20 | ,cell_state :: 0 | 1 | |
21 | ,neighbors :: list(atom()) | |
22 | ,live_neighbors :: integer() | |
23 | ,num_neighbors :: integer() | |
24 | ,replies_pending :: integer() | |
0a9b8c17 | 25 | ,gen_id :: integer() |
d2a0e2f9 SK |
26 | }). |
27 | ||
28 | ||
29 | %% ============================================================================ | |
30 | %% API | |
31 | %% ============================================================================ | |
32 | ||
29199e7e | 33 | start_link({_, Name, _}=Datum) -> |
d2a0e2f9 SK |
34 | ServerName = {local, Name}, |
35 | Args = [Datum], | |
36 | Opts = [], | |
37 | gen_server:start_link(ServerName, ?MODULE, Args, Opts). | |
38 | ||
39 | ||
40 | %% ============================================================================ | |
41 | %% Callbacks | |
42 | %% ============================================================================ | |
43 | ||
29199e7e SK |
44 | init([{CellID, Name, NeighborNames}]) -> |
45 | State = #state{cell_id = CellID | |
0a9b8c17 SK |
46 | ,name = Name |
47 | ,cell_state = crypto:rand_uniform(0, 2) | |
48 | ,neighbors = NeighborNames | |
49 | ,num_neighbors = length(NeighborNames) | |
50 | ,live_neighbors = 0 | |
51 | ,replies_pending = 0 | |
d2a0e2f9 SK |
52 | }, |
53 | {ok, State}. | |
54 | ||
55 | ||
56 | terminate(_Reason, State) -> | |
57 | {ok, State}. | |
58 | ||
59 | ||
60 | code_change(_Old, State, _Other) -> | |
61 | {ok, State}. | |
62 | ||
63 | ||
64 | handle_call(_Msg, _From, State) -> | |
65 | {reply, ok, State}. | |
66 | ||
67 | ||
0a9b8c17 | 68 | handle_cast({next_gen, GenID}, |
d2a0e2f9 SK |
69 | #state{name=Name |
70 | ,neighbors=Neighbors | |
71 | ,num_neighbors=NumNeighbors | |
72 | }=State) -> | |
29199e7e SK |
73 | |
74 | ok = cast_all(Neighbors, {request_state, GenID, Name}), | |
0a9b8c17 | 75 | {noreply, State#state{replies_pending=NumNeighbors, gen_id=GenID}}; |
d2a0e2f9 | 76 | |
29199e7e SK |
77 | %% If we receive this before we receive next_gen, throw it back in the queue. |
78 | %% (Took me a while to realize this, but sometimes it is possible. The more | |
79 | %% there're cells, the more likely this is to happen.) | |
80 | handle_cast({request_state, GenID, _Requester}=Msg, | |
81 | #state{gen_id=MyGenID | |
82 | ,name=Name | |
83 | }=State) when GenID =/= MyGenID-> | |
d2a0e2f9 | 84 | |
29199e7e | 85 | gen_server:cast(Name, Msg), |
d2a0e2f9 SK |
86 | {noreply, State}; |
87 | ||
29199e7e SK |
88 | %% Now that we can be sure that this request is for the current generation, we |
89 | %% can handle it | |
90 | handle_cast({request_state, GenID, Requester}, | |
91 | #state{gen_id=MyGenID | |
92 | ,cell_state=CellState | |
93 | }=State) when GenID =:= MyGenID-> | |
d2a0e2f9 | 94 | |
29199e7e SK |
95 | ok = gen_server:cast(Requester, {response_state, MyGenID, CellState}), |
96 | {noreply, State}; | |
97 | ||
98 | handle_cast({response_state, GenID, NeighborState}, | |
99 | #state{cell_id=CellID | |
100 | ,gen_id=GenID | |
d2a0e2f9 SK |
101 | ,replies_pending=Pending |
102 | ,cell_state=CellState | |
103 | ,live_neighbors=LiveNeighbors | |
104 | }=State) -> | |
105 | ||
106 | NewPending = Pending - 1, | |
107 | NewLiveNeighbors = LiveNeighbors + NeighborState, | |
108 | ||
109 | NewState = State#state{replies_pending=NewPending | |
110 | ,live_neighbors=NewLiveNeighbors | |
111 | }, | |
112 | ||
113 | case NewPending of | |
114 | 0 -> | |
115 | NewCellState = new_state(CellState, NewLiveNeighbors), | |
29199e7e | 116 | ok = life_time:report_state(CellID, GenID, NewCellState), |
d2a0e2f9 SK |
117 | |
118 | {noreply, NewState#state{live_neighbors=0 | |
119 | ,cell_state=NewCellState | |
172421cb SK |
120 | } |
121 | }; | |
d2a0e2f9 SK |
122 | |
123 | _N -> | |
124 | {noreply, NewState} | |
125 | end; | |
126 | ||
172421cb SK |
127 | handle_cast(_Msg, State) -> |
128 | {noreply, State}. | |
d2a0e2f9 SK |
129 | |
130 | ||
131 | handle_info(_Msg, State) -> | |
132 | {noreply, State}. | |
133 | ||
134 | ||
135 | %% ============================================================================ | |
136 | %% Internal | |
137 | %% ============================================================================ | |
138 | ||
172421cb SK |
139 | cast_all([], _) -> ok; |
140 | cast_all([Server | Servers], Msg) -> | |
141 | ok = gen_server:cast(Server, Msg), | |
142 | cast_all(Servers, Msg). | |
d2a0e2f9 SK |
143 | |
144 | ||
145 | new_state(1, LiveNeighbors) when LiveNeighbors < 2 -> 0; | |
146 | new_state(1, LiveNeighbors) when LiveNeighbors < 4 -> 1; | |
147 | new_state(1, LiveNeighbors) when LiveNeighbors > 3 -> 0; | |
148 | new_state(0, LiveNeighbors) when LiveNeighbors =:= 3 -> 1; | |
149 | new_state(State, _LiveNeighbors) -> State. |