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}, |
ad4f7e77 | 69 | #state{cell_state=CellState |
d2a0e2f9 SK |
70 | ,neighbors=Neighbors |
71 | ,num_neighbors=NumNeighbors | |
72 | }=State) -> | |
29199e7e | 73 | |
ad4f7e77 | 74 | ok = cast_all(Neighbors, {state_broadcast, GenID, CellState}), |
0a9b8c17 | 75 | {noreply, State#state{replies_pending=NumNeighbors, gen_id=GenID}}; |
d2a0e2f9 SK |
76 | |
77 | ||
ad4f7e77 SK |
78 | %% If we receive 'state_broadcast' before we receive 'next_gen', throw it back |
79 | %% in the queue. (Took me a while to realize this, but sometimes it is | |
80 | %% possible. The more there're cells, the more likely this is to happen.) | |
81 | handle_cast({state_broadcast, ReceivedGenID, _NeighborState}=Msg, | |
82 | #state{gen_id=GenID, name=Name}=State) when GenID =/= ReceivedGenID-> | |
1d121bcf | 83 | ok = gen_server:cast(Name, Msg), |
d2a0e2f9 SK |
84 | {noreply, State}; |
85 | ||
ad4f7e77 | 86 | |
29199e7e SK |
87 | %% Now that we can be sure that this request is for the current generation, we |
88 | %% can handle it | |
ad4f7e77 | 89 | handle_cast({state_broadcast, GenID, NeighborState}, |
29199e7e SK |
90 | #state{cell_id=CellID |
91 | ,gen_id=GenID | |
d2a0e2f9 SK |
92 | ,replies_pending=Pending |
93 | ,cell_state=CellState | |
94 | ,live_neighbors=LiveNeighbors | |
95 | }=State) -> | |
96 | ||
97 | NewPending = Pending - 1, | |
98 | NewLiveNeighbors = LiveNeighbors + NeighborState, | |
99 | ||
100 | NewState = State#state{replies_pending=NewPending | |
101 | ,live_neighbors=NewLiveNeighbors | |
102 | }, | |
103 | ||
104 | case NewPending of | |
105 | 0 -> | |
106 | NewCellState = new_state(CellState, NewLiveNeighbors), | |
29199e7e | 107 | ok = life_time:report_state(CellID, GenID, NewCellState), |
d2a0e2f9 SK |
108 | |
109 | {noreply, NewState#state{live_neighbors=0 | |
110 | ,cell_state=NewCellState | |
172421cb SK |
111 | } |
112 | }; | |
d2a0e2f9 SK |
113 | |
114 | _N -> | |
115 | {noreply, NewState} | |
116 | end; | |
117 | ||
ad4f7e77 | 118 | |
172421cb SK |
119 | handle_cast(_Msg, State) -> |
120 | {noreply, State}. | |
d2a0e2f9 SK |
121 | |
122 | ||
123 | handle_info(_Msg, State) -> | |
124 | {noreply, State}. | |
125 | ||
126 | ||
127 | %% ============================================================================ | |
128 | %% Internal | |
129 | %% ============================================================================ | |
130 | ||
172421cb SK |
131 | cast_all([], _) -> ok; |
132 | cast_all([Server | Servers], Msg) -> | |
133 | ok = gen_server:cast(Server, Msg), | |
134 | cast_all(Servers, Msg). | |
d2a0e2f9 SK |
135 | |
136 | ||
137 | new_state(1, LiveNeighbors) when LiveNeighbors < 2 -> 0; | |
138 | new_state(1, LiveNeighbors) when LiveNeighbors < 4 -> 1; | |
139 | new_state(1, LiveNeighbors) when LiveNeighbors > 3 -> 0; | |
140 | new_state(0, LiveNeighbors) when LiveNeighbors =:= 3 -> 1; | |
141 | new_state(State, _LiveNeighbors) -> State. |