working to compute a single program that meets the target output

This commit is contained in:
Josh Holtrop 2014-02-11 08:56:30 -05:00
parent 78b45a84cc
commit 69784ff0de
2 changed files with 106 additions and 9 deletions

View File

@ -1,3 +1,58 @@
#!/usr/bin/env ruby
require_relative "program"
NUM_PROGRAMS = 100
EXPECTED_OUTPUT = "HELLOWORLD"
def ord(chr)
chr.unpack("C").first
end
def grade_program(p)
output = p.execute
grade = (EXPECTED_OUTPUT.size - output.size).abs * 100
[EXPECTED_OUTPUT.size, output.size].min.times do |i|
grade += (ord(EXPECTED_OUTPUT[i]) - ord(output[i])).abs
end
grade
end
def main
programs = NUM_PROGRAMS.times.map do
p = Program.new
[grade_program(p), p]
end
generation = 0
begin
last_best = nil
while true
generation += 1
programs += programs.map do |grade, p|
p2 = p.clone
p2.mutate
[grade_program(p2), p2]
end
programs.sort! do |a, b|
a.first <=> b.first
end
programs = programs.take(NUM_PROGRAMS)
if last_best.nil? or programs.first.first < last_best
last_best = programs.first.first
$stdout.write("\rGrade: #{last_best}, output: #{programs.first.last.execute.inspect} ")
end
if programs.first.first == 0
puts "Program found after #{generation} generations:"
puts programs.first.last
break
end
end
rescue Interrupt
puts
puts "Canceled at #{generation} generations"
puts "The best program (grade #{programs.first.first}) is:"
puts programs.first.last
end
end
main

View File

@ -6,15 +6,8 @@ class Program
end
end
private
def random_instruction
case rand(2)
when 0
[:load, (65 + rand(26)).chr]
when 1
[:output]
end
def initialize_copy(other)
@instructions = Marshal.load(Marshal.dump(@instructions))
end
def to_s
@ -29,5 +22,54 @@ class Program
end
def execute
output = ""
register = "A"
@instructions.each do |instruction, *params|
case instruction
when :load
register = params.first
when :output
output += register
end
end
output
end
def mutate
case rand(4)
when 0 # remove an instruction
if @instructions.size > 1
i = rand(@instructions.size)
@instructions.delete_at(i)
end
when 1 # insert an instruction
i = rand(@instructions.size + 1)
@instructions.insert(i, random_instruction)
when 2 # replace an instruction
i = rand(@instructions.size)
@instructions[i] = random_instruction
when 3 # modify parameter
i = rand(@instructions.size)
if @instructions[i].first == :load
@instructions[i][1] = mutate_param(@instructions[i][1])
end
end
end
private
def random_instruction
case rand(2)
when 0
[:load, (65 + rand(26)).chr]
when 1
[:output]
end
end
def mutate_param(old_value)
chr = old_value.unpack("C").first
chr = (chr + (rand(2) + 1) * [-1, 1][rand(2)]) % 26
chr.chr
end
end