$verbosity = 0 @tests = [] def report(msg) puts msg if $verbosity == 2 end def setup!(bf) @highest_floor = 99 @break_floor = bf @eggs_in_hands = 2 @eggs_on_ground = 0 @floor, @walks, @drops = 0,0,0 @solution = false end def walk(arg) if arg.is_a? Symbol walk arg => 1 elsif arg[:to] if arg[:to] < 0 || arg[:to] > @highest_floor puts "Cannot go to floor #{arg[:to]}" return false end @walks += (@floor - arg[:to]).abs @floor = arg[:to] report "Walk to #{@floor}" true elsif arg[:up] walk :to => (@floor + arg[:up]) elsif arg[:down] walk :to => (@floor - arg[:down]) end end def drop_egg! @egg_broken = false @drops += 1 if @eggs_in_hands <= 0 puts "No eggs to drop" return end @eggs_in_hands -= 1 if @break_floor > floor @eggs_on_ground += 1 report "Dropped egg! -- Didn't break" else @egg_broken = true report "Dropped egg! -- Whoa, broken!" end end def egg_broken? @egg_broken end def eggs_in_hands? eggs_in_hands > 0 end def no_eggs_in_hands? !eggs_in_hands? end def eggs_in_hands @eggs_in_hands end def eggs @eggs_in_hands + @eggs_on_ground end def pick_up_eggs! report "Picking up eggs -- found #{@eggs_on_ground}" verb = $verbose $verbose = false walk :to => 0 $verbose = verb @eggs_in_hands += @eggs_on_ground @eggs_on_ground = 0 end def floor @floor end def highest_floor @highest_floor end def report_solution!(floor) @solution = floor throw :finished end def test(algorithm, &alg_proc) @tests << [algorithm, alg_proc] end def run_test(break_floor, alg) setup!(break_floor) catch :finished do alg[1].call end {:algorithm => alg[0], :walks => @walks, :drops => @drops, :solved => @solution == @break_floor, :solution => @solution} end def run_tests! results = [] for alg in @tests for n in (1..99) results << run_test(n, alg) end end if $verbosity == 1 || $verbosity == 2 for result in results puts "\"#{result[:algorithm]}\" #{result[:solved] ? 'solved' : 'didn\'t solve'} the puzzle. It took #{result[:walks]} walks and #{result[:drops]} drops. Solution: #{result[:solution]}" end end for alg in @tests alg_results = results.select{|r| r[:algorithm] == alg[0]} walks = alg_results.inject(0){|n,r| r[:walks] + n} drops = alg_results.inject(0){|n,r| r[:drops] + n} solutions = alg_results.inject(0){|n,r| r[:solved] ? n + 1 : n} puts "\"#{alg[0]}\" solved #{solutions} of #{results.size / @tests.size} puzzles. It took #{walks} walks and #{drops} drops" end end test "Simple algorithm" do next_floor = 0 while eggs_in_hands? walk :to => next_floor next_floor = floor + 1 drop_egg! if egg_broken? report_solution! floor elsif no_eggs_in_hands? pick_up_eggs! end end end test "Skip 7 algorithm" do n = 6 next_floor = n + 1 while eggs != 0 if no_eggs_in_hands? pick_up_eggs! elsif eggs == 1 walk :to => next_floor next_floor = floor + 1 if floor != 99 drop_egg! if egg_broken? report_solution! floor end else walk :to => next_floor drop_egg! if egg_broken? next_floor = floor - n else if floor + n + 1 > 99 next_floor = floor + 1 else next_floor = floor + n + 1 end end end end end