Skip to content

Instantly share code, notes, and snippets.

@maxcountryman
Created January 13, 2011 17:48
Show Gist options
  • Save maxcountryman/778266 to your computer and use it in GitHub Desktop.
Save maxcountryman/778266 to your computer and use it in GitHub Desktop.
This is my poor attempt at implementing a genetic algorithm that will evolve Brainfuck towards a target string.
import random, time, string
CHARS = '-+<>[].'
SEED_RANGE = 350
GAUSS_RANGE = 15
TIMEOUT = 0.0009
class Biosphere(object):
'''Attempts to find `target` by producing generations of random strings that
mutate by `self.rate` percent. Generational population is determined by
`progeny`.
'''
def __init__(self, target='Hello, world!', rate=0.25, genrange=600):
self.rate = rate
self.target = target
self.genrange = genrange
self.genotypes = [Interpreter(self._randbf()) for i in range(SEED_RANGE)]
self.population = [x.raw for x in self.genotypes]
parsed = ''
i = 0
while parsed != self.target:
raw, score, parsed, self.population = self._fitness(self.population)
print('%s: %s -- %s -- score: %s rate: %s' % (i, parsed, raw, score, self.rate))
i += 1
def _fitness(self, pop):
self.rate = abs(random.gauss(self.rate, 0.0001))
raw, score = min(
[(x, self._distance(x)) for x in pop], key = lambda x: x[1]
)
genotypes = [''.join(self._mutate(raw)) for x in range(self.genrange)]
phenotypes = [Interpreter(x) for x in genotypes]
phenotypes.append(Interpreter(raw))
pop = [x.raw for x in phenotypes]
parsed = Interpreter(raw).parsed
return raw, score, parsed, pop
def _distance(self, phenotype):
raw = phenotype
phenotype = Interpreter(phenotype).parsed
p = map(ord, list(phenotype))
t = map(ord, list(self.target))
p.extend([0] * abs(len(self.target) - len(phenotype)))
t.extend([0] * abs(len(self.target) - len(phenotype)))
dis = sum(map(lambda x: abs(x[1] - x[0]), zip(p, t)))
dis += sum([x for x in range(len(raw)-1)
if raw[x] == '[' and raw[x+1] == ']'])
dis += sum([x for x in range(len(raw)-1)
if raw[x] == '>' and raw[x+1] == '<'])
dis += sum([x for x in range(len(raw)-1)
if raw[x] == '<' and raw[x+1] == '>'])
dis += sum([x for x in range(len(raw)-1)
if raw[x] == '-' and raw[x+1] == '+'])
dis += sum([x for x in range(len(raw)-1)
if raw[x] == '+' and raw[x+1] == '-'])
dis += sum([x for x in range(len(raw)-1)
if raw[x] == '[' and raw[x+1] == '['])
dis += sum([x for x in range(len(raw)-1)
if raw[x] == ']' and raw[x+1] == ']'])
if phenotype == '':
dis += dis * dis
elif len(phenotype) != len(self.target):
dis += dis + len(self.target)
else:
dis
return dis
def _mutate(self, phenotype):
substitute = [(random.choice(CHARS) if random.random() < 0.005 else c) for c in phenotype]
insert = [(c + random.choice(CHARS) if random.random() < 0.009 else c) for c in phenotype]
delete = [('' if random.random() < 0.008 else c) for c in phenotype]
mutation = [substitute, insert, delete]
phenotype = random.choice(mutation)
count = phenotype.count('[') - phenotype.count(']')
if count > 0:
phenotype += ']' * abs(count)
elif count < 0:
phenotype += '[' * count
return phenotype
def _randbf(self):
bf = ''.join([random.choice(CHARS) for x in range(int(random.gauss(GAUSS_RANGE, 5)))])
count = bf.count('[') - bf.count(']')
if count > 0:
bf += ']' * abs(count)
elif count < 0:
bf += '[' * count
return bf
class Loop(object):
'''Used by `loopstack` to hold the position of the cell prefixing the loop.'''
def __init__(self, prefix):
self.prefix = prefix
class Interpreter(object):
'''Takes raw Brainfuck input, returns the parsed result as `output`.'''
def __init__(self, raw):
self.raw = raw
self.parsed = ''
self._parse(raw)
def _parse(self, raw):
if raw.count('[') != raw.count(']'):
return None
tape_len = 30000 #thirty-thousand cells
tape = [0] * tape_len
cell = 0
loopstack = []
parsed = ''
start = time.time()
pointer = 0
while pointer < len(raw):
if time.time() - start >= TIMEOUT:
return None
instr = raw[pointer]
if instr == '>':
if cell >= tape_len - 1:
return None
cell += 1
elif instr == '<':
if cell <= 0:
return None
cell -= 1
elif instr == '+':
tape[cell] += 1
elif instr == '-':
if tape[cell] <= 0:
return None
tape[cell] -= 1
elif instr == '.':
if tape[cell] not in range(256):
return None
parsed += chr(tape[cell])
elif instr == '[':
loopstack.insert(0, Loop(pointer))
elif instr == ']':
try:
loop = loopstack.pop(0)
if tape[cell] != 0:
pointer = loop.prefix
loopstack.insert(0, loop)
except IndexError:
return None
pointer += 1
self.parsed = parsed
if __name__ == '__main__':
Biosphere()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment