From 69784ff0dec13afd2441b5d970274e3cb43e3283 Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Tue, 11 Feb 2014 08:56:30 -0500 Subject: [PATCH] working to compute a single program that meets the target output --- genetic.rb | 55 +++++++++++++++++++++++++++++++++++++++++++++++++ program.rb | 60 ++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 106 insertions(+), 9 deletions(-) diff --git a/genetic.rb b/genetic.rb index 550f21f..70ced16 100755 --- a/genetic.rb +++ b/genetic.rb @@ -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 diff --git a/program.rb b/program.rb index 0556b8e..4e41091 100644 --- a/program.rb +++ b/program.rb @@ -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