Commit | Line | Data |
---|---|---|
7f968603 SK |
1 | -module(life). |
2 | ||
3 | -export([bang/1]). | |
4 | ||
5 | ||
6 | -define(CHAR_DEAD, 32). % " " | |
7 | -define(CHAR_ALIVE, 111). % "o" | |
8 | -define(INTERVAL, 100). | |
9 | ||
10 | ||
11 | %% ============================================================================ | |
12 | %% API | |
13 | %% ============================================================================ | |
14 | ||
15 | bang(Args) -> | |
16 | [X, Y] = [atom_to_integer(A) || A <- Args], | |
17 | Board = init_board(X, Y), | |
68194920 | 18 | life_loop(X, Y, Board). |
7f968603 SK |
19 | |
20 | ||
21 | %% ============================================================================ | |
22 | %% Internal | |
23 | %% ============================================================================ | |
24 | ||
68194920 | 25 | life_loop(X, Y, Board) -> |
7f968603 SK |
26 | ok = do_print_board(Board), |
27 | timer:sleep(?INTERVAL), | |
68194920 | 28 | life_loop(X, Y, next_generation(X, Y, Board)). |
7f968603 SK |
29 | |
30 | ||
31 | do_print_board(Board) -> | |
32 | CharLists = array:to_list( | |
33 | array:map( | |
34 | fun(_, Row) -> | |
35 | array:to_list( | |
36 | array:map( | |
37 | fun(_, State) -> | |
38 | state_to_char(State) | |
39 | end, | |
40 | Row | |
41 | ) | |
42 | ) | |
43 | end, | |
44 | Board | |
45 | ) | |
46 | ), | |
47 | ||
48 | ok = lists:foreach( | |
49 | fun(CharList) -> | |
50 | ok = io:format("~s~n", [CharList]) | |
51 | end, | |
52 | CharLists | |
53 | ). | |
54 | ||
55 | ||
56 | state_to_char(0) -> ?CHAR_DEAD; | |
57 | state_to_char(1) -> ?CHAR_ALIVE. | |
58 | ||
59 | ||
68194920 | 60 | next_generation(W, H, Board) -> |
7f968603 SK |
61 | array:map( |
62 | fun(Y, Row) -> | |
63 | array:map( | |
64 | fun(X, State) -> | |
65 | Neighbors = filter_offsides(H, W, neighbors(X, Y)), | |
66 | States = neighbor_states(Board, Neighbors), | |
67 | LiveNeighbors = lists:sum(States), | |
68 | new_state(State, LiveNeighbors) | |
69 | end, | |
70 | Row | |
71 | ) | |
72 | end, | |
73 | Board | |
74 | ). | |
75 | ||
76 | ||
77 | new_state(1, LiveNeighbors) when LiveNeighbors < 2 -> 0; | |
78 | new_state(1, LiveNeighbors) when LiveNeighbors < 4 -> 1; | |
79 | new_state(1, LiveNeighbors) when LiveNeighbors > 3 -> 0; | |
80 | new_state(0, LiveNeighbors) when LiveNeighbors =:= 3 -> 1; | |
81 | new_state(State, _LiveNeighbors) -> State. | |
82 | ||
83 | ||
84 | neighbor_states(Board, Neighbors) -> | |
85 | [array:get(X, array:get(Y, Board)) || {X, Y} <- Neighbors]. | |
86 | ||
87 | ||
88 | filter_offsides(H, W, Coordinates) -> | |
89 | [{X, Y} || {X, Y} <- Coordinates, is_onside(X, Y, H, W)]. | |
90 | ||
91 | ||
92 | is_onside(X, Y, H, W) when (X >= 0) and (Y >= 0) and (X < W) and (Y < H) -> true; | |
93 | is_onside(_, _, _, _) -> false. | |
94 | ||
95 | ||
96 | neighbors(X, Y) -> | |
97 | [{X + OffX, Y + OffY} || {OffX, OffY} <- offsets()]. | |
98 | ||
99 | ||
100 | offsets() -> | |
101 | [offset(D) || D <- directions()]. | |
102 | ||
103 | ||
104 | offset('N') -> { 0, -1}; | |
105 | offset('NE') -> { 1, -1}; | |
106 | offset('E') -> { 1, 0}; | |
107 | offset('SE') -> { 1, 1}; | |
108 | offset('S') -> { 0, 1}; | |
109 | offset('SW') -> {-1, 1}; | |
110 | offset('W') -> {-1, 0}; | |
111 | offset('NW') -> {-1, -1}. | |
112 | ||
113 | ||
114 | directions() -> | |
115 | ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']. | |
116 | ||
117 | ||
118 | init_board(X, Y) -> | |
119 | array:map(fun(_, _) -> init_row(X) end, array:new(Y)). | |
120 | ||
121 | ||
122 | init_row(X) -> | |
123 | array:map(fun(_, _) -> init_cell_state() end, array:new(X)). | |
124 | ||
125 | ||
126 | init_cell_state() -> | |
127 | crypto:rand_uniform(0, 2). | |
128 | ||
129 | ||
130 | atom_to_integer(Atom) -> | |
131 | list_to_integer(atom_to_list(Atom)). |