Game of Life

Game of Life #

In object-oriented programming, maximizing API use streamlines development, leading to simpler and more concise code. This clarity not only improves readability but also eases debugging. Leveraging an API effectively in OOP thus ensures a blend of simplicity, efficiency and maintainability in software development.

Observation
Before diving into development, thoroughly reading the API documentation is key to efficient coding, preventing mistakes, and ensuring optimal performance. By acquainting yourself with the API's nuances, you set the stage for informed decision-making, seamless collaboration, and a smoother development journey.

The following demo illustrates the game of life using a pentadecathlon pattern as its initial seed:

The program uses three quadrille instances: game, next and pattern. While the game quadrille visualizes the current state of the game, the next quadrille calculates its subsequent iteration, and the pattern quadrille establishes the game’s initial seed. Cells filled with the life value represent living cells (i.e., fill() resurrects a dead cell), while empty cells denote the dead ones (i.e., clear() kills a living cell).

code
```js Quadrille.cellLength = 20; // i. Object declaration // quadrille objects let game, next, pattern; // value used to fill living cells let life; function setup() { // ii. Object instantiation game = createQuadrille(20, 20); life = color('lime'); pattern = createQuadrille(3, 16252911n, life); // stick the pattern into the game game = Quadrille.or(game, pattern, 6, 8); createCanvas(game.width * Quadrille.cellLength, game.height * Quadrille.cellLength); frameRate(2); } function draw() { background('blue'); // iii. Object usage next = game.clone(); // visit the game instance while updating the next instance visitQuadrille(game, updateCell); game = next; drawQuadrille(game, { outline: color('magenta') }); } // visitQuadrille callback function updateCell(row, col) { // order of the game ring of dimension 1 centered at (row, col) const order = game.ring(row, col).order; if (game.isFilled(row, col)) { // living cells if (order - 1 < 2 || order - 1 > 3) { next.clear(row, col); } } else { // death cells if (order === 3) { next.fill(row, col, life); } } } ```

API references #

Patterns #

The initial seed is defined using a bitboard (which is just a bigint encoding a quadrille filling pattern), i.e., pattern = createQuadrille(3, 16252911n, life) which is equivalent to:

pattern = createQuadrille([[life, life, life],
                           [life, null, life],
                           [life, life, life],
                           [life, life, life],
                           [life, life, life],
                           [life, life, life],
                           [life, null, life],
                           [life, life, life]
                           ]);
pattern.toBigInt(); // 16252911n

Rules #

The game of life rules are applied to each cell by visitQuadrille(game, updateCell) which is equivalent to:

for (row = 0; row < game.height; row++) {
  for (col = 0; col < game.width; col++) {
    updateCell(row, col);
  }
}

since a quadrille is an iterable object the same results can also be obtained with a for…of loop:

for (cell of game) {
  // cell is an object literal having value, row and col properties
  updateCell(cell.row, cell.col);
}

The updateCell(row, col) function first compute the order of the game ring (of dimension 1) centered at (row, col): const order = game.ring(row, col).order, and then use it to apply the game of life rules to the next quadrille:

  1. Any live cell with less than two or more than three live neighbors dies:
    if (game.isFilled(row, col)) {
      if (order - 1 < 2 || order - 1 > 3) {
        next.clear(row, col);
      }
    } 
    
  2. Any dead cell with three live neighbors becomes a live cell:
    else {
      if (order === 3) {
        next.fill(row, col, life);
      }
    }
    

Exercises #

Possible improvements of the demo include, but are not limited to:

  • Implement other seeding patterns possibly enabling mouse edition of them.
  • Implement a similar game, like Langton’s ant.

It’s also advisable to compare and contrast the game of life implementation introduced here with the one presented in the nature of code which employs raw p5 commands.

References #