Better function names.
[cellular-automata.git] / 001 / src / life_cell.erl
CommitLineData
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()
b699b8a4 26 ,early_msgs :: list()
d2a0e2f9
SK
27 }).
28
29
30%% ============================================================================
31%% API
32%% ============================================================================
33
29199e7e 34start_link({_, Name, _}=Datum) ->
d2a0e2f9
SK
35 ServerName = {local, Name},
36 Args = [Datum],
37 Opts = [],
38 gen_server:start_link(ServerName, ?MODULE, Args, Opts).
39
40
41%% ============================================================================
42%% Callbacks
43%% ============================================================================
44
29199e7e
SK
45init([{CellID, Name, NeighborNames}]) ->
46 State = #state{cell_id = CellID
0a9b8c17
SK
47 ,name = Name
48 ,cell_state = crypto:rand_uniform(0, 2)
49 ,neighbors = NeighborNames
50 ,num_neighbors = length(NeighborNames)
51 ,live_neighbors = 0
52 ,replies_pending = 0
b699b8a4 53 ,early_msgs = []
d2a0e2f9
SK
54 },
55 {ok, State}.
56
57
58terminate(_Reason, State) ->
59 {ok, State}.
60
61
62code_change(_Old, State, _Other) ->
63 {ok, State}.
64
65
66handle_call(_Msg, _From, State) ->
67 {reply, ok, State}.
68
69
0a9b8c17 70handle_cast({next_gen, GenID},
b699b8a4
SK
71 #state{name=Name
72 ,cell_state=CellState
d2a0e2f9
SK
73 ,neighbors=Neighbors
74 ,num_neighbors=NumNeighbors
b699b8a4 75 ,early_msgs=EarlyMsgs
d2a0e2f9 76 }=State) ->
29199e7e 77
263bc3ae 78 ok = cast_one2all(Neighbors, {state_broadcast, GenID, CellState}),
b699b8a4
SK
79
80 % Put stashed messages back in the mailbox,
81 % now that we're ready to handle them
263bc3ae 82 ok = cast_all2one(Name, EarlyMsgs),
b699b8a4
SK
83
84 NewState = State#state{replies_pending=NumNeighbors
85 ,gen_id=GenID
86 ,early_msgs=[]
87 },
88
89 {noreply, NewState};
d2a0e2f9
SK
90
91
b699b8a4
SK
92%% If we receive 'state_broadcast' before we receive 'next_gen',
93%% stash it until we do.
94%%
95%% Took me a while to realize this, but sometimes it is possible. The more
96%% there're cells, the more likely this is to happen.
97%%
ad4f7e77 98handle_cast({state_broadcast, ReceivedGenID, _NeighborState}=Msg,
b699b8a4
SK
99 #state{gen_id=GenID
100 ,early_msgs=EarlyMsgs
101 }=State) when GenID =/= ReceivedGenID ->
102
103 {noreply, State#state{early_msgs=[Msg|EarlyMsgs]}};
d2a0e2f9 104
ad4f7e77 105
29199e7e
SK
106%% Now that we can be sure that this request is for the current generation, we
107%% can handle it
ad4f7e77 108handle_cast({state_broadcast, GenID, NeighborState},
29199e7e
SK
109 #state{cell_id=CellID
110 ,gen_id=GenID
d2a0e2f9
SK
111 ,replies_pending=Pending
112 ,cell_state=CellState
113 ,live_neighbors=LiveNeighbors
114 }=State) ->
115
116 NewPending = Pending - 1,
117 NewLiveNeighbors = LiveNeighbors + NeighborState,
118
119 NewState = State#state{replies_pending=NewPending
120 ,live_neighbors=NewLiveNeighbors
121 },
122
123 case NewPending of
124 0 ->
125 NewCellState = new_state(CellState, NewLiveNeighbors),
29199e7e 126 ok = life_time:report_state(CellID, GenID, NewCellState),
d2a0e2f9
SK
127
128 {noreply, NewState#state{live_neighbors=0
129 ,cell_state=NewCellState
172421cb
SK
130 }
131 };
d2a0e2f9
SK
132
133 _N ->
134 {noreply, NewState}
135 end;
136
ad4f7e77 137
172421cb
SK
138handle_cast(_Msg, State) ->
139 {noreply, State}.
d2a0e2f9
SK
140
141
142handle_info(_Msg, State) ->
143 {noreply, State}.
144
145
146%% ============================================================================
147%% Internal
148%% ============================================================================
149
263bc3ae
SK
150% Cast all messages to one destination
151cast_all2one(_, []) -> ok;
152cast_all2one(Server, [Msg | Msgs]) ->
b699b8a4 153 ok = gen_server:cast(Server, Msg),
263bc3ae 154 cast_all2one(Server, Msgs).
b699b8a4
SK
155
156
263bc3ae
SK
157% Cast one message to all destinations
158cast_one2all([], _) -> ok;
159cast_one2all([Server | Servers], Msg) ->
172421cb 160 ok = gen_server:cast(Server, Msg),
263bc3ae 161 cast_one2all(Servers, Msg).
d2a0e2f9
SK
162
163
164new_state(1, LiveNeighbors) when LiveNeighbors < 2 -> 0;
165new_state(1, LiveNeighbors) when LiveNeighbors < 4 -> 1;
166new_state(1, LiveNeighbors) when LiveNeighbors > 3 -> 0;
167new_state(0, LiveNeighbors) when LiveNeighbors =:= 3 -> 1;
168new_state(State, _LiveNeighbors) -> State.
This page took 0.050143 seconds and 4 git commands to generate.