# -*- 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"
}
