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
API references #
- Quadrille.cellLength.
- Quadrille.or.
- createQuadrille(width, height).
- createQuadrille(width, bitboard, value).
- visitQuadrille(quadrille, fx).
- drawQuadrille(quadrille, params).
- isFilled(row, col).
- clone().
- clear().
- fill().
- ring().
- order.
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:
- 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); } }
- 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.