I used Codex 5.5 heavily throughout the development of the project. Main code reference: https://github.com/devosoft/avida
I am currently validating this implementation against the original Avida experiments.
Bugs might still be around. Any help is appreciated.
avida.py is a compact, hackable Python implementation inspired by the
Avida artificial life system. Digital
organisms execute circular genomes on a small virtual CPU, copy themselves into
neighboring cells, mutate, solve logic tasks, and earn merit that gives them
more CPU time.
This is not a port of Avida. It keeps the core digital evolution loop readable: the per-organism CPU is scalar, while population bookkeeping uses NumPy arrays for scheduling, occupancy, neighbors, statistics, and rendering.
- Python 3.9+
- NumPy
- ImageIO plus ffmpeg support for MP4/GIF output
Run a basic experiment:
python avida.py --updates 200 --width 20 --height 20 --seed 1Render solved-function counts instead of genotype IDs:
python avida.py --updates 200 --view functionsWrite an animation:
python avida.py --updates 500 --video videos/avida.mp4 --video-every 1 --fps 20Save and resume a checkpoint:
python avida.py --updates 1000 --checkpoint checkpoints/run.pkl --checkpoint-every 100
python avida.py --resume checkpoints/run.pkl --updates 1000Checkpoints are pickle files containing the full world state, including organisms, CPU state, genomes, NumPy arrays, counters, task history, and RNG state. Only load checkpoint files you trust.
render() prints an ASCII genotype map. Empty cells are ., and living cells
are grouped by genotype with symbols like 0, 1, 2, and so on.
render_functions() prints solved-function counts per organism. Empty cells are
., and living cells show 0 through 9, meaning how many logic tasks that
organism has solved.
render_rgb() returns a NumPy RGB frame for videos. Genotype view uses stable
colors per genotype; function view colors cells by solved-function count.
When an organism executes IO, it outputs a register value. That output is
checked against the two most recent inputs. A newly solved task is recorded and,
unless --no-task-rewards is used, multiplies the organism's merit.
Merit controls CPU scheduling: organisms are selected for execution with probability proportional to merit. Higher merit does not directly prevent replacement, but it gives an organism more execution time and therefore more chances to reproduce.
| Task | Operation | Merit |
|---|---|---|
not |
~A or ~B |
2 |
nand |
~(A & B) |
2 |
and |
A & B |
4 |
or_n |
`A | ~Bor~A |
or |
`A | B` |
and_n |
A & ~B or ~A & B |
8 |
nor |
~A & ~B |
16 |
xor |
A ^ B |
16 |
equ |
~(A ^ B) |
32 |
All bitwise outputs are wrapped to unsigned 32-bit values.
General rule: after each instruction, cpu.ip moves to the next genome position
unless the instruction changes the next instruction pointer.
nop-A: no operation; also modifies commands to selectaxorip.nop-B: no operation; also modifies commands to selectbxorread_head.nop-C: no operation; also modifies commands to selectcxorwrite_head.if-n-equ: skips the next instruction if the selected register equals its complement.if-less: skips unless selected register is signed-less-than its complement.pop: pops the active stack into the selected register, or0if empty.push: pushes the selected register onto the active stack.swap-stk: switches between the two stacks.swap: swaps the selected register with its complement.shift-r: right-shifts the selected register by one bit.shift-l: left-shifts the selected register by one bit.inc: increments the selected register.dec: decrements the selected register.add: adds the complement register into the selected register.sub: subtracts the complement register from the selected register.nand: sets selected register to~(selected & complement).IO: outputs the selected register, records solved tasks, then receives a new input.h-alloc: allocates child memory equal to parent genome length.h-divide: divides if child memory is complete, applying divide mutations.h-copy: copies one instruction fromread_headto child memory atwrite_head.h-search: searches for the complement of the following NOP label.mov-head: moves a selected head toflow_head; can jumpip.jmp-head: moves a selected head by signed offsetcx; can jumpip.get-head: stores the selected head position incx.if-label: checks recently copied instructions against the complement NOP label.set-flow: setsflow_head = cx.
Modifier selection:
| Modifier | Register | Head |
|---|---|---|
nop-A |
ax |
ip |
nop-B |
bx |
read_head |
nop-C |
cx |
write_head |
| none | bx |
ip |
--ancestor {nofunction,nand,original}: choose the injected ancestor genome.--ancestor-copies N: inject multiple ancestor copies at startup.--mutation RATE: per-instruction copy mutation rate.--insert RATE: per-instruction insertion rate at division.--delete RATE: per-instruction deletion rate at division.--report-every N: print stats everyNupdates.--view {genotype,functions}: choose final render and video view.--checkpoint PATH: save a checkpoint.--checkpoint-every N: save periodically;0disables periodic saves.--resume PATH: resume from a saved checkpoint.