Artificial life prototype

Evosim

A three-dimensional, real time simulation of natural selection. Creatures made of jointed physics blocks know nothing but eating and mating. Over generations they get better at moving around, finding food and finding mates. Or they die out.

Download on itch.io View source on GitHub

Pink block creatures in the teal water, two large ones in the foreground, with green lines from their mouths to the foodbits they are chasing.
A population mid-run. Green lines show which foodbit each hungry creature is chasing.

Overview

Evosim is a proof of concept. It set out to find how well an ordinary consumer PC could simulate a 3D artificial gene pool, where every creature is built from several rigid bodies connected by joints and every one of them is simulated by the physics engine at once.

It is meant as a toy, not a scientific tool. Nothing about the creatures is designed: the first generation is random, and everything after that comes from who managed to eat and who managed to mate.

Inspiration

The project was first inspired by Karl Sims' Evolving Virtual Creatures (1994), in which creatures built from jointed blocks evolved to swim, walk and compete, and by Jeffrey Ventrella's Swimbots, a world of swimming creatures that look for food and choose their own mates. The other spark was noticing that this is what many people expected Spore (2008) to be: a world where creatures really evolve, rather than being designed by the player.

What a creature does

  1. Eat When its energy drops below its hunger threshold, it looks for the nearest foodbit and swims towards it.
  2. Mate When it is fed and old enough, it looks for a partner, preferring one of a similar colour.
  3. Pass on genes A mating pair gives a third of each parent's energy to one child, whose genes are a mix of both, with random mutations.
  4. Die Being alive costs energy every second. A creature that runs out slows, darkens and dies.
Engine
Unity, scripts in C#
Physics
Rigid bodies joined by configurable joints, driven by a sine wave
Platforms
Windows, Linux
Settings
One JSON file, no rebuild needed
Started
2011
Inspired by
Karl Sims' Evolving Virtual Creatures, Jeffrey Ventrella's Swimbots
Licence
CC BY-NC-SA 4.0

The world and its energy

The simulation is a closed system. A fixed amount of energy, 5,000 units by default, is put in at the start and never created or destroyed. Energy that isn't in a creature or a foodbit sits in a pool called the ether. The ether limits how big the population can grow: more creatures means less energy left for food.

Energy flow A closed system holding a fixed total of energy. The ether pays for new foodbits. Creatures gain energy by eating foodbits. Creatures return energy to the ether through metabolism every second and when they die. When two creatures mate, each gives a third of its energy to the child. Closed system total_energy = ether + foodbits + creatures Ether The pool of unused energy Pays for every new foodbit Corrects rounding leaks each minute Foodbits 5 to 25 units each; size shows energy Sprout near an existing foodbit, so food grows in clusters Creatures Start with 70 units Spend 0.8 units a second to live Give 33% each to a child New foodbit every 0.2 s, if the ether can afford it Eaten Metabolism every second, everything left at death
Energy moves round a loop. The only way into a creature is through its mouth.

Foodbits sprout from other foodbits, a short distance from a random parent, so food spreads in patches across the world. Creatures that can reach a patch and stay near it do well, and the patches thin out as they are grazed.

Creatures

Every creature is built at runtime from its chromosome. The body is a coloured box, the root, with up to four branches of limbs attached to it. Each branch is a chain of up to three box-shaped segments, joined to the root and to each other with physics joints.

Creature body plan A creature's root box with three limb branches. One branch has two segments and the others have one each, joined by physics joints. The mouth sits on the front face and draws a green line to the foodbit it is chasing. The genital sits on the back face and draws a white line, ending in a red heart, to its chosen mate. The eye at the centre sees 12 units in every direction, shown by a dashed circle. Root A box with body colour and scale genes. The eye at its centre picks a target. Branches 1 to 4 chains of 1 to 3 limb segments, all rocked by one sine wave. Mouth and genitalia The mouth on the front face eats food; the genital on the back face mates.
The body plan, simplified to two dimensions. Lines drawn from the mouth and genital show where a creature is heading. The dashed line is the creature's line-of-sight.

How they move

Each creature has a sine wave set by three genes: frequency, amplitude and phase. The wave drives every joint, so the limbs flap together in rhythm. On each upswing the creature is pushed forwards and turns towards its target; it is pushed harder the more branches it has. Drag on the root and limbs makes the world feel like water, and a body that flails in the wrong direction gets nowhere.

A creature whose energy falls below 5 units slows down and fades to black. Food brings its colour and speed back. If its energy reaches zero, it dies and its remaining energy returns to the ether.

Close-up of creatures with mustard limb chains and red and coral bodies, two of them with green mouth lines to the same foodbit.
Close up, the body plans vary a lot from one creature to the next.
A spread-out population of small pink creatures, a few of them darkened to near black, with green lines running from mouths to foodbits.
A starving creature slows down and darkens.

Behaviour

Creatures run a small state machine, updated ten times a second. The eye refreshes four times a second and picks a target to suit the current state: the nearest foodbit when hungry, or the most similarly coloured creature in sight when looking for a mate.

StateEntered when
Searching for foodEnergy below the hunger threshold and no foodbit in sight
Pursuing foodHungry and a foodbit is in sight
Searching for mateFed, at least 6 seconds old, and no partner in sight
Pursuing mateFed, mature and a partner is in sight
MatingA pursuing creature comes within 1 unit of its partner
DeadEnergy reaches zero, from any state
Creature state machine A hungry creature searches for food, then pursues a foodbit once it sees one and eats it on contact. A fed, mature creature searches for a mate, pursues the partner it sees, and mates on contact, producing a child. After eating a foodbit, and after spawning a child, a decision checks whether the creature's energy is above its hunger threshold: if not, it goes back to searching for food; if so, it goes to searching for a mate. Hungry Fed and mature Searching for food Foodbit seen Pursuing food Eat foodbit Energy above threshold? Searching for mate Partner seen Pursuing mate Mating Child spawned Yes No Energy spent on child
After eating, or after giving energy to a child, the creature's energy decides whether it looks for food or a mate next.

The hunger threshold is itself a gene. A creature with a low threshold spends more of its life looking for mates; one with a high threshold keeps eating. Which works better depends on how much food is around, and the population drifts accordingly.

Two orange creatures with long yellow limbs, joined by a white mate line, swim together and mate, and a child appears beside them.
Two creatures meet, mate, and a child spawns between them.

Genetics

A chromosome holds everything needed to build and run a creature. The first generation, 20 creatures by default, gets random genes within limits set in the settings file. After that every new creature comes from two parents through crossover and then mutation.

GeneFirst generationCrossoverMutation
Body colourRandom RGBAverage of both parentsYes
Limb colourSame as body colourAverage of both parentsYes
Root scaleRandom, 0.3 to 1.0 on each axisFrom one parentOnly for roots over 1 unit on every axis
Limb structure1 to 4 branches of 1 to 3 segmentsFrom one parentNo
Segment size and positionRandom within limitsEach axis may be swapped for a random segment's from the other parentSize only
Joint wave: frequency, amplitude, phaseRandomEach from either parentYes
Hunger threshold50 unitsFrom either parentYes

With the default settings, each value has a 50% chance of being crossed over and a 10% chance of mutating, by a random amount up to ±0.3. Because colour is averaged, and mates are chosen by colour, related creatures tend to look alike and lineages show up as colour groups across the world.

Playing with it

Evosim is something to watch rather than play. Fly the camera around, click on creatures to follow them, save the ones you like, and kill the ones you don't like.

Controls

Right drag
Move forwards and back, and turn
Left and right drag
Turn and look up or down
Middle drag
Pan
Left click
Select a creature
F1
Show or hide simulation info
F2
Show or hide the tools panel
Esc
Quit

Inspecting creatures

Selecting a creature opens a panel with its energy, age, number of offspring, food eaten and current state. From there you can save it, kill it, or unlock the camera from it. Saved creatures are written as JSON to data/saved_creatures and can be imported back into a later run, so a creature that evolved well can be dropped into a fresh world.

There's already one in there that I saved called "Seahorse" because I thought it looked and behaved a bit like a seahorse.

A selected coral creature chasing a large foodbit. The panel in the corner shows creature-4478 with energy 37.8, age 19 s, no offspring, one food eaten, state pursuing food, and Save, Kill and Unlock buttons.
A selected creature and its info panel.

Settings

Every number in the simulation lives in evosim_Data/settings.json: total energy, starting population, food growth, metabolism, line of sight, body size limits, and the crossover and mutation rates. Non-integer values must be written with a decimal point, such as 3.0, and nothing is validated. Population and foodbit counts can be logged to CSV for graphing afterwards.

"ether": {
    "total_energy"          : 5000.0,
    "start_number_foodbits" : 200,
    "starting_creatures"    : 20
},
"genetics": {
    "crossover_rate"        : 0.5,
    "mutation_rate"         : 0.1,
    "mutation_factor"       : 0.3
}

On a laptop or an older machine, lower total_energy. It puts a soft limit on how many creatures can be alive at once, and so on how many rigid bodies the physics engine has to handle. The default suits a 5th generation Core i5 or equivalent.

Building from source

Clone the repository and check out develop, since master is out of date. Open the folder as an existing project in Unity, open the scene named Main, and make sure the Main script is attached to the Main GameObject.

git clone https://github.com/clomax/evosim-prototype.git
cd evosim-prototype
git checkout develop

Limitations

This is a prototype, and some of the shortcuts it takes are worth knowing about:

  • Movement is helped along. The root is pushed forwards in time with the joint wave, so limbs shape how a creature moves rather than doing all the work. The intention is to gradually introduce complexity.
  • The number of limbs and segments is fixed by the first generation; crossover picks one parent's layout and mutation never adds or removes a segment.
  • Root size almost never mutates, because the mutation only applies to roots larger than any first-generation creature.
  • The behaviour is a hand-written state machine, not something that evolves.
  • Performance is bounded by the physics engine. Large populations slow down quickly on modest hardware.

Ideas for further work

Plans and ideas are tracked on the project's Trello board.

Bodies and movement

  • Limbs that evolve: mutation that adds or removes segments and whole branches, and crossover that mixes branches from both parents, along with their shape, segments, colour and position. This is in progress.
  • A complete rebuild of how limbs work, drawing on Karl Sims' paper and Graph Craft.
  • Movement of limbs to determine propulsion rather than cheating it for effect, as Karl Sims' swimming creatures did.
  • An option to enforce symmetry in body shape.
  • Newborn creatures that grow slowly to full size.

Brains and behaviour

  • Neural network brains. Inputs would be the target's position, the creature's own position, its energy and how the distance to its goal is changing; outputs would set the joint amplitudes, frequencies and phases and the direction to face. Feedforward, recurrent, LSTM and NEAT or HyperNEAT networks are all candidates.
  • Sexual selection, and sexual dimorphism, with two sexes that can look different.
  • Carnivores, with creatures that can eat each other. This is more than a switch to flip, since it changes the whole energy balance.
  • Bioluminescence, perhaps as a way for creatures to signal to each other. Polyworld gave its creatures lights without knowing what they would use them for.
  • Groups of creatures at the start, each group identical or only slightly mutated, rather than all random.

Watching and experimenting

  • A settings panel inside the simulation for every value in the settings file, plus time scaling and a pause menu.
  • Saving and loading a whole gene pool, not just single creatures.
  • Statistics for the current gene pool, with live graphs of population and gene values.
  • A database behind it all: a time series of creature and foodbit numbers, together with settings, saved creatures, saved gene pools and a Clade diagram of genetic history.
  • Creature tracking: a list of creatures you're interested in, camera modes for following them, and a clear marker on the selected one.
  • Generated names for creatures, made of a genus name and a given name.
  • A creature editor that changes the genes as you edit the body, with import and export, and silhouettes of saved creatures.
  • Telling two chosen creatures to mate with each other.
  • A prettier environment.

Performance

  • A lookup table for the joint sine wave instead of calculating it every physics step.
  • Spreading the total energy check over several frames or threads.