6 -define(CHAR_DEAD, 32). % " "
7 -define(CHAR_ALIVE, 111). % "o"
8 -define(CHAR_BAR, 45). % "-"
10 -define(GEN_INTERVAL, 100).
13 -record(state, {x :: non_neg_integer()
14 ,y :: non_neg_integer()
16 ,bar :: nonempty_string()
18 ,gen_count :: pos_integer()
19 ,gen_duration :: non_neg_integer()
20 ,print_time :: non_neg_integer()
24 %% ============================================================================
26 %% ============================================================================
29 [X, Y] = [atom_to_integer(A) || A <- Args],
30 {Time, Board} = timer:tc(fun() -> init_board(X, Y) end),
34 ,bar = [?CHAR_BAR || _ <- lists:seq(1, X)]
36 ,gen_count = 1 % Consider inital state to be generation 1
38 ,print_time = 0 % There was no print time yet
43 %% ============================================================================
45 %% ============================================================================
55 ,print_time = LastPrintTime
58 {PrintTime, ok} = timer:tc(
60 do_print_screen(Board, Bar, X, Y, N, GenCount, Time, LastPrintTime)
64 {NewTime, NewBoard} = timer:tc(
66 next_generation(X, Y, Board)
70 NewState = State#state{board = NewBoard
71 ,gen_count = GenCount + 1
72 ,gen_duration = NewTime
73 ,print_time = PrintTime
76 NewTimeMil = NewTime / 1000,
77 NextGenDelay = at_least_zero(round(?GEN_INTERVAL - NewTimeMil)),
78 timer:sleep(NextGenDelay),
83 at_least_zero(Integer) when Integer >= 0 -> Integer;
84 at_least_zero(_) -> 0.
87 do_print_screen(Board, Bar, X, Y, N, GenCount, Time, PrintTime) ->
88 ok = do_print_status(Bar, X, Y, N, GenCount, Time, PrintTime),
89 ok = do_print_board(Board).
92 do_print_status(Bar, X, Y, N, GenCount, TimeMic, PrintTimeMic) ->
93 TimeSec = TimeMic / 1000000,
94 PrintTimeSec = PrintTimeMic / 1000000,
95 ok = io:format("~s~n", [Bar]),
97 "X: ~b Y: ~b CELLS: ~b GENERATION: ~b DURATION: ~f PRINT TIME: ~f~n",
98 [X, Y, N, GenCount, TimeSec, PrintTimeSec]
100 ok = io:format("~s~n", [Bar]).
103 do_print_board(Board) ->
104 % It seems that just doing a fold should be faster than map + to_list
105 % combo, but, after measuring several times, map + to_list has been
106 % consistently (nearly twice) faster than either foldl or foldr.
107 RowStrings = array:to_list(
125 ok = io:format("~s~n", [RowString])
131 state_to_char(0) -> ?CHAR_DEAD;
132 state_to_char(1) -> ?CHAR_ALIVE.
135 next_generation(W, H, Board) ->
140 Neighbors = filter_offsides(H, W, neighbors(X, Y)),
141 States = neighbor_states(Board, Neighbors),
142 LiveNeighbors = lists:sum(States),
143 new_state(State, LiveNeighbors)
152 new_state(1, LiveNeighbors) when LiveNeighbors < 2 -> 0;
153 new_state(1, LiveNeighbors) when LiveNeighbors < 4 -> 1;
154 new_state(1, LiveNeighbors) when LiveNeighbors > 3 -> 0;
155 new_state(0, LiveNeighbors) when LiveNeighbors =:= 3 -> 1;
156 new_state(State, _LiveNeighbors) -> State.
159 neighbor_states(Board, Neighbors) ->
160 [array:get(X, array:get(Y, Board)) || {X, Y} <- Neighbors].
163 filter_offsides(H, W, Coordinates) ->
164 [{X, Y} || {X, Y} <- Coordinates, is_onside(X, Y, H, W)].
167 is_onside(X, Y, H, W) when (X >= 0) and (Y >= 0) and (X < W) and (Y < H) -> true;
168 is_onside(_, _, _, _) -> false.
172 [{X + OffX, Y + OffY} || {OffX, OffY} <- offsets()].
176 [offset(D) || D <- directions()].
179 offset('N') -> { 0, -1};
180 offset('NE') -> { 1, -1};
181 offset('E') -> { 1, 0};
182 offset('SE') -> { 1, 1};
183 offset('S') -> { 0, 1};
184 offset('SW') -> {-1, 1};
185 offset('W') -> {-1, 0};
186 offset('NW') -> {-1, -1}.
190 ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'].
194 array:map(fun(_, _) -> init_row(X) end, array:new(Y)).
198 array:map(fun(_, _) -> init_cell_state() end, array:new(X)).
202 crypto:rand_uniform(0, 2).
205 atom_to_integer(Atom) ->
206 list_to_integer(atom_to_list(Atom)).