UNPKG

entwickeln

Version:

A JavaScript library for Conway's Game of Life

216 lines (170 loc) 6.92 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); function Entwickeln() { function countNeighbours(x0, y0, alive, game) { var w = game[0].length; var h = game.length; var xi = x0 - 1 < 0 ? w - 1 : x0 - 1; var xf = x0 + 1 === w ? 0 : x0 + 1; var yi = y0 - 1 < 0 ? h - 1 : y0 - 1; var yf = y0 + 1 === h ? 0 : y0 + 1; var numberOfNeighbours = -(game[y0][x0].state !== 'dead'); for (var i = 0; i < 3; i++) { var y = [yi, y0, yf][i]; for (var j = 0; j < 3; j++) { var x = [xi, x0, xf][j]; var cell = game[y][x]; numberOfNeighbours += cell.state !== 'dead'; } } return numberOfNeighbours; } // Public API functions function initialise(width, height) { var alpha = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0.25; // Handling unexpected inputs if (Number.isNaN(Number(width)) || Number.isNaN(Number(height))) { console.error(['entwickeln.initialise(width, height[, alpha]):\n\n', 'The width and/or height argument entered is not a number.', ' The defualt value of 20 has been used.'].join('')); } if (Number.isNaN(Number(alpha))) { console.error(['entwickeln.initialise(width, height[, alpha]):\n\n', 'The optional argument alpha entered is not a number.', ' The defualt value of 0.25 has been used.'].join('')); } // Clean up arguments width = Math.floor(Math.abs(width)) || 20; height = Math.floor(Math.abs(height)) || 20; alpha = Math.abs(alpha) || 0.25; // Update publicly available values and create the game grid this.width = width; this.height = height; this.alpha = alpha; this.generation = 0; this.game = Array.from(Array(height)).map(function (row) { return Array.from(Array(width)).map(function (cell) { return { state: Math.random() < alpha ? 'new' : 'dead', gen: 0 }; }); }); return this.game; } function evolve() { var generations = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1; var target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.game; if (Number.isNaN(Number(generations))) { console.error(['entwickeln.evolve([generations]):\n\n', 'The optional argument generations entered is not a number.', ' The defualt value of 1 has been used.'].join('')); } else if (Number(generations) < 1) { console.error(['entwickeln.evolve([generations]):\n\n', 'The optional argument generations entered is less than 1.', ' The defualt value of 1 has been used.'].join('')); } generations = Math.floor(Math.abs(generations) || 1) || 1; var notCustomInput = target === this.game; var width = target[0].length; var height = target.length; for (var generation = 0; generation < generations; generation++) { this.generation += 1; var nextGeneration = []; for (var y = 0; y < height; y++) { if (!nextGeneration[y]) { nextGeneration[y] = []; } for (var x = 0; x < width; x++) { if (!nextGeneration[y][x]) { nextGeneration[y][x] = []; } var cell = target[y][x]; var state = cell.state, gen = cell.gen; var nextCell = { state: state, gen: gen }; var alive = state !== 'dead'; var numberOfNeighbours = countNeighbours(x, y, alive, target); if (alive) { // "1. Any live cell with fewer than two live neighbours dies, as // if caused by underpopulation." if (numberOfNeighbours < 2) { nextCell.state = 'dead'; nextCell.gen = this.generation; } // "2. Any live cell with two or three live neighbours lives on to // the next generation." else if (numberOfNeighbours < 4) { nextCell.state = 'alive'; nextCell.gen = this.generation; } // "3. Any live cell with more than three live neighbours dies, as // if by overpopulation." else { nextCell.state = 'dead'; nextCell.gen = this.generation; } } else { // "4. Any dead cell with exactly three live neighbours becomes a // live cell, as if by reproduction." if (numberOfNeighbours === 3) { nextCell.state = 'new'; nextCell.gen = this.generation; } } nextGeneration[y][x] = nextCell; } } if (notCustomInput) { this.game = nextGeneration; } target = nextGeneration; } return target; } function restart() { return initialise(this.width, this.height, this.alpha); } function edit(x, y, state) { if (Number.isNaN(Number(x)) || Number.isNaN(Number(y))) { return console.error(['entwickeln.edit(x, y, state):\n\n', 'The argument x and y must be a number but the arguments received', ' are ' + x + ' and ' + y + ', respectively. No cell was edited.'].join('')); } var states = ['dead', 'alive', 'new']; if (states.indexOf(state) === -1) { return console.error(['entwickeln.edit(x, y, state):\n\n', 'The state provided, ' + state + ' is not a valid option. The valid options', ' are "dead", "new" or "alive". The cell ' + x + ', ' + y + ' was not edited.'].join('')); } var width = this.width, height = this.height; // Allow the use of x and y that are outside the range specified // by the width and height of the game (periodic boundary conditions) x = (x < 0 ? width - x : x) % width; y = (y < 0 ? height - y : y) % height; var cell = this.game[y][x]; cell.state = state; cell.gen = this.generation; return this.game; } function toggle(x, y) { if (Number.isNaN(Number(x)) || Number.isNaN(Number(y))) { return console.error(['entwickeln.toggle(x, y):\n\n', 'The argument x and y must be a number but the arguments received', ' are ' + x + ' and ' + y + ', respectively. No cell was edited.'].join('')); } var width = this.width, height = this.height; // Allow the use of x and y that are outside the range specified // by the width and height of the game (periodic boundary conditions) x = (x < 0 ? width - x : x) % width; y = (y < 0 ? height - y : y) % height; var cell = this.game[y][x]; cell.state = cell.state === 'dead' ? 'new' : 'dead'; cell.gen = this.generation; return this.game; } var publicAPI = { width: 0, height: 0, alpha: 0, game: null, generation: 0, init: initialise, evolve: evolve, restart: restart, edit: edit, toggle: toggle }; return publicAPI; } exports.default = Entwickeln();