working to compute a single program that meets the target output
This commit is contained in:
parent
78b45a84cc
commit
69784ff0de
55
genetic.rb
55
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
|
||||
|
60
program.rb
60
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user