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