Added generation count to time and cell states.
[cellular-automata.git] / 001 / src / life_cell.erl
1 -module(life_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 ,generation :: integer()
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
68 handle_cast({tick, Generation},
69 #state{name=Name
70 ,neighbors=Neighbors
71 ,num_neighbors=NumNeighbors
72 }=State) ->
73 ok = cast_all(Neighbors, {request_state, Name}),
74 {noreply, State#state{replies_pending=NumNeighbors, generation=Generation}};
75
76
77 handle_cast({request_state, Requester}, State) ->
78 ok = gen_server:cast(Requester, {response_state, State#state.cell_state}),
79 {noreply, State};
80
81
82 handle_cast({response_state, NeighborState},
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),
99 ok = life_time:tock(ID, NewCellState),
100
101 {noreply, NewState#state{live_neighbors=0
102 ,cell_state=NewCellState
103 }
104 };
105
106 _N ->
107 {noreply, NewState}
108 end;
109
110
111 handle_cast(_Msg, State) ->
112 {noreply, State}.
113
114
115 handle_info(_Msg, State) ->
116 {noreply, State}.
117
118
119 %% ============================================================================
120 %% Internal
121 %% ============================================================================
122
123 cast_all([], _) -> ok;
124 cast_all([Server | Servers], Msg) ->
125 ok = gen_server:cast(Server, Msg),
126 cast_all(Servers, Msg).
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.
This page took 0.060784 seconds and 4 git commands to generate.