Skip to content

Instantly share code, notes, and snippets.

@micrub
Created November 2, 2021 22:10
Show Gist options
  • Save micrub/e9a6e7f80d4935710267eb47f3cc6002 to your computer and use it in GitHub Desktop.
Save micrub/e9a6e7f80d4935710267eb47f3cc6002 to your computer and use it in GitHub Desktop.
Conway's Game of Life - JavaScript
<head>
<meta charset = "UTF-8" />
<title>The Game of Life</title>
<link rel="stylesheet" href="life.css">
</head>
<body>
<div id="hud" />
<div id="gridContainer" />
<div class="controls">
<button id="start"><span>Start</span></button>
<button id="clear"><span>Clear</span></button>
<button id="random"><span>Random</span></button>
</div>
<script src="code.js"></script>
</body>
const ROWS = 25;
const COLS = 50;
let IS_PLAYING = false;
let GRID = new Array(ROWS);
let NEXT_GRID = new Array(ROWS);
const REPRODUCTION_TIME = 100;
let epochs = 0;
function initializeGrids() {
for (var i = 0; i < ROWS; i++) {
GRID[i] = new Array(COLS);
NEXT_GRID[i] = new Array(COLS);
}
}
function resetGrids() {
for (var i = 0; i < ROWS; i++) {
for (var j = 0; j < COLS; j++) {
GRID[i][j] = 0;
NEXT_GRID[i][j] = 0;
}
}
}
function copyAndResetGrid() {
for (var i = 0; i < ROWS; i++) {
for (var j = 0; j < COLS; j++) {
GRID[i][j] = NEXT_GRID[i][j];
NEXT_GRID[i][j] = 0;
}
}
}
// Initialize
function initialize() {
createHud();
createTable();
initializeGrids();
resetGrids();
setupControlButtons();
}
function createHud() {
var hudCont = document.getElementById('hud');
if (!hudCont) {
// Throw error
console.error("Problem: No div for the hud info!");
}
const label = document.createElement("label");
const span = document.createElement("span");
label.textContent = 'Epochs passed: ';
span.textContent = epochs;
hudCont.appendChild(label);
hudCont.appendChild(span);
}
// Lay out the board
function createTable() {
var gridContainer = document.getElementById('gridContainer');
if (!gridContainer) {
// Throw error
console.error("Problem: No div for the drid table!");
}
var table = document.createElement("table");
for (var i = 0; i < ROWS; i++) {
var tr = document.createElement("tr");
for (var j = 0; j < COLS; j++) {//
var cell = document.createElement("td");
cell.setAttribute("id", i + "_" + j);
cell.setAttribute("class", "dead");
cell.onclick = cellClickHandler;
tr.appendChild(cell);
}
table.appendChild(tr);
}
gridContainer.appendChild(table);
}
function cellClickHandler() {
var rowcol = this.id.split("_");
var row = rowcol[0];
var col = rowcol[1];
var classes = this.getAttribute("class");
if (classes.indexOf("live") > -1) {
this.setAttribute("class", "dead");
GRID[row][col] = 0;
} else {
this.setAttribute("class", "live");
GRID[row][col] = 1;
}
}
function updateView() {
for (var i = 0; i < rows; i++) {
for (var j = 0; j < cols; j++) {
var cell = document.getElementById(i + "_" + j);
if (GRID[i][j] == 0) {
cell.setAttribute("class", "dead");
} else {
cell.setAttribute("class", "live");
}
}
}
}
function setupControlButtons() {
// button to start
var startButton = document.getElementById('start');
startButton.onclick = startButtonHandler;
// button to clear
var clearButton = document.getElementById('clear');
clearButton.onclick = clearButtonHandler;
// button to set random initial state
var randomButton = document.getElementById("random");
randomButton.onclick = randomButtonHandler;
}
function randomButtonHandler() {
if (IS_PLAYING) return;
clearButtonHandler();
for (var i = 0; i < rows; i++) {
for (var j = 0; j < cols; j++) {
var isLive = Math.round(Math.random());
if (isLive == 1) {
var cell = document.getElementById(i + "_" + j);
cell.setAttribute("class", "live");
GRID[i][j] = 1;
}
}
}
}
// clear the grid
function clearButtonHandler() {
console.log("Clear the game: stop playing, clear the grid");
IS_PLAYING = false;
var startButton = document.getElementById('start');
startButton.innerHTML = "Start";
clearTimeout(timer);
var cellsList = document.getElementsByClassName("live");
// convert to array first, otherwise, you're working on a live node list
// and the update doesn't work!
var cells = [];
for (var i = 0; i < cellsList.length; i++) {
cells.push(cellsList[i]);
}
for (var i = 0; i < cells.length; i++) {
cells[i].setAttribute("class", "dead");
}
resetGrids;
}
// start/pause/continue the game
function startButtonHandler() {
if (IS_PLAYING) {
console.log("Pause the game");
IS_PLAYING = false;
this.innerHTML = "Continue";
clearTimeout(timer);
} else {
console.log("Continue the game");
IS_PLAYING = true;
this.innerHTML = "Pause";
play();
}
}
// run the life game
function play() {
computeNextGen();
if (IS_PLAYING) {
timer = setTimeout(play, REPRODUCTION_TIME);
}
}
function computeNextGen() {
for (var i = 0; i < rows; i++) {
for (var j = 0; j < cols; j++) {
applyRules(i, j);
}
}
// copy NextGrid to grid, and reset nextGrid
copyAndResetGrid();
// copy all 1 values to "live" in the table
updateView();
}
// RULES
// Any live cell with fewer than two live neighbours dies, as if caused by under-population.
// Any live cell with two or three live neighbours lives on to the next generation.
// Any live cell with more than three live neighbours dies, as if by overcrowding.
// Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
function applyRules(row, col) {
var numNeighbors = countNeighbors(row, col);
if (GRID[row][col] == 1) {
if (numNeighbors < 2) {
NEXT_GRID[row][col] = 0;
} else if (numNeighbors == 2 || numNeighbors == 3) {
NEXT_GRID[row][col] = 1;
} else if (numNeighbors > 3) {
NEXT_GRID[row][col] = 0;
}
} else if (GRID[row][col] == 0) {
if (numNeighbors == 3) {
NEXT_GRID[row][col] = 1;
}
}
}
function countNeighbors(row, col) {
var count = 0;
if (row - 1 >= 0) {
if (GRID[row - 1][col] == 1) count++;
}
if (row - 1 >= 0 && col - 1 >= 0) {
if (GRID[row - 1][col - 1] == 1) count++;
}
if (row - 1 >= 0 && col + 1 < cols) {
if (GRID[row - 1][col + 1] == 1) count++;
}
if (col - 1 >= 0) {
if (GRID[row][col - 1] == 1) count++;
}
if (col + 1 < cols) {
if (GRID[row][col + 1] == 1) count++;
}
if (row + 1 < rows) {
if (GRID[row + 1][col] == 1) count++;
}
if (row + 1 < rows && col - 1 >= 0) {
if (GRID[row + 1][col - 1] == 1) count++;
}
if (row + 1 < rows && col + 1 < cols) {
if (GRID[row + 1][col + 1] == 1) count++;
}
return count;
}
// Start everything
window.onload = initialize;
body {
padding: 20px;
background-color: #444;
}
#gridContainer {
padding-bottom: 10px;
}
table {
background-color: #C5D6C6;
border-spacing: 0;
}
td {
border: 2px solid #F1F5DA;
border-radius: 3px;
width: 10px;
height: 10px;
}
span {
color: #222;
}
#start,
#clear,
#random {
padding: .75em;
border-radius: 5px;
border: none;
background: linear-gradient(
to bottom right,
#C5DEC6, #587559);
}
td.dead {
background-color: transparent;
}
td.live {
background-color: #CC4774;
border-radius: 10px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment