3 type GridLocation = {r: number, k: number};
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;
12 class Grid<T> implements GridInterface<T> {
13 private rows : number;
14 private columns : number;
15 private cells : Array<Array<T>>;
18 {rows, columns, init} :
21 , init : (location: GridLocation) => T
26 this.columns = columns;
28 for (let r = 0; r < rows; r++) {
30 for (let k = 0; k < columns; k++) {
31 cells[r][k] = init({r: r, k: k})
37 get({r, k}: GridLocation) : T {
38 return this.cells[r][k]
41 moore_neighbors(origin : GridLocation) : Array<GridLocation> {
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}
47 const offset_to_location =
48 (offset) => {return {r: origin.r + offset.r, k: origin.k + offset.k}};
49 const locations = offsets.map(offset_to_location);
50 const is_location_within_bounds =
51 ({r, k}) => r >= 0 && k >= 0 && r < this.rows && k < this.columns;
52 return locations.filter(is_location_within_bounds)
55 map(f : (location: GridLocation) => T) {
57 for (let r = 0; r < this.rows; r++) {
59 for (let k = 0; k < this.columns; k++) {
60 const location = {r: r, k: k};
61 cells[r][k] = f(location);
64 const init = ({r, k}) => cells[r][k];
65 const grid = new Grid({rows: this.rows, columns: this.columns, init: init});
69 private print_border(): void {
70 process.stdout.write("+");
71 for (let k = 0; k < this.columns; k++) {
72 process.stdout.write("-");
74 process.stdout.write("+");
75 process.stdout.write("\n");
78 print(to_string) : void {
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);
87 process.stdout.write("|");
88 process.stdout.write("\n");
94 type State = "Dead" | "Alive";
96 type States = Array<State>;
98 type Board = Grid<State>;
100 const state_of_integer = (i : number) : State => {
102 { case 0 : return "Dead"
103 ; case 1 : return "Alive"
104 ; default: throw("No known State for integer: " + i)
108 const state_to_string = (state : State) : string => {
110 { case "Dead" : return " "
111 ; case "Alive": return "o"
112 ; default : throw("Illegal member of State type: " + state)
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});
121 const state_is_alive = (s : State) : boolean => {
124 } else if (s === "Dead") {
127 throw("Illegal member of State type: " + s)
131 const state_next = (state : State, neighbor_states : Array<State>) : State => {
132 const neighbors_alive = neighbor_states.filter(state_is_alive).length;
133 if (state === "Alive" && neighbors_alive < 2) {
135 } else if (state === "Alive" && neighbors_alive < 4) {
137 } else if (state === "Alive" && neighbors_alive > 3) {
139 } else if (state === "Dead" && neighbors_alive === 3) {
146 const board_next = (b0 : Board) : Board => {
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);
159 const console_clear = () : void => {
160 process.stdout.write("\x1B[2J");
163 const console_reset = () : void => {
164 process.stdout.write("\x1B[1;1H");
167 const board_loop = (b0 : Board) : void => {
169 b0.print(state_to_string);
170 const b1 = board_next(b0);
171 setTimeout(() => board_loop(b1), 250)
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});