Art with TURmites/Cellular ANTomata

Which is the better pun: ‘turmite’ or ‘cellular antomata’? I couldn’t decide, so the title has both …

This semester, I’m taking a course on artificial life which has us programming some neat things. Recently, I completed a programming assignment to generate turmites. The best part about that programming assignment was a section that had us generate random rules for the turmite’s behaviour and classifying them in terms of wolfram’s four classes of cellular ant-omata. Being the glitch art lover that I am, I was pretty interested with the outcome of some of the ants. A lot of them looked dumb, like this one:

bla ant

But a lot of them were super cool, like this one, which kind of looks like an Escher drawing:

purple escher

Or this one, which I’ve named “You shall not pass”:

you shall not pass

I might have spent just as much time generating and sorting through the generated images as I did writing the code to generate them. In this post, I mainly just want to take about the code.

A lot of the code for displaying and saving images in Java was provided for us. The code I contributed was the turmite object and the algorithms for running based on a ruleset and a way to generate random rulesets.

We first started off with things every turmite needs:

1
2
3
4
5
6
7
8
9
// a handy dandy array of all the colors in the image:
private int[][] colorArray;
// an array containing a numerical representation of all the rules:
private int[][] ruleArray;
// and variables that deal with the ant's current predicament:
private int state;
private int direction;
private int row;
private int col;

There are also some extra variables for image-related things like the actual image, default color, grid size, etc.

The constructor is straightforward:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// a bunch of necessary but not exciting defaults
public Turmite(int gridSize, int cellSize, int[][] ruleArray) {
  this.gridSize = gridSize;
  this.cellSize = cellSize;
  image = new AutomatonImage(gridSize, cellSize, defaultColor);
  colorArray = new int[gridSize][gridSize];
  this.ruleArray = ruleArray;

  // defaults
  state = 0;
  direction = 1; // 0 - Left, 1 - Up, 2 - Right, 3 - Down
  row = gridSize / 2; // starts off in middle
  col = gridSize / 2; // starts off in middle
}

And the actual algorithm, save for the turning and movement logic, a pretty straightforward task:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// for a certain number of steps, appl a rule, color the cell, and move forward
// at the end, mark the image complete
public void step(int numSteps) {
  for (int i = 0; i < numSteps; i++) {
    applyRule();
    color = convertNumToColor(colorArray[row][col]);
    image.colorCell(col, row, color);
    moveForward();
  }
  image.markComplete();
}

private void applyRule() {
  int readState, readColor;
  int newState, newColor, newTurn;
  int count = 0;

  // look through all rules until we find one that matches our current state
  do {
    readColor = ruleArray[count][0];
    readState = ruleArray[count][1];
    newColor = ruleArray[count][2];
    newState = ruleArray[count][3];
    newTurn = ruleArray[count][4];
    count++;
  } while (readState!=state || readColor!=colorArray[row][col]);

  // turn, color the square in the color array, and save the new state
  turn(newTurn);
  colorArray[row][col] = newColor;
  state = newState;
}

Then, it is only when our client code creates a new object does the magic happen:

1
2
3
4
5
6
int[][] langtonRules = {{0, 0, 1, 0, 0}, {1, 0, 0, 0, 1}};

Turmite langtonAnt = new Turmite(gridSize, cellSize, langtonRules);
ant.step(300000);
ant.getImage().saveImage("randomant" + i + ".png");

And that creates Langton’s ant! The rule array here is simple. Because a combination of state and color causes the ant to color the square, change state, and change turning direction, we need to have |colors| * |states| amount of rules - one rule per color/state pair.

The array just cleans up the process a little bit. For each subarray, the index …

  • 0 is current color
  • 1 is current states
  • 2 is the new color
  • 3 is the new state
  • 4 is the turning direction

Langton’s ant is straightforward: there is only 1 state, and only 2 colors. On a white square, the ant colors the square black, and turns left. On a black square, the ant colors the square white, and turns right.

But the fun is in generating new rulesets. To generate a ruleset:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Step 1 - generate random number of colors for grid
int colors = rand.nextInt(9)+2;;
// Step 2 - select random number of internal states
int states = rand.nextInt(9)+2;
// Step 3 - create an array containing all possible combinations of rules
int[][] ruleArray = generateRuleArray(colors, states);

public static int[][] generateRuleArray(int colors, int states) {
  // a rule consists of:
  //   a new color to be written
  //   a new internal state
  //   a turning direction (straight, left, or right)

  // length of array indexes are:
  //   color, state, new color, new state, turning direction
  // height of array will include:
  //   colors*states different rules
  int[][] ruleArray = new int[(colors+1)*(states+1)][5];

  Random rand = new Random();
  int current = 0;

  for (int oldColor = 0; oldColor <= colors; oldColor++) {
    for (int oldState = 0; oldState <= states; oldState++) {
      ruleArray[current][0] = oldColor;
      ruleArray[current][1] = oldState;
      // can't set a rule to create a new color or state if our total number
      // of colors and states doesn't contain it
      ruleArray[current][2] = rand.nextInt(colors+1);
      ruleArray[current][3] = rand.nextInt(states+1);
      ruleArray[current][4] = rand.nextInt(3);
      current++;
    }
  }

  return ruleArray;
}

And that’s it! There was also some code to generate images, save images, and output a list of rulesets to a text file (for later enjoyment), as well as some more logic for making the turmite move correctly. But at the core, we’re just working with a simple algorithm, a simple way to store rules, and a simple way to generate them. And we get a lot of neat pictures out of it all!

The code, and some more explanations, are available here. Check it out if you want, or not if this was enough Java for you (I can’t blame you…). Overall, a pretty fun assignment, and even more fun results!

Brandeis MakeMonth Catch Up