3 ms·
Nanopond's instruction set is based on Brainfuck, and it's "evolvable" because it's very easy for self-replicating programs to be generated at random. Quines ar
by Scaevolus 15y ago
Nanopond's instruction set is based on Brainfuck, and it's "evolvable" because it's very easy for self-replicating programs to be generated at random. Quines are a sizable percentage of the total program space.
The nanopond world is a 640x480 grid (edges wrap) where each cell has an energy, a genome, an ID, and a parent ID (a dead cell has an ID of zero). Each tick, a random cell's genome is executed. At various intervals, a random cell is selected and given a random genome and some energy.
The first four bits of a cell's genome is called the logo, and is skipped over during execution.
The virtual machine has an instruction pointer, a 4-bit register, an output buffer, a pointer, and a facing direction, which selects which adjacent cell it interacts with.
These are the 16 instructions, with associated pseudocode:
0 ZERO: register = pointer = facing = 0
> FWD: pointer++
< BACK: pointer--
+ INC: register++
- DEC: register--
g READG: register = genome[pointer]
G WRITEG: genome[pointer] = register
b READB: register = buffer[pointer]
B WRITEB: buffer[pointer] = register
[ LOOP: if register is nonzero jump forward to matching REP
] REP: if register is zero jump back to matching
t TURN: facing = register & 3
x XCHG: register = genome[instruction_pointer++]
k KILL: kill adjacent cell (set ID to 0)
s SHARE: equalize energy with adjacent cell
. STOP: end execution
Every instruction executed costs 1 energy. When a cell has zero energy, it is dead, though a SHARE command can revive it.
At the end of execution, the output buffer is written into the adjacent (facing) cell. This SHARE and KILL require permissions, which are basically whether the cell is dead or a random chance depending on how close the current register is close enough to logo. A failed SHARE or KILL costs extra energy.
Here's the most common genome in my simulation after 3 billion iterations:
#gtsg-[G>gB]kx0. (where # is the logo and can be any value)
Decomposed:
#gtsg-[G : turn in the direction given by the logo, share energy with the adjacent cell, decrement, and write that as the logo for the output genome. This has the effect of rotating the cell's direction by 90 degrees every execution.
[G>gB] : copy the genome into the output buffer. G[>gB] would be more efficient, but that's evolution is blind!
kx0. : Try to kill the cell the output is going into, to increase chances of successful replication. I'm unsure if x0 (loads a zero into the register) improves fitness.
After running for a bit, the core replication loop tends to be very stable-- much like how parts of ribosomes have been unchanged for 2 billion years. There are many ways to achieve low-level replication, but it's almost impossible to transition between them by gradual changes.
- javert 15y agoThanks!!!