Open In App

Conway’s Game of Life in JavaScript

Last Updated : 30 Jul, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

We will create Conway's Game of Life, a classic cellular automaton devised by mathematician John Conway. The game consists of a grid of cells, each of which can be in one of two states: alive or dead. The state of each cell evolves over time based on simple rules, leading to fascinating patterns and behaviors.

Prerequisites

About Conway's Game of Life

Conway's Game of Life is a fascinating cellular automaton created by mathematician John Conway. It's a game that doesn't require players; instead, it evolves based on its initial setup. Imagine a grid of cells, where each cell can either be alive or dead. Here's how the game works:

Setting Up: You start by placing living cells on the grid. This could be done randomly or following specific patterns that you choose.

Neighbors Matter: Each cell looks at its eight neighbors – those cells that are next to it horizontally, vertically, or diagonally.

Rules for Life and Death:

  • Birth: If a dead cell has exactly three live neighbors, it springs to life in the next generation.
  • Survival: A live cell with two or three live neighbors continues to live in the next generation.
  • Death: Any cell with fewer than two live neighbors dies due to loneliness, while a cell with more than three live neighbors dies from overcrowding.

Next Generation: After applying these rules to every cell on the grid simultaneously, a new generation is born. This new layout replaces the old one, and the cycle continues.

What Happens Next: Depending on how you set things up, various outcomes are possible:

  • Stable Patterns: Some configurations settle into a stable state where no further changes happen.
  • Oscillation: Certain setups oscillate between different patterns, repeating over and over.
  • Growth or Decay: Other configurations may grow indefinitely, stabilize into a repeating pattern, or eventually fade away.

Despite its simplicity, Conway's Game of Life showcases incredibly complex behaviors. It's been used in many fields like computer science and biology to explore emergent properties and computational systems.

Approach

  • A canvas element is created with an ID of "gameCanvas" and a width of 600 pixels and height of 400 pixels. The canvas is styled with a black border.
  • The script calculates the number of rows and columns based on the canvas size and a cell size of 10 pixels. A function createGrid() initializes a 2D array representing the grid. Each cell in the grid is randomly assigned a value of 1 (alive) or 0 (dead) based on a 30% chance of being alive.
  • drawGrid() function clears the canvas and then iterates through the grid, drawing live cells as black squares. updateGrid() function applies Conway's Game of Life rules to update the grid based on the current state of each cell and its neighbors. It creates a new grid with the updated cell states.
  • The countNeighbors(row, col) function calculates the number of live neighbors for a given cell at coordinates (row, col). It checks the eight neighboring cells and counts the live ones.
  • mainLoop() is the main function that orchestrates the updating and drawing of the grid. It calls updateGrid() to update the grid based on Conway's rules, followed by drawGrid() to redraw the grid on the canvas. This loop continues as long as the isRunning flag is set to true.
  • Event listeners are added to three buttons: Start, Pause, and Restart.

Example: This example shows the implementation of the above-explained approach.

HTML
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,
                                   initial-scale=1.0">
    <title>Conway's Game of Life</title>
    <style>
        canvas {
            border: 1px solid black;
        }
    </style>
</head>

<body>
    <canvas id="gameCanvas"
            width="600"
            height="400"></canvas>
    <br />
    <br />
    <button id="startButton">Start</button>
    <button id="pauseButton">Pause</button>
    <button id="restartButton">Restart</button>
    <script>
        // Initialize canvas and context
        const canvas = document.getElementById('gameCanvas');
        const ctx = canvas.getContext('2d');

        // Define cell size and grid dimensions
        const cellSize = 10;
        const numRows = Math.floor(canvas.height / cellSize);
        const numCols = Math.floor(canvas.width / cellSize);

        // Function to initialize the grid
        function createGrid() {
            const grid = [];
            for (let i = 0; i < numRows; i++) {
                grid[i] = [];
                for (let j = 0; j < numCols; j++) {
                    grid[i][j] = Math.
                    random() > 0.7 ? 1 : 0; // Random initialization
                }
            }
            return grid;
        }

        let grid = createGrid();
        let isRunning = false;
        let animationId = null;

        // Function to draw the grid
        function drawGrid() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            for (let i = 0; i < numRows; i++) {
                for (let j = 0; j < numCols; j++) {
                    if (grid[i][j] === 1) {
                        ctx.fillStyle = 'black';
                        ctx.fillRect(j * cellSize, i *
                                     cellSize, cellSize, cellSize);
                    }
                }
            }
        }

        // Function to update the grid based on Conway's rules
        function updateGrid() {
            const newGrid = [];
            for (let i = 0; i < numRows; i++) {
                newGrid[i] = [];
                for (let j = 0; j < numCols; j++) {
                    const neighbors = countNeighbors(i, j);
                    if (grid[i][j] === 1 && (neighbors < 2 || neighbors > 3)) {
                        newGrid[i][j] = 0; 
                    } else if (grid[i][j] === 0 && neighbors === 3) {
                        newGrid[i][j] = 1; 
                    } else {
                        newGrid[i][j] = grid[i][j]; 
                    }
                }
            }
            grid = newGrid;
        }

        // Function to count live neighbors of a cell
        function countNeighbors(row, col) {
            let count = 0;
            for (let i = -1; i <= 1; i++) {
                for (let j = -1; j <= 1; j++) {
                    const r = row + i;
                    const c = col + j;
                    if (r >= 0 && r < numRows && c >= 0 &&
                        c < numCols && !(i === 0 && j === 0)) {
                        count += grid[r][c];
                    }
                }
            }
            return count;
        }

        // Main loop to update and draw the grid
        function mainLoop() {
            updateGrid();
            drawGrid();
            if (isRunning) {
                animationId = requestAnimationFrame(mainLoop);
            }
        }

        document.getElementById('startButton')
                  .addEventListener('click', function () {
            if (!isRunning) {
                isRunning = true;
                mainLoop();
            }
        });

        document.getElementById('pauseButton')
                  .addEventListener('click',
            function () {
            isRunning = false;
            cancelAnimationFrame(animationId);
        });

        document.getElementById('restartButton')
                  .addEventListener('click',
            function () {
            isRunning = false;
            cancelAnimationFrame(animationId);
            grid = createGrid();
            drawGrid();
        });

    </script>
</body>

</html>

Output:


Next Article

Similar Reads