Processing Beauty: Cellular Automata

RedXIII | April 12, 2021, 11:17 p.m.

An Introduction to Processing

“Everything has its beauty, but not everyone sees it.” — Confucius

Can code create beauty?


Many artworks contain patterns and designs. For instance, mosaic art and hard-edge abstraction. The rules inherent in the design are pleasing.

We can use code to created similar patterns using Processing. Processing is a tool developed for artists, designers, teachers, and students who are new to computer graphics. Using Processing, it’s easy to make simple visual programs.

Cellular Automata

A good foundation for this type of programming is Cellular Automata (CA). CA is a way of displaying complex information using a grid of “cells.” In our case, we’ll be creating a version of Conway’s Game of Life, a very famous Cellular Automata exercise.

To create the game, we’ll first need to draw a grid with columns and rows. Each cell on the grid will either be “alive” or “dead.” Keeping track of the states of each cell will allows us to generate complex, animated patterns from a simple set of rules.

Each cell will have neighbors, and these neighbors will also be either living or dead.

CA was first discovered by the Mathematician John Von Neumann in the 1940’s. The game begins by randomly determine the state of each cell in the grid (either alive or dead). The game then takes a step, and moves to a new state depending on the following conditions:

1. If a cell is Alive, it dies if there are more than three neighbors, or less than two.

2. If a cell is Dead, it comes alive if there are exactly three neighbors.

3. In all other cases, the cell stays the same.

Using these rules, we will create the game of life, and perhaps a little beauty in the process. The complete code is provided at the end. It can be copied and pasted into a new Processing sketch.

Getting Started

All you need to complete this exercise is a copy of Processing. You can download if for free from processing.org. Once you’ve opened processing, you’ll see a text editor that contains your sketch.

Now we’re ready to write some code. Let’s begin by building the game board. The board is a grid of cells, much like a chessboard. To store this data, it makes sense to use a 2D array.

int[][] board = new int[columns][rows];

Each cell in the array will have a state, either on or off (0 or 1). The game starts by looping through every cell and setting them to a random starting state.

// Initialize the game board
for (int x = 0; x < columns; x++){
for (int y =0; y < rows; y++){
gameboard[x][y] = int(random(2));
}
}

Now that the game board is initialized, we can draw it to the screen. We can draw each cell using the rect() function provided by Processing. We’ll create a DrawBoard() function that loops through every cell and draws either a white or black rectangle in the designated position.

To do this, we’ll use an int to store the size of our tile graphic. By multiplying this value with the x and y coordinates of each cell, we can draw the cells.

Processing provides a function for initializing the sketch: setup(). Calling DrawBoard() from the setup() function will give us an image of the board.

There are two more functions needed in our sketch: CheckNeighbors() and TakeStep(). CheckNeighbors will have two parameters (x and y). We’ll use this function to count the number of living neighbors that a cell has.

The second function, TakeStep(), will loop through every cell in the game board, count its neighbors, and then check the rules of the game of life to determine whether that cell lives or dies.

We’ll use a second 2D array (called next) to store the state of each cell. At the end of the function, the old data is replaced by the new data.

gameboard = next;

The CheckNeighbors() function uses a double for loop (also called a nested loop) to check a cell’s neighbors. First, a check is made to ensure the neighbor is inside the game board. Next, we make sure not to count the current cell. In all other cases, if the cell is living, we count it as a neighbor.

Using the neighbor count, and the rules of the game of life, we can compute the next state in the game based on the current state of the game board.

The TakeStep() function uses a nested loop to grab each cell in the game board. It will then need to determine if the cell is currently alive or not.

If the cell is alive, another check determines if it is killed. Remember, in the game of life, a cell dies if it has more than 3 neighbors (over population), or less than 2 (under population).

If the cell is dead, it can come back to life if it has exactly 3 neighbors.

In all other cases, the cell remains the same.

In order to run the simulation, we’ll need one more function: draw(). This function is called directly after the setup() function, and it executes repeatedly. Using a variable named “tick,” we can count how many loops we want to wait before taking the next step and re-drawing the board.

Using this method, we can control the speed at which the cells live and die.

Here’s the complete Processing sketch:

// GAME OF LIFEint columns = 16; // width of the gameboard
int rows = 12; //height of the gameboard
int tile_size = 40; // size of the tiles
int tick = 1;
int tick_time = 10;
int[][] gameboard = new int[columns][rows];int[][] next = new int[columns][rows];void setup(){
size(640,480);

// Initialize the game board
for (int x = 0; x < columns; x++){
for (int y =0; y < rows; y++){
gameboard[x][y] = int(random(2));
next[x][y] = gameboard[x][y];
}
}
DrawBoard();
}
void draw(){
tick++;
if (tick > tick_time){
tick = 0;
TakeStep();
DrawBoard();
}
}
void TakeStep(){
// loop through the board in it's current state
for (int y = 0; y < rows; y++){
for (int x = 0; x < columns; x++){

int n = CheckNeighbors(x,y);
//print(n, " ");
// if cell is alive
if (gameboard[x][y] == 1){
// if over or under populated, kill it
if (n > 3 || n < 2){
next[x][y] = 0;
}
}
else if(gameboard[x][y] == 0){
if (n == 3){
next[x][y] = 1;
}
}
else{
next[x][y] = gameboard[x][y];
}
}
}

gameboard = next;
}
int CheckNeighbors(int x, int y) {
int neighbors = 0;
// loop through the 9possible neighbors
for (int i = -1; i < 2; i++){
for (int j = -1; j < 2; j++){
if (x + i < 0 || x + i > columns-1 || y + j < 0 || y + j > rows-1){
// outside the bounds of the map. do nothing
}
else if (i == 0 && j == 0){
// don't count ourselves
}

else if (gameboard[x+i][y+j] == 1){
neighbors++;
}
}
}

return neighbors;
}
void DrawBoard(){
for (int i = 0; i < columns; i++){
for (int j = 0; j < rows; j++){
if (gameboard[i][j] == 1) fill(0);
else fill(255);
stroke(0);
rect(i*tile_size, j*tile_size, tile_size, tile_size);
}
}
}

Once you have the complete code, running the sketch will produce a random game of life. Each new play will be unique. Experimenting with the number of rows and columns will give you a smaller or larger map, and changing the tile size will make for interesting results.

The beauty of Cellular Automata is in it’s simplicity. Simple rules can lead to infinite variety. That alone has a certain aesthetic, and a kinship with nature.

Using Processing, anyone can learn and take advantage of the power of code to produce beauty.

About Us

Learning at the speed of light.

We created Start Prism to help students learn programming. You can find exercises and recent tutorials below.

Topics Quizzes Tutorials

1 comments

April 13, 2021, 9:29 a.m.

Posted By:

Jesse April 13, 2021, 9:29 a.m.

This looks fantastic. I really like this design.

Leave a comment