UNPKG

dat-life

Version:
197 lines (196 loc) 6.27 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Life = void 0; class Life { constructor(width = 100, height = 100) { this.width = width; this.height = height; this.colors = 1; this.decay = false; this.board = this.createBoard(); this.generation = 1; this.population = 0; this.reset(); } setWidth(width) { this.width = width; this.board = this.createBoard(); this.reset(); } setHeight(height) { this.height = height; this.board = this.createBoard(); this.reset(); } setColors(colors) { if (colors != 1 && colors != 2 && colors != 4) { throw new Error("invalid number of colors"); } this.colors = colors; this.reset(); } getWidth() { return this.width; } getHeight() { return this.height; } getColors() { return this.colors; } createBoard() { return new Array(this.width * this.height); } visit(visitor) { for (let x = 0; x < this.width; x++) { for (let y = 0; y < this.height; y++) { visitor(x, y); } } } index(x, y) { return x + (this.width * y); } reset() { this.visit((x, y) => { this.board[this.index(x, y)] = 0; }); this.generation = 1; this.population = 0; } clear() { this.reset(); return this.getBoard(); } set(x, y, state) { if (x < 0 || x > this.width || y < 0 || y > this.height) { throw new Error("array index is out of bounds"); } if (state < 0 || state > this.colors) { throw new Error("invalid state"); } if (state) { this.population++; } this.board[this.index(x, y)] = state; } get(x, y) { if (x < 0 || x > this.width || y < 0 || y > this.height) { throw new Error("array index is out of bounds"); } return this.board[this.index(x, y)]; } setBoard(board) { if (board.length != this.width * this.height) { throw new Error('array values do not match dimensions'); } this.reset(); this.board = board.slice(); this.board.forEach((state) => { if (state > this.colors) { throw new Error('state value out of range'); } else if (state) { this.population++; } }); } setDecay(decay) { this.decay = decay; } getBoard() { return this.board.slice(); } getPopulation() { return this.population; } getGeneration() { return this.generation; } getDecay() { return this.decay; } randomize() { this.population = 0; this.visit((x, y) => { // 1 in 4 whether a cell is alive; if it is, it has a random color const alive = Math.floor(Math.random() * 4) === 0; const color = Math.floor(Math.random() * this.colors) + 1; const state = alive ? color : 0; if (state) { this.population++; } this.board[this.index(x, y)] = state; }); return this.getBoard(); } // returns a one-dimensional array selecting the neighbors around a cell neighbors(x, y) { const neighbors = new Array(8); neighbors[0] = (x > 0 && y > 0) ? this.get(x - 1, y - 1) : 0; neighbors[1] = (x > 0) ? this.get(x - 1, y) : 0; neighbors[2] = (x > 0 && y < this.height - 1) ? this.get(x - 1, y + 1) : 0; neighbors[3] = (y > 0) ? this.get(x, y - 1) : 0; neighbors[4] = (y < this.height - 1) ? this.get(x, y + 1) : 0; neighbors[5] = (x < this.width - 1 && y > 0) ? this.get(x + 1, y - 1) : 0; neighbors[6] = (x < this.width - 1) ? this.get(x + 1, y) : 0; neighbors[7] = (x < this.width - 1 && y < this.height - 1) ? this.get(x + 1, y + 1) : 0; return neighbors; } getColor(neighbors) { switch (this.colors) { case 1: return 1; // two colors: newborns are the majority color of the parents case 2: { const count = [0, 0]; for (let color of neighbors) { if (color && ++count[color - 1] == 2) { return color; } } break; } // four colors: newborns are the majority color of the parents if // there is a majority; otherwise its the fourth color (the color // that no parent has) case 4: { const count = [0, 0, 0, 0]; for (let color of neighbors) { if (color && ++count[color - 1] == 2) { return color; } } return count.findIndex((x) => { return x === 0; }) + 1; } } throw new Error('unsolved color'); } diminish(current) { if (this.decay) { return current - 1; } return 0; } next() { const next = this.createBoard(); this.population = 0; this.visit((x, y) => { const idx = this.index(x, y); const alive = this.board[idx] !== 0; const neighbors = this.neighbors(x, y); const count = neighbors.filter((x) => { return x !== 0; }).length; if (alive && count < 2) { next[idx] = this.diminish(this.board[idx]); // exposure } else if (alive && count > 3) { next[idx] = this.diminish(this.board[idx]); // overcrowding } else if (!alive && count === 3) { next[idx] = this.getColor(neighbors); // birth } else { next[idx] = this.board[idx]; } if (next[idx]) { this.population++; } }); this.board = next; this.generation++; return this.getBoard(); } } exports.Life = Life;