CS 467 - Ants

Due: 2020-04-21 5:00p

Goals

  • Implement a simple example of ant painting

Prerequisites

  1. Accept the assignment on our Github Classroom.
  2. Download the git repository to your local computer.

Assignment

Practical 8 Goal

In this practical, you will do a little ant painting. The inspiration for these ants is a description in Artificial Art Made with Artificial Ants by Monmarché et al. (here is the original paper, if you care to read it -- we are focusing on the description in section 11.4).

Our ants are fairly simple beings. They wander around, leaving a color trail behind them (a pheromone trail, if you like). In addition to leaving a trail behind them, they are on the lookout for other pheromone trails, and if they see one of a color they don't like they will sometimes stop their random wander and follow the rival trail, covering it up. By tinkering with the probabilities that they will consume other trails or turn, we can observe a number of different patterns.

More specifically, we will provide each ant with five different tunable parameters:

  • paintColor The color deposited by the ant as it moves around
  • rivalColor The color the ant is looking to cover
  • aggression The probability that the ant will follow a color trail
  • leftProb The probability that it will turn left (a value between 0-1)
  • rightProb The probability that it will turn right (a value between 0-1)

At every time step, the ant will:

  • Check the neighborhood for the presence of rival pheromone trails. If it finds one, based on its aggression setting it will opt to follow it or not.
  • If no pheromone trails are found, the ant will stochastically decide which direction it wants to go next: straight ahead, left or right. A property of the original ants was that right or left could be along the diagonals or a full 90 degree turn. We will stick to 90 degree turns.
  • Advance forward one step.
  • Paint its locale with its pheromone.

Starter code

I've given you some starter code with the boilerplate that you should be able to produce in your sleep. We have a setup and draw function as well as a class descriptor for the Ant class. The draw function iterates over the ants and calls their update function. This time the ant has not explicit draw function as we can just take care of it in update.

The Ant

The first thing you are going to do is to add position and heading to the ant constructor. The position should be a vector initialized to a random location on the screen. Use Math.floor to make sure that the random values you calculate for the x and y components are integers.

The heading property will also be a vector, and will be the direction the ant is facing (we could also think of it as the ant's velocity, if you like). To compensate for the fact that our ant can only make right angle turns, we will allow the initial heading to be in any of the eight possible directions. To implement this, initialize the vector's components with random([-1, 0,1]). As hopefully you recall, the random function can pick randomly from a list. Of course, there is a non-trivial chance that this will produce a heading of (0,0), freezing the ant. Check for this and if it has happened, set the x component of the heading to random([-1, 1]).

update

The first line in the update function is a call to setHeading. The setHeading function will determine any turns that the ant will make. We will leave that to one side at the moment.

Apply the standard update to the position using the heading. We would like the canvas to wrap, so do the normal voodoo to get the ant to wrap around.

Finally, set the stroke to the color of your ant and draw a point at the ant's location.

Create an ant in setup and try it out. The only parameter that will really matter is the paintColor. This won't be too exciting, as the ant will never turn -- it will just head off in a totally straight line. This is just a sanity test.

setHeading: making turns

Let's turn our attention to setHeading now. We will start by just focusing on the steering. Our ant can either go forwards, right or left. It has properties leftProb and rightProb to determine when it will turn (with the assumption that the rest of the time it should go straight).

The way we will calculate this is by using random() to generate a random number between 0 and 1 (calling it with no arguments will do this). We then think of the probabilities as being stacked together to sum to 1. So, for example, if leftProb is .25 and rightProb is .25, then we give lefts turns the numbers from 0-.25, right turns the numbers from .25-.5, and forward from .5-1.

So, if the random number is less than the leftProb, then the ant will turn left. If it is less than the sum of leftProb and rightProb it will turn right. Otherwise it goes straight (and we don't have to do anything to the heading).

Because all of our turns are by 90 degrees, the new headings are fairly easy to calculate.

To make a left turn, the x component of the heading is set to the old y component, and the y component is set to the negation of the old x component. So, for example, a heading of (1,0) becomes (0, -1), and a heading of (1,-1) becomes (-1, -1). If this seems odd, draw yourself a picture.

Right turns are just the inverse of this. The x component becomes the negation of the old y value, and the y value becomes the old x value.

Try this out. The ant will now do a bit of a wander. If you set the turn probabilities fairly low, it will tend to set out in long straight lines, while a high probability will cause it to stay a little more local and densely fill in a small region.

setHeading: pheromone detection

The last piece to write into the ant is its pheromone detection. This will go before the turn logic in getHeading.

Start by generating another random number and checking if it is below the aggression property value. If it is, then the ant will look around for trails to follow (which is not a guarantee that there will be one). If not, you should just skip down to the turn logic.

To check for pheromone trails in the area, you will use a nested loop structure like you did for the cellular automata to check the neighborhood, with both loops iterating from -1 to 1.

Inside the loops, calculate the position of the neighborhood cells by adding the offsets to the ant's position. Make sure that you account for the wrap around, and that you don't consider the ant's current position.

Once you have figured out the real position of the neighbors on the canvas, use the get function to get the color at that position.

You then want to compare that pixel color to the rivalColor. I provided you with a colorMatch function that will do this comparison for you and return a Boolean indicating if this is a pheromone trail worth pursuing. This function is based on the original function in Monmarché's paper. This is actually a luminance based function that is designed to cope with colors that are "close". While our ants are painting with pure color, we could certainly add a little blur or transparency to make the image more interesting, so I left the function as is.

You may wonder at the strange constants in the function. Or, perhaps you took 101 with me or TA'd for the class and you recognize them. These are the values for computing "perceptual luminance" -- luminance as perceived through human eyes.

If the pheromone trail is in several directions, we would like our ant to pick one at random, so we will just make a list of the headings that lead towards trails we want. Create an empty array before the nested loops and call it options. When you detect a neighbor with the right color, push a short array containing the two offsets into options.

Outside of the for loops, check if there are any options in options. If there are, use random to pick one, and use it to set the heading. If there aren't, advance to the steering logic.

Experiment

Now you should have some interesting ant behavior. Add some ants with different colors. Tinker with strategies. Who will dominate? Ants who do long runs and turn infrequently, or ants that like tight circles? What happens when the aggression is turned up to 1? or turned down low? The different behaviors you specify will lead to different end results. Note that you don't need many ants -- the image above was produced by eight ants (though it did take some time).

There is a lot you could play with here. In addition to trying different strategies and different numbers of ants, you could experiment with different drawing styles, such as larger dots, or alpha blending.

Finishing up

Commit your changes to git and push them back up to GitHub. I will find them there.