Conway's Game of Life - Trying it on images

The beginning

In the previous article I wrote about pixel sorting. Today, in this article, I'm going to show you what can Life do to an image. It can also generate glitch art or databending in a totally different way. The transformation below is an example what can happen after 1000 living generations.

In this example, red pixels were used as alive cells.

What is Conway's Game of Life?

Game of Life, or simply, Life is a cellular automation devised by a famous British mathematician John Conway. It's a simple zero-player game and it's result is determined by the initial configuration. The game has four simple rules that cells follow. If the cell is alive and it has less than 2 living neighbours or more than 3 living neighbours, it dies from underpopulation or overpopulation respectively. If it has 2 or 3 living neighbours it survives. If the cell is dead and if it has exactly 3 living neighbours, it becomes alive as if by division. That's basically all the info you need to know for the sake of this article. The only other thing I'll state is that Life is a Turing-complete machine, so with right initial states it can "do" any computer algorithm.

The idea

As I've said the game needs a way to know which cells are alive and which are not, and for this, I'll use pixels. As I've stated in the previous article, each pixel is just an object represented by a 32-bit integer that can be split into four 8-bit integers that determine it's core color values (RGB values). Since every color is represented by an 8-bit integer, the value ranges from 0 to 255. These values will determine our cells. The cell is alive if it's (red/green/blue) color value is over 200.

The algorithm(s)

All of the code snippets below are written in Java and are a part of open-source project PxlSort. The whole code and project can be freely accessed at my github repository.

Loading the image

For keeping images in memory, I’m going to use a standard Java structure called BufferedImage. It’s perfect for sorting because of the following two methods:

		int getRGB(int x, int y); /* returns the 32-bit integer representation of a pixel at x, y */
		void setRGB(int x, int y, int rgb); /* sets 32-bit RGB value to the pixel at x, y */
Determining core color values is quite simple.
		getRGB(x, y) & 0x00ff0000 >> 16; /* returns the first 8-bit value - red color */
		getRGB(x, y) & 0x0000ff00 >> 8; /* returns the second 8-bit value - green color */
		getRGB(x, y) & 0x000000ff;  /* returns the third 8-bit value - blue color */
Now that the image is in memory we can get onto the main algorithm, the Game of Life.

The Game of Life

First, we need a simple wrapper function that determines how much generations we will simulate and which color will determine alive cells.

public void gof(int iter, int by) { 
		this.by = by;
		
		for(int i = 0; i < iter; i++) 
			gameOfLife();
	}

Now for the actual life algorithm. For this, we will need two different methods.

First one is manCell, a method used to kill or create living cells. The idea here is the following: If a cell dies, it gets replaced with a dead neighbour. If a cell is born, it takes the form of another living neighbour. I use these rules so the pixels in the image remain as untouched as possible.

public void manCell(int i, int j, int state) {
		int value;
		
		for(int x = 0; x < 3; x++) {	
			try {
				if(by == 1)
					value = (image.getRGB(i-1, j+x-1) & 0x00ff0000) >> 16;
				else if(by == 2)
					value = (image.getRGB(i-1, j+x-1) & 0x0000ff00) >> 8;
				else 
					value = (image.getRGB(i-1, j+x-1) & 0x000000ff);
				
				if((value > Info.ALIVE_CONSTANT && state == 1) || (value <= Info.ALIVE_CONSTANT && state == 0)) {
					image.setRGB(i, j, image.getRGB(i-1, j+x-1));
					return;
				}
			} catch(ArrayIndexOutOfBoundsException e) {}
		}

		for(int x = 0; x < 3; x++) {
			try {
				if(by == 1)
					value = (image.getRGB(i+1, j+x-1) & 0x00ff0000) >> 16;
				else if(by == 2)
					value = (image.getRGB(i+1, j+x-1) & 0x0000ff00) >> 8;
				else 
					value = (image.getRGB(i+1, j+x-1) & 0x000000ff);

				if((value > Info.ALIVE_CONSTANT && state == 1) || (value <= Info.ALIVE_CONSTANT && state == 0)) {
					image.setRGB(i, j, image.getRGB(i+1, j+x-1));
					return;
				}
			} catch(ArrayIndexOutOfBoundsException e) {}
		}

		for(int x = 0; x < 2; x++) {
			try {
				if(by == 1)
					value = (image.getRGB(i, j+2*x-1) & 0x00ff0000) >> 16;
				else if(by == 2)
					value = (image.getRGB(i, j+2*x-1) & 0x0000ff00) >> 8;
				else 
					value = (image.getRGB(i, j+2*x-1) & 0x000000ff);

				if((value > Info.ALIVE_CONSTANT && state == 1) || (value <= Info.ALIVE_CONSTANT && state == 0)) {
					image.setRGB(i, j, image.getRGB(i, j+2*x-1));
					return;
				}
			} catch(ArrayIndexOutOfBoundsException e) {}
		}
	}
    

Using these try and catch blocks is not really a nice programming style, but it does the job and eliminates unnecessary if clauses.

For the main algorithm, I use the following method. It loops through all the pixels (read cells) and determines what happens to each.

	public void gameOfLife() {
		int h = image.getHeight();
		int w = image.getWidth();

		for(int i = 0; i < w; i++) {
			for(int j = 0; j < h; j++) {				
				int val;

				if(by == 1)
					val = (image.getRGB(i, j) & 0x00ff0000) >> 16;
				else if(by == 2)
					val = (image.getRGB(i, j) & 0x0000ff00) >> 8;
				else 
					val = (image.getRGB(i, j) & 0x000000ff);	

				boolean alive = val > Info.ALIVE_CONSTANT;
		
				int value, aliveNeighbours = 0;
		
		
				for(int x = 0; x < 3; x++) {	
					try {
						if(by == 1)
							value = (image.getRGB(i-1, j+x-1) & 0x00ff0000) >> 16;
						else if(by == 2)
							value = (image.getRGB(i-1, j+x-1) & 0x0000ff00) >> 8;
						else 
							value = (image.getRGB(i-1, j+x-1) & 0x000000ff);
		
						aliveNeighbours = aliveNeighbours + (value > Info.ALIVE_CONSTANT ? 1 : 0);
					} catch(ArrayIndexOutOfBoundsException e) {}
				}
		
				for(int x = 0; x < 3; x++) {
					try {
						if(by == 1)
							value = (image.getRGB(i+1, j+x-1) & 0x00ff0000) >> 16;
						else if(by == 2)
							value = (image.getRGB(i+1, j+x-1) & 0x0000ff00) >> 8;
						else 
							value = (image.getRGB(i+1, j+x-1) & 0x000000ff);
		
						aliveNeighbours = aliveNeighbours + (value > Info.ALIVE_CONSTANT ? 1 : 0);
					} catch(ArrayIndexOutOfBoundsException e) {}
				}
		
				for(int x = 0; x < 2; x++) {
					try {
						if(by == 1)
							value = (image.getRGB(i, j+2*x-1) & 0x00ff0000) >> 16;
						else if(by == 2)
							value = (image.getRGB(i, j+2*x-1) & 0x0000ff00) >> 8;
						else 
							value = (image.getRGB(i, j+2*x-1) & 0x000000ff);
		
						aliveNeighbours = aliveNeighbours + (value > Info.ALIVE_CONSTANT ? 1 : 0);
					} catch(ArrayIndexOutOfBoundsException e) {}
				}
		
				if(alive && (aliveNeighbours < 2 || aliveNeighbours > 3)) 
					manCell(i, j, 0);
				else if(!alive && aliveNeighbours == 3) 
					manCell(i, j, 1);		
			}
		}

	}
    

That's basically it. The effects this simple game produces can truly be magnificent.