Termites

Consider a simple termite that only follows these simple rules:

  1. If you see a wood chip, and you are not carrying one, pick it up
  2. If you see a wood chip, and you are carrying one, then drop it
  3. Otherwise, move in your default direction
  4. Every once in awhile, change direction

Now, imagine a few of these termites roaming around a flat world, scattered with wood chips. What would happen? How can we model this world?

Simulation

First, we must identify the objects we wish to model. Possibilities include:

  • termites
  • world
  • wood chips

First, let's consider what properties a termite should have:

  • position in the world
  • whether it is carrying a wood chip or not
  • direction heading (vector, velocity on x and y)

Also, what actions must a termite be able to do:

  • move in the world
  • pick up a wood chip
  • drop a wood chip
  • draw itself

We might want to add other methods, but it needs to at least support those.

Termite

So, let's put together a termite class. It might look something like:

class Termite {
    int x;
    int y;
    int vx;
    int vy;
    int carrying;

    Termite(int x, int y) { //constructor
        this.x = int(x);
        this.y = int(y);
        this.randomizeDirection();
        this.carrying = 0;
    }

    ...
}

In thinking about the world, and wood chips, it might be that representing them as an object might be overkill. For example, if we imagine the world as a 2D matrix, then we could represent the presence of food with a 1, and the absence of food with a 0. Likewise, carrying can just be a 1 (for yes), and 0 (for no).

It may be that instead of having termites bounce off of the walls (like a ball simulation) that we instead have them "wrap" around (like a torus). For example, if a termite goes too far to the left, it will wrap around to the right, and instantly appear there. Likewise, if a turtle goes below 0 or above height, then it wraps back around. Thus, we implement an infinite, repeating surface.

This putting it all together might look like the following.

Note:

  • termites are represented by ovals. You could make them face the correct direction, or draw legs on them.
  • termites turn red when they are carrying a wood chip
  • wood chips are represented by red squares
In [86]:
Termite [] termites; // the array of termites; we'll decide how many later
int[][] gworld; // the 2D world
int RADIUS = 5; // distance that we can sense a wood chip

class Termite {
    int x;
    int y;
    int vx;
    int vy;
    int carrying; // zero - not carying chip; 1 - carrying chip
    
    Termite(int x, int y) {
        this.x = int(x);
        this.y = int(y);
        this.randomizeDirection();
        this.carrying = 0;
    }
    
    void draw() {
        if (this.carrying == 0) {
            fill(0);
        } else {
            fill(255, 0, 0);
        }
        ellipse(this.x, this.y, 10, 5);
    }
    
    int get_x(int delta) {
        int dx = this.x + delta;
        if (dx >= width)
            dx -= width;
        if (dx < 0)
            dx += width;
        return dx;
    }
    
    int get_y(int delta) {
        int dy = this.y + delta;
        if (dy >= height)
            dy -= height;
        if (dy < 0)
            dy += height;
        return dy;
    }
    
    void step() {
        this.x = this.get_x(this.vx);
        this.y = this.get_y(this.vy);
    }
    
    void randomizeDirection() {
        this.vx = 0;
        this.vy = 0;
        while (this.vx == 0 && this.vy == 0) {
            this.vx = floor(random(3)) - 1;
            this.vy = floor(random(3)) - 1;
        }
    }
    
    void pickup_chip(int nx, int ny) {
        // Pick it up!
        world[nx][ny] = 0;
        this.carrying = 1;
    }
    
    void drop_chip(int nx, int ny) {
        // Drop it!
        world[nx][ny] = 1;
        this.carrying = 0;
    }

    void move() {
        if (random() < .01) { // 1% of the time, change directions
            this.randomizeDirection();
        }
        this.step();
        // not carrying a woodchip
        if (this.carrying == 0) { // can you smell a chip?
            for (int i = -RADIUS; i <= RADIUS; i++) {
                if (this.carrying == 0) {
                    for (int j = -RADIUS; j <= RADIUS; j++) {
                        nx = this.get_x(i);
                        ny = this.get_y(j);
                        if (world[nx][ny] == 1) { // yes!
                            this.pickup_chip(nx, ny);
                            break;
                        }
                    }
                }
            }
        } else { // carrying a woodchip
            // is there a chip nearby?
            boolean found = false;
            for (int i = -1; i <= 1; i++) { 
                if (!found) {
                    for (int j = -1; j <= 1; j++) {
                        nx = this.get_x(i);
                        ny = this.get_y(j);
                        if (world[nx][ny] == 1) { // yes!
                            // One nearby!
                            found = true; 
                            break;
                        }
                    }
                }
            }
            if (found) {
                // now, find an empty spot nearby and drop it!         
                for (int i = -1; i <= 1; i++) {
                    if (this.carrying == 1) {
                        for (int j = -1; j <= 1; j++) {
                            nx = this.get_x(i);
                            ny = this.get_y(j);
                            if (world[nx][ny] == 0) {
                                // Drop it!
                                this.drop_chip(nx, ny);
                                break;
                            }
                        }
                    }
                }
            }
            if (this.carrying == 0) {
                // head in opposite direction
                this.vx = -this.vx;
                this.vy = -this.vy;
                // make sure you are far enough away
                for (int i = 0; i < RADIUS * 1.5; i++) {
                    this.step();
                }
            }
        }
    }
}

void setup() {
    size(250, 250); // the world
    termites = new Termite[10]; // start termites
    for (int i =0; i < termites.length; i++) {
        termites[i] = new Termite(random(width), random(height));
    }
    world = new int[width][height]; // match the canvas size
    for (int i =0; i < width; i++) {
        for (int j =0; j < height; j++) {
            world[i][j] = 0;
        }
    }
    for (int i =0; i < 200; i++) { // put some wood chips out:
        world[int(random(width))][int(random(height))] = 1;
    }
}

void draw() {
    background();
    noStroke();
    // Move the termites:
    for (int i =0; i < termites.length; i++) {
        termites[i].move();
        termites[i].draw();
    }    
    // Draw them:
    for (int i =0; i < width; i++) {
        for (int j =0; j < height; j++) {
            if (world[i][j] == 1) {
                fill(255, 0, 0);
                rect(i, j, 3, 3);
            }
        }
    }
}
Sketch #86:

Sketch #86 state: Loading...