Printing a status bar per generation.
[cellular-automata.git] / 003 / src / life.erl
CommitLineData
7f968603
SK
1-module(life).
2
3-export([bang/1]).
4
5
6-define(CHAR_DEAD, 32). % " "
7-define(CHAR_ALIVE, 111). % "o"
7a9e70eb
SK
8-define(CHAR_BAR, 45). % "-"
9
7f968603
SK
10-define(INTERVAL, 100).
11
12
13%% ============================================================================
14%% API
15%% ============================================================================
16
17bang(Args) ->
18 [X, Y] = [atom_to_integer(A) || A <- Args],
7a9e70eb
SK
19 {Time, Board} = timer:tc(fun() -> init_board(X, Y) end),
20 Generation = 1,
21 life_loop(X, Y, Generation, Time, Board).
7f968603
SK
22
23
24%% ============================================================================
25%% Internal
26%% ============================================================================
27
7a9e70eb
SK
28life_loop(X, Y, Generation, Time, Board) ->
29 ok = do_print_status(X, Y, Generation, Time),
7f968603 30 ok = do_print_board(Board),
7a9e70eb
SK
31
32 {NextTime, NextBoard} = timer:tc(fun() -> next_generation(X, Y, Board) end),
33 NextGeneration = Generation + 1,
7f968603 34 timer:sleep(?INTERVAL),
7a9e70eb
SK
35 life_loop(X, Y, NextGeneration, NextTime, NextBoard).
36
37
38do_print_status(X, Y, Generation, TimeMic) ->
39 TimeSec = TimeMic / 1000000,
40 Bar = [?CHAR_BAR || _ <- lists:seq(1, X)],
41 ok = io:format("~s~n", [Bar]),
42 ok = io:format(
43 "X: ~b Y: ~b CELLS: ~b GENERATION: ~b DURATION: ~f~n",
44 [X, Y, X * Y, Generation, TimeSec]
45 ),
46 ok = io:format("~s~n", [Bar]).
7f968603
SK
47
48
49do_print_board(Board) ->
50 CharLists = array:to_list(
51 array:map(
52 fun(_, Row) ->
53 array:to_list(
54 array:map(
55 fun(_, State) ->
56 state_to_char(State)
57 end,
58 Row
59 )
60 )
61 end,
62 Board
63 )
64 ),
65
66 ok = lists:foreach(
67 fun(CharList) ->
68 ok = io:format("~s~n", [CharList])
69 end,
70 CharLists
71 ).
72
73
74state_to_char(0) -> ?CHAR_DEAD;
75state_to_char(1) -> ?CHAR_ALIVE.
76
77
68194920 78next_generation(W, H, Board) ->
7f968603
SK
79 array:map(
80 fun(Y, Row) ->
81 array:map(
82 fun(X, State) ->
83 Neighbors = filter_offsides(H, W, neighbors(X, Y)),
84 States = neighbor_states(Board, Neighbors),
85 LiveNeighbors = lists:sum(States),
86 new_state(State, LiveNeighbors)
87 end,
88 Row
89 )
90 end,
91 Board
92 ).
93
94
95new_state(1, LiveNeighbors) when LiveNeighbors < 2 -> 0;
96new_state(1, LiveNeighbors) when LiveNeighbors < 4 -> 1;
97new_state(1, LiveNeighbors) when LiveNeighbors > 3 -> 0;
98new_state(0, LiveNeighbors) when LiveNeighbors =:= 3 -> 1;
99new_state(State, _LiveNeighbors) -> State.
100
101
102neighbor_states(Board, Neighbors) ->
103 [array:get(X, array:get(Y, Board)) || {X, Y} <- Neighbors].
104
105
106filter_offsides(H, W, Coordinates) ->
107 [{X, Y} || {X, Y} <- Coordinates, is_onside(X, Y, H, W)].
108
109
110is_onside(X, Y, H, W) when (X >= 0) and (Y >= 0) and (X < W) and (Y < H) -> true;
111is_onside(_, _, _, _) -> false.
112
113
114neighbors(X, Y) ->
115 [{X + OffX, Y + OffY} || {OffX, OffY} <- offsets()].
116
117
118offsets() ->
119 [offset(D) || D <- directions()].
120
121
122offset('N') -> { 0, -1};
123offset('NE') -> { 1, -1};
124offset('E') -> { 1, 0};
125offset('SE') -> { 1, 1};
126offset('S') -> { 0, 1};
127offset('SW') -> {-1, 1};
128offset('W') -> {-1, 0};
129offset('NW') -> {-1, -1}.
130
131
132directions() ->
133 ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'].
134
135
136init_board(X, Y) ->
137 array:map(fun(_, _) -> init_row(X) end, array:new(Y)).
138
139
140init_row(X) ->
141 array:map(fun(_, _) -> init_cell_state() end, array:new(X)).
142
143
144init_cell_state() ->
145 crypto:rand_uniform(0, 2).
146
147
148atom_to_integer(Atom) ->
149 list_to_integer(atom_to_list(Atom)).
This page took 0.0432 seconds and 4 git commands to generate.