Before we begin, we’re happy to announce that the release candidate for Thrive 0.4.2 is now available. Download and post your bugs/thoughts here, and while you’re at it, scream wildly into the void as your hype for the upcoming release consumes you here.
Now, back to regularly scheduled programming.
Hey guys, Nick here, and today we’ve got an update for you on one of the essential components of Thrive, evolution.
The oldest unanswered question of Thrive’s game design is: How will we create an evolution simulation that is both realistic and fun? It was one of the ideas that originally inspired the game, and it is one of the fundamental selling points of the first half of Thrive. Yet, it’s still a looming obstacle that we have just never been able to overcome. To get a sense of why we haven’t implemented it yet, here are just some of the questions that come to mind when thinking about how to go about making the evolution simulation we hope for:
- How do we design a system that automatically evolves the species alongside the player?
- How do we it in a way that stays competitive with the player’s species and other AI species?
- How do we even design a system that can look at the design of an organism and determine its survivability?
- How can the system calculate the change in survivability with a change to a certain trait?
- How do we use this system to track the changes in population of the player’s and other species based off of their traits over the generations?
- How do we make it respond effectively to changes in the environment?
- How can we do this all without melting the first computer that tries to run it?
In Thrive, we call this theoretical system Auto-Evo. This is short for Automatic Evolution, since it’s what evolves the world around the player, while the player uses Manual Evolution to evolve their own species. There have been some attempts throughout the project’s history to build an Auto-Evo system, but nothing that has stuck.
Recently though, I’ve had a a fortunate series of inspirations on how to approach solving this problem. Each one came months apart, and it took me some time to put it all together and realize I could use these to try and design the algorithm we wanted. So using these methods, me and the team finally decided to tackle what Thrive was meant to be, a simulation of evolution.
Introducing the Problem
I started this thread on the development forum. The goal? To step by step build an entire Auto-Evo algorithm with the cooperation of the other developers. In the introduction, I described the difficulties with designing the evolution simulation, as well as some of the realizations I had had in the previous months for strategies we could use to overcome these difficulties (proportional estimations, life activities, building from the basics, etc.). Some of these strategies I described were:
- Averages – Wherever possible, it helps to use the average value of a species rather than worrying about distinguishing between individual species members. For example, instead of calculating the number of successful hunts of each species member, it’s easier to just find the average hunting success rate for the entire species.
- Proportional Estimations – Proportions and percentages can be an amazing tool for representing complex values. How do you quantify something like aggression? You can’t, but you can say something like a species with 50% Aggressiveness responds aggressively in 50% of confrontations, and a species with 100% Aggressiveness will choose to act aggressively in 100% of confrontations.
- Life Activities – If you think about the life of any organism, it’s essentially just a compilation of time spent doing all the different activities of life. These include sleeping, eating, drinking, reproducing, socializing, migrating, etc. Each of these activities pose their own challenges to species, which can lead evolution to evolve traits adapting to each one. It might be hard to imagine how a longer leg would affect the survivability of a species when looking at the effect on its entire life all at once, but if you break it down it’s much easier to imagine how a longer leg would affect its individual activities such as its hunting, its migrating, its drinking, and more. Additionally, each of these activities can be completed at differing levels of performance. As a result, if we want to look at the overall performance of a species, we can start by looking at the performance across each of the different life activities it engages in and factoring them all into an overall performance score.
- Building from the Basics – This is undoubtedly a very complex topic to tackle, so we’ll start with the most basic and simplified scenario possible. Every part of the series we will slowly remove these simplifications and instead replace them with realistic features. This ensures that the algorithm is built at a steady rate that we can keep up with, without the whole thing getting too complicated to understand.
The series tries to address the basic premise of Auto-Evo as described in the current concept:
Auto-Evo is a system that automatically evolves the species around the player. Evolution is semi-random but geared towards choosing good evolutions.
Sound simple enough, but this statement raises three questions:
- How do we calculate the effect of an evolution on a species?
- Once we calculate an effect, how do we determine how “good” it is?
- How will this tie into the overall gameplay?
The series is designed to go for three episodes, with each episode answering one of the above questions. Episode 1 will be about creating a population algorithm that calculates how a change to a trait changes the population of a species. Episode 2 will then use the population algorithm from Episode 1 to calculate which mutations would create the most growth in a population and use those to evolve species. Episode 3 will close by describing how the entire system will fit into Thrive’s gameplay.
Math and Populations
In Part 1 of the first episode of the series, we started by looking at the very basics of how populations are modelled mathematically. We decided to go with a model that is discrete, meaning the population’s growth is calculated in specific intervals (to avoid having to work with calculus). The simulation interval we decided on was one month, since most species have lifespans at least one month long. We created a very basic environment with only a single species, no requirement for food, and many other assumptions to make things as simple as possible. The algorithm tracks factors like the mating frequency of the species, the offspring they birth every time they reproduce, and their average lifespan. Using this, we created the very first iteration of the simulation:
One important takeaway from this part was defining the different between Performance Statistics and Traits. Performance Statistics are variable parameters of a species, like population, births, old age deaths, etc. They change from month to month, and represent how well a species is doing in an environment. Traits are constant parameters of a species, and can only change through evolution. Examples of traits are things like mating frequency, lifespan, or base metabolism.
Food, Food, Food!
Once the basic model was in place, we added in food as a requirement for the species’ survival. We made it so that the species members have a Base Metabolism and need to feed before they can reproduce, and if there’s not enough energy to do so they die of starvation. Then, anyone who survived the feeding can reproduce. We made the food itself an endlessly replenishing resource, representing free floating clouds of sugars in the ocean.
Now suddenly it was not as simple as just growing forever. A food shortage eventually would kick in when the population grew past what the food could sustain, and there’d be a big famine until population levels stabilize.
Motion in the Ocean
Next up it was time to add some locomotion to the simulation. Until this point, the species was teleporting instantly to their food, and then spending all of their remaining time reproducing. Instead, we started calculating the average distance that members of the species were from pieces of food, and calculated the time it would take them to swim to the food based off their Swimming Speed. They would keep consuming until they were full, they ran out of time, or the food ran out. Then, the remaining time would be spent reproducing. This caused us to have to introduce a very simple behaviour system:
Ultimately, this served to limit how often the species could reproduce. The final simulation produced results similar to Part 2, but with a lower final population (since some time was being spent hunting instead of reproducing).
Now reading this Devblog right now it might seem like this was all some rapid progress, but if you read the thread itself you’ll see it took many paragraphs, many diagrams, and many equations to get to this point. So, to not lose track of what we were building because of it getting too complex, we decided to pause and analyze the system a bit to get a sense of what we’d created so far. We did some different demo simulations, tweaking the numbers of different traits to see how the species would fare.
Doing so gave us our first taste of Auto-Evo in action! Auto-Evo is intended to work by picking a species, randomly picking 5 mutations for that species, and then calculating which mutation increases its population the most. Yes, if you were astute you would have noticed that the fifth in this case is not actually a “mutation” but rather a change in the environment. This was only because the model was so simple at this moment that there was not many options for mutation, but that changed with the following parts!
Demographics and Swimming
We then returned to the action and in Part 4 analyzed the impact of demographics on the simulation. How does where the species live affect the calculations? Are they clustered together? What’s their habitat? Do their habitats overlap? The main takeaway of this part was the addition of the Clustering trait, which defines how much members of a species group together (and tends to increase the average hunting distances).
Part 5 saw the introduction of proper physics equations behind the swimming speed calculations. We took into account factors like the liquid density of the ocean, the shape and size of the species, their muscle area and strength, and their type of motion (for now the only option being undulation). The changes to the results were minimal to the simulation because all it did at the moment was slightly increase the species’ speed. Most of the changes in this part were under the hood, and will become more apparent in future parts as the model becomes more complex.
Time for Some Other Species!
We finally added other species in Part 6. This warranted drastic changes in the code, but brought us a big step closer towards a realistic simulation. We have currently made it so the 2nd species hunts the same food as the 1st, but in a future part will add in proper predation between species. We added in competition for hunting between species, where its possible for one species to catch the food before the other and increase the average hunting time of the loser. We also added in dividing up food between species during shortages, with more competitive species receiving a larger share and thus doing better during famines.
As a result of the new additions to the code, we saw the emergence of the Competitive Exclusion Principle in the simulation (a concept of evolutionary biology) without even specifically trying to code it in! The Principle refers to the fact that when two species compete for the same role, the better competitor will tend to drive the weaker one to extinction. It was amazing to see this emerge naturally from the simulation. All we did was work on building a realistic algorithm and we’d started creating realistic interactions and outcomes.
Following this, we did a sub-part on competition within a species. This was an important addition, because competition from other members of your own species can be just as bad as competition from other competitor species. This serves to naturally limit the population density of a species, because if too many of them live in close proximity they will constantly compete for food. Thus, species need to evolve to become more solitary to overcome this hurdle, or evolve to become more social and cooperate with their fellow species members, which will be implemented in a future part. The results to the simulation confirmed my prediction, showing that the more competitive species was not able to as quickly take over the environment due to increased within-species competition as they multiplied.
Following this, we couldn’t resist and added a third species to the simulation to observe a more complex ecosystem. We got the following results:
This time it appeared that the least competitive species went extinct, but the two more competitive species were able to coexist. The blue species, Minimus Rex, has the advantage of having stronger muscles making it faster at catching prey. The yellow species, Propagatus Maxima, has the advantage of reproducing more often than the other species. It’s fascinating to start to see the evolutionary arms race between species in such a simple model.
And so that brings us to the current state of the series! There’s still plenty more to go, but we wanted to showcase to you guys what we’ve done with it so far and see what you all thought of it. We want to hear your feedback! If you have any questions, comments, concerns, or suggestions about this series and the Auto-Evo algorithm we are working on, please post them in response to this Devblog. You can also post them on any social media platforms that the devblog is posted to. I’ll answer them all in the next part of the series.
The goal is to give a chance to clear up any misunderstandings by the readers and bring everyone on to the same page, as well as a chance for input from you guys into the algorithm’s design. Once that’s done, we’ll resume with the next part of the series!