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 | ||
18 | -record(state, {id :: integer() | |
19 | ,name :: string() | |
20 | ,cell_state :: 0 | 1 | |
21 | ,neighbors :: list(atom()) | |
22 | ,live_neighbors :: integer() | |
23 | ,num_neighbors :: integer() | |
24 | ,replies_pending :: integer() | |
a88b2916 | 25 | ,generation :: integer() |
d2a0e2f9 SK |
26 | }). |
27 | ||
28 | ||
29 | %% ============================================================================ | |
30 | %% API | |
31 | %% ============================================================================ | |
32 | ||
33 | start_link({_ID, Name, _NeighborNames}=Datum) -> | |
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 | ||
44 | init([{ID, Name, NeighborNames}]) -> | |
45 | State = #state{id=ID | |
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 | |
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 | ||
a88b2916 | 68 | handle_cast({tick, Generation}, |
d2a0e2f9 SK |
69 | #state{name=Name |
70 | ,neighbors=Neighbors | |
71 | ,num_neighbors=NumNeighbors | |
72 | }=State) -> | |
172421cb | 73 | ok = cast_all(Neighbors, {request_state, Name}), |
a88b2916 | 74 | {noreply, State#state{replies_pending=NumNeighbors, generation=Generation}}; |
d2a0e2f9 SK |
75 | |
76 | ||
172421cb SK |
77 | handle_cast({request_state, Requester}, State) -> |
78 | ok = gen_server:cast(Requester, {response_state, State#state.cell_state}), | |
d2a0e2f9 SK |
79 | {noreply, State}; |
80 | ||
81 | ||
172421cb | 82 | handle_cast({response_state, NeighborState}, |
d2a0e2f9 SK |
83 | #state{id=ID |
84 | ,replies_pending=Pending | |
85 | ,cell_state=CellState | |
86 | ,live_neighbors=LiveNeighbors | |
87 | }=State) -> | |
88 | ||
89 | NewPending = Pending - 1, | |
90 | NewLiveNeighbors = LiveNeighbors + NeighborState, | |
91 | ||
92 | NewState = State#state{replies_pending=NewPending | |
93 | ,live_neighbors=NewLiveNeighbors | |
94 | }, | |
95 | ||
96 | case NewPending of | |
97 | 0 -> | |
98 | NewCellState = new_state(CellState, NewLiveNeighbors), | |
982ec720 | 99 | ok = life_time:tock(ID, NewCellState), |
d2a0e2f9 SK |
100 | |
101 | {noreply, NewState#state{live_neighbors=0 | |
102 | ,cell_state=NewCellState | |
172421cb SK |
103 | } |
104 | }; | |
d2a0e2f9 SK |
105 | |
106 | _N -> | |
107 | {noreply, NewState} | |
108 | end; | |
109 | ||
172421cb SK |
110 | |
111 | handle_cast(_Msg, State) -> | |
112 | {noreply, State}. | |
d2a0e2f9 SK |
113 | |
114 | ||
115 | handle_info(_Msg, State) -> | |
116 | {noreply, State}. | |
117 | ||
118 | ||
119 | %% ============================================================================ | |
120 | %% Internal | |
121 | %% ============================================================================ | |
122 | ||
172421cb SK |
123 | cast_all([], _) -> ok; |
124 | cast_all([Server | Servers], Msg) -> | |
125 | ok = gen_server:cast(Server, Msg), | |
126 | cast_all(Servers, Msg). | |
d2a0e2f9 SK |
127 | |
128 | ||
129 | new_state(1, LiveNeighbors) when LiveNeighbors < 2 -> 0; | |
130 | new_state(1, LiveNeighbors) when LiveNeighbors < 4 -> 1; | |
131 | new_state(1, LiveNeighbors) when LiveNeighbors > 3 -> 0; | |
132 | new_state(0, LiveNeighbors) when LiveNeighbors =:= 3 -> 1; | |
133 | new_state(State, _LiveNeighbors) -> State. |