Converted to OTP app.
[cellular-automata.git] / 001 / src / cell.erl
1 -module(cell).
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()
25 }).
26
27
28 %% ============================================================================
29 %% API
30 %% ============================================================================
31
32 start_link({_ID, Name, _NeighborNames}=Datum) ->
33 ServerName = {local, Name},
34 Args = [Datum],
35 Opts = [],
36 gen_server:start_link(ServerName, ?MODULE, Args, Opts).
37
38
39 %% ============================================================================
40 %% Callbacks
41 %% ============================================================================
42
43 init([{ID, Name, NeighborNames}]) ->
44 State = #state{id=ID
45 ,name=Name
46 ,cell_state=crypto:rand_uniform(0, 2)
47 ,neighbors=NeighborNames
48 ,num_neighbors=length(NeighborNames)
49 ,live_neighbors=0
50 ,replies_pending=0
51 },
52 {ok, State}.
53
54
55 terminate(_Reason, State) ->
56 {ok, State}.
57
58
59 code_change(_Old, State, _Other) ->
60 {ok, State}.
61
62
63 handle_call(_Msg, _From, State) ->
64 {reply, ok, State}.
65
66
67 handle_cast(_Msg, State) ->
68 {noreply, State}.
69
70
71 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
72
73 handle_info(tick,
74 #state{name=Name
75 ,neighbors=Neighbors
76 ,num_neighbors=NumNeighbors
77 }=State) ->
78
79 ok = send_all(Neighbors, {request_state, Name}),
80 {noreply, State#state{replies_pending=NumNeighbors}};
81
82
83 handle_info({request_state, Requester}, State) ->
84 Requester ! {response_state, State#state.cell_state},
85 {noreply, State};
86
87
88 handle_info({response_state, NeighborState},
89 #state{id=ID
90 ,replies_pending=Pending
91 ,cell_state=CellState
92 ,live_neighbors=LiveNeighbors
93 }=State) ->
94
95 NewPending = Pending - 1,
96 NewLiveNeighbors = LiveNeighbors + NeighborState,
97
98 NewState = State#state{replies_pending=NewPending
99 ,live_neighbors=NewLiveNeighbors
100 },
101
102 case NewPending of
103 0 ->
104 NewCellState = new_state(CellState, NewLiveNeighbors),
105 ok = time:cast({tock, {ID, NewCellState}}),
106
107 {noreply, NewState#state{live_neighbors=0
108 ,cell_state=NewCellState
109 }};
110
111 _N ->
112 {noreply, NewState}
113 end;
114
115 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
116
117
118 handle_info(_Msg, State) ->
119 {noreply, State}.
120
121
122 %% ============================================================================
123 %% Internal
124 %% ============================================================================
125
126 send_all([], _) -> ok;
127 send_all([PID | PIDs], Msg) ->
128 PID ! Msg,
129 send_all(PIDs, Msg).
130
131
132 new_state(1, LiveNeighbors) when LiveNeighbors < 2 -> 0;
133 new_state(1, LiveNeighbors) when LiveNeighbors < 4 -> 1;
134 new_state(1, LiveNeighbors) when LiveNeighbors > 3 -> 0;
135 new_state(0, LiveNeighbors) when LiveNeighbors =:= 3 -> 1;
136 new_state(State, _LiveNeighbors) -> State.
This page took 0.077384 seconds and 4 git commands to generate.