Open In App

Conway’s Game of Life using React

Last Updated : 19 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

In this article, we’ll explore how to build an interactive version of Conway’s Game of Life using React. Conway’s Game of Life is a classic cellular automaton devised by mathematician John Conway. It’s a fascinating simulation that demonstrates emergent complexity from simple rules.

Output Preview: Let us have a look at how the final output will look like.

Preview of Conway’s Game of Life using React

Approach to build Conway’s Game:

The GameOfLife component initializes a grid representing the game’s environment, where each cell can either be alive or dead. It incorporates the rules of Conway’s Game of Life to determine the state of each cell based on its neighbors. These rules govern the evolution of the grid over successive generations.

Steps to Create Conway’s Game in React:

Step 1: Set up React project using the command.

npx create-react-app <<name of project>>

Step 2: Change to the project directory

cd <<Project_Name>>

Step 3: Add the new files in it namely GameOfLife.js in src directory.

Project Structure:

list2

The updated dependencies in package.json file will look like this:

"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}

Example: Write the following code in respective files.

  • App.js: This component is responsible, for rendering the layout of the application.
  • GameOfLife.js : In GameOfLife.js, the heart of the project, the Conway’s Game of Life logic is encapsulated within a React component. This component manages the grid’s state, handling the evolution of cells based on their neighbors according to the game’s rules.
  • App.css : App.css contributes to the project’s visual appeal by providing styling rules for various elements.
CSS
/* App.css */

.App {
    text-align: center;
}

.App-header {
    background-color: #e0e2e6;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    font-size: calc(10px + 2vmin);
    color: black;
}

h1 {
    margin-bottom: 20px;
}

.controls {
    margin-bottom: 20px;
}

.controls button {
    margin: 5px;
    padding: 10px 20px;
    background-color: #61dafb;
    color: white;
    border: none;
    border-radius: 5px;
    cursor: pointer;
}

.controls button:hover {
    background-color: #4fa3d4;
}

.grid {
    display: grid;
    grid-template-columns: repeat(30, 20px);
    gap: 1px;
}

.cell {
    width: 20px;
    height: 20px;
    border: 1px solid #333;
    background-color: white;
}

.cell.active {
    background-color: black;
}
JavaScript
// App.js

import React from 'react';
import './App.css';
import GameOfLife
    from './GameOfLife';

function App() {
    return (
        <div className="App">
            <header className="App-header">
                <h1>Conway's Game of Life</h1>
                <GameOfLife />
            </header>
        </div>
    );
}

export default App;
JavaScript
// GameOfLife.js

import React, {
    useState,
    useCallback,
    useRef
} from 'react';
import produce from 'immer';

const numRows = 22;
const numCols = 40;
const operations = [
    [0, 1],
    [0, -1],
    [1, -1],
    [-1, 1],
    [1, 1],
    [-1, -1],
    [1, 0],
    [-1, 0],
];

const generateEmptyGrid = () => {
    const rows = [];
    for (let i = 0; i < numRows; i++) {
        rows.push(Array.from(Array(numCols), () => 0));
    }
    return rows;
};

const GameOfLife = () => {
    const [grid, setGrid] = useState(() => {
        return generateEmptyGrid();
    });

    const [running, setRunning] = useState(false);
    const runningRef = useRef(running);
    runningRef.current = running;

    const runSimulation = useCallback(() => {
        if (!runningRef.current) {
            return;
        }
        setGrid((g) => {
            return produce(g, (gridCopy) => {
                for (let i = 0; i < numRows; i++) {
                    for (let j = 0; j < numCols; j++) {
                        let neighbors = 0;
                        operations.forEach(([x, y]) => {
                            const newI = i + x;
                            const newJ = j + y;
                            if (
                                newI >= 0 &&
                                newI < numRows &&
                                newJ >= 0 &&
                                newJ < numCols
                            ) {
                                neighbors += g[newI][newJ];
                            }
                        });

                        if (neighbors < 2 || neighbors > 3) {
                            gridCopy[i][j] = 0;
                        } else if (g[i][j] === 0 && neighbors === 3) {
                            gridCopy[i][j] = 1;
                        }
                    }
                }
            });
        });

        setTimeout(runSimulation, 100);
    }, []);

    return (
        <>
            <button
                onClick={() => {
                    setRunning(!running);
                    if (!running) {
                        runningRef.current = true;
                        runSimulation();
                    }
                }}
            >
                {running ? 'Stop' : 'Start'}
            </button>
            <button
                onClick={() => {
                    const rows = [];
                    for (let i = 0; i < numRows; i++) {
                        rows.push(
                            Array.from(Array(numCols),
                                () => (Math.random() > 0.7 ? 1 : 0))
                        );
                    }
                    setGrid(rows);
                }}>
                Randomize
            </button>
            <button
                onClick={() => {
                    setGrid(generateEmptyGrid());
                }}>
                Clear
            </button>
            <div
                style={{
                    display: 'grid',
                    gridTemplateColumns: `repeat(${numCols}, 20px)`,
                }}>
                {grid.map((rows, i) =>
                    rows.map((col, j) => (
                        <div
                            key={`${i}-${j}`}
                            onClick={() => {
                                const newGrid = produce(grid,(gridCopy) => {
                                    gridCopy[i][j] = grid[i][j] ? 0 : 1;
                                });
                                setGrid(newGrid);
                            }}
                            style={{
                                width: 20,
                                height: 20,
                                backgroundColor:
                                    grid[i][j] ?
                                        'black' :
                                        undefined,
                                border: 'solid 1px gray',
                            }} />
                    ))
                )}
            </div>
        </>
    );
};

export default GameOfLife;

Steps to run the project:

npm start

Output:

Preview Conway’s Game of Life using React



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads