Commit | Line | Data |
---|---|---|
e16b8b54 SK |
1 | type State = "Dead" | "Alive"; |
2 | ||
3 | type States = Array<State>; | |
4 | ||
5 | type Board = Array<States>; | |
6 | ||
c942e34c SK |
7 | type GridLocation = {r: number, k: number}; |
8 | ||
9 | class Grid<T> { | |
10 | private rows : number; | |
11 | private columns : number; | |
12 | private cells : Array<Array<T>>; | |
13 | ||
14 | constructor( | |
15 | {rows, columns, init} : | |
16 | { rows : number | |
17 | , columns : number | |
18 | , init : (location: GridLocation) => T | |
19 | } | |
20 | ) | |
21 | { | |
22 | this.rows = rows; | |
23 | this.columns = columns; | |
24 | let cells = []; | |
25 | for (let r = 0; r < rows; r++) { | |
26 | cells[r] = []; | |
27 | for (let k = 0; k < columns; k++) { | |
28 | cells[r][k] = init({r: r, k: k}) | |
29 | }; | |
30 | }; | |
31 | this.cells = cells | |
32 | }; | |
33 | ||
34 | moore_neighbors(origin : GridLocation) : Array<GridLocation> { | |
35 | let offsets = | |
36 | [ {r: -1, k: -1}, {r: -1, k: 0}, {r: -1, k: 1} | |
37 | , {r: 0, k: -1}, {r: 0, k: 1} | |
38 | , {r: 1, k: -1}, {r: 1, k: 0}, {r: 1, k: 1} | |
39 | ]; | |
40 | let offset_to_location = | |
41 | (offset) => {return {r: origin.r + offset.r, k: origin.k + offset.k}}; | |
42 | let locations = offsets.map(offset_to_location); | |
43 | let is_location_within_bounds = | |
44 | ({r, k}) => r >= 0 && k >= 0 && r < this.rows && k < this.columns; | |
45 | return locations.filter(is_location_within_bounds) | |
46 | }; | |
47 | ||
48 | mapi(f : (location: GridLocation) => T) { | |
49 | let cells = []; | |
50 | for (let r = 0; r < this.rows; r++) { | |
51 | cells[r] = []; | |
52 | for (let k = 0; k < this.columns; k++) { | |
53 | let location = {r: r, k: k}; | |
54 | let neighbors = this.moore_neighbors(location); | |
55 | cells[r][k] = f(location); | |
56 | } | |
57 | }; | |
58 | let init = ({r, k}) => cells[r][k]; | |
59 | let grid = new Grid({rows: this.rows, columns: this.columns, init: init}); | |
60 | return grid | |
61 | }; | |
62 | }; | |
63 | ||
e16b8b54 SK |
64 | let state_of_integer = (i : number) : State => { |
65 | switch (i) | |
66 | { case 0 : return "Dead" | |
67 | ; case 1 : return "Alive" | |
68 | } | |
69 | }; | |
70 | ||
71 | let state_to_string = (state) : string => { | |
72 | switch (state) | |
73 | { case "Dead" : return " " | |
74 | ; case "Alive": return "o" | |
75 | } | |
76 | }; | |
77 | ||
78 | let board_new = ({rows, columns} : {rows: number, columns: number}) : Board => { | |
79 | let b = []; | |
80 | for (let r = 0; r < rows; r++) { | |
81 | b[r] = []; | |
82 | for (let k = 0; k < columns; k++) { | |
83 | let zero_or_one = Math.round(Math.random()); | |
84 | b[r][k] = state_of_integer(zero_or_one); | |
85 | }; | |
86 | }; | |
87 | return b | |
88 | }; | |
89 | ||
c942e34c | 90 | let board_neighbors = (b : Board, origin : GridLocation) : States => { |
e16b8b54 SK |
91 | let rows = b.length; |
92 | let cols = b[0].length; | |
93 | let offsets = | |
94 | [ {r: -1, k: -1}, {r: -1, k: 0}, {r: -1, k: 1} | |
95 | , {r: 0, k: -1}, {r: 0, k: 1} | |
96 | , {r: 1, k: -1}, {r: 1, k: 0}, {r: 1, k: 1} | |
97 | ]; | |
98 | let offset_to_location = | |
99 | (offset) => {return {r: origin.r + offset.r, k: origin.k + offset.k}}; | |
100 | let locations = offsets.map(offset_to_location); | |
101 | let is_location_within_bounds = | |
102 | ({r, k}) => { | |
103 | return r >= 0 && k >= 0 && r < rows && k < cols | |
104 | }; | |
105 | let locations_within_bounds = locations.filter(is_location_within_bounds); | |
106 | return locations_within_bounds.map(({r, k}) => b[r][k]); | |
107 | }; | |
108 | ||
109 | let state_is_alive = (s : State) : boolean => { | |
110 | if (s === "Alive") {return true} else if (s === "Dead") {return false} | |
111 | }; | |
112 | ||
113 | let state_next = (state : State, neighbor_states : Array<State>) : State => { | |
114 | let neighbors_alive = neighbor_states.filter(state_is_alive).length; | |
115 | if (state === "Alive" && neighbors_alive < 2) { | |
116 | return "Dead" | |
117 | } else if (state === "Alive" && neighbors_alive < 4) { | |
118 | return "Alive" | |
119 | } else if (state === "Alive" && neighbors_alive > 3) { | |
120 | return "Dead" | |
121 | } else if (state === "Dead" && neighbors_alive === 3) { | |
122 | return "Alive" | |
123 | } else { | |
124 | return state | |
125 | } | |
126 | } | |
127 | ||
128 | let board_next = (b0 : Board) : Board => { | |
129 | let b1 = []; | |
130 | for (let r = 0; r < b0.length; r++) { | |
131 | b1[r] = []; | |
132 | for (let k = 0; k < b0[r].length; k++) { | |
133 | let neighbors = board_neighbors(b0, {r: r, k: k}); | |
134 | let state_0 = b0[r][k]; | |
135 | let state_1 = state_next(state_0, neighbors); | |
136 | b1[r][k] = state_1; | |
137 | } | |
138 | }; | |
139 | return b1 | |
140 | }; | |
141 | ||
142 | let board_print_border = (b : Board) : void => { | |
143 | process.stdout.write("+"); | |
144 | for (let k = 0; k < b[0].length; k++) { | |
145 | process.stdout.write("-"); | |
146 | }; | |
147 | process.stdout.write("+"); | |
148 | process.stdout.write("\n"); | |
149 | }; | |
150 | ||
151 | let board_print = (b : Board) : void => { | |
152 | board_print_border(b); | |
153 | let rows = b.length; | |
154 | let columns = b[0].length; | |
155 | for (let r = 0; r < rows; r++) { | |
156 | process.stdout.write("|"); | |
157 | for (let k = 0; k < columns; k++) { | |
158 | let state = b[r][k]; | |
159 | let state_string = state_to_string(state); | |
160 | process.stdout.write(state_string); | |
161 | }; | |
162 | process.stdout.write("|"); | |
163 | process.stdout.write("\n"); | |
164 | }; | |
165 | board_print_border(b); | |
166 | }; | |
167 | ||
168 | let console_clear = () : void => { | |
169 | process.stdout.write("\033[2J"); | |
170 | } | |
171 | ||
172 | let console_reset = () : void => { | |
173 | process.stdout.write("\033[1;1H"); | |
174 | } | |
175 | ||
176 | let board_loop = (b0 : Board) : void => { | |
177 | console_reset(); | |
178 | board_print(b0); | |
179 | let b1 = board_next(b0); | |
180 | setTimeout(() => board_loop(b1), 250) | |
181 | }; | |
182 | ||
183 | let main = () : void => { | |
184 | let height = parseInt(process.argv[2]) | |
185 | let width = parseInt(process.argv[3]) | |
186 | let b = board_new({rows: height - 3, columns: width - 2}); | |
187 | console_clear(); | |
188 | board_loop(b); | |
189 | }; | |
190 | ||
191 | main(); |