-define(CHAR_DEAD, 32). % " "
-define(CHAR_ALIVE, 111). % "o"
--define(INTERVAL, 100).
+-define(CHAR_BAR, 45). % "-"
+
+-define(GEN_INTERVAL, 100).
+
+
+-record(state, {x :: non_neg_integer()
+ ,y :: non_neg_integer()
+ ,n :: pos_integer()
+ ,bar :: nonempty_string()
+ ,board :: array()
+ ,gen_count :: pos_integer()
+ ,gen_duration :: non_neg_integer()
+ ,print_time :: non_neg_integer()
+ }).
%% ============================================================================
bang(Args) ->
[X, Y] = [atom_to_integer(A) || A <- Args],
- Board = init_board(X, Y),
- life_loop(Board).
+ {Time, Board} = timer:tc(fun() -> init_board(X, Y) end),
+ State = #state{x = X
+ ,y = Y
+ ,n = X * Y
+ ,bar = [?CHAR_BAR || _ <- lists:seq(1, X)]
+ ,board = Board
+ ,gen_count = 1 % Consider inital state to be generation 1
+ ,gen_duration = Time
+ ,print_time = 0 % There was no print time yet
+ },
+ life_loop(State).
%% ============================================================================
%% Internal
%% ============================================================================
-life_loop(Board) ->
- ok = do_print_board(Board),
- timer:sleep(?INTERVAL),
- life_loop(next_generation(Board)).
+life_loop(
+ #state{x = X
+ ,y = Y
+ ,n = N
+ ,bar = Bar
+ ,board = Board
+ ,gen_count = GenCount
+ ,gen_duration = Time
+ ,print_time = LastPrintTime
+ }=State) ->
+
+ {PrintTime, ok} = timer:tc(
+ fun() ->
+ do_print_screen(Board, Bar, X, Y, N, GenCount, Time, LastPrintTime)
+ end
+ ),
+
+ {NewTime, NewBoard} = timer:tc(
+ fun() ->
+ next_generation(X, Y, Board)
+ end
+ ),
+
+ NewState = State#state{board = NewBoard
+ ,gen_count = GenCount + 1
+ ,gen_duration = NewTime
+ ,print_time = PrintTime
+ },
+
+ NewTimeMil = NewTime / 1000,
+ NextGenDelay = round(?GEN_INTERVAL - NewTimeMil),
+ timer:sleep(NextGenDelay),
+
+ life_loop(NewState).
+
+
+do_print_screen(Board, Bar, X, Y, N, GenCount, Time, PrintTime) ->
+ ok = do_print_status(Bar, X, Y, N, GenCount, Time, PrintTime),
+ ok = do_print_board(Board).
+
+
+do_print_status(Bar, X, Y, N, GenCount, TimeMic, PrintTimeMic) ->
+ TimeSec = TimeMic / 1000000,
+ PrintTimeSec = PrintTimeMic / 1000000,
+ ok = io:format("~s~n", [Bar]),
+ ok = io:format(
+ "X: ~b Y: ~b CELLS: ~b GENERATION: ~b DURATION: ~f PRINT TIME: ~f~n",
+ [X, Y, N, GenCount, TimeSec, PrintTimeSec]
+ ),
+ ok = io:format("~s~n", [Bar]).
do_print_board(Board) ->
- CharLists = array:to_list(
+ % It seems that just doing a fold should be faster than map + to_list
+ % combo, but, after measuring several times, map + to_list has been
+ % consistently (nearly twice) faster than either foldl or foldr.
+ RowStrings = array:to_list(
array:map(
fun(_, Row) ->
array:to_list(
),
ok = lists:foreach(
- fun(CharList) ->
- ok = io:format("~s~n", [CharList])
+ fun(RowString) ->
+ ok = io:format("~s~n", [RowString])
end,
- CharLists
+ RowStrings
).
state_to_char(1) -> ?CHAR_ALIVE.
-next_generation(Board) ->
- H = array:size(Board),
- W = array:size(array:get(0, Board)),
-
+next_generation(W, H, Board) ->
array:map(
fun(Y, Row) ->
array:map(