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.

Monday, February 14, 2011

Jiffle meets Mandelbrot


The Jiffle scripting language has been getting some new bells and whistles recently. While the idea is still to keep the language small and simple, by adding a few extra constructs it becomes much more flexible.

Above is the JiffleDemo application showing a script that uses two of these new constructs, scoped variables and loops, to generate a binary image of the Mandelbrot set. Below is the script itself (it is one of the examples distributed with JAI-tools).

The "init" block is where you declare variables that you want to remain in scope throughout processing. Jiffle calls these image-scope variables. Variables declared outside this block are termed pixel-scope, ie. their values are discarded after each pixel is processed. In the Java runtime class that the Jiffle compiler creates, the image-scope variables are class fields while the pixel-scope variables are local to the evaluate method called to process each destination image pixel. The runtime class is also provided with a getter method for the image-scope variables so you can retrieve them after running the script. You could use this, for instance, to count pixels that are in a certain value range.

The script below uses an until-loop (lifted from Ruby) for the iterative calculation. Jiffle also offers a while-loop and I'm musing about adding some kind of for-loop syntax as well.

/*
* An example Jiffle script: draws a binary
* image of the Mandelbrot set. Adapted from
* C code at http://warp.povusers.org/Mandelbrot/
*
* Author: Michael Bedward
*/

/* We declare variables that we want to remain
* in scope between pixels in the 'init' block.
* The functions width() and height() return
* the dimensions of the destination area.
*/
init {
MaxIter = 30;
MinRe = -2.0;
MaxRe = 1.0;
MinIm = -1.2;
MaxIm = MinIm + (MaxRe-MinRe) * height() / width();
Re_scale = (MaxRe-MinRe)/(width()-1);
Im_scale = (MaxIm-MinIm)/(height()-1);
}

/* Calculations performed for each pixel.
* The functions x() and y() return current
* pixel coordinates.
*/
c_im = MaxIm - y()*Im_scale;
c_re = MinRe + x()*Re_scale;

Z_re = c_re;
Z_im = c_im;

outside = 0;
n = 0;

/* Jiffle has an 'until' loop construct */
until (n >= MaxIter || outside) {
Z_re2 = Z_re*Z_re;
Z_im2 = Z_im*Z_im;
outside = if (Z_re2 + Z_im2 > 4);
Z_im = 2*Z_re*Z_im + c_im;
Z_re = Z_re2 - Z_im2 + c_re;
n++ ;
}

/* The variable 'result' represents the
* destination image (you can use any name
* you like in your own scripts).
*/
result = outside;

As an aside, I recall using much the same algorithm, though in FORTRAN, to generate Mandelbrot set images on an 80286 PC last century. Some people never change...

Wednesday, February 9, 2011

Continuous JAI-tools builds

Now that the project is getting bigger and (gasp) actually being used it was time to get more organized about our build process, or rather, to actually have one. To date we've relied on an ad hoc process of people checking out the sources, building them on their local system, and then reporting that Michael has broken the build again.

Now, courtesy of the kind folks at OpenGeo, in particular Justin Deoliveira, JAI-tools has entered the world of continuous integration. OpenGeo's Hudson server now builds the development code (ie. sources from trunk the project's subversion repository) and you can check the status of the builds at http://hudson.opengeo.org/hudson/view/jai-tools/. If you are subscribed to the project discussion list you will also be sent notices of any build failures.

Thanks to Justin and Andrea Aime for organizing all of this.