We use cookies to ensure you have the best possible experience. If you click accept, you agree to this use. For more information, please see our privacy policy.

Illustrating the speed of modern software development with a genetic algorithm (and the help of some sheep)

Jack Tacchi
Jack Tacchi
8
min read

AI is evolving quickly and the sheer number of applications can make it difficult for any one person to keep up with everything. That’s why, at Osedea, we take a group approach to staying at the cutting edge! Every week, someone from the AI Department gives an informal talk about their work or a personal project they have been working on, along with sharing some AI-specific news. 

In one such recent talk, Jack Tacchi, a Senior AI Developer, presented a comparison between two implementations of the same idea: one was his Bachelor’s thesis from 2017, the other was an AI-assisted experiment built nearly a decade later. Comparing these implementations gives a stark contrast of just how much faster modern development has become. A shorter version of this talk was also given at AI Tinkerers Montreal!

But first, what was the idea being implemented?

The idea

The primary goal of this project was to create a dynamic 3D environment populated by animats (simulated virtual animals, in this case sheep) that must learn to survive autonomously.

To stay alive, each sheep has physiological drives: hunger and thirst. Food sources (bushes and flowers) and water are scattered across a procedurally generated terrain. The sheep have no hard-coded rules telling them how to find food or water. Instead, their neural network "brains" map visual sensor inputs to movement outputs. Over multiple generations, the most successful sheep will pass on their genes, leading to better and better survival strategies.

Let’s take a look at the world these sheep call home!

The environment

Building the 3D world required three core components: procedural terrain, dynamic water, and resource spawners.

1. Procedural terrain

Varying layers of Perlin Noise were superimposed using different amplitudes to generate a smooth and natural-looking heightmap for a 3D mesh. 

The generation function used multiple parameters, which could be easily modified to adjust the terrain. The mesh could then be procedurally coloured based on the height of each part of the mesh.

2. Dynamic water

Water was added to the map in a similar, but simplified manner. Using a sine function instead of Perlin Noise created “waves”, rather than a terrain-like mesh. The wave height and distance between the waves could also be controlled by parameters, and the water could also be animated by adding in a time variable:

y = wave_height * Math.sin(dist_between_waves * (x - speed * time))

3. Food spawners

Food was randomly spawned across the map. The sheep were able to eat two different types of food:

  • Flowers: would greatly decrease hunger, but disappear after a few seconds.
  • Bushes: smaller decrease to hunger, but last for longer.

With this last addition, the world is finally ready for the virtual sheep!

Sheep and their senses

In order for the sheep to make reasonable decisions, they need to be able to receive information about their surroundings.

The sheep’s vision was approximated using 3D Raycasting. Each sheep projects a fan-like array of 31 horizontal rays across 5 vertical elevation angles (totaling 155 rays).

When a ray intersects an object, it records two key variables:

  1. Distance: How far away the object is (normalized between 0 and 1).
  2. Object type: Encoded as a one-hot vector representing Land, Water, Bush, Flower, Other Sheep, or Nothing.

Simply put, each sheep uses 155 rays to sense what is around it, including what type of object each ray encounters and how far away it is.

In real life, sheep possess an extraordinary field of vision, roughly 320 degrees, allowing them to constantly scan for predators in a wide arc. The Osedea sheep were gifted the same field of view. 

Here’s a glimpse of it in action!

The cubes denote objects the sheep can see. The white lines are raycasts that reached their maximum length before intersecting anything.

Brains, brains, brains!

Next, we need to feed the information the sheep receive from their “eyes” into their “brains”: an artificial neural network.

Artificial neural networks, as their name suggests, are inspired by real brains. They have series, or “layers”, of different nodes (similar to the neurons of real brains) connected to each other by different weights (similar to synapses). These weights determine how strongly each input influences a node. Many heavily weighted inputs will probably lead to a strong activation of a node.

The first layer of the sheep’s neural networks is the input layer, which is made up of all the values it receives from its vision: 155 rays for distances + (155 rays * 6 object types) = 1,085 nodes in the input layer.

Next, 2 “hidden layers” of nodes reduce the total number of nodes to 64, and then 32. The second hidden layer also receives the level of hunger and thirst from the sheep’s internal state. Leaky ReLU activation functions introduce nonlinearity throughout these hidden layers while allowing small negative values to remain, enabling the genetically evolved network to produce more complex behaviours.

Finally, the output layer produces 2 values, one to control their turning (left/right) and one to control their speed (forward/backward). These values are passed through a tanh function to ensure they are between -1 and 1.

Here is a diagram of the entire neural network structure.

This setup also allows us to save the weights of the connections in each sheep’s brain as a list of values. These values are all we need to recreate that particular sheep; you can think of them as analogous to the DNA of real animals.

The next section will explain how we can use these weights to evolve behaviour that is better suited to our virtual world!

Lights, camera, action

Now the sheep are ready to start eating, drinking and moving around their little world. Unfortunately, as they will start with randomly weighted connections, their behaviour will similarly be mostly random.

However, if we define a metric for judging how well each sheep is at surviving, then we can rank the weights of a generation of sheep and prioritise those weights for future generations. This metric is often known as the “objective function” or sometimes simply as “fitness”. If we also add occasional random mutations to the new generations, then we can expect their survival skills to slowly improve over many generations. This is very similar to how evolution works in real life, albeit much, much simpler.

Some technical details: For each generation, 120 sheep were generated. The very first generation was given random weights (using Kaiming Initialization). For each subsequent generation, the top three performing sheep automatically pass their weights to the next generation (once each as exact copies, and once each with mutations). The remaining population slots are filled by choosing the best-performing individual from randomly sampled groups of three (pseudo-deterministic elitism combined with tournament selection).

Results

After running the simulation for 200 generations, we could already see some interesting results!

Generation 0: Mean lifespan was around 60 seconds, with a max of 122 seconds. Most deaths were caused by thirst (88/120 sheep).

Generation 200+: Mean lifespan increased to around 140 seconds, with maximum lifespans exceeding 1,812 seconds (30+ minutes). The number of thirst deaths dropped significantly to 54/120, making hunger the leading cause of death, likely due to the less predictable nature of the plant locations.

How to survive like a sheep

In their 200 generations, the sheep managed to come up with some pretty interesting and observable behaviours:

  1. The "water-hugging" strategy: Because water sources always appeared in specific places (lower ground), sheep learned to stay near water and only leave to grab food, which could spawn anywhere on the map.
  2. The "shark" strategy (Never stop moving): The sheep were able to move forward faster than they could backwards. There was also no penalty for constantly moving. This resulted in the sheep constantly running at maximum speed in order to cover the most ground. 

How software development changed over the last decade?

As mentioned at the start of this article, the initial idea was to recreate a project from nearly a decade ago in order to get a concrete senseof how programming has changed over this time .

I created the original version of this evolution simulator for my Bachelor’s Thesis in 2017, and it took me around 6 months to complete (working part-time). Of course, the second time around I started with a much better understanding of evolutionary algorithms, and programming in general. Even with that important caveat, completing the new version in just 11 hours was a striking contrast with the six months I had spent on the original.

In addition, the 2026 version was created in 3D (albeit with the help of an engine), while the 2017 version was a simple 2D map. The change to 3D not only made the map generation significantly harder, but also affected the implementation of the sheep’s vision, which also needed to work in 3D space. For comparison, here is an image from my thesis project. I think you’ll agree the 2026 version is much more pleasing to the eye!

Takeaways

The use of LLM coding assistants was clearly a great benefit. However, they often struggled with certain cases:

  • I found that coding assistants would often try to use deprecated Unity functions, meaning that thorough testing was essential.
  • They similarly sometimes struggled with the complexity of the task. As the number of moving parts in a project increases (i.e. the neural network architecture, the inputs/outputs, the Unity map generation, Unity docs, and so on), the larger the LLM’s context needs to be, which can lead to degraded performance. While this may be solved with more advanced, future models, the best solution is for the human to correctly manage the context and information the LLM receives.
  • Sometimes they would even just create very stupid bugs. At the end of the day, LLMs do rely on pattern recognition, which means that they can have oversights that are extremely obvious for humans to see. One such instance of this was the first attempt at the evolutionary algorithm of this project: the LLM was clearing the weights of all the sheep before copying their weights to the next generation, which fully prevented any behavioural improvements.

These observations show the value of combining AI-assisted development with human expertise. AI can dramatically accelerate parts of the work, while human judgment remains essential for managing context, spotting errors, and making sound technical decisions. 

That is how we approach AI at Osedea.

We stay close to the advances shaping the industry, but we learn by applying them. Experiments like this help us understand not only what new tools can accelerate, but where human expertise, context, and judgment still make the difference. 

For us, progress in AI isn’t only about faster or more capable models. It’s about learning how to turn those capabilities into systems that are useful, responsible, and able to perform in the real world. 

Talk to us about your AI project.

Jack Tacchi
About the author
Jack Tacchi

Did this article start to give you some ideas? We’d love to work with you! Get in touch and let’s discover what we can do together.

Get in touch
Button Arrow