Natural Selection in a Digital Ecosystem
Rick van Kersbergen
In this report I will go through the steps I have taken to create my product. My final product is a digital simulation of an ecosystem where natural selection picks the fittest organisms and lets them reproduce. The subject of my product is Artificial Intelligence. This is also the guild I was placed in. The reason I chose to make something with AI, is that I probably want to do a master’s programme Artificial Intelligence at the Vrije Universiteit van Amsterdam. I thought Game Programming and Engineering was the perfect time to challenge myself and finally do something with Artificial Intelligence to prepare myself for the future of my education.
During the project I’ve done tons of research on natural selection, simulations and genetic algorithms. Throughout this paper, references can be found to these subjects. As usual, at the end of this rapport, I’ve given my sources. Furthermore, I want to thank the teachers that lead this project and the students I’ve had the pleasure of doing this project with. These people helped me a lot during the development of my final product.
Table of contents
- The Research on Natural Selection and Genetic Algorithms.
- The Project Setup
- The Organisms
- The Environment
- The Results
- The Conclusion
- The Continuation
- The Code
- The Sources
The Research on Natural Selection and Genetic Algorithms
In 1859, a man named Charles Darwin published his book On The Origin of Species. In this book, Darwin published his ideas about how organisms change over time based on changes in heritable physical and/or behavioral traits. This meant that creates weren’t just created as they were and stayed that way. This is now know as the theory of evolution.
Darwin further explains something which he calls Survival of the Fittest. Due to modern imperialism by the Europeans, the meaning of this sentence was often changed to mean survival of the strongest. However, this is not what Darwin meant with “fittest”. Fittest refers to the creatures within a population that are the most adapted to their environment. This means that they are better equipped with he necessary traits to survive in their environment. These creatures will eventually have offspring that also carry these genes with their favorable traits.
An example: The environment of the proto-giraffe changes and these creatures find themselves in an environment with trees of greater and greater height. Creatures with longer necks can reach the leafs of these trees more easily than their kin with shorter necks. This means that the creatures with the longer necks have a better chance of reproduction than the other creatures. Their genes will be passed on, while the genes of the creatures with shorter necks won’t. Over time, the population of these proto-giraffes will evolve to have longer necks and eventually transform into the giraffes that we all know and love today.
A Genetic Algorithm is an algorithm based on this theory of Charles Darwin. This algorithm uses the theory of natural selection to find the best solution to a problem. The algorithm does this by combining the “fittest” solutions and creating new ones. Eventually, a best solution will be found. Without this Genetic Algorithm, my project wouldn’t have turned out the way it is. This algorithm is therefore essential. The Genetic Algorithm does not know only one way of implementation. I have tweaked the operation of the algorithm a bit to be better suited to my project. How I tweaked the algorithm will be told in the next chapter. The basis of the algorithm stays same. What is this basis and how does it work?
To implement the Genetic Algorithm, we begin with a population. This population consists of a certain quantity of individuals, each equipped with a certain set of genes. All genes of an organism are saved in a chromosome, which is basically the DNA of the organism. Each gene has a value between 0 and 1.
Each creature has a fitness score. This score ensures that creatures can compete with each other to be the best solution. The probability of reproduction of an individual is based on this score. A selection process will select a certain amount of individuals within this population for reproduction. Two parents will have offspring and create new individuals.
The reproduction process is the bread and butter of a genetic algorithm. The algorithm makes use of something called crossover. Within the chromosome, a crossover point will be chosen. Until the crossover point is reached, genes will be swapped among parents. Two new solutions (individuals) are created with swapped version of the genes of the parents.
Just as in real life, genes can mutate. What this means, is that genes have a probability to change value randomly. This gives a certain RNG element to the algorithm. However, this is how it works in real life as well. Randomly, a better gene value can be created due to mutation and this gene will be passed on to the next generation.
The Project Setup
In this chapter, I would like to explain what my idea was about how my project was going to work. I am going to explain my thought process on how I think the environment with it’s individuals was supposed to work and how I tweaked the genetic algorithm to fit my ideas better, while still producing better solutions.
First, an initial population is spawned in an environment. This population contains organisms. Each individual organism has its own DNA. This DNA contains four genes. I would like to get into the genes in the next chapter when I’m going to explain my organisms thoroughly. Anyway, these four genes each represent a trait. All traits together decide how well this organism is able to survive in the environment. Food is spawned into the world and organisms eat this to stay alive. Again, I will get deeper into the organisms’ mechanics in the next chapter.
The world has iterations. These iterations are depicted as a day and night cycle. Each day and night together are one iteration. Each organism has a fitness score. This fitness score goes up the more iterations the organism is alive, as the longer it’s alive, the better suited it is to the environment. After the fitness score reaches a certain number (this number is equal to the amount of iterations the organism is alive), the organism will be selected.
When an organism is selected, it is able to sense other selected organisms in a certain radius. When two selected organisms are close enough to one another, they move toward each other and mate. When they mate, with help of the the genetic algorithm, they will spawn two new organisms, each with new genes inherited from their parents. A slight mutation happens, because realistically, no child is exactly the same as their parent.
Over time, the organisms that don’t get to mate, will die off. The organisms that do, will pass on their genes to the next generation. This way, the population will evolve to be able to survive more easily in the environment they find themselves in. Values like the amount of mutation, how much food spawns in the world, how big the initial population is and how large the environment is all influence the development of the population. Different values will result in different solutions to the question “What is the best set of genes to survive in this environment”. More on this in the results chapter.
I thought it would be a good idea to create the organisms first and make sure the mating and DNA mechanics work, before I started working on the environment. There are a few things that every organism needs and those are the following:
- DNA and Genes
- Fitness Score
I will go through every characteristic named above and explain how it works and how I have implemented it. First, let’s dive into the DNA and Genes.
As explained earlier, each organism has DNA. This DNA is a chromosome existing of multiple genes. I made the decision to have each organism have four types of genes and therefore four different traits. These traits are Speed, Sense, Size and Resistance.
The Speed, as you might have guessed, dictates how fast an organism is able to move. The benefit of being fast is that you’re able to get to food quicker. The downside of being fast, is that it costs more energy to be fast and so the organism will be alive for less time unless it finds food.
The Sense trait dictates how far an organism can see. Let this be other organisms or food. The benefit of a heightened Sense is that you’re able to see other organisms and food earlier than organisms with a low Sense. The downside of this trait, is that it will cost you more energy to have a heightened sense.
The Size trait, as you might have expected, dictates how large an organism is. The benefit of being large is that you get an extra food source. If you are large enough, you’re able to eat not only the apples of the ground, you’re also able to eat the insects that walk around the map. These insects give you more energy than apples, but do move around slowly. The downside of being large is that it costs more energy.
The Resistance of an organism is sort of a special trait. The climate of the environment has a 10% chance each iteration to become very cold. Organisms with a higher resistance have a higher chance to survive this than other organisms. More on this climate in the environment chapter.
All organisms contain an float array where the value of these genes are saved. For each organism in the initial population, the values of these genes are randomized + an offset with the mutation value. The following generations no longer have randomized traits. Their genes are inherited from their parents and thus don’t need randomized values. What they do have, are slight mutations.
All these genes together, like you’ve read above, have influence on the energy usage of the organism. Each organism has therefore their own unique energy usage value. Each organism spawns with a certain amount of energy. This can be changed by the user. Right now, it’s set to 30. Each second, a certain amount of energy will be subtracted from this number. The value of this energy usage is based on the formula for kinetic energy. The higher the values, the more kinetic energy an organism needs and the faster an organisms’ energy depletes. The resistance gene has only an influence on the energy usage during the climate switch. When it gets cold, based on the amount of resistance of the organism, an extra amount of energy will be subtracted from their total energy. The higher the resistance, the less energy will be added to the energy usage during the winter.
What happens when the total energy of an organism depletes? Well, they die… This mechanic of subtracting energy and death is done by a Coroutine, As you notice in the image here, I set a state of an organism to “Death”. The behavior of each organism is based on which state they’re in. More on this later.
As stated earlier in this paper, each organism has a fitness score. This score increases with each iteration that they’re alive. When it reaches a certain value, they are added to a list of selected organisms. When another selected organism is in range, they move toward one another and have offspring together. The creation of children is done in the Mate method. This method is based on the genetic algorithm that was explained earlier in this paper. As you can see here, the crossover happens when the genes of the children are inherited from their parents. After the offspring is created, the parents go back to their wandering state and are “exhausted”, which means they can’t have children immediately again. They need to wait for a certain amount of seconds before they can be selected for mating again.
As mentioned earlier, organisms have certain “states”. The behavior of an organism is based on which state he is in. This is called a state machine. Each state has its own behavior. This is the state machine diagram where all states and conditions are visible. The “idle” state of an organism is Wander. When an organism wanders, it picks a random position in the environment and walks towards it. When the organism reached its target, it picks a new one. In the wander state, an organism can also detect food. When an organism finds food within its sense radius, its state changes to Eat. It moves towards the food and eats it. When the food has been eaten, the organism goes wandering again. Normally, an organism finds itself in this cycle. This cycle is broken however, when it gets selected and finds another selected organism. Its state changes to Mate. When in the mate state, all actions are stopped. This makes sure the overarching script “Population” can take control and make the mating happen.
In this chapter I will explain how the environment is made and how the surroundings of the organism can impact the results of the best solution (in this case, the best chromosome to have in this environment).
Let’s begin with how the environment is generated. The world exists of cubes that are procedurally generated. Each cube is thereafter called a tile and added to a tile list. This will come in useful when we want to spawn objects into our world. Each tile has a script on them with only two booleans in it. One is called “hasFood” and the other is called “hasTree”. This way, we can check if the tile already has food or a tree on it when we start spawning stuff. If the food of the tile is eaten, hasFood will become false again. The size of the world is based on the value of the resolution.
When all tiles are placed, we can start spawning objects on these tiles. I downloaded a free low-poly tree collection and put all trees in an array. A method called PlaceTrees chooses a random tree from this array and places it on a tile where there’s no food or tree already present. The method does this until the preferred amount of trees is reached. This is the same way food and insects are spawned. In contrary to the trees, food and insects needs to keep spawning during runtime, otherwise organisms won’t have any food to eat. The amount of food and insects will therefore always stay the same. Once one piece is eaten, another one will be spawned on a random tile. This is the way the environment is maintained and so the organisms can keep doing their thing. This is how the environment looks when finished:
As can be seen in the video above, there can also be a climate switch. It is during this time the resistance trait comes in handy. There is a 10% change this swap happens and when it does, it takes ten seconds to change back to a warm climate. What also can be seen in the video, is a UI in the top left corner. This little UI shows the average value of each gene by comparing all organisms within the current population. It also shows the best organism and genome. This is not super trustworthy early on, because no organisms die off instantly. Therefore all organisms have the same fitness score. This best organism and genome data comes in handy later when the environment has had some iterations done.
Of course the settings of the environment have impact on the results you get after some iterations. A smaller environment will make speed less desirable for example, because you can reach all the food more easily. I’d like to go over these results in the next chapter.
Now that the environment and the organisms are done, we can finally begin testing. I will let the simulation run for fifty iterations. After that, we will look at the results. What’s the difference between the average genes in iteration 1 and iteration 50? How did this impact the population? Did the population even survive?
The settings I used for this simulation are the following:
- Resolution: 30
- Amount of Food: 60
- Amount of Insects: 15
- Initial Population Size: 25
- Required fitness to reproduce: 3
- Maximum energy: 30
First, let’s look at the population size.
As you can see, the amount of organisms drops first, but after a while, the population starts to grow. How is this possible? Realize that when the initial population is spawned, their traits and thus genes are randomized. A lot of these organisms aren’t adapted well enough to survive in this environment. After a while, these organisms and their offspring die off, while the fitter organisms survive. When the weaker organisms died off, the organisms left flourished.
Now, let’s look at the traits and how their values developed over time.
Speed: As can clearly be seen, speed is the dominant trait here. It keeps rising until it finally caps around 0.93. So, the most valuable trait to have for surviving in this environment is speed.
Sense: The average sense radius first slowly rises, but after a few iterations diminishes. Why is this? Well, when an organism is fast, it wouldn’t need to have such a big sense radius. After all, a fast organism scours the environment quickly and will find food more quickly, thus it wouldn’t need a bigger sense radius. As can be seen, the value of the sense radius declines when speed starts it’s significant rise.
Size: The average value of the size of an organism first greatly grows, but after a while it diminishes again. To be big is quite costly. At first, the extra energy insects give is extraordinary handy and thus to be bigger is a great advantage. However, when the speed starts growing, the size diminishes. You don’t have to be able to eat insects when you can find apples more quickly. After all, the amount of insects in the environment wasn’t that high: 15. If this amount would’ve been higher, perhaps size would’ve been a more favorable trait.
Resistance: The average value of resistance first seems to diminish quiet quickly, but suddenly, it grows really quick. Why is this? The environment has had a few winters: one at iteration 6, one at iteration 27, one at iteration 29 and one at iteration 42. To have a winter at such an early iteration as 6 is hard to survive. Thus, the organisms without a high enough resistance died off. The ones with a high resistance passed this gene on to their offspring. This explains the quick growth of the average value. Around iteration 23, there hadn’t been a winter in a long time, so the value wasn’t as important anymore. thus it dropped. However, iteration 27 had a winter and the value rose again.
The question I wanted to answer was as followed: “How do I simulate natural selection in a digital ecosystem”. I think this question has been answered. First, I did my research on the basics of genetic algorithms and natural selection. With this information, I could start programming the individual parts I needed to run a simulation: the organisms, the environment and the interaction between them. Finally, I could run the simulation and gather data from this simulation. By visualizing this data, I learned a lot about how an environment can impact the genes of an individual organism and steer the evolution towards a certain path.
I hope I can inspire some future students to also choose this subject. It’s quite interesting and there is so much creativity at play here. You can make the organisms, the environment and the mechanics as complicated and weird as you want. Results will always be there and thus you can learn from these and apply your findings to your simulation. In the final chapter, I would like to talk about some of my ideas to improve or expand this simulation.
The R&D project is only five weeks long. Had I had more time, there would’ve been much I could’ve added to my project. In this chapter, I’d like to share some of my ideas about how to expand this project and make it even more interesting and fun.
Predators: Right now, organisms have it quite good. The only other alive beings are either trees or insects, both who aren’t any threat to the survival of these organism. The only thing that can really hurt them, are the sudden harsh winters. What would make this simulation more interesting, is a predator. I actually worked on this during the Research and Development project, but couldn’t make it work. Sometimes, my Unity crashed and I never exactly found out why it did. It would’ve worked as followed:
A predator searches for organisms. When an organism is in his sense radius, he attacks. When he attacks, he slowly gets more exhausted. His sense radius diminishes every second. When an organism is no longer in range, he stops attacking and is able to rest, increasing his sense radius again to full. An organism flees from this predator by calculating the vector towards the predator and running in the opposite direction. An organism starts doing this when a predator is in its sense radius.
Multiple types of organisms: In the current version of the project, there is only one type of organism. Perhaps it would be nice to have two different types of organisms and thus two different populations inhabiting the same environment. These two different species would have to compete for dominance in this environment. I already have some code for an organism that doesn’t mate, but copies itself and passes on his traits. With a few tweaks, this could become and organism that lays eggs and doesn’t need a mate. Would this be more beneficial?
If you are interested in my project, the full project can be found in this repository. Test it out for yourself, change some things, enjoy! https://github.com/RIckvkersbergen/Natural-Selection-Simulation
Coding Adventure: Simulating an Ecosystem. (2019, 10 june). [Videofile]. Consulted from https://www.youtube.com/watch?v=r_It_X7v-1E&ab_channel=SebastianLague
Creating an Evolution Simulator: Part 1. (2018, 24 december). [Videofile]. Consulted from https://www.youtube.com/watch?v=iTtt5lbfl0k&ab_channel=PlaycraftToysandGames
Mallawaarachchi, V. (2020, 1 march). Introduction to Genetic Algorithms — Including Example Code. Consulted from https://towardsdatascience.com/introduction-to-genetic-algorithms-including-example-code-e396e98d8bf3
Simulating Natural Selection. (2018, 15 november). [Videofile]. Consulted from https://www.youtube.com/watch?v=0ZGbIKd0XrM&ab_channel=Primer