$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