# -*- ruby -*- class Food attr_accessor :name def initialize(name, energy) @name = name @energy = energy end def eaten; @energy end end class Meat < Food def initialize(name, energy) super(name, energy) @valid = true end def eaten @valid or raise "bad food" @valid = false super end end class Animal attr_accessor :energy, :name def initialize(name, energy, when_slaughtered, food_accepted) @valid = true @name = name @energy = energy @food_accepted = food_accepted @when_slaughtered = when_slaughtered end def eat(food) @valid or raise "bad animal" if @food_accepted.call(food.name) @energy += food.eaten else raise "#{@name} doesn't accept food #{food.name}" end end def slaughter @valid or raise "bad animal" @valid = false Meat.new(@when_slaughtered, @energy) end end $meats = [ "beef", "dead_rabbit", "dead_human" ] def is_meat(food_name) $meats.member?(food_name) end def new_cow(energy) Animal.new("cow", energy, "beef", proc{|n| n == "grass"}) end def new_rabbit(energy) Animal.new("rabbit", energy, "dead_rabbit", proc{|n| n == "carrot"}) end def new_human(energy) Animal.new("human", energy, "dead_human", proc{|n| n == "carrot" || is_meat(n)}) end grass = Food.new("grass", 5) carrot = Food.new("carrot", 10) a_rabbit = new_rabbit(100) a_cow = new_cow(1000) a_human = new_human(300) another_human = new_human(350) [ a_rabbit, a_cow, a_human ].each{|o| print "#{o.name} -> #{o.energy}\n" } a_rabbit.eat(carrot) a_cow.eat(grass) a_dead_rabbit = a_rabbit.slaughter a_beef = a_cow.slaughter a_human.eat(carrot) a_human.eat(carrot) a_human.eat(a_beef) a_human.eat(a_dead_rabbit) a_human.eat(another_human.slaughter) a_human.energy == 1785 or raise "failed" should_fail = """ new_cow(10).slaughter.eat(grass) # meat (food) can't eat new_cow(10).slaughter.slaughter # meat (food) can't be slaughtered carrot.eat(grass) # vegetable (food) can't eat carrot.slaughter # vegetable (food) can't be slaughtered new_cow(10).eat(carrot) # cow do not eat carrot new_cow(10).eat(new_cow(10).slaughter) # cow do not eat beef a_human.eat(new_cow(10)) # can't eat live animals a_human.eat(grass) # human do not eat grass a_human.eat(a_beef) # a_beef is already eaten a_cow.eat(grass) # a_cow is dead, it can't eat a_cow.slaughter # a_cow is dead, it can't be slaughtered again """ should_fail.split("\n").each{|i| next if i == "" begin eval(i) rescue Exception next end raise "#{i} should_have_failed" }