Monday, February 21, 2011

Jiffle plays the Game of Life


After showing off with fractals in the last Jiffle demo, it seemed only natural to delve back in time for another classic to illustrate new features and uses of the language. And nothing could be more classic than John Conway's Game of Life: a cellular simulation with simple rules that can display exceptionally complex behaviour. If you're not familiar with Life, this Wikipedia article is a good place to start. You can also marvel at the compendious collection of Life patterns at LifeWiki:

The Java Swing program shown in the screen-shot above uses a Jiffle runtime object as a simulation engine to play Life. Two different Jiffle scripts are used: one which simulates a world with edges; and a second where the world is a toroid, ie. opposite edges of the image are joined to form a continuous surface. In both scripts the world is an image where an unoccupied location is represented by pixel value 0 and an occupied location by pixel value 1.

Here is the entire script for the world with edges:

options { outside = 0; }

n = 0;
foreach (iy in -1:1) {
foreach (ix in -1:1) {
n += world[ix, iy];
}
}

n -= world;
nextworld = (n == 3) || (world && n==2);

The expression world[ix, iy] accesses a relative neighbour location. For example world[-1, 1] would get the value of a pixel at (x-1, y+1) where x and y are the ordinates of the current pixel.

The two foreach loops use integer sequence syntax, startValue:endValue, to iterate over the 3x3 neighbourhood centred on the current pixel and count the number of occupied cells (those with a value of 1). The rules of Life are expressed only in terms of the number of neighbouring cells occupied, so we adjust the value of n by subtracting the value of the current pixel.

The options block at the top of the script sets a value to be returned for any neighbour positions that are beyond the bounds of the image. Without this option, the runtime object would throw an Exception at the very first pixel when trying to access the relative neighbour position world[-1, -1].

The final line of the script expresses all of the Game of Life rules in a single statement ! It uses naked conditional statements which return 1 or 0.

A second Jiffle script provides an alternative version of the simulation, where the image representing the world is treated as a toroid:

n = 0;
foreach (iy in -1:1) {
yy = y() + iy;
yy = if (yy < 0, height() - 1, yy);
yy = if (yy >= height(), 0, yy);

foreach (ix in -1:1) {
xx = x() + ix;
xx = if (xx < 0, width()-1, xx);
xx = if (xx >= width(), 0, xx);
n += world[$xx, $yy];
}
}

n -= world;
nextworld = (n == 3) || (world && n==2);


This script works with absolute rather than relative neighbour positions. These are indicated by the $ prefix. When a neighbour position is beyond an edge, it is adjusted to the corresponding position at the opposite edge. Note that we don't need the outside option in this script.

The Game of Life is an iterative simulation where the output for time t becomes the input for time t+1. To accomplish this, the demo program caches the runtime objects compiled from the Jiffle scripts, and uses them repeatedly with two images which are represented by the variables world and nextworld in the scripts. These images are simply swapped between source and destination roles at each time step as shown in this code fragment...

activeRuntime.setSourceImage(WORLD_NAME, curWorld);
activeRuntime.setDestinationImage(NEXT_WORLD_NAME, nextWorld);
activeRuntime.evaluateAll(null);

TiledImage temp = curWorld;
curWorld = nextWorld;
nextWorld = temp;

You can view and download the complete source code for this program from the JAI-tools web site.

No comments:

Post a Comment