Wednesday, March 9, 2011

My ruby zoo

Ruby is fun. A lot of fun. It makes programmers smile.  It makes them try crazy things... and those crazy things work.

Thank you Yukihiro Matsumoto!

Here is a little game I wrote with my kids one Saturday afternoon maybe a year ago.
There is an animal.rb and a zoo.rb and there is myzoo.rb which runs all that. I ran it using IronRuby.

animal.rb:
class Animal
  # create accessor methods for reading these properties 
  attr_reader :attractiveness, :upkeep, :name, :age, :dies_at, :cost_to_buy, :current_value, :dead
  
  MAX_ATTRACTIVENESS = 100
  MAX_UPKEEP = 100
  MAX_AGE_WHEN_BUYING = 30
  MAX_YEARS_TO_LIVE_AFTER_BUYING = 30
  MAX_COST_TO_BUY = 100

  ADVERBS = ["Small", "Big", "Smelly", "Cutest", "Sweetest", "Greenish", "Killer", "Harmless", "Angry"]
  NOUNS = ["Croc", "Elephant", "Hippo", "Ant", "Bear", "Mollusk", "Octopus", "Snake", "Snail"]

  def initialize(name)
    @attractiveness = rand(MAX_ATTRACTIVENESS)
    @upkeep = rand(MAX_UPKEEP)
    @name = name
    @age = rand(MAX_AGE_WHEN_BUYING)
    @dies_at = @age + rand(MAX_YEARS_TO_LIVE_AFTER_BUYING)
    @cost_to_buy = rand(MAX_COST_TO_BUY)
    @dead = false
  end

  def Animal.get
    name = ADVERBS[rand(ADVERBS.length)] + " " + NOUNS[rand(NOUNS.length)]  
    return Animal.new(name)
  end

  def make_older
    return if @dead
    if age == @dies_at
       @dead = true
       puts @name + " died of old age."
    else      
       @age += 1
       @attractiveness -= 1
    end
  end

  def show
    puts 
    puts "Name: #{@name}"
    puts "Attractiveness: #{@attractiveness.to_s} max: #{MAX_ATTRACTIVENESS.to_s}"
    puts "Cost to buy: $#{@cost_to_buy.to_s} max: #{MAX_COST_TO_BUY.to_s}"
    puts "Upkeep per year: $#{@upkeep.to_s} max: #{MAX_UPKEEP.to_s}"
    puts "Age: #{@age.to_s} year(s). max: #{MAX_AGE_WHEN_BUYING.to_s}" 
    puts "Dies at: #{@dies_at.to_s} year(s). max: #{(MAX_AGE_WHEN_BUYING + MAX_YEARS_TO_LIVE_AFTER_BUYING).to_s}" 
    puts 
  end
end # class Animal


zoo.rb:
class Zoo
  # ticket price can be set directly
  attr_accessor :ticket_price 
  # other properties are set internally 
  attr_reader :overhead_cost, :seed_money, :animals, :upkeep, :year, :animals_bought, :guests, :revenue, :cash

  def initialize()
    @animals = Array.new
    @upkeep = 0
    @overhead_cost = 100
    @ticket_price = 10
    @guests = 0
    @revenue = 0
    @seed_money = 500
    @cash = @seed_money
    @year = 0
  end

  def show
    puts
    puts "Animals: " + @animals.length.to_s
    puts "Upkeep last year: $" + @upkeep.to_s
    puts "Overhead cost: $" + @overhead_cost.to_s
    puts "Ticket price: $" + @ticket_price.to_s
    puts "Revenue last year: $" + @revenue.to_s
    puts "Guests last year: " + @guests.to_s
    puts "Cash: $" + @cash.to_s
    puts "Year: " + @year.to_s
    puts
  end

  # This defines how we buy animals:
  def buy_animal(animal)
      @cash = @cash - animal.cost_to_buy
      puts "You now have: $" + @cash.to_s
      @animals.push animal
  end

  def next_year
    @upkeep = 0
    @animals.each do |animal|
         animal.make_older
    end

    @animals.delete_if do |animal| 
         animal.dead 
    end

    @animals.each do |animal|
         @upkeep += animal.upkeep
    end

    @guests = 0
    @animals.each do |animal|
        @guests += animal.attractiveness
    end
    
    @guests = (@guests/Math.sqrt(@ticket_price)).to_i
    @revenue =  @guests * @ticket_price
    @cash += @revenue - @overhead_cost - @upkeep
    @year += 1
  end

end #class Zoo


myzoo.rb:
require "zoo.rb"
require "animal.rb"

YEARS_TO_PLAY = 10
CREATE_OR_APPEND_MODE = "a+"
WRITE_MODE = "w"
SCORES_FILE = "scores.txt"

def show_hall_of_fame
  File.open(SCORES_FILE, CREATE_OR_APPEND_MODE) {}
  scores = IO.readlines(SCORES_FILE) 

  puts "Hall of fame: "
  puts "...is waiting for your name" if scores.length == 0
  puts scores if scores.length > 0
end

def update_hall_of_fame (cash, player)
  File.open(SCORES_FILE, CREATE_OR_APPEND_MODE) {}
  scores = IO.readlines(SCORES_FILE) 
  
  scores[scores.length] = format("%10.10s %s", cash.to_s, player) 
  scores.sort!.reverse!
  File.open(SCORES_FILE, WRITE_MODE) {|f| f.write(scores) }
end

       
def show_menu
  puts
  puts "b = Buy Animals"
  puts "s = Sell Animals" 
  puts "p = Set Ticket Price" 
  puts "a = Show Animals" 
  puts "z = Show Zoo" 
  puts "n = Next Year" 
  puts "h = Hall of Fame" 
  puts "q = Quit"
  puts
end

puts "Welcome to MyZoo!"
puts "You manage it. Try to make as much money as you can in #{YEARS_TO_PLAY.to_s} years."

print "What's your name boss? "
player = gets

myzoo = Zoo.new
show_hall_of_fame

puts "You have in the bank: $" + myzoo.cash.to_s

playing = true

while playing and myzoo.year <= YEARS_TO_PLAY do
  show_menu
  print "Enter command:"
  command = gets

  case command
    when "b\n"
      some_animal = Animal.get
      some_animal.show
      print "Do you want to buy this animal? (y/n) "
      answer = gets
      myzoo.buy_animal(some_animal) if answer == "y\n"
  
    when "s\n" 
      if myzoo.animals.length > 0
         puts "Your salesperson has been eaten by #{myzoo.animals[0].name}." 
      else
         puts "There is no fauna in your zoo."
      end

    when "p\n" 
      print "New ticket price: "
      answer = gets
      if answer.to_i > 0
         myzoo.ticket_price = answer.to_i  
      else      
         puts "Sorry, no free tickets this year." 
      end  

    when "a\n" 
        if myzoo.animals.length > 0 
           puts "Your animals: " 
           myzoo.animals.each do |animal|
                animal.show
           end
        else
           puts "Did you buy any? Are they still alive?"
        end

    when "z\n" 
      myzoo.show    

    when "n\n"
      myzoo.next_year
      myzoo.show

    when "h\n"
      show_hall_of_fame

    when "q\n"
      print "Abandoning your enterprise? (y/n)"
      answer = gets
      if answer == "y\n" then 
         puts "Good-bye!"
         playing = false
      end 
  end
end

update_hall_of_fame(myzoo.cash, player) if myzoo.cash > 0
show_hall_of_fame



No comments:

Post a Comment