WIP Grid (and maybe Board) class
[cellular-automata.git] / life / 006 / life.ts
CommitLineData
e16b8b54
SK
1type State = "Dead" | "Alive";
2
3type States = Array<State>;
4
5type Board = Array<States>;
6
c942e34c
SK
7type GridLocation = {r: number, k: number};
8
9class 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
64let state_of_integer = (i : number) : State => {
65 switch (i)
66 { case 0 : return "Dead"
67 ; case 1 : return "Alive"
68 }
69};
70
71let state_to_string = (state) : string => {
72 switch (state)
73 { case "Dead" : return " "
74 ; case "Alive": return "o"
75 }
76};
77
78let 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 90let 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
109let state_is_alive = (s : State) : boolean => {
110 if (s === "Alive") {return true} else if (s === "Dead") {return false}
111};
112
113let 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
128let 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
142let 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
151let 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
168let console_clear = () : void => {
169 process.stdout.write("\033[2J");
170}
171
172let console_reset = () : void => {
173 process.stdout.write("\033[1;1H");
174}
175
176let 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
183let 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
191main();
This page took 0.030433 seconds and 4 git commands to generate.