X-Git-Url: https://git.xandkar.net/?a=blobdiff_plain;f=life%2F006%2Flife.ts;h=ac4b969a177ab109bbd9fb756888bd56ff0063b3;hb=6fe41dc7e88d0b191c2e8d522d66500203ed2bb8;hp=0204e5f0d3a72f858e2b79687f5392868a14b760;hpb=e16b8b54be348430aef022ac39696df272100cd7;p=cellular-automata.git diff --git a/life/006/life.ts b/life/006/life.ts index 0204e5f..ac4b969 100644 --- a/life/006/life.ts +++ b/life/006/life.ts @@ -1,133 +1,193 @@ -type State = "Dead" | "Alive"; +"use strict"; -type States = Array; +type GridLocation = {r: number, k: number}; -type Board = Array; - -let state_of_integer = (i : number) : State => { - switch (i) - { case 0 : return "Dead" - ; case 1 : return "Alive" - } +interface GridInterface { + get : (location: GridLocation) => A; + map : (f : (location: GridLocation) => A) => void; + moore_neighbors: (origin : GridLocation) => Array; + print : (toString : (A: A) => string) => void; }; -let state_to_string = (state) : string => { - switch (state) - { case "Dead" : return " " - ; case "Alive": return "o" - } -}; +class Grid implements GridInterface { + private rows : number; + private columns : number; + private cells : Array>; -let board_new = ({rows, columns} : {rows: number, columns: number}) : Board => { - let b = []; - for (let r = 0; r < rows; r++) { - b[r] = []; - for (let k = 0; k < columns; k++) { - let zero_or_one = Math.round(Math.random()); - b[r][k] = state_of_integer(zero_or_one); + constructor( + {rows, columns, init} : + { rows : number + , columns : number + , init : (location: GridLocation) => A + } + ) + { + 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 }; - return b -}; -let board_neighbors = (b : Board, origin : {r: number, k: number}) : States => { - let rows = b.length; - let cols = b[0].length; - let 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} - ]; - let offset_to_location = - (offset) => {return {r: origin.r + offset.r, k: origin.k + offset.k}}; - let locations = offsets.map(offset_to_location); - let is_location_within_bounds = - ({r, k}) => { - return r >= 0 && k >= 0 && r < rows && k < cols + get({r, k}: GridLocation) : A { + return this.cells[r][k] + }; + + moore_neighbors(origin : GridLocation) : Array { + 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) => A) { + 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); + } }; - let locations_within_bounds = locations.filter(is_location_within_bounds); - return locations_within_bounds.map(({r, k}) => b[r][k]); -}; + this.cells = cells; + }; -let state_is_alive = (s : State) : boolean => { - if (s === "Alive") {return true} else if (s === "Dead") {return false} -}; + 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"); + }; -let state_next = (state : State, neighbor_states : Array) : State => { - let neighbors_alive = neighbor_states.filter(state_is_alive).length; - if (state === "Alive" && neighbors_alive < 2) { - return "Dead" - } else if (state === "Alive" && neighbors_alive < 4) { - return "Alive" - } else if (state === "Alive" && neighbors_alive > 3) { - return "Dead" - } else if (state === "Dead" && neighbors_alive === 3) { - return "Alive" - } else { - return state - } -} - -let board_next = (b0 : Board) : Board => { - let b1 = []; - for (let r = 0; r < b0.length; r++) { - b1[r] = []; - for (let k = 0; k < b0[r].length; k++) { - let neighbors = board_neighbors(b0, {r: r, k: k}); - let state_0 = b0[r][k]; - let state_1 = state_next(state_0, neighbors); - b1[r][k] = state_1; - } + 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(); }; - return b1 }; -let board_print_border = (b : Board) : void => { - process.stdout.write("+"); - for (let k = 0; k < b[0].length; k++) { - process.stdout.write("-"); +namespace Terminal { + export const clear = () : void => { + process.stdout.write("\x1B[2J"); + }; + + export const reset = () : void => { + process.stdout.write("\x1B[1;1H"); }; - process.stdout.write("+"); - process.stdout.write("\n"); }; -let board_print = (b : Board) : void => { - board_print_border(b); - let rows = b.length; - let columns = b[0].length; - for (let r = 0; r < rows; r++) { - process.stdout.write("|"); - for (let k = 0; k < columns; k++) { - let state = b[r][k]; - let state_string = state_to_string(state); - process.stdout.write(state_string); +namespace Life { + namespace State { + export type T = "Dead" | "Alive"; + + export const of_integer = (i : number) : T => { + switch (i) + { case 0 : return "Dead" + ; case 1 : return "Alive" + ; default: throw new RangeError("No known State for integer: " + i) + } + }; + + export const to_string = (t : T) : string => { + switch (t) + { case "Dead" : return " " + ; case "Alive": return "o" + ; default : throw new TypeError("Illegal member of Life.State.T: " + t) + } + }; + + export const is_alive = (t : T) : boolean => { + switch (t) + { case "Dead" : return false + ; case "Alive": return true + ; default : throw new TypeError("Illegal member of Life.State.T: " + t) + } + }; + + export const next = (t : T, neighbors_alive : number) : T => { + const is_cell_alive = is_alive(t); + if (is_cell_alive && neighbors_alive < 2) { + return "Dead" + } else if (is_cell_alive && neighbors_alive < 4) { + return "Alive" + } else if (is_cell_alive && neighbors_alive > 3) { + return "Dead" + } else if (!is_cell_alive && neighbors_alive === 3) { + return "Alive" + } else { + return t + } }; - process.stdout.write("|"); - process.stdout.write("\n"); }; - board_print_border(b); -}; -let console_clear = () : void => { - process.stdout.write("\033[2J"); -} + export class Board { + private grid: Grid; + + constructor( + {rows, columns} : + { rows : number + , columns : number + } + ) + { + const init = (_) => State.of_integer(Math.round(Math.random())); + this.grid = new Grid({rows: rows, columns: columns, init: init}); + }; + + next() : void { + this.grid.map( + (location) => { + const neighbor_locations = this.grid.moore_neighbors(location); + const neighbor_states = neighbor_locations.map((l) => this.grid.get(l)); + const state_0 = this.grid.get(location); + const neighbors_alive = neighbor_states.filter(State.is_alive).length; + const state_1 = State.next(state_0, neighbors_alive); + return state_1 + } + ); + }; -let console_reset = () : void => { - process.stdout.write("\033[1;1H"); -} + print() : void { + this.grid.print(State.to_string) + }; + }; +}; -let board_loop = (b0 : Board) : void => { - console_reset(); - board_print(b0); - let b1 = board_next(b0); - setTimeout(() => board_loop(b1), 250) +const board_loop = (b : Life.Board) : void => { + Terminal.reset(); + b.print(); + b.next(); + setTimeout(() => board_loop(b), 250) }; -let main = () : void => { - let height = parseInt(process.argv[2]) - let width = parseInt(process.argv[3]) - let b = board_new({rows: height - 3, columns: width - 2}); - console_clear(); +const main = () : void => { + const height = parseInt(process.argv[2]) + const width = parseInt(process.argv[3]) + const b = new Life.Board({rows: height - 3, columns: width - 2}); + Terminal.clear(); board_loop(b); };