Survival Through Specialization: Co-Adaptive Learning in an Ant–Predator Simulation
Advisor: Movshovitz
Internship Location: Remote
Onsite Mentor: Layton Alexander Rosen, Masters Student in Entomology, Purdue University
My research aims to explore how predation pressure influences the evolution of division of labor in eusocial societies. Specifically, I will investigate whether the presence of an adaptive predator can drive the emergence of specialized behavior, such as the emergence of a caste system or coordinated defense strategies, within a simulated ant colony. With guidance from my outside advisor, Layton Alexander Rosen, I will research various common ant colony behaviors that I will model through Unity, then attempt to code a digital anteater, adjusting its behavior to various survival strategies in real life.
I hope that by the end, I will have created a simulation space that roughly models what ants go through in the real world, along with a functional anteater AI that interacts dynamically with the ants. I hope that my final simulation could be dynamically used by anyone with some experience in coding to create further analysis. If time permits, I hope to attempt a reinforcement-learning based anteater model, or coding the abstraction behind genetics and reproduction within ants that gives rise to caste differentiation.
Project Blog
Week 1: Introduction
Welcome to my first blog post! I'm Luke, and I couldn't be more excited to share my research of ants with you! In this blog, I'll go over what my project is about, why I'm interested, what I've accomplished so far, and future goals.
Context
Throughout this project, I'll be trying to simulate ants and deducing how they've evolved. Specifically, simulating ants under a hostile environment, where they are forced to adapt in order to survive.
To that end, I will:
1. Use the game engine Unity to create a general, simplified model of ants, including the ability to scout, forage, and complete various tasks.
2. Introduce a predator into the simulation: a simulated "anteater" that eats up ants.
3. Test out various ways ants have adapted to these predators in real life, and conditions that can give rise to the evolution of these tactics.
4. If time permits, investigating the conditions that can give rise to the development of castes (by coding in various simulations of genetics).
But why?
Having grown up around a lot of ants, I find ants fascinating in their ability to accomplish tasks through teamwork. Unsurprisingly, that led me to spent a significant amount of time on youtube in ant related channels. There was even a period of time where I wanted to own an ant farm.
I've also grown up around a lot of computers. Everyone in my immediate family works on a computer, and I'm also pursuing the study of computer science. Despite my brief attempt at competitive programming, I still find programming to be quite fun.
What pleasantly surprised me was that there were quite a few youtube channels that combined the two concepts - computers and ants - through simulations. Two channels in particular, Sebastian Lague and Pezzza's Work, have created beautifully detailed videos of building ant simulations ground up. Having seen the videos, I wanted to use the senior project as an opportunity to create my own simulation modeling ants.
I also believe there's a lot of potential for experiments within simulations, especially in the fields of ecology. For my trimester 1 research projects in my multivariable calculus and differential equations class, I created simulations to model heat flow and predator-prey dynamics. Through that experience, I found simulations to be very test-friendly. Theres no cleanup involved after an experiment, tests can be performed in quick succession, and any changes to the conditions of a test could be added however I want. For my specific project, trying to investigate causes for evolution, simulation can compress the timescale of tests while letting me isolate key environmental factors.
Progress this week
I started off building my ant simulation in Unity2D by...creating a video game under a tutorial (highly recommended for starting to learn unity). The idea is that this will help me get used to the various features on unity. Gameplay shown below (Note: I did not complete the full tutorial).
I then jumped into creating my own simulation, starting by creating a crude ant that can follow around my cursor. She's not exactly winning a beauty pageant, but she gets the job done:
In order to mimic basic models of ant behavior, I coded it so that ants would wander around randomly. When food enters their detection zoon, they'll wander towards it until they eat it. For now, the food simply disappears and the ant subsequently begins wandering again if there's no more food nearby. If the food is too far away (as shown in the video), the ant may simply wander away towards other locations.
Here's what it looks like when a group of ants are spawned in and set to wander around randomly:
Up until this point, all the ants had to be spawned in manually by me using left clicks. Eventually, I got tired and decided to code in an anthill that spawns in ants for me:
With the anthill completed, I made it so instead of eating the food on the spot, ants would carry and deposit their food one at a time back into their anthill before searching for more food:
You may have noticed some problems with the ant simulation above. Actually, this perfectly leads to the next section...
Future goals
Currently, the movement system for ants as a whole is quite rudimentary. Ants basically determine a direction they wish to travel in, and runs towards it with all the speed they can. Consequently, I haven't quite figured out how to get the ants to properly rotate and turn in the direction they're always traveling in, causing ants to often skid and glide around.
Additionally, the pathfinding system needs work. Ants can locate food from far away, so multiple ants will sometimes try to pathfind to the same food. Previously, when ants simply "ate" the food, I just destroyed the gameobject instance by setting it to null. Now, when ants have to bring it back to base, other ants still try to pathfind to its...previous location? So now there's some ants chasing and bringing back to base a food that doesn't exist.
Not destroying the gameobject also makes it so that new ants who bump into an ant currently carrying food will think it just ran into some food and "steal" the food from the previous ant. I've tried to fix this by deleting the collider of the food after an ant picks it up. Somehow, that caused ants to be unable to deposit food back into the anthill.
Below is a video that shows these errors. Notice ants trying to retrieve food that isn't there, or not depositing:
Besides fixing these bugs, I hope that by next week, I can code in basic pheromones that ants use to travel about and retrieve food in groups. If time allows, I'll also try to create various obstacles that ants can't cross, as right now, they often just run off the map. Finally, I'll try figuring out how to upload my code on github, so everyone can download what I make.
Thanks for reading until the end!
P.S: I've linked the ant simulation videos made by Pezzza's Work and Sebatian Lague for anyone interested. Both are fantastic, and are a huge inspiration to my own project.
Week 2: Pheromones!!
99 little bugs in the code, 99 little bugs. Take one down, patch it around, 127 little bugs in the code.
Fixes!
Thankfully, I managed to complete all the fixes I wanted to do last week.
First, ants used to skid while moving around. This turned out to be the easiest fix by far. The reason why ants were gliding around in the first place was because I had limited them to a maximum rotation speed. After getting rid of that and switching to Unity's built in "LookRotation" function, they now promptly face in the direction of their velocity.
The second and third issue was that ants were chasing around "imaginary pieces of food" that doesn't exist, and that they somtimes didn't deposit food properly.
Trying to fix this led me down a rabbithole of bugs, fixes, and new bugs that arose from the fixes. The old system that I had in place relied on every ant keeping a list of food in their general area, always pathfinding to the closest one, and changing targets if their previous food was suddenly gone or picked up by another ant, or some other event. Note the phrasing on "some other event". As you can imagine, this left a lot of room for edge cases. Eventually, after hours of tussling with bugs, the code I had was almost entirely replaced by a new system, where ants "pair up" with a piece of food as soon as they're near one another through an ID system. Once paired, they become intangible to everything else.
Looking back, the new system I had a much simpler concept and implementation anyways. I should've started with that.
New stuff!
The first thing I built was walls around the simulation area. Once ants touch these walls, their velocity and the direction they want to travel in flips. Now ants won't run off infinitely.
I also wrote a short piece of code that can place a ton of food (~1000) on the map in a square formation. This is helpful for...
Pheromones!!!
Previously, the ants had the following decision making regarding the direction they want to travel in:
1. If the ant is currently carrying food, pathfind back to base.
2. If the ant isn't currently carrying food, but have been paired up with a piece of food, run towards it and carry it.
3. If none of the above, wander around.
This is great, but this doesn't leave any room for teamwork between ants. That's where pheromones, one of the primary ways for ants to communicate, comes in.
Originally, I was going to code it so that scouts would leave a trail of "to-home" pheromones as they venture out, then once they find food, following that same trail back while laying a new "to-food" pheromone for other ants to follow. However, after consulting with my outside advisor, I've learned that most ants won't passively leave "to-home" trail in order to avoid sensory overload. I've also found out that even without pheromones, most ants can find their way home through a combination of landmark recognition and counting their own steps. Some ants can even use sunlight to form their own internal compass. After discovering this, I will also opt to only code the "to-food" pheromone.
Here's how the pheromones can guide ants in their navigation process:
1. If an ant has found food and is carrying it back, lay out a trail of pheromones as they venture back to base. Each pheromone has an associated vector that points to the direction of food. While pheromones in real life don't have an inherent direction, I've found that most ants still can differentiate which way to travel to end up away from their home (either through the sun compass mentioned before, landmarks, or seeing the direction of other food-carrying ant), so I figured this approximation isn't too far off.
2. If the ant isn't currently carrying food but have been paired up, run towards it and carry it (same as previously).
3. Otherwise, Ants can detect pheromones in their general vicinity, and will add up the vectors from all pheromones nearby. Pheromones farther away are weaker. Pheromones will also slowly decay through time and fade away. After both factors are taken into consideration, ants will use that vector as their direction, while adding a "randomness factor" from the previous script used to wander around randomly.
I personally considered this a fairly elegant implementation. It's very easy to keep track of pheromones in its vicinity, and keeping the randomness factor means that if there are no pheromones nearby, ants will still wander around and explore.
With all that said, here is a video of the ants in action with all the fixes and additions. Note how the pheromones slowly guide the ants towards a more significant target, and fosters teamwork between ants.
Up Next:
No glaring bugs at the moment! I can focus my attention on coding new features!
Currently, the way they pathfind back home is by running back in a straight line. I hope to further evolve the complexity by making it so they can pathfind back even when there are obstacles in the way (while also making the map more complex)
I also hope to begin coding a predator that will seek out and attack ants. Nothing complicated so far, as long as its functional.
Finally, I'll try to actually figure out how to upload my project onto github for proper documentation.
See you all next week!
Week 3: There and Back Again
Last week, I added pheromones to the simulation. This week, I ended up changing most of it anyways...
Changes:
If you recall from last week, I made it so ants would wander around. When they spot food, they'd lay down a trail of pheromones back to base that guides future ants.
This works fine for when the ants are in an open field with no obstacles, because after getting the food, I can just assume the ant knows its way around and have it walk in a straight line back home.
Yet this week, when I was trying to give the ants ability to navigate complex territory, I couldn't have it walk back in a straight line anymore.
One option is to use pathfinding algorithms, but I didn't want ants to rely solely on these algorithms. The reason for this is because in nature, ants navigating to and from pheromone trails will sometimes wander around and explore new trails in hopes of finding a more efficient path. This leads to a fantastic heuristic pathing that slowly optimizes itself over time, and I didn't want to take that optimization away from ants.
So I decided to implement a solution that utilizes both. When the ants leave the nest, they'd lay down a "to-home" marker as they explore. After finding food, they'd follow the to-home marker (with a little variance, so the trail can improve over time), while laying their "to-food" marker to guide other ants. In addition, I'd code in a general pathfinding algorithm that in general guides the ants back home.
Note that last week I was against implementing a "to-home" marker, mainly because I said ants avoid laying "to-home" markers passively. This isn't exactly accurate, as ant species differ from one another drastically. Ants that do long-distance foraging tend to use pheromones as they wander into unfamiliar territory, while ants focused on short-distance foraging tend to rely on eyesight and other senses, as they know the environment well. So, after considering the coolness of a self-optimizing path, I decided to add back in the "to-home" markers.
This is where some problems started to emerge...
Problems:
Pheromone Behavior:
Previously, the pheromones I coded all had an inherent vector coded in. This worked fine for the "to-food" sources, but not for following the "to-home" sources back. A video below shows the issue:
With directional vectors, when paths cross, ants would often be confused on which path to stick with. Additionally, suppose a scout ant took a loop-de-loop while in the process of exploring finding the food. All subsequent ants will follow this loop-de-loop while laying their own trails, reinforcing the loop-de-loop even further. As the complexity of the terrain increases, cases such as "loop-de-loop" resulting from colliding against walls and whatnot would only be more frequent, making it hard for ants to find their way home. In nature, ants fix this by mostly simply traveling towards the direction with highest concentration of pheromones.
So, I came up with an imitation of how ants navigate in nature:
Suppose the blue circles are pheromones, the red line is its current direction, and the black semicircle is the ant's detection radius. The ant would...
1. Take note of all the pheromones in its detection radius.
2. Record the angles between all pheromones and the direction its currently facing.
3. Calculate a weighted average of the angles (based on how much the pheromones have decayed)
4. Travel in the average direction.
In this case, the ant would turn left slightly.
Lag:
That was great, but after some testing, I discovered that this system is only consistent with more pheromones. Hence, I quadrupled the rate at which ants placed pheromones.
This was when my computer started to be really unhappy with the changes. Attached below is a profiler that displayed how my resources were used:
As shown in the image, as the ants were slowly spawned in, the resources spent on the Physics module quickly spiraled out of control. I was getting average frame rates of 1-2 FPS.
Troubleshooting revealed the cause: with ever-higher amounts of pheromones, every frame forced unity to tens of thousands of collisions between every single object, quickly scaling out of control.
Fixes:
The main fix was in how ants interacted with pheromones. Previously, I used a unity function that trigged at every collision between pheromones and a shape that represented the ant's detection radius. Then, I added and removed all pheromones in range to an array.
Unfortunately, as the number of pheromones and ants increased, the number of times this function is called also gets scaled. Suppose, on average, an ant walks into 100 pheromones per second. That single ant would cause this pheromone function to trigger 100 times that second. 20 ants walking around could easily trigger the function 2000 times per second.
To fix this, I switched to using another function: "Physics2D.OverlapCircle". At any given point, I could call this function, and it would return every object in a small circle around the ant.
The benefit of this is that I could call this function at any time. So instead of triggering at every collision, every ant calls this function at 1 second intervals. Now, 20 ants are hard capped at triggering this function 20 times per second, instead of 2000 times per second. I also randomized the start time for checks of each ant, meaning the checks are all distributed across a second, instead of piling up to form lag spikes.
I cannot overstate how significant this was to optimizing the time. This change alone almost singlehandedly got rid of all the resources spent on the CPU. Below is a screenshot for the new allocation of resources. As you can see, the FPS is now consistenly between 30 and 60, and the orange marks representing physics allocation is severly diminished.
With this optimization in place, below is a video for the new pheromone model of ants:
Up Next:
With a more scalable system for pheromones in place, I plan to spend next week implemeting a basic pathfinding system for ants. This way, they could begin using both systems in tangent to pathfind across difficult obstacles. Additionally, I hope I could finally add in the anteater, and create basic attack patterns targeting ants.
Thanks for reading thus far, and see you all next week!
P.S: I uploaded my project on github. For those interested, all of my code is located in the Assets/Scripts folder. As I make progress in future weeks, I will continuing to update the Git repo.
Week 4: Antlions
Hi everyone, and welcome back to the 4th week of my blogs!
Pheromone changes:
I started off the week with more changes to how ants worked with pheromones. I already revised the model last time, but as the video showed, the results were still mostly scattered. Ants were unable to find their way home despite sometimes knowing their general direction. This made it hard to navigate around large obstacles.
This included with me playing around with various solution attempts, including...
1. Making it so to-home pheromones evaporated faster the farther it is from home (and vice versa for to-food pheromones), so the gradient would easier for ants to navigate.
2. Creating not 1, but 3 detection zones around ants in an attempt to guide the way they navigated around.
3. Further changing and optimizing ant movement systems so they deviated from their tracks less frequently.
Most of these were pretty tedious to implement (usually from the string of bugs that came along as I try to implement these features). Unfortunately, despite being tedious, none of these yielded significantly better results compared to last week's pheromones.
Right when I was about to give up and move on to predators, I stumbled upon a solution while rewatching the second series of the ant simulation by YouTuber Pezzza’s Works. He assigns each pheromone a value indicating its distance from home or food. Ants then travel toward the most optimal pheromone based on that value.
I implemented this property onto my simulation by adding a value to every pheromone indicating the time it was placed since visiting either food or home. This time, ants created thin, smooth lines and were able to navigate around obstacles.
This system is also great because the timed-property of pheromones meant that trails could often optimize themselves when ants detect a better path. In the example below (done with a higher "wander" probability), despite the first ant being lost, new ants quickly picked up on the pheromones and were able to optimize the trails.
Overall, this was great! Despite taking 3 weeks, I think I'm now finally close to a model of pheromone that I think accurately capture how ants move. Now, it's time to introduce predators...
Antlions:
Antlions are insects who burrow cone-shaped pits in the sand. Once ants fall into these pits, they are unable to climb out, and the antlion waits at the bottom to devour their meal using their mandibles. Fun fact, this was the real life inspiration for the Sarlacc pit from Star Wars: Return of the Jedi.
I started out by coding a basic model for the antlion and its pit (and even made a death animation for the ants):
Next, in order to let the ants have a basic defense mechanism, I decided to give them...more pheromones.
Once ants fall into these pits, they release an expanding gas of pheromones that stand for "avoid danger". Ants that are near this pheromone will try to pathfind away, breaking their original pheromone paths.
Note above how the expanding pheromones cause the ants to break formation and create new trails around these antlions.
Future goals:
If you'll notice the video above, you can see how sometimes the ants seem to be spinning around. This seemed to be caused by some new bugs in the code I haven't found yet, since it wasn't present before I added the antlions. I'll try to fix that next week.
I've also already started on creating a second predator for the ants to fend off: the wolf spider.
Currently, the wolf-spider simply runs towards ants and devours them. Unfortunately, I seem to have accidentally messed up the collider system, causing them to no longer detect ants (or walls, for that matter). Hopefully by next week, I'll also finish building a model for the wolf-spider and ways ants can defend themselves.
I hope to see you all next week!
Week 5: Fixes and Complexity
Last week, we ended off with the addition of antlions and a couple of new bugs. This week, I fixed some prominent bugs and changed the behavior of the antlions.
Bugs and Fixes:
The biggest issue last week was that the movement was off. Namely, ants sometimes made small loops before they went on the correct direction.
I spent quite a long time trying to figure out where the issue was, mostly using the Debug.Log tool that unity provided. Eventually, after noticing ants sometimes detected pheromones "outside" their detection zone, I decided to try visualize the ant's detection radius using the "OnDrawGizmo" tool.
It turns out the ant's detection zone was actually to it's left, instead of above itself. This was a result of me being confused with the direction of vector.up and vector.right during a previous debug session. Oops.
The silver lining was that the OnDrawGizmo tool was very handy for visualization, and I used it to also visualize the ant's desired direction. This made it very easy to know what the ant is currently tracking. Below is a video showcasing what it looks like in editor mode.
Anthill changes:
I made it so new ants would spawn every time ants brought enough food back to the anthole. This made it so ants would always spawn "to-home" pheromones in relative close proximity to home, instead of all wandering. This is important for antlion changes. This was a pretty small fix, but please enjoy a customary video of ants running around foraging their food.
Antlion changes:
This was the big addition this week: previously, the alarm pheromone would "contaminate" other normal pheromones (ones used for tracking), telling ants to avoid the tracking pheromones, instead of the alarm pheromones itself. Now, I made it so that ants enter a "panic mode" when they touch an area with alarm pheromone. Panic mode overrides other functions, and instead has the ant run in a straight line as far as possible for 5 seconds, before returning to other tasks.
I love this change because the ants are finally showing a bit of "back and fourth" with the antlions. As the video below shows, the antlions start by eating a couple of unlucky ants, but soon the alarm pheromones guide ants to avoid the area the antlions are in and establish their own safe channels.
Future changes:
Now that antlions are in a somewhat complete state, I hope to expand further by finally adding the wolf spider to the simulation.
Additionally, no new additional bugs at this time! so I'm excited to get working by adding new features immediately. No bugs also meant all new code and changes have been published on github.
I hope to see you all next week!
Week 6: Evolution
After wrapping up ants and antlions last week, I tried to add ants and wolf spiders into an ecosystem where they live together. Inside this new area, ants scatter while wolf spiders chase them down.
Wolf spiders:
To summarize their behavior, wolf spiders have a simple detection radius, and will chase down (and eat) the ant closest to it within its detection radius. If there are no ants, the wolf spider wanders aimlessly.
They also have 3 primary attributes: speed, sense, and mass. The higher the speed, the faster the spider moves. The higher the sense, the greater its detection radius for spiders(meaning it can find ants farther away).
As for mass, every spider had a "curEnergy" variable, representing their hunger bar. When a spider's hunger bar reaches 0, they die. A spider with a higher mass has a higher maximum hunger bar. This comes from something known as the Kleiber's law, which states larger animals are more efficient with their energy.
Every second, spiders lose energy, getting hungrier and hungrier. The loss per unit time function is as follows:
Energy loss per unit time = mass * speed2 + sense2
The idea behind this when I wrote this function was that mass * speed2 represents the standard equation for kinetic energy. Meanwhile, I've seen sense as the energy the wolf spider uses to process the environment around itself in its brain. The reason why it's sense2 instead of just sense is because sense is the radius of the detection radius. Meaning the area of detection is proportional to sense2.
I've organized all 3 of these attributes inside a new class: the SpiderGenome. You'll see this referenced later as I build the framework for evoltuion.
Anyways, here's a picture of the wolf spider in my simulations.
Framework:
In order to simulate evolution in a neat manner, I created 2 new gameobjects to manage everything going on: the EvolutionManager and the SimulationManager.
Here's how the simulation manager works:
1. SimManger creates a new iteration of simulation, spawns in an anthole (that further spawns in more ants)
2. After the ants have spread around (~60 seconds), SimManager calls EvoManager for the current set of genomes, and spawns in spiders at the corner
3. The spiders chase down ants; those who manage to catch 5 ants turn golden and remain in a "satiated" state, guaranteeing their survival. If no spiders manage to catch ants, the last surviving spider is extracted.
4. SimManager sends the surviving spiders to EvoManager to create the next set of genomes.
5. SimManager clears the field
6. Repeat from step 1.
On the other hand, the evolution manager doesn't have a strict time line to follow, so I will instead describe how parts of it work.
1. EvoManager only creates 5 spider genomes at a time. This makes sure SimManager also only spawns in 5 spiders, preventing unrestrited growth of spiders. In real life, the number of spiders would probably be limited by predators and other environmental factors.
2. The first generation is created by randomly generating values to create the first 5 SpiderGenomes.
3. When SimManager sends EvoManager its surviving spiders, EvoManager sorts the list in order of descending curEnergy / MaxEnergy ratio.
4. The top performing spider has its gnome cloned into the next generation (with no changes). This is my attempt at preserving the top performing genes across generations.
5. The next 4 spiders in order (if they've survived) have their genomes preserved, added with a mutation factor that either increases or decreases each attribute.
6. If they didn't survive, the rest are randomly generated again.
Results:
The reality was, while implementing all this, I ran into quite a few bugs that had to be resolved one after another. I've recorded one case below that shows unlimited spiders spawning.
In the end, however, I did manage to get everything in working order. If you're interested, I also have debug logs on the right that displays information about things like current generation within each run.
While running this, I've documented some current archetypes of spiders that I've seen have a lot of success:
1. Well rounded wolf spiders: spiders with solid attributes that can consistently find ants.
2. Fast/active spiders: spiders that have maximized their speed, and rely almost entirely on running fast enough to hit 5 ants by luck.
3. Slow/passive spiders: spiders that have very low speed and sense, while extremely high mass. This makes their max energy very high while minimizing their energy consumption, meaning they can just sit around waiting for ants to fall into them. I was really surprised by this one, because at that point, the spider basically turned back into an antlion.
Next week:
I've had a lot of fun coding and adding evolution into the simulation. With this framework in place, I hope to expand on the complexity of the simulation by adding more factors into the mix: for example, what if the ants could fight back, and deal damage to the wolf spiders? What about giving the wolf spiders a "health" stat?
I also hope to go more deeply into the results, plotting and analyzing the data from wolf spiders, as well as how they evolve and change over time. Unfortunately, this blog is a little too long, so I'll save it for my next blog.
See you all then!
Week 7: Data collection
Continuing off of last week's introduction of the EvolutionManager and SimulationManager, this week I ran and analyzed some results.
Changes:
A fundamental reason why I didn't post any simulation results last week was because I didn't receive data stable enough to mimic real life. Meaning, when I ran the simulations, most wolf spiders died within the simulations and sometimes the gene pool would be entirely reshuffled. Given real world environments are much more nuanced and balanced, scenes like an entire area of wolf spiders all dying in one generation likely wouldn't happen, hence why I considered the data inaccurate.
To address these issues, I tried keeping the simulation at a level such that at least 3 wolf spiders were able to survive on average. However, that also led to a lot of generations where every spiders survived, meaning the genomes were stagnant.
Recall last week the evolution included an "Elitism" mechanic, such that the fittest spider would have its genes cloned in addition to reproducing. To introduce variance, I made it so the least-fittest spider would always be kicked out of the gene pool.
So in summary, the fittest spider would slowly take over the gene pool, while the least-fittest spider would be kicked out and replaced with a randomly generated genome. In doing so, I could visualize a much larger variety of "archetypes" that evolve from an environment. I thought doing so also mimicked the introduction of invasive/foreign species of ant-predators that may or may not outperform the current population.
I also added a new attribute to the genomes: age. The age of a genome represents how long it's been passed down from the moment it randomly generated (including mutations). A genome that has survived for 10 generation has an age of 10. A newly generated genome has age 0. This would be difficult to track in nature, but is instead a simple task with simulations.
Now, onto the runs.
Run 1 (wolf spider heaven):
The first run I made lots of ants. As in, the screen was practically swarming with ants. This made it very easy for the wolf spiders to find food and survive into the next generation. This comes with its unique set of spiders.
I graphed the results over time, with each light blue dot representing specific genomes at every generation and the blue line representing the median values.
Notice how other than how dots are clumped around the median line, there are dots floating around in the entire graph. Those tend to be from the randomly generated ants each generation, which (mostly) die out that same generation.
Some notes about the current attributes so far: within an environment where food is plentiful, sense seems to be completely deprioritized. High-awareness spiders seemed to be just wasting energy, when food was all around them anyways. I was initially confused on why speed was so high, considering spiders could just wait for ants to run into them. But it seems like too low speed would cause spiders to sometimes lock on and chase a single ants they're never able to catch, leading to starvation.
I also specifically saved the graph for "age" of each genome at the end:
This graph is special, because it represents when a certain defined "archetype" of wolf spiders that is doing well in its current niche is suddenly overtaken by one of the randomly generated wolf spiders. Looking at the median line, there's a small crash at first around generation 3 where a relatively well-adapted wolf spider type takes over, then a large crash at around generation 15 when an almost fully optimized wolf spider species takes over, surviving until the end of the simulation at generation 150. I find this really interesting, because the fact that the original genome completely died out also mimics what happens when an invasive species is introduced to an area.
I've also made a graph of combined attributes, this time showing median only from each of the three attributes:
As shown, the crash in "age" at generation 15 also perfectly matches when a big shift in the way the wolf spiders behaved; compared to the spider with relatively even statistics at the start, it seems like choosing to prioritize sense and completely dropping mass worked much better.
Some final comments about the process of evolution overall: I have no control over how the attributes would evolved, and was thus surprised that mass was so low; I had thought having a high mass (and thus high energy) would help create a new breed of spiders that just sat in place and waited for ants to come into them, but it seems the mass trade off was not worth it, and chasing ants to some degree was still more beneficial. It's very nice being able to see this process walk in completely unpredictable ways.
Run 2 (wolf spider hell):
For the second run, I severely toned down the number of ants in the simulation. This means that the biggest competition spiders had was actually with each other, a race to see who could catch the ants first. I ran this simulation an entire night, and below are the results.
Comparing the numbers between the here and the first run, even the lowest speed and sense here was comparible to the highest speed and sense in the first run. Mass, on the other hand, seemed to again play a much smaller role than I expected.
You'll notice that the big crashes where genomes completely died out are mostly replacements in sense. It seems that speed is invaluable in this run, but sometimes spiders have had varying successes on different senses. I think the reason why sometimes an entire generation gets taken over mostly comes down to luck; a group of high-sense spiders can lose all ants due to a lucky low-sense spider that happens to run into the ants, while a low-sense spider can similarly die from being spawned too far from any ants at the start. In real life, these cases of an entire genome being wiped based off of luck is probably rare, but it can happen here given we only have 5 wolf spiders in the environment.
Up next:
I got caught up with running the simulation and graphing the data, but next week I promise to finally add in more attributes involving the ants fighting back. It'll be an interesting visualization and hopefully we can see more aspects and nuances of the model.
I also hope to eventually add back in the antlions and alarm pheromones, but that's maybe 2 weeks down.
See you all next week!
Week 8: The complete setup
Hello everyone!
As the senior project is nearing its end as a whole, I've also finished adding all features I wanted to add into the simulation.
Additions:
Since I've made a lot of changes, I'll go rapid fire into everything:
Simulation side:
1. Merged the previous simulation (food, obstacles, and ant pheromones) with SimulationManager.
2. Decreased spider spawn time and total simulation time. This way, when spiders are spawned in, ants will likely have already established a stable trail to food.
3. Added value "wolf spiders deaths caused by ants" in EvolutionManager.
Wolf spiders:
1. Wolf spiders now have an HP bar. When HP reaches 0, the spider dies.
2. When wolf spiders eats an ant, they will always lose HP. The normally, HP loss is set equal to 25 * antMass / spiderMass. However, when spiders eat 2 ants within a 20 second time frame, an "Ants_stack" counter will go up, and the damage will begin scaling exponentially. In total, the damage is set to 25 * antMass / spiderMass * 1.7ants_stack, meaning the second ant an ant eats in a row will do 1.7 times the damage of the first. This is meant to represent ants being able to "gang up" and often defeat enemies of larger sizes by working together. In real life, this might be surrounding the spider and attacking all legs at once. (Side note: this makes it especially dangerous for wolf spiders to attack on a food-trail, since lots of ants are crowding there)
3. Wolf spiders are now stunned for 10 seconds after eating every ant. This represents both the process of fighting and the process of eating an ant.
4. In addition to eating 5 ants, a wolf spider now also needs to survive for a 30 second time period after fulfilling its ant quota.
5. Gave wolf new trait: bravado. A wolf spider with high bravado will attack ants even when many are nearby, while a wolf spider with low bravado will actually keep their distance (sometimes even fleeing) when they are near a high number of ants, attacking only when they encounter 1 or 2 ants.
Ants:
1. When the wolf spiders eats an ant, the ant will release either an attack pheromone or alarm pheromone. Attack pheromones sends ant into a temporary "attack mode", while avoid pheromones work the same way they did for the antlions (have ants scatter and run away).
2. "Attack mode" is when ants drop what they are doing, and this time begins actively hunting for wolf spiders. They have a speed buff in that time period.
3. Ants are now split in a 60-40 ratio of worker / scout ants. Currently, there is no difference in terms of traits between the two, but soldier ants will neither release nor follow pheromones, meaning there are always some ants wandering around the world.
Summary:
Throughout this process of implementing everything, I also did some research on attack pheromones in nature. Ants tend to use attack pheromones and avoid pheromones internchangably, releasing attack pheromones when they believe the threat to be small, and avoid pheromoens when the threat is unfightable. There is obviously a lot more nuance to this, but what I found interesting is that some ants use the same pheromones for attack and avoid, and instead differentiates the two based on concentration (normal = attack, too much = avoid). This actually mirrors how I've coded attack/avoid pheromones in my own simulation, where they both fall under the "AlarmPheromone" class, but only has different initializations for the two.
Anyways, to wrap up this week's blog (and all of the backend coding work on simulations), I added a raw 16 minute video fo the simulation running. There's still some small bugs here and there, but overall I really like the place the simulation's at.
Up Next:
I'm really glad to finally be done coding for this project! All that's left is to analyze the current data I have. After that, I hope to spend the remaining time adding finishing touches (polishing UI) before I ship off the simulation.
For those trying to download my project, also note that this project was done using Unity version 6000.3.11.f1.
See you all next week!
Week 9: Collected data, differentiating spiders
Hello everyone, welcome back to another week of blog posts!
Given that most of the coding is already finished, I've published all my changes in code to the github repository. In addition, I've also updated the README file, which now contains a full guide in how to use my simulation, along with what's needed to make adjustments (linked here).
Additionally, below are some data I've collected with the new model of ants that can fight back.
Data:
The first thing regarding data is that with ants fighting back, surivability becomes a lot harder. There are a lot of generations where no wolf spiders are able to survive, leading to the gene pool being completely reshuffled. The gene pool is sable only when a significant number of spiders who are good at surviving their niche all survive. For this reason, variance greatly decreased within traits of these spiders.
For this reason (and conciseness), I'm only going to show the median-over-time of all traits for these spiders. Just know that in every case the data surrounding the median are practically all direclty on the median.
For the same reason, I've also gotten rid of any genome that has survived for less than 30 generations. This gets rid of a lot of noise in the data, when an entire colony of spiders is wiped out and the simulation is back to randomly generating genomes. If you ever see any jumps between variables in the data, it means a previous surviving generation has died out and switched to a new generation.
Below is the first test I ran: wolf spiders against ants who fight back, vs ants who run away:
It can be a little hard to tell the difference between these two scenarios, so I've created some boxplots to represent the data.
Using this plot, I concluded there was a statistically significant amount of difference in Mass and Speed, since the quartiles do not overlap. Keep in mind that sample sizes for both graphs were relatively big, so I found this result to be quite convincing that when spiders were up against aggressive ants, mass needed to be higher to reduce damage. Consequently, speed was lost as a trade off to keep the energy loss function abalanced.
For the second test, I compared wolf spiders in an environment where ants fight back, to the data I collected in previous simulations where the ants have yet to develop pheromones (blog 7). The results are as follows:
Both simulations were ran in an environment where ants were plentiful (60 ants). However, similar to the previous simulation of attack and avoid pheromones, in an environment with hostile ants, the wolf spiders were forced to significantly increase their "mass" state and sacrifice speed as a trade off, while having ants who don't attack meant mass could be ignored entirely.
For the attacking simulation, sense was more important as well. Even though the wolf spider didn't need high sense to find ants, having high sense could also help it avoid ant clusters, hence why the distribution on sense was so high.
Conclusions:
This concludes the data I'm collecting from the simualtions. In any case, I really enjoyed seeing how different spiders had to adopt different traits in order to stand against an ever changing environment. Seeing the "Mass" state gaining prominence, compared to the data in week 6, also made me realize how the traits all had their own importance as the simulation is built to be more and more complicated.
With the senior project coming to a close, I'm now turning my attention towards the icing on the cake. Hopefully, before I run out of time, I can update the models for ants and spiders from their current hand-drawn self by me.
It has been a very exciting journey so far working on this project. See you all next week, if I manage to get another blog out in time!
Week 10: Final update, UI changes
Hello everyone!
As my final project is overall complete, this will be some final updates regarding the ant simulation.
Changes:
The final changes I've made to this project are all mostly UI changes.
First of all, I've finally updated my spider model from a spiky ball to an actual spider (made using microsoft paint):
Note, the spider is still purple, but only for higher contrast.
I've also made my own ant sprite (made using canva).
Now both of these sprites are relatively crude, but this is fine, since most of the simulations (shown later) are observed at a distance, where a lot of fine details don't matter as much.
Huge shoutout to my sister who also remade the background and obstacles. We've now transformed from the simple walls to a far more visually appealing setting. Below is a video showcasing the basic pheromone tracking feature in the new environment.
So as shown in the video, the UI features are now a lot better.
Download instructions:
Again, my full project is available to download at: https://github.com/chengzansong/Ants_project
I've left detailed instructions there, but for anyone who may suffer from issues downloading the project, feel free to reach out and ask me at lukesong2008@gmail.com! I'm happy to answer any questions.
Important side note: if you download and extract the project, you may end up with a larger Ant_project folder that contains another Ant_project folder that contains unity assets and whatnot. When that happens, remove the smaller Ant_project folder from the larger folder. (Essentially the folder uploaded to unity should directly contain assets/scripts)
Final future plans:
This project as a whole is complete, and is already up on github. If there's anything I would ever change about this project, it'd be about the possibility of embedding it directly onto my personal website (when I actually make one).
With that aside, thank you all so much for following along this project!