| 1 | type State = "Dead" | "Alive"; |
| 2 | |
| 3 | type States = Array<State>; |
| 4 | |
| 5 | type Board = Array<States>; |
| 6 | |
| 7 | let state_of_integer = (i : number) : State => { |
| 8 | switch (i) |
| 9 | { case 0 : return "Dead" |
| 10 | ; case 1 : return "Alive" |
| 11 | } |
| 12 | }; |
| 13 | |
| 14 | let state_to_string = (state) : string => { |
| 15 | switch (state) |
| 16 | { case "Dead" : return " " |
| 17 | ; case "Alive": return "o" |
| 18 | } |
| 19 | }; |
| 20 | |
| 21 | let board_new = ({rows, columns} : {rows: number, columns: number}) : Board => { |
| 22 | let b = []; |
| 23 | for (let r = 0; r < rows; r++) { |
| 24 | b[r] = []; |
| 25 | for (let k = 0; k < columns; k++) { |
| 26 | let zero_or_one = Math.round(Math.random()); |
| 27 | b[r][k] = state_of_integer(zero_or_one); |
| 28 | }; |
| 29 | }; |
| 30 | return b |
| 31 | }; |
| 32 | |
| 33 | let board_neighbors = (b : Board, origin : {r: number, k: number}) : States => { |
| 34 | let rows = b.length; |
| 35 | let cols = b[0].length; |
| 36 | let offsets = |
| 37 | [ {r: -1, k: -1}, {r: -1, k: 0}, {r: -1, k: 1} |
| 38 | , {r: 0, k: -1}, {r: 0, k: 1} |
| 39 | , {r: 1, k: -1}, {r: 1, k: 0}, {r: 1, k: 1} |
| 40 | ]; |
| 41 | let offset_to_location = |
| 42 | (offset) => {return {r: origin.r + offset.r, k: origin.k + offset.k}}; |
| 43 | let locations = offsets.map(offset_to_location); |
| 44 | let is_location_within_bounds = |
| 45 | ({r, k}) => { |
| 46 | return r >= 0 && k >= 0 && r < rows && k < cols |
| 47 | }; |
| 48 | let locations_within_bounds = locations.filter(is_location_within_bounds); |
| 49 | return locations_within_bounds.map(({r, k}) => b[r][k]); |
| 50 | }; |
| 51 | |
| 52 | let state_is_alive = (s : State) : boolean => { |
| 53 | if (s === "Alive") {return true} else if (s === "Dead") {return false} |
| 54 | }; |
| 55 | |
| 56 | let state_next = (state : State, neighbor_states : Array<State>) : State => { |
| 57 | let neighbors_alive = neighbor_states.filter(state_is_alive).length; |
| 58 | if (state === "Alive" && neighbors_alive < 2) { |
| 59 | return "Dead" |
| 60 | } else if (state === "Alive" && neighbors_alive < 4) { |
| 61 | return "Alive" |
| 62 | } else if (state === "Alive" && neighbors_alive > 3) { |
| 63 | return "Dead" |
| 64 | } else if (state === "Dead" && neighbors_alive === 3) { |
| 65 | return "Alive" |
| 66 | } else { |
| 67 | return state |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | let board_next = (b0 : Board) : Board => { |
| 72 | let b1 = []; |
| 73 | for (let r = 0; r < b0.length; r++) { |
| 74 | b1[r] = []; |
| 75 | for (let k = 0; k < b0[r].length; k++) { |
| 76 | let neighbors = board_neighbors(b0, {r: r, k: k}); |
| 77 | let state_0 = b0[r][k]; |
| 78 | let state_1 = state_next(state_0, neighbors); |
| 79 | b1[r][k] = state_1; |
| 80 | } |
| 81 | }; |
| 82 | return b1 |
| 83 | }; |
| 84 | |
| 85 | let board_print_border = (b : Board) : void => { |
| 86 | process.stdout.write("+"); |
| 87 | for (let k = 0; k < b[0].length; k++) { |
| 88 | process.stdout.write("-"); |
| 89 | }; |
| 90 | process.stdout.write("+"); |
| 91 | process.stdout.write("\n"); |
| 92 | }; |
| 93 | |
| 94 | let board_print = (b : Board) : void => { |
| 95 | board_print_border(b); |
| 96 | let rows = b.length; |
| 97 | let columns = b[0].length; |
| 98 | for (let r = 0; r < rows; r++) { |
| 99 | process.stdout.write("|"); |
| 100 | for (let k = 0; k < columns; k++) { |
| 101 | let state = b[r][k]; |
| 102 | let state_string = state_to_string(state); |
| 103 | process.stdout.write(state_string); |
| 104 | }; |
| 105 | process.stdout.write("|"); |
| 106 | process.stdout.write("\n"); |
| 107 | }; |
| 108 | board_print_border(b); |
| 109 | }; |
| 110 | |
| 111 | let console_clear = () : void => { |
| 112 | process.stdout.write("\033[2J"); |
| 113 | } |
| 114 | |
| 115 | let console_reset = () : void => { |
| 116 | process.stdout.write("\033[1;1H"); |
| 117 | } |
| 118 | |
| 119 | let board_loop = (b0 : Board) : void => { |
| 120 | console_reset(); |
| 121 | board_print(b0); |
| 122 | let b1 = board_next(b0); |
| 123 | setTimeout(() => board_loop(b1), 250) |
| 124 | }; |
| 125 | |
| 126 | let main = () : void => { |
| 127 | let height = parseInt(process.argv[2]) |
| 128 | let width = parseInt(process.argv[3]) |
| 129 | let b = board_new({rows: height - 3, columns: width - 2}); |
| 130 | console_clear(); |
| 131 | board_loop(b); |
| 132 | }; |
| 133 | |
| 134 | main(); |