# 【100sites #011】LifeGame, a simulation of life's infinite possibilities

- Canonical: https://easonchang.com/posts/100sites-011-lifegame
- Date: 2016-03-28T03:54:00.000Z
- Language: en
- Translation: AI-assisted, from the zh-TW original

# LifeGame, a simulation of life's infinite possibilities

[Play the Game of Life live demo](http://kamigami55.github.io/100sites/011_LifeGame/)

[View the source code on Github](https://github.com/eason-dev/100sites/tree/gh-pages/011_LifeGame)

- ENTER: pause
- Left click: place or remove a cell

![Screenshot 2016-03-27 12.35.39 AM.png](https://i.imgur.com/pZmuBeF.jpg)

## What is the Game of Life?

The [Game of Life](https://zh.wikipedia.org/wiki/%E5%BA%B7%E5%A8%81%E7%94%9F%E5%91%BD%E6%B8%B8%E6%88%8F) is a [cellular automaton](https://zh.wikipedia.org/wiki/%E7%B4%B0%E8%83%9E%E8%87%AA%E5%8B%95%E6%A9%9F) model created by the British mathematician [John Horton Conway](https://zh.wikipedia.org/wiki/%E7%B4%84%E7%BF%B0%C2%B7%E4%BD%95%E9%A0%93%C2%B7%E5%BA%B7%E5%A8%81). Every cell on the map decides whether it lives or dies based on the state of the 8 cells around it. The detailed rules are as follows (excerpted from [Wikipedia](https://zh.wikipedia.org/wiki/%E5%BA%B7%E5%A8%81%E7%94%9F%E5%91%BD%E6%B8%B8%E6%88%8F)):

1. A live cell with fewer than 2 live neighbors dies. (simulating underpopulation)
2. A live cell with 2 or 3 live neighbors stays as it is.
3. A live cell with more than 3 live neighbors dies. (simulating overpopulation)
4. A dead cell with exactly 3 live neighbors becomes alive. (simulating reproduction)

With just these 4 rules, you can build a complex and beautiful world of life. You can find some units with special shapes that show interesting behavior patterns.

Three classic special units:
![Screenshot 2016-03-28 11.36.59 AM.png](https://i.imgur.com/tCVC0LI.jpg)

- The Blinker on the left changes in a fixed cycle
- The Spaceship in the middle moves toward the bottom right (you can also design ships that move in other directions)
- The Beehive on the right stays still

## Today's LifeGame

In today's LifeGame, I've already placed a Blinker in the top left corner for you. The rest of the space is yours to create infinite possibilities!

Today's code:

```html index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset-"UTF-8">
    <title>LifeGame</title>
    <link rel="stylesheet" type="text/css" href="style.css" />
    <script src="http://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.23/p5.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.23/addons/p5.dom.min.js"></script>
    <script src="lifegame.js"></script>
  </head>

  <body></body>
</html>
```

```css style.css
* {
  margin: 0;
  padding: 0;
}

body {
  overflow: hidden;
}
```

```javascript lifegame.js
var SIZE = 20;
var oldMap = [],
  newMap = [],
  num_x = 0,
  num_y = 0,
  paused = false,
  frameCount = 0;
var pauseButton;

function setup() {
  createCanvas(windowWidth, windowHeight);
  frameRate(30);

  // init map
  num_x = windowWidth / SIZE;
  num_y = windowHeight / SIZE;
  for (var i = 0; i < num_x; ++i) {
    oldMap.push([]);
    newMap.push([]);
    for (var j = 0; j < num_y; ++j) {
      oldMap[i].push(false);
      newMap[i].push(false);
    }
  }
  loadDefaultMap();

  // init pause button
  pauseButton = createButton("Pause");
  pauseButton.size(80, 30);
  pauseButton.position(windowWidth / 2 - 40, windowHeight - 40);
  pauseButton.mousePressed(togglePauseSimulate);
}

function draw() {
  // fresh map every 5 frames
  if (!paused) {
    if (frameCount % 5 == 0) {
      freshMap();
    }
  }
  // increase frame count
  ++frameCount;
  if (frameCount >= 30) {
    frameCount = 0;
  }

  drawMap();
}

// default map: a blink unit at top left corner
function loadDefaultMap() {
  oldMap[1][1] = true;
  oldMap[1][2] = true;
  oldMap[1][3] = true;
}

function drawMap() {
  background(0);
  // draw grid
  stroke(30);
  for (var i = 0; i < num_x; ++i) {
    line(i * SIZE, 0, i * SIZE, windowHeight);
  }
  for (var i = 0; i < num_y; ++i) {
    line(0, i * SIZE, windowWidth, i * SIZE);
  }
  fill(255);
  // draw cells
  for (var i = 0; i < num_x; ++i) {
    for (var j = 0; j < num_y; ++j) {
      if (oldMap[i][j]) {
        rect(i * SIZE, j * SIZE, SIZE, SIZE);
      }
    }
  }
  // draw mouse cell
  fill(color("rgba(100,100,100,0.5)"));
  var mouseCellX = int(mouseX / SIZE);
  var mouseCellY = int(mouseY / SIZE);
  rect(mouseCellX * SIZE, mouseCellY * SIZE, SIZE, SIZE);
}

// press ENTER to pause simulate
function keyPressed() {
  if (keyCode == ENTER) {
    togglePauseSimulate();
  }
}

// press mouse to add or remove cell
function mousePressed() {
  var mouseCellX = int(mouseX / SIZE);
  var mouseCellY = int(mouseY / SIZE);
  oldMap[mouseCellX][mouseCellY] = !oldMap[mouseCellX][mouseCellY];
}

function togglePauseSimulate() {
  if (paused) {
    paused = false;
  } else {
    paused = true;
  }
}

// count how many neighbors there are of a cell
function neighbors(xpos, ypos) {
  var total = 0;
  // four corners
  if (xpos != 0 && ypos != 0) {
    if (oldMap[xpos - 1][ypos - 1]) {
      ++total;
    }
  }
  if (xpos != 0 && ypos != num_y - 1) {
    if (oldMap[xpos - 1][ypos + 1]) {
      ++total;
    }
  }
  if (xpos != num_x - 1 && ypos != 0) {
    if (oldMap[xpos + 1][ypos - 1]) {
      ++total;
    }
  }
  if (xpos != num_x - 1 && ypos != num_y - 1) {
    if (oldMap[xpos + 1][ypos + 1]) {
      ++total;
    }
  }
  // left and right
  if (xpos != 0) {
    if (oldMap[xpos - 1][ypos]) {
      ++total;
    }
  }
  if (xpos != num_x - 1) {
    if (oldMap[xpos + 1][ypos]) {
      ++total;
    }
  }
  if (ypos != 0) {
    if (oldMap[xpos][ypos - 1]) {
      ++total;
    }
  }
  if (ypos != num_y - 1) {
    if (oldMap[xpos][ypos + 1]) {
      ++total;
    }
  }
  return total;
}

// calculate the next map according to the Life Game rule
function freshMap() {
  for (var i = 0; i < num_x; ++i) {
    for (var j = 0; j < num_y; ++j) {
      var neighbor_count = neighbors(i, j);
      if (neighbor_count <= 1 || neighbor_count >= 4) {
        newMap[i][j] = false;
      } else if (neighbor_count === 2) {
        newMap[i][j] = oldMap[i][j];
      } else {
        // neighbor_count === 3
        newMap[i][j] = true;
      }
    }
  }

  for (var i = 0; i < num_x; ++i) {
    for (var j = 0; j < num_y; ++j) {
      oldMap[i][j] = newMap[i][j];
    }
  }
}
```
