X-Git-Url: https://git.xandkar.net/?a=blobdiff_plain;f=003%2Fsrc%2Flife.erl;fp=003%2Fsrc%2Flife.erl;h=0000000000000000000000000000000000000000;hb=8d06c463b83b79e046250fbe60e5429f182e3a7a;hp=605fa95ce873533b2b4d2da920001db536cb531c;hpb=4eb3be32ce405e69f39573b5e38171764cd0d789;p=cellular-automata.git diff --git a/003/src/life.erl b/003/src/life.erl deleted file mode 100644 index 605fa95..0000000 --- a/003/src/life.erl +++ /dev/null @@ -1,206 +0,0 @@ --module(life). - --export([bang/1]). - - --define(CHAR_DEAD, 32). % " " --define(CHAR_ALIVE, 111). % "o" --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() - }). - - -%% ============================================================================ -%% API -%% ============================================================================ - -bang(Args) -> - [X, Y] = [atom_to_integer(A) || A <- Args], - {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( - #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 = at_least_zero(round(?GEN_INTERVAL - NewTimeMil)), - timer:sleep(NextGenDelay), - - life_loop(NewState). - - -at_least_zero(Integer) when Integer >= 0 -> Integer; -at_least_zero(_) -> 0. - - -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) -> - % 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( - array:map( - fun(_, State) -> - state_to_char(State) - end, - Row - ) - ) - end, - Board - ) - ), - - ok = lists:foreach( - fun(RowString) -> - ok = io:format("~s~n", [RowString]) - end, - RowStrings - ). - - -state_to_char(0) -> ?CHAR_DEAD; -state_to_char(1) -> ?CHAR_ALIVE. - - -next_generation(W, H, Board) -> - array:map( - fun(Y, Row) -> - array:map( - fun(X, State) -> - Neighbors = filter_offsides(H, W, neighbors(X, Y)), - States = neighbor_states(Board, Neighbors), - LiveNeighbors = lists:sum(States), - new_state(State, LiveNeighbors) - end, - Row - ) - end, - Board - ). - - -new_state(1, LiveNeighbors) when LiveNeighbors < 2 -> 0; -new_state(1, LiveNeighbors) when LiveNeighbors < 4 -> 1; -new_state(1, LiveNeighbors) when LiveNeighbors > 3 -> 0; -new_state(0, LiveNeighbors) when LiveNeighbors =:= 3 -> 1; -new_state(State, _LiveNeighbors) -> State. - - -neighbor_states(Board, Neighbors) -> - [array:get(X, array:get(Y, Board)) || {X, Y} <- Neighbors]. - - -filter_offsides(H, W, Coordinates) -> - [{X, Y} || {X, Y} <- Coordinates, is_onside(X, Y, H, W)]. - - -is_onside(X, Y, H, W) when (X >= 0) and (Y >= 0) and (X < W) and (Y < H) -> true; -is_onside(_, _, _, _) -> false. - - -neighbors(X, Y) -> - [{X + OffX, Y + OffY} || {OffX, OffY} <- offsets()]. - - -offsets() -> - [offset(D) || D <- directions()]. - - -offset('N') -> { 0, -1}; -offset('NE') -> { 1, -1}; -offset('E') -> { 1, 0}; -offset('SE') -> { 1, 1}; -offset('S') -> { 0, 1}; -offset('SW') -> {-1, 1}; -offset('W') -> {-1, 0}; -offset('NW') -> {-1, -1}. - - -directions() -> - ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']. - - -init_board(X, Y) -> - array:map(fun(_, _) -> init_row(X) end, array:new(Y)). - - -init_row(X) -> - array:map(fun(_, _) -> init_cell_state() end, array:new(X)). - - -init_cell_state() -> - crypto:rand_uniform(0, 2). - - -atom_to_integer(Atom) -> - list_to_integer(atom_to_list(Atom)).