Lets play the game of life simulation interactively.

You can click on any square to make it toggle state of live or dead and see how it affects the cells next to where you clicked. When all cells are white or dead the game will continue for ever so to start over press the F5 key to refresh the screen.

The Game of Life, devised by the British mathematician John Horton Conway in 1970, is a cellular automaton that simulates the life cycle of bacteria or cells. It’s a zero-player game, meaning its evolution is determined by its initial state, requiring no further input from human players. The game unfolds on an infinite two-dimensional square grid of cells, each of which is in one of two possible states, alive or dead. Every cell interacts with its eight neighbors, which are the cells adjacent to it, including diagonally.

The rules of the Game of Life are simple yet create complex, fascinating patterns. These rules, applied to each cell in the grid, determine whether it will live, die, or reproduce in the next generation:

  1. Underpopulation: If a living cell is surrounded by fewer than two living neighbors, it dies, as if by underpopulation.
  2. Stasis: If a living cell is surrounded by two or three living neighbors, it survives into the next generation.
  3. Overpopulation: If a living cell is surrounded by more than three living neighbors, it dies, as if by overpopulation.
  4. Reproduction: If a dead cell is surrounded by exactly three living neighbors, it becomes a living cell, as if by reproduction.

The initial pattern constitutes the ‘seed’ of the system. The first generation is created by applying the above rules simultaneously to every cell in the seed; births and deaths happen simultaneously, and the discrete moment at which this happens is sometimes called a tick. Each generation is a pure function of the preceding one, and the rules continue to be applied repeatedly to create further generations.

Patterns in the Game of Life can be categorized broadly into a few types:

  • Still lifes: Do not change from one generation to the next.
  • Oscillators: Return to their initial state after a finite number of generations.
  • Spaceships: Translate themselves across the grid.
  • Complex or chaotic patterns: Do not settle into a stable pattern.

Despite its simple rules, the Game of Life is Turing complete, which means it can simulate a universal constructor or any other Turing machine. This property underscores the game’s significance in demonstrating how complex patterns and behaviors can emerge from simple rules in systems, an insight that has implications in various fields, including computer science, mathematics, physics, philosophy, and biology.

This is the code that makes this game work. It is a WordPress plugin that creates a shortcode that gets executed when the page loads. It is made up of 2 programs. 1 is a php program that defines the shortcode and 2 is a javascript program that displays the game in the canvas.

Here is the JavaScript code

JavaScript
jQuery(document).ready(function($) {
    let animationFrameId; // Variable to store requestAnimationFrame ID
    let restartButtonAdded = false; // Flag to track if the restart button has been added

    class GameOfLife {
        constructor(canvasId) {
            this.canvas = document.getElementById(canvasId); // Get the canvas element by ID
            this.context = this.canvas.getContext('2d'); // Get the 2D drawing context of the canvas
            this.canvas.width = 400; // Set the canvas width
            this.canvas.height = 400; // Set the canvas height
            this.context.fillStyle = 'rgb(220, 220, 220)'; // Set the background color to light gray
            this.context.fillRect(0, 0, this.canvas.width, this.canvas.height); // Fill the canvas with the background color

            this.cellSize = 10; // Set the size of each cell in pixels
            this.cellsInRow = this.canvas.width / this.cellSize; // Calculate the number of cells in a row
            this.cellsInColumn = this.canvas.height / this.cellSize; // Calculate the number of cells in a column
            this.gameState = []; // Initialize an empty array to store the game state
            this.initEmptyGameState(); // Initialize the game state with all cells dead
            this.drawGame(); // Draw the initial game state on the canvas
        }

        initEmptyGameState() {
            for (let x = 0; x < this.cellsInRow; x++) {
                this.gameState[x] = []; // Create an empty array for each row
                for (let y = 0; y < this.cellsInColumn; y++) {
                    this.gameState[x][y] = false; // Set all cells to dead (false)
                }
            }

            // Add an initial pattern or randomly set some cells to alive
            this.gameState[10][10] = true;
            this.gameState[10][11] = true;
            this.gameState[10][12] = true;
            this.gameState[10][15] = true;
            this.gameState[10][16] = true;
            this.gameState[10][17] = true;
            this.gameState[10][18] = true;
            this.gameState[10][19] = true;
            this.gameState[11][12] = true;
            this.gameState[11][10] = true;
            this.gameState[11][11] = true;
            this.gameState[11][12] = true;
            this.gameState[15][10] = true;
            this.gameState[15][11] = true;
            this.gameState[15][12] = true;
            this.gameState[15][15] = true;
            this.gameState[15][16] = true;
            this.gameState[15][17] = true;
            this.gameState[15][18] = true;
            this.gameState[15][19] = true;
            this.gameState[16][12] = true;
            this.gameState[16][10] = true;
            this.gameState[16][11] = true;
            this.gameState[16][12] = true;
            console.log("Draw initEmptyGameState...");
        }

        drawGame() {
            this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); // Clear the canvas before drawing

            for (let x = 0; x < this.cellsInRow; x++) {
                for (let y = 0; y < this.cellsInColumn; y++) {
                    this.context.beginPath(); // Start a new path for each cell
                    this.context.rect(x * this.cellSize, y * this.cellSize, this.cellSize, this.cellSize); // Draw a rectangle for the cell
                    this.context.fillStyle = this.gameState[x][y] ? 'black' : 'white'; // Set the fill color to black for alive cells, white for dead cells
                    this.context.fill(); // Fill the rectangle with the appropriate color
                    this.context.stroke(); // Draw the outline of the rectangle
                }
            }
        }

        updateGameState() {
            const newGameState = []; // Create a new array to store the updated game state

            for (let row = 0; row < this.cellsInRow; row++) {
                newGameState[row] = []; // Create an empty array for each row in the new game state
                for (let col = 0; col < this.cellsInColumn; col++) {
                    const aliveNeighbors = this.countAliveNeighbors(row, col); // Count the number of alive neighbors for the current cell

                    if (this.gameState[row][col]) {
                        // Apply the rules for a living cell:
                        // - A living cell with 2 or 3 alive neighbors survives.
                        // - A living cell with less than 2 or more than 3 alive neighbors dies (underpopulation or overpopulation).
                        newGameState[row][col] = aliveNeighbors === 2 || aliveNeighbors === 3;
                    } else {
                        // Apply the rules for a dead cell:
                        // - A dead cell with exactly 3 alive neighbors becomes alive (reproduction).
                        newGameState[row][col] = aliveNeighbors === 3;
                    }
                }
            }

            this.gameState = newGameState; // Update the game state with the new values

            if (this.isAllCellsDead()) {
                this.stopGame(); // Stop the game if all cells are dead
            }

            this.drawGame(); // Redraw the game on the canvas with the updated game state
        }

        countAliveNeighbors(row, col) {
            let count = 0; // Initialize the count of alive neighbors to 0

            // Iterate over the 8 neighboring cells (including diagonals)
            for (let i = -1; i <= 1; i++) {
                for (let j = -1; j <= 1; j++) {
                    if (i === 0 && j === 0) continue; // Skip the current cell itself

                    const r = row + i; // Calculate the row index of the neighbor
                    const c = col + j; // Calculate the column index of the neighbor

                    // Check if the neighbor is within the bounds of the game board
                    if (r >= 0 && r < this.cellsInRow && c >= 0 && c < this.cellsInColumn) {
                        if (this.gameState[r][c]) { // If the neighbor is alive, increment the count
                            count++;
                        }
                    }
                }
            }

            return count; // Return the total count of alive neighbors
        }

        isAllCellsDead() {
            // Iterate over all cells and check if any are alive
            for (let row = 0; row < this.cellsInRow; row++) {
                for (let col = 0; col < this.cellsInColumn; col++) {
                    if (this.gameState[row][col]) {
                        return false; // If any cell is alive, return false
                    }
                }
            }

            return true; // If no cells are alive, return true
        }

        stopGame() {
            cancelAnimationFrame(animationFrameId); // Stop the animation loop
            animationFrameId = null; // Reset the animation frame ID

            // Add a message and a "Start New Game" button above the canvas
            const canvasElement = document.getElementById('game-of-life-canvas');
            const parentElement = canvasElement.parentElement;

            if (!restartButtonAdded) {
                const message = document.createElement('div');
                message.textContent = 'All cells are dead.';
                parentElement.insertBefore(message, canvasElement);

                const restartButton = document.createElement('button');
                restartButton.textContent = 'Start New Game';
                restartButton.addEventListener('click', () => {
                    parentElement.removeChild(message);
                    parentElement.removeChild(restartButton);

                    // Reinitialize game state with starting live cells
                    gameOfLife.initEmptyGameState(); // Reset to an empty state
                    gameOfLife.drawGame(); // Draw the empty state

                    // Apply the initial pattern or random live cells again
                    gameOfLife.gameState[10][10] = true;
                    gameOfLife.gameState[10][11] = true;
                    gameOfLife.gameState[10][12] = true;
                    gameOfLife.gameState[10][15] = true;
                    gameOfLife.gameState[10][16] = true;
                    gameOfLife.gameState[10][17] = true;   
                    gameOfLife.gameState[10][18] = true;
                    gameOfLife.gameState[10][19] = true;
                    gameOfLife.gameState[11][12] = true;    
                    gameOfLife.gameState[11][10] = true;
                    gameOfLife.gameState[11][11] = true;
                    gameOfLife.gameState[11][12] = true;
                    gameOfLife.gameState[15][10] = true;
                    gameOfLife.gameState[15][11] = true;
                    gameOfLife.gameState[15][12] = true;
                    gameOfLife.gameState[15][15] = true;
                    gameOfLife.gameState[15][16] = true;
                    gameOfLife.gameState[15][17] = true;   
                    gameOfLife.gameState[15][18] = true;
                    gameOfLife.gameState[15][19] = true;
                    gameOfLife.gameState[16][12] = true;    
                    gameOfLife.gameState[16][10] = true;
                    gameOfLife.gameState[16][11] = true;
                    gameOfLife.gameState[16][12] = true;
                    gameOfLife.drawGame(); // Redraw with the initial pattern

                    // Re-enable game interaction after restarting
                    gameOfLife.run(); // Restart the game

                    // Reset the restart button flag
                    restartButtonAdded = false;
                });
                parentElement.insertBefore(restartButton, canvasElement);
                restartButtonAdded = true; // Set the flag to true to prevent further additions
            }
        }

        run() {
            this.canvas.addEventListener('click', (event) => {
                const rect = this.canvas.getBoundingClientRect(); // Get the bounding rectangle of the canvas
                const x = event.clientX - rect.left; // Calculate the x-coordinate of the click relative to the canvas
                const y = event.clientY - rect.top; // Calculate the y-coordinate of the click relative to the canvas
                const cellX = Math.floor(x / this.cellSize); // Calculate the cell index in the x-direction
                const cellY = Math.floor(y / this.cellSize); // Calculate the cell index in the y-direction
                this.gameState[cellX][cellY] = !this.gameState[cellX][cellY]; // Toggle the state of the clicked cell
                this.drawGame(); // Redraw the game with the updated cell state
            });

            const animate = () => {
                this.updateGameState(); // Update the game state based on the rules
                animationFrameId = requestAnimationFrame(animate); // Schedule the next animation frame
            };
            animate(); // Start the animation loop
        }
    }

    const gameOfLife = new GameOfLife('game-of-life-canvas'); // Create a new instance of the GameOfLife class
    gameOfLife.run(); // Start the game
});

Here is the php code

PHP
<?php

/**
Plugin Name: Interactive Game of Life
Plugin URI: http://geekzonebooks.com/interactive-game-of-life
Description: A WordPress plugin to display an interactive Game of Life.
Version: 1.0
Author: Michael Scott McGinn
Author URI: http://michaelscottmcginn.com
*/
function gol_enqueue_scripts() {
    wp_enqueue_script('game-of-life-js', plugins_url('/js/game-of-life.js', __FILE__), array('jquery'), '1.0', true);
}
add_action('wp_enqueue_scripts', 'gol_enqueue_scripts');

function game_of_life_shortcode() {
    // Ensure the JS is enqueued
    wp_enqueue_script('game-of-life-js'); // This might be redundant but ensures script is enqueued if shortcode is used

    // Output for the shortcode
    $output = '<canvas id="game-of-life-canvas" width="400" height="400"></canvas>';
    $output .= '<script type="text/javascript">
                    jQuery(document).ready(function($) {
                        if (typeof GameOfLife !== "undefined") {
                            const gameOfLife = new GameOfLife("game-of-life-canvas");
                            gameOfLife.run(); // Start the game loop
                        }
                    });
                </script>';
    return $output;
}

add_shortcode('game_of_life', 'game_of_life_shortcode');
?>

There is a logic error in this code that is making it not restart properly and let you continue playing the game again in an interactive way.

Keep checking this page for updates to it as I work on it or see if you can create your own plugin and fix the error on your site.

Have fun and happy coding.

Chat Icon

Site Hosted by MTBN.NET