Code Buckets

Buckets of code

Projects React

Cellular Automata with React

Conway’s Game of Life

Here is a fun thing – above is cellular automation coded (by me) in React. Specifically, it is Conway’s Game of Life

Rules

The rules are

  1. Any live cell with two or three live neighbours survives.
  2. Any dead cell with three live neighbours becomes a live cell.
  3. All other live cells die in the next generation. Similarly, all other dead cells stay dead.

With these rules you get an impressive array of emergent behaviour. The example above is the evolution of the R-pentomino. The starting point is this simple 5 square pattern

Which develops into the above pattern that last for well over two thousand generations. If you look carefully, you’ll see simpler patterns within it, such as the glider

which propagates across the screen until it is stopped by another object. If it isn’t stopped it will go on forever.

Live Application

three gliders gliding

I’ve published the game of life application here for anyone to gaze at

http://gameoflife.codebuckets.com

You can set the size of the grid, interval between generations and the zoom. The grid wraps so it’s interesting to try the same pattern on different sizes and watch the effect of the available space on how the game develops.

Code

It’s coded up in React and is published here

https://github.com/timbrownls20/Game-Of-Life

and is freely available to download and tinker round with

The Game of Life rules are held within one file

https://github.com/timbrownls20/Game-Of-Life/blob/master/src/hooks/transformers/gameOfLife.js

import cellUtil from "../../utils/cellUtil";

const gameOfLife = (gameState, gridSettingState, gameStateDispatch) => {
  let arrTransformed = [];

  gameState.grid.filter((row) => {
    row.filter((item) => {
      if (item) {
        let rowUp = item.row <= 1 ? gridSettingState.rows : item.row - 1;
        let rowDown = item.row >= gridSettingState.rows ? 1 : item.row + 1;
        let columnUp =
          item.column <= 1 ? gridSettingState.columns : item.column - 1;
        let columnDown =
          item.column >= gridSettingState.columns ? 1 : item.column + 1;

        let neighbours = [
          { row: rowUp, column: columnUp },
          { row: rowUp, column: item.column },
          { row: rowUp, column: columnDown },
          { row: item.row, column: columnUp },
          { row: item.row, column: columnDown },
          { row: rowDown, column: columnUp },
          { row: rowDown, column: item.column },
          { row: rowDown, column: columnDown },
        ];

        let activeNeighbours = neighbours.reduce((acc, searchItem) => {
          return gameState.grid[searchItem.row][searchItem.column].selected
            ? acc + 1
            : acc;
        }, 0);

        if (item.selected && (activeNeighbours < 2 || activeNeighbours > 3)) {
          addCellToTransform(cellUtil.deselectCell(item));
        } else if (!item.selected && activeNeighbours == 3) {
          addCellToTransform(cellUtil.selectCell(item));
        }
      }
    });
  });

  arrTransformed.filter((element) => {
    gameState.grid[element.row][element.column] = { ...element };
  });

  gameState.generation = gameState.generation + 1;
  gameStateDispatch({ type: "set-state", value: gameState });

  function addCellToTransform(cellToAdd) {
    let existingCell = arrTransformed.find(
      (e) => e.column == cellToAdd.column && e.row == cellToAdd.row
    );
    if (existingCell) {
      cellToAdd =
        existingCell.selected && !cellToAdd.selected ? existingCell : cellToAdd;
    }

It would be easy to swap these out and try other cellular automation rules. You can see two other simpler transformations in the same folder, that I wrote along the way.

So enjoy it if you choose to. In the age of Zoom it’s good to start a pattern then gaze at it hypnotically during online meetings. It makes you look very focused to your fellow meeting participants.

LEAVE A RESPONSE

Your email address will not be published. Required fields are marked *