Created
December 24, 2013 20:18
-
-
Save jnaglick/8117451 to your computer and use it in GitHub Desktop.
flower.rb - breed flowers with a genetic algorithm - john naglick - 2013
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Flower | |
class Stem | |
module Bits | |
LENGTH = 3 | |
WIDTH = 2 | |
OUTER_COLOR = 8 | |
INNER_COLOR = 8 | |
TOTAL = 21 # TODO compute this | |
end | |
attr_accessor :length, :width, :outer_color, :inner_color | |
def initialize binary_s = nil | |
if binary_s == nil | |
r = Random.new | |
self.length = r.rand 2 ** Bits::LENGTH | |
self.width = r.rand 2 ** Bits::WIDTH | |
self.outer_color = r.rand 2 ** Bits::OUTER_COLOR | |
self.inner_color = r.rand 2 ** Bits::INNER_COLOR | |
else | |
self.length = binary_s[0, Bits::LENGTH].to_i 2 | |
self.width = binary_s[Bits::LENGTH, Bits::WIDTH].to_i 2 | |
self.outer_color = binary_s[Bits::LENGTH + Bits::WIDTH, Bits::OUTER_COLOR].to_i 2 | |
self.inner_color = binary_s[Bits::LENGTH + Bits::WIDTH + Bits::OUTER_COLOR, Bits::INNER_COLOR].to_i 2 | |
end | |
end | |
def to_s | |
"length: #{length} width: #{width} ocolor: #{outer_color} icolor: #{inner_color}" | |
end | |
def to_binary_s | |
length.to_s(2).rjust(Bits::LENGTH, '0') + width.to_s(2).rjust(Bits::WIDTH, '0') + outer_color.to_s(2).rjust(Bits::OUTER_COLOR, '0') + inner_color.to_s(2).rjust(Bits::INNER_COLOR, '0') | |
end | |
end | |
class Petals | |
module Bits | |
LENGTH = 3 | |
WIDTH = 2 | |
OUTER_COLOR = 8 | |
INNER_COLOR = 8 | |
AMOUNT = 4 | |
TOTAL = 25 # TODO compute this | |
end | |
attr_accessor :length, :width, :outer_color, :inner_color, :amount | |
def initialize binary_s = nil | |
if binary_s == nil | |
r = Random.new | |
self.length = r.rand 2 ** Bits::LENGTH | |
self.width = r.rand 2 ** Bits::WIDTH | |
self.outer_color = r.rand 2 ** Bits::OUTER_COLOR | |
self.inner_color = r.rand 2 ** Bits::INNER_COLOR | |
self.amount = r.rand 2 ** Bits::AMOUNT | |
else | |
self.length = binary_s[0, Bits::LENGTH].to_i 2 | |
self.width = binary_s[Bits::LENGTH, Bits::WIDTH].to_i 2 | |
self.outer_color = binary_s[Bits::LENGTH + Bits::WIDTH, Bits::OUTER_COLOR].to_i 2 | |
self.inner_color = binary_s[Bits::LENGTH + Bits::WIDTH + Bits::OUTER_COLOR, Bits::INNER_COLOR].to_i 2 | |
self.amount = binary_s[Bits::LENGTH + Bits::WIDTH + Bits::OUTER_COLOR + Bits::INNER_COLOR, Bits::AMOUNT].to_i 2 | |
end | |
end | |
def to_s | |
"length: #{length} width: #{width} ocolor: #{outer_color} icolor: #{inner_color} amount: #{amount}" | |
end | |
def to_binary_s | |
length.to_s(2).rjust(Bits::LENGTH, '0') + width.to_s(2).rjust(Bits::WIDTH, '0') + outer_color.to_s(2).rjust(Bits::OUTER_COLOR, '0') + inner_color.to_s(2).rjust(Bits::INNER_COLOR, '0') + amount.to_s(2).rjust(Bits::AMOUNT, '0') | |
end | |
end | |
attr_accessor :stem, :petals | |
def initialize binary_s = nil | |
if binary_s == nil | |
self.stem = Stem.new | |
self.petals = Petals.new | |
else | |
self.stem = Stem.new binary_s[0, Stem::Bits::TOTAL] | |
self.petals = Petals.new binary_s[Stem::Bits::TOTAL, Petals::Bits::TOTAL] | |
end | |
end | |
def to_s | |
"stem: [#{stem}] petals: [#{petals}]" | |
end | |
def to_binary_s | |
stem.to_binary_s + petals.to_binary_s | |
end | |
end | |
def random_gen size | |
gen = [] | |
(1..size).each do | |
gen.push Flower.new | |
end | |
gen | |
end | |
def pick gen | |
selected = {} | |
gen.each_with_index do |g, i| | |
selected[i] = false | |
end | |
while true | |
gen.each_with_index do |g, i| | |
puts "#{i}: [#{selected[i]}] #{g}" | |
end | |
puts '>> TOGGLE ONES YOU WANT (\'q\' when done)' | |
input = gets.chomp | |
break if input == 'q' | |
selected[input.to_i] = !selected[input.to_i] | |
end | |
gen.values_at *selected.select{|k,v|v}.keys | |
end | |
def breed_gen flowers, mutation_rate = 0.20 | |
gen = [] | |
flowers.product(flowers).select{|i|i[0]!=i[1]}.each do |flower1, flower2| # everything is bred with everything else, but not itself | |
total_bits = Flower::Stem::Bits::TOTAL + Flower::Petals::Bits::TOTAL | |
breed_pt = 0 | |
breed_pt = (Random.rand total_bits) while (breed_pt < (0.2 * total_bits).to_i || breed_pt > (0.8 * total_bits).to_i) | |
new_flower_binary_s = flower1.to_binary_s[0, breed_pt] + flower2.to_binary_s[breed_pt, total_bits] | |
if Random.rand < mutation_rate | |
(1..5).each do | |
mutate_pt = Random.rand total_bits | |
new_flower_binary_s[mutate_pt] = new_flower_binary_s[mutate_pt] == '0' ? '1' : '0' | |
end | |
end | |
gen.push Flower.new new_flower_binary_s | |
end | |
gen | |
end | |
# init 10 random flowers | |
# loop: | |
# display them | |
# score them (user selects favorites) | |
# breed + mutate | |
gen_i = 0 | |
gen = random_gen 10 | |
while true | |
puts "*** GENERATION #{gen_i} ***" | |
puts gen | |
puts '>> KEEP GOING?' | |
input = gets.chomp | |
break if input == 'q' | |
picked = pick gen | |
puts "you picked #{picked.count} that time" | |
gen = breed_gen picked | |
gen_i += 1 | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment