X-Git-Url: https://git.xandkar.net/?p=cellular-automata.git;a=blobdiff_plain;f=life%2F006%2Flife.ts;fp=life%2F006%2Flife.ts;h=58f3f8b5d7c965f762aa1fc63db13af22d74b39c;hp=987f21e352b6232ea38d4a4f30e13a373e38583e;hb=ecf4b00f3ae28e13f666803eac456b62c4a40d66;hpb=b1310fe0f74e4766a3a177b2ac4b0f241c7476d2 diff --git a/life/006/life.ts b/life/006/life.ts index 987f21e..58f3f8b 100644 --- a/life/006/life.ts +++ b/life/006/life.ts @@ -2,23 +2,23 @@ type GridLocation = {r: number, k: number}; -interface GridInterface { - get : (location: GridLocation) => T; - map : (f : (location: GridLocation) => T) => Grid; +interface GridInterface { + get : (location: GridLocation) => A; + map : (f : (location: GridLocation) => A) => Grid; moore_neighbors: (origin : GridLocation) => Array; - print : (toString : (T: T) => string) => void; + print : (toString : (A: A) => string) => void; }; -class Grid implements GridInterface { +class Grid implements GridInterface { private rows : number; private columns : number; - private cells : Array>; + private cells : Array>; constructor( {rows, columns, init} : { rows : number , columns : number - , init : (location: GridLocation) => T + , init : (location: GridLocation) => A } ) { @@ -34,7 +34,7 @@ class Grid implements GridInterface { this.cells = cells }; - get({r, k}: GridLocation) : T { + get({r, k}: GridLocation) : A { return this.cells[r][k] }; @@ -52,7 +52,7 @@ class Grid implements GridInterface { return locations.filter(is_location_within_bounds) }; - map(f : (location: GridLocation) => T) { + map(f : (location: GridLocation) => A) { const cells = []; for (let r = 0; r < this.rows; r++) { cells[r] = []; @@ -91,91 +91,106 @@ class Grid implements GridInterface { }; }; -type State = "Dead" | "Alive"; +namespace Terminal { + export const clear = () : void => { + process.stdout.write("\x1B[2J"); + }; -type States = Array; + export const reset = () : void => { + process.stdout.write("\x1B[1;1H"); + }; +}; -type Board = Grid; +namespace Life { + namespace State { + export type T = "Dead" | "Alive"; -const state_of_integer = (i : number) : State => { - switch (i) - { case 0 : return "Dead" - ; case 1 : return "Alive" - ; default: throw("No known State for integer: " + i) - } -}; + export const of_integer = (i : number) : T => { + switch (i) + { case 0 : return "Dead" + ; case 1 : return "Alive" + ; default: throw("No known State for integer: " + i) + } + }; -const state_to_string = (state : State) : string => { - switch (state) - { case "Dead" : return " " - ; case "Alive": return "o" - ; default : throw("Illegal member of State type: " + state) - } -}; + export const to_string = (t : T) : string => { + switch (t) + { case "Dead" : return " " + ; case "Alive": return "o" + ; default : throw("Illegal member of Life.State.T: " + t) + } + }; -const board_new = ({rows, columns} : {rows: number, columns: number}) : Board => { - const init = (_) => state_of_integer(Math.round(Math.random())); - return new Grid({rows: rows, columns: columns, init: init}); -}; + export const is_alive = (t : T) : boolean => { + switch (t) + { case "Dead" : return false + ; case "Alive": return true + ; default : throw("Illegal member of Life.State.T: " + t) + } + }; -const state_is_alive = (s : State) : boolean => { - if (s === "Alive") { - return true - } else if (s === "Dead") { - return false - } else { - throw("Illegal member of State type: " + s) - } -}; + 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 + } + }; + }; -const state_next = (state : State, neighbor_states : Array) : State => { - const 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 - } -} - -const board_next = (b0 : Board) : Board => { - const b1 = b0.map( - (location) => { - const neighbor_locations = b0.moore_neighbors(location); - const neighbor_states = neighbor_locations.map((l) => b0.get(l)); - const state_0 = b0.get(location); - const state_1 = state_next(state_0, neighbor_states); - return state_1 - } - ); - return b1 -}; + 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}); + }; -const console_clear = () : void => { - process.stdout.write("\x1B[2J"); -} + next() : void { + const grid = 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 + } + ); + this.grid = grid + }; -const console_reset = () : void => { - process.stdout.write("\x1B[1;1H"); -} + print() : void { + this.grid.print(State.to_string) + }; + }; +}; -const board_loop = (b0 : Board) : void => { - console_reset(); - b0.print(state_to_string); - const 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) }; const main = () : void => { const height = parseInt(process.argv[2]) const width = parseInt(process.argv[3]) - const b = board_new({rows: height - 3, columns: width - 2}); - console_clear(); + const b = new Life.Board({rows: height - 3, columns: width - 2}); + Terminal.clear(); board_loop(b); };