]).
--record(state, {id :: integer()
+-record(state, {cell_id :: integer()
,name :: string()
,cell_state :: 0 | 1
,neighbors :: list(atom())
%% API
%% ============================================================================
-start_link({_ID, Name, _NeighborNames}=Datum) ->
+start_link({_, Name, _}=Datum) ->
ServerName = {local, Name},
Args = [Datum],
Opts = [],
%% Callbacks
%% ============================================================================
-init([{ID, Name, NeighborNames}]) ->
- State = #state{id = ID
+init([{CellID, Name, NeighborNames}]) ->
+ State = #state{cell_id = CellID
,name = Name
,cell_state = crypto:rand_uniform(0, 2)
,neighbors = NeighborNames
,neighbors=Neighbors
,num_neighbors=NumNeighbors
}=State) ->
- ok = cast_all(Neighbors, {request_state, Name}),
+
+ ok = cast_all(Neighbors, {request_state, GenID, Name}),
{noreply, State#state{replies_pending=NumNeighbors, gen_id=GenID}};
+%% If we receive this before we receive next_gen, throw it back in the queue.
+%% (Took me a while to realize this, but sometimes it is possible. The more
+%% there're cells, the more likely this is to happen.)
+handle_cast({request_state, GenID, _Requester}=Msg,
+ #state{gen_id=MyGenID
+ ,name=Name
+ }=State) when GenID =/= MyGenID->
-handle_cast({request_state, Requester}, State) ->
- ok = gen_server:cast(Requester, {response_state, State#state.cell_state}),
+ gen_server:cast(Name, Msg),
{noreply, State};
+%% Now that we can be sure that this request is for the current generation, we
+%% can handle it
+handle_cast({request_state, GenID, Requester},
+ #state{gen_id=MyGenID
+ ,cell_state=CellState
+ }=State) when GenID =:= MyGenID->
-handle_cast({response_state, NeighborState},
- #state{id=ID
+ ok = gen_server:cast(Requester, {response_state, MyGenID, CellState}),
+ {noreply, State};
+
+handle_cast({response_state, GenID, NeighborState},
+ #state{cell_id=CellID
+ ,gen_id=GenID
,replies_pending=Pending
,cell_state=CellState
,live_neighbors=LiveNeighbors
case NewPending of
0 ->
NewCellState = new_state(CellState, NewLiveNeighbors),
- ok = life_time:report_state(ID, NewCellState),
+ ok = life_time:report_state(CellID, GenID, NewCellState),
{noreply, NewState#state{live_neighbors=0
,cell_state=NewCellState
{noreply, NewState}
end;
-
handle_cast(_Msg, State) ->
{noreply, State}.
%% API
-export([start_link/3
- ,report_state/2
+ ,report_state/3
]).
%% Callbacks
gen_server:start_link(ServerName, ?MODULE, Args, Opts).
-report_state(CellID, CellState) ->
- gen_server:cast(?MODULE, {report_state, {CellID, CellState}}).
+report_state(CellID, GenID, CellState) ->
+ gen_server:cast(?MODULE, {report_state, {CellID, GenID, CellState}}).
%% ============================================================================
ok = cast_all(Cells, {next_gen, NewGenID}),
{noreply, State#state{replies_pending=NumCells, gen_id=NewGenID}};
-handle_cast({report_state, {ID, CellState}},
+handle_cast({report_state, {CellID, GenID, CellState}},
#state{x=X
,y=Y
,state_pairs=StatePairs
,num_cells=NumCells
}=State) ->
- NewStatePairs = [{ID, CellState} | StatePairs],
+ NewStatePairs = [{CellID, CellState} | StatePairs],
NewRepliesPending = RepliesPending - 1,
NewState = State#state{replies_pending=NewRepliesPending},
[X, Y, NumCells, GenID]
),
ok = do_print_bar(X),
-
ok = do_print_state_chars(X, StateChars),
-
ok = timer:sleep(?INTERVAL),
schedule_next_gen(),
{noreply, NewState#state{state_pairs=[]}};