So how do our fish perform over the generations? It’s interesting to see that after about fifty generations, the population average, best and worst performance stabilises. In the early generations, some efficient fish were able to get comparably high scores because of weak competition, but as the generations progressed, the weaker fish were replaced with children of the fitter fish.

Now, here, you see, it takes all the running you can do, to keep in the same place — Lewis Carroll, Alice Through the Looking Glass

Some Thoughts on the Project

There were a few things I had to tweak to get the fish to behave in a way I wanted. First was the rate of convergence of the population. I adjusted this by choosing the number of individuals to select for breeding in the next generation. When I set this to 20% of the population, the population was very quick to converge, setting it to 50% seemed to work better. Increasing the population size also slows down convergence. Another variable I experimented with was the number of food. I noticed that a higher fish to food ratio led the fish to converge more quickly and to move towards food more aggressively.

My first experimental runs did not penalise against organisms getting too close together, and so when the population converged, all the fish had similar brains and would clump together if they were after the same food. This led me to try out reward shaping. To encourage the fish not to clump up, I created a small cost to their fitness if they got too close to each other. The ‘small cost’ I tried first was too big and sent the fish swimming away from each other and off the screen away from the food. Getting the fish to behave how I wanted them to was a matter of testing different cost values.

In generation 100, it looks like a fish that ignored the other fish and went straight for the food might do well. I reasoned that a fish that ignores other fish when all the other fish avoid it might excel. This strategy would not always do well though, because when it’s strategy became the dominant one in the population, the fish would clump together. This type of pressure is called frequency-dependent selection and can sometimes create a cyclic pattern in behaviour. Despite searching, I wasn’t able to observe this in any of the experimental runs.

Lastly, there was a bug in my code. My code still ran and the fish still optimised, it was only after staring at their behaviour for several generations that I realised something must be wrong. Optimising systems with stochastic elements can make discovering bugs difficult. The system will still likely achieve some incremental improvements, and odd behaviour can be attributed to mutations.

Bloopers

Lot’s of things can go wrong when coding up ‘artificial life’. Here are some of the weirder things I came across.