class Device attr_reader :weight, :cost, :lifetime def initialize(cost, lifetime, weight) @cost = cost @lifetime = lifetime @weight = weight end def to_s "#{@cost}:#{lifetime}:#{weight}" end end class PowerSource < Device attr_reader :power def initialize(cost, lifetime, weight, power, kind) super(cost, lifetime, weight) @power = power @kind = kind end def to_s "#{@kind}%#{super.to_s}->#{@power}" end end class Gear < Device def initialize(cost, lifetime, weight, loSpeed, hiSpeed, minPower, optimalPower, kind) super(cost, lifetime, weight) @loSpeed = loSpeed @hiSpeed = hiSpeed @minPower = minPower @optimalPower = optimalPower @kind = kind end def speed(power) return @hiSpeed if power >= @optimalPower return @loSpeed if power >= @minPower return 0 end def to_s "#{@kind}%#{super.to_s}<#{@loSpeed}-#{@hiSpeed}>:<#{@minPower}-#{@optimalPower}>" end end class Instrument < Device def initialize(count) super(count * 50, 3000, count * 20) @count = count end def required_power 2 * @count end def value(availablePower) return @count * 1000 if required_power < availablePower return 0 end def consume_power(availablePower) if availablePower > required_power availablePower - required_power else 0 end end def to_s "#{@count}Instr%#{super.to_s}" end end # Abstract Mars Explorer hardware configuration class Explorer def initialize(dna) @dna = dna @powerSource = buildPowerSource(next_dna_fragment(2)) @gear = buildGear(next_dna_fragment(2)) @instruments = buildInstruments(next_dna_fragment(2)) end def value lifetime = [ @powerSource.lifetime, @gear.lifetime, @instruments.lifetime] power = @powerSource.power speed = @gear.speed(@instruments.consume_power(power)) distance = speed * lifetime.min return distance * @instruments.value(power) end def buildPowerSource(fragment) case fragment when 0b00 PowerSource.new(100, 1000, 100, 20, 'Solar') when 0b01 PowerSource.new( 10, 400, 400, 60, 'Chemical') when 0b10 PowerSource.new(700, 700, 20, 10, 'Bioelectric') when 0b11 PowerSource.new(900, 3000, 600, 50, 'Atomic') else raise "Internal error" end end def buildGear(fragment) case fragment when 0b00 Gear.new(30, 1000, 20, 100, 200, 10, 20, 'wheels') when 0b01 Gear.new(60, 1000, 50, 100, 200, 10, 20, 'tracks') when 0b10 Gear.new(90, 50, 30, 500, 900, 40, 50, 'aerial') when 0b11 Gear.new(80, 700, 60, 100, 300, 20, 30, 'walking') else raise "Internal error" end end def buildInstruments(fragment) count = 1 + fragment Instrument.new count end def next_dna_fragment(bits) fragment = (@dna & ((1 << bits) - 1)) @dna = @dna >> bits return fragment end def to_s "[Explorer #{@powerSource} #{@gear} #{@instruments}]" end end seed = Generation.new(10, 7, 1000000, proc { |dna| explorer = Explorer.new(dna) explorer.value }, proc { |dna1, dna2| len = 6 dna = combine_random_bits(dna1, dna2, len) dna = mutate_random_bits(dna, len) if rand(10) == 1 return dna }) while seed.has_more_space seed.add Individual.new(rand(0b1000000)) end universe = Universe.new(seed) universe.run 100 best = universe.best puts "Best indivdual: #{best}" explorer = Explorer.new(best.dna) puts "Optimal hardware configuration: #{explorer} -> #{explorer.value}"