+"use strict";
+
+type GridLocation = {r: number, k: number};
+
+interface GridInterface<T> {
+ get : (location: GridLocation) => T;
+ map : (f : (location: GridLocation) => T) => Grid<T>;
+ moore_neighbors: (origin : GridLocation) => Array<GridLocation>;
+ print : (toString : (T: T) => string) => void;
+};
+
+class Grid<T> implements GridInterface<T> {
+ private rows : number;
+ private columns : number;
+ private cells : Array<Array<T>>;
+
+ constructor(
+ {rows, columns, init} :
+ { rows : number
+ , columns : number
+ , init : (location: GridLocation) => T
+ }
+ )
+ {
+ this.rows = rows;
+ this.columns = columns;
+ const cells = [];
+ for (let r = 0; r < rows; r++) {
+ cells[r] = [];
+ for (let k = 0; k < columns; k++) {
+ cells[r][k] = init({r: r, k: k})
+ };
+ };
+ this.cells = cells
+ };
+
+ get({r, k}: GridLocation) : T {
+ return this.cells[r][k]
+ };
+
+ moore_neighbors(origin : GridLocation) : Array<GridLocation> {
+ const offsets =
+ [ {r: -1, k: -1}, {r: -1, k: 0}, {r: -1, k: 1}
+ , {r: 0, k: -1}, {r: 0, k: 1}
+ , {r: 1, k: -1}, {r: 1, k: 0}, {r: 1, k: 1}
+ ];
+ const offset_to_location =
+ (offset) => {return {r: origin.r + offset.r, k: origin.k + offset.k}};
+ const locations = offsets.map(offset_to_location);
+ const is_location_within_bounds =
+ ({r, k}) => r >= 0 && k >= 0 && r < this.rows && k < this.columns;
+ return locations.filter(is_location_within_bounds)
+ };
+
+ map(f : (location: GridLocation) => T) {
+ const cells = [];
+ for (let r = 0; r < this.rows; r++) {
+ cells[r] = [];
+ for (let k = 0; k < this.columns; k++) {
+ const location = {r: r, k: k};
+ cells[r][k] = f(location);
+ }
+ };
+ const init = ({r, k}) => cells[r][k];
+ const grid = new Grid({rows: this.rows, columns: this.columns, init: init});
+ return grid
+ };
+
+ private print_border(): void {
+ process.stdout.write("+");
+ for (let k = 0; k < this.columns; k++) {
+ process.stdout.write("-");
+ };
+ process.stdout.write("+");
+ process.stdout.write("\n");
+ };
+
+ print(to_string) : void {
+ this.print_border();
+ for (let r = 0; r < this.rows; r++) {
+ process.stdout.write("|");
+ for (let k = 0; k < this.columns; k++) {
+ const element = this.cells[r][k];
+ const element_string = to_string(element);
+ process.stdout.write(element_string);
+ };
+ process.stdout.write("|");
+ process.stdout.write("\n");
+ };
+ this.print_border();
+ };
+};
+