Organizing state into a record.
[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
b4e740f3
SK
13-record(state, {x :: non_neg_integer()
14 ,y :: non_neg_integer()
15 ,n :: non_neg_integer()
16 ,bar :: string()
17 ,board=array:new() :: array()
18 ,gen_count :: non_neg_integer()
19 ,gen_duration :: non_neg_integer()
20 }).
21
22
7f968603
SK
23%% ============================================================================
24%% API
25%% ============================================================================
26
27bang(Args) ->
28 [X, Y] = [atom_to_integer(A) || A <- Args],
7a9e70eb 29 {Time, Board} = timer:tc(fun() -> init_board(X, Y) end),
b4e740f3
SK
30 State = #state{x = X
31 ,y = Y
32 ,n = X * Y
33 ,bar = [?CHAR_BAR || _ <- lists:seq(1, X)]
34 ,board = Board
35 ,gen_count = 1
36 ,gen_duration = Time
37 },
38 life_loop(State).
7f968603
SK
39
40
41%% ============================================================================
42%% Internal
43%% ============================================================================
44
b4e740f3
SK
45life_loop(
46 #state{x = X
47 ,y = Y
48 ,n = N
49 ,bar = Bar
50 ,board = Board
51 ,gen_count = GenCount
52 ,gen_duration = Time
53 }=State) ->
54
55 ok = do_print_status(Bar, X, Y, N, GenCount, Time),
7f968603 56 ok = do_print_board(Board),
7a9e70eb 57
b4e740f3
SK
58 {NewTime, NewBoard} = timer:tc(fun() -> next_generation(X, Y, Board) end),
59 NewState = State#state{board = NewBoard
60 ,gen_count = GenCount + 1
61 ,gen_duration = NewTime
62 },
63
7f968603 64 timer:sleep(?INTERVAL),
b4e740f3 65 life_loop(NewState).
7a9e70eb
SK
66
67
b4e740f3 68do_print_status(Bar, X, Y, N, GenCount, TimeMic) ->
7a9e70eb 69 TimeSec = TimeMic / 1000000,
7a9e70eb
SK
70 ok = io:format("~s~n", [Bar]),
71 ok = io:format(
72 "X: ~b Y: ~b CELLS: ~b GENERATION: ~b DURATION: ~f~n",
b4e740f3 73 [X, Y, N, GenCount, TimeSec]
7a9e70eb
SK
74 ),
75 ok = io:format("~s~n", [Bar]).
7f968603
SK
76
77
78do_print_board(Board) ->
79 CharLists = array:to_list(
80 array:map(
81 fun(_, Row) ->
82 array:to_list(
83 array:map(
84 fun(_, State) ->
85 state_to_char(State)
86 end,
87 Row
88 )
89 )
90 end,
91 Board
92 )
93 ),
94
95 ok = lists:foreach(
96 fun(CharList) ->
97 ok = io:format("~s~n", [CharList])
98 end,
99 CharLists
100 ).
101
102
103state_to_char(0) -> ?CHAR_DEAD;
104state_to_char(1) -> ?CHAR_ALIVE.
105
106
68194920 107next_generation(W, H, Board) ->
7f968603
SK
108 array:map(
109 fun(Y, Row) ->
110 array:map(
111 fun(X, State) ->
112 Neighbors = filter_offsides(H, W, neighbors(X, Y)),
113 States = neighbor_states(Board, Neighbors),
114 LiveNeighbors = lists:sum(States),
115 new_state(State, LiveNeighbors)
116 end,
117 Row
118 )
119 end,
120 Board
121 ).
122
123
124new_state(1, LiveNeighbors) when LiveNeighbors < 2 -> 0;
125new_state(1, LiveNeighbors) when LiveNeighbors < 4 -> 1;
126new_state(1, LiveNeighbors) when LiveNeighbors > 3 -> 0;
127new_state(0, LiveNeighbors) when LiveNeighbors =:= 3 -> 1;
128new_state(State, _LiveNeighbors) -> State.
129
130
131neighbor_states(Board, Neighbors) ->
132 [array:get(X, array:get(Y, Board)) || {X, Y} <- Neighbors].
133
134
135filter_offsides(H, W, Coordinates) ->
136 [{X, Y} || {X, Y} <- Coordinates, is_onside(X, Y, H, W)].
137
138
139is_onside(X, Y, H, W) when (X >= 0) and (Y >= 0) and (X < W) and (Y < H) -> true;
140is_onside(_, _, _, _) -> false.
141
142
143neighbors(X, Y) ->
144 [{X + OffX, Y + OffY} || {OffX, OffY} <- offsets()].
145
146
147offsets() ->
148 [offset(D) || D <- directions()].
149
150
151offset('N') -> { 0, -1};
152offset('NE') -> { 1, -1};
153offset('E') -> { 1, 0};
154offset('SE') -> { 1, 1};
155offset('S') -> { 0, 1};
156offset('SW') -> {-1, 1};
157offset('W') -> {-1, 0};
158offset('NW') -> {-1, -1}.
159
160
161directions() ->
162 ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'].
163
164
165init_board(X, Y) ->
166 array:map(fun(_, _) -> init_row(X) end, array:new(Y)).
167
168
169init_row(X) ->
170 array:map(fun(_, _) -> init_cell_state() end, array:new(X)).
171
172
173init_cell_state() ->
174 crypto:rand_uniform(0, 2).
175
176
177atom_to_integer(Atom) ->
178 list_to_integer(atom_to_list(Atom)).
This page took 0.038933 seconds and 4 git commands to generate.