Skip to content

Instantly share code, notes, and snippets.

@holmberd
Last active November 26, 2021 19:27
Show Gist options
  • Save holmberd/c57a1b6ad744f6a6e795252e24c98f22 to your computer and use it in GitHub Desktop.
Save holmberd/c57a1b6ad744f6a6e795252e24c98f22 to your computer and use it in GitHub Desktop.
Game of life Javascript [2]
/**
* Conway's Game of Life
*
* The world of the Game of Life is an infinite two-dimensional orthogonal grid of square
* "cells", each of which is in one of two possible states, alive or dead.
*
* Rules:
* 1. Any live cell with fewer than two live neighbours dies, as if by underpopulation.
* 2. Any live cell with two or three live neighbours lives on to the next generation.
* 3. Any live cell with more than three live neighbours dies, as if by overpopulation.
* 4. Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
*
* Example usage:
* const boardScheme = '......\n' +
* '***...\n' +
* '......\n' +
* '......\n' +
* '......\n' +
* '......\n';
* const world = World.createFromScheme(boardScheme);
* world.evolve();
* world.toString();
*
* Output:
*
* .*....
* .*....
* .*....
* ......
* ......
* ......
*/
class Cell {
constructor(state) {
this.state = state;
}
}
class Location {
constructor(rowIndex, colIndex) {
this.row = rowIndex;
this.col = colIndex;
}
}
var _board = [];
class World {
constructor(board = [], rows, cols) {
_board = board;
this.generation = 0;
this.rows = board.length;
this.cols = board[0].length;
// B3/S23 (Conway's Life)
this.rule = {
born: 3,
survival: [2, 3],
};
}
getBoard() {
return _board;
}
setBoard(newBoard = []) {
_board = newBoard;
}
getCell(location) {
var board = this.getBoard();
return board[location.row][location.col];
}
setCell(location, cell) {
var board = this.getBoard();
board[location.row][location.col] = cell;
return this;
}
evolve() {
try {
// Evolve board.
var newBoard = [];
// Evolve each row.
for (var rowIndex = 0; rowIndex < this.rows; rowIndex++) {
// Evolve each column in each row.
for (var colIndex = 0; colIndex < this.cols; colIndex++) {
var location = new Location(rowIndex, colIndex);
var cell = this.getCell(location);
if (!newBoard[location.row]) {
newBoard[location.row] = [];
}
// Evolve each cell.
newBoard[location.row][location.col] = this.evolveCell(location, cell);
}
}
this.setBoard(newBoard);
this.generation = this.generation + 1;
} catch(err) {
return false;
}
return this;
}
evolveCell(location, cell) {
var cellNeighbourPositions = [[0, -1], [1, -1], [1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1]];
var aliveNeighbours = 0;
var neighbourLocation = null;
var neighbourCell = null;
// Calculate alive neighbours count.
for (var i = 0; i < cellNeighbourPositions.length; i++) {
neighbourLocation = new Location(
location.row + cellNeighbourPositions[i][0],
location.col + cellNeighbourPositions[i][1]
);
// Only check cell neighbour if it's within the world bounds.
var inBounds = true;
if (neighbourLocation.row < 0 || neighbourLocation.row > (this.rows - 1)) {
inBounds = false;
}
if (neighbourLocation.col < 0 || neighbourLocation.col > (this.cols - 1)) {
inBounds = false;
}
if (inBounds == true) {
neighbourCell = this.getCell(neighbourLocation);
// Increase alive neighbour count if cell is alive.
if (neighbourCell.state) {
aliveNeighbours++;
}
}
}
// Appy rules on cell.
if (cell.state == true) {
for (var j = 0; j < this.rule.survival.length; j++) {
if (this.rule.survival[j] == aliveNeighbours) {
return new Cell(true);
}
}
return new Cell(false);
} else {
if (this.rule.born == aliveNeighbours) {
return new Cell(true);
}
return new Cell(false);
}
}
static createFromScheme(boardScheme) {
return new World(World.createBoard(boardScheme));
}
static createBoard(boardScheme) {
var board = World.getBoardRows(boardScheme);
for (var rowIndex = 0; rowIndex < board.length; rowIndex++) {
board[rowIndex] = World.createRowCells(board[rowIndex]);
}
return board;
}
static getBoardRows(boardScheme) {
return boardScheme.split('\n').slice(0, -1);
}
static createRowCells(row) {
var chars = [];
for (var char of row.split('')) {
chars.push(World.convertCharToCell(char));
}
return chars;
}
static convertCharToCell(char) {
return new Cell(char == '*');
}
// Converts a World instance to a string.
toString() {
var str = '';
for (var row = 0; row < this.rows; row++) {
for (var col = 0; col < this.cols; col++) {
if (this.getBoard()[row][col].state) {
// Note: While JavaScript strings are immutable as in Java, a StringBuilder is generally not required
// for optimizing string concatenation. Though in certain environments `Array.join()` can be more performant
// when building very large strings.
str += '*';
}
else {
str += '.';
}
}
str += '\n';
}
return str;
}
}
function main() {
var boardScheme =
'.*......\n' +
'.*......\n' +
'.*......\n' +
'........\n' +
'....***.\n' +
'...***..\n' +
'........\n' +
'........\n';
var world = World.createFromScheme(boardScheme);
world.evolve();
console.log(world.toString());
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment