3 type GridLocation = {r: number, k: number};
5 interface GridInterface<A> {
6 get : (location: GridLocation) => A;
7 map : (f : (location: GridLocation) => A) => void;
8 moore_neighbors: (origin : GridLocation) => Array<GridLocation>;
9 print : (toString : (A: A) => string) => void;
12 class Grid<A> implements GridInterface<A> {
13 private rows : number;
14 private columns : number;
15 private cells : Array<Array<A>>;
18 {rows, columns, init} :
21 , init : (location: GridLocation) => A
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) : A {
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) => A) {
57 this.cells.map((row, r) => row.map((_, k) => f({r: r, k: k})));
60 private print_border(): void {
61 process.stdout.write("+");
62 for (let k = 0; k < this.columns; k++) {
63 process.stdout.write("-");
65 process.stdout.write("+");
66 process.stdout.write("\n");
69 print(to_string) : void {
71 for (let r = 0; r < this.rows; r++) {
72 process.stdout.write("|");
73 for (let k = 0; k < this.columns; k++) {
74 const element = this.cells[r][k];
75 const element_string = to_string(element);
76 process.stdout.write(element_string);
78 process.stdout.write("|");
79 process.stdout.write("\n");
86 export const clear = () : void => {
87 process.stdout.write("\x1B[2J");
90 export const reset = () : void => {
91 process.stdout.write("\x1B[1;1H");
97 export type T = "Dead" | "Alive";
99 export const of_integer = (i : number) : T => {
101 { case 0 : return "Dead"
102 ; case 1 : return "Alive"
103 ; default: throw new RangeError("No known State for integer: " + i)
107 export const to_string = (t : T) : string => {
109 { case "Dead" : return " "
110 ; case "Alive": return "o"
111 ; default : throw new TypeError("Illegal member of Life.State.T: " + t)
115 export const is_alive = (t : T) : boolean => {
117 { case "Dead" : return false
118 ; case "Alive": return true
119 ; default : throw new TypeError("Illegal member of Life.State.T: " + t)
123 export const next = (t : T, neighbors_alive : number) : T => {
124 const is_cell_alive = is_alive(t);
125 if (is_cell_alive && neighbors_alive < 2) {
127 } else if (is_cell_alive && neighbors_alive < 4) {
129 } else if (is_cell_alive && neighbors_alive > 3) {
131 } else if (!is_cell_alive && neighbors_alive === 3) {
140 private grid: Grid<State.T>;
149 const init = (_) => State.of_integer(Math.round(Math.random()));
150 this.grid = new Grid({rows: rows, columns: columns, init: init});
156 const neighbor_locations = this.grid.moore_neighbors(location);
157 const neighbor_states = neighbor_locations.map((l) => this.grid.get(l));
158 const state_0 = this.grid.get(location);
159 const neighbors_alive = neighbor_states.filter(State.is_alive).length;
160 const state_1 = State.next(state_0, neighbors_alive);
167 this.grid.print(State.to_string)
172 const board_loop = (b : Life.Board) : void => {
176 setTimeout(() => board_loop(b), 250)
179 const main = () : void => {
180 const height = parseInt(process.argv[2])
181 const width = parseInt(process.argv[3])
182 const b = new Life.Board({rows: height - 3, columns: width - 2});