「憂鬱な勇者」を特異メソッドで

憂鬱な勇者」がおもしろかったので、特異メソッドを使いまくって、趣味的に書き直して見ました。
「憂鬱な勇者」を作った件 - どんなジレンマ
モンスター名とスキル名は省略してます。リンク先のページからコードへのリンクを辿って、そこからコピペして使ってみてください。

#!/usr/bin/ruby

require 'optparse'

# Factrorial[n] で n の階乗を求められるようにしておきます。
def (Factorial = [1, 1]).[](n)
  Integer === n or raise TypeError
  n >= 0 or raise RangeError
  super or self[n] = n * self[n - 1]
end

# Array のオブジェクトを作り、Array クラスの fetch メソッドを特異メソッドで上書きします。
# 通常通り、fetch(i) とし、配列インデックス i を指定することもできますが、
# i を省略してランダムに要素を返させることもできるようにします。
def (Array_RandomFetchable = Array.new).fetch(i = rand(self.size))
  super(i)
end

Game = Struct.new(:max_round, :sleep_sec).new(30, 2)

class <<Game
  # 勇者
  Hero = Struct.new(:name, :level).new("勇者", 0)

  class <<Hero
    # 一段階レベルアップする。
    def level_up!
      self.level += 1  # この self は Hero です。
    end

    # 経験値はレベルの階乗
    def experience() Factorial[level] end
  end

  # モンスター軍団
  Monster = Array_RandomFetchable.clone.replace(["焼きたてパン","強いシャチホコ",わっふるわっふる])
  # スキル群
  Skill = Array_RandomFetchable.clone.replace(["お豆腐の買い方","鉛筆の買い方",わっふるわっふる])

  def run(argv)
    option_parse!(argv)
    rounds_loop
  end

  private

  # コマンドライン・オプションはスクリプト名の後に [-r 30] [-s 2] のように指定します。
  def option_parse!(argv)
    opts = OptionParser.new
    opts.on("-r MAX_ROUND") {|v| self.max_round = v.to_i }
    opts.on("-s SLEEP_SEC") {|v| self.sleep_sec = v.to_i }
    opts.parse!(argv)
  end

  # メッセージを出力するまでの間をとります。
  def wait_a_moment
    sleep sleep_sec if sleep_sec > 0
  end

  # max_round 回、勇者をレベルアップします。
  def rounds_loop
    (rounds = 1 .. max_round).each do |i|
      Hero.level_up!
      puts "*-----"
      puts "#{Monster.fetch}を倒した!"
      puts "#{Hero.experience}の経験値を得た。"
  
      # 最後のラウンドでは、残りの処理をおこなわずにループを抜け出します。
      break if i >= rounds.last
  
      puts "#{Hero.name}#{Hero.level}にレベルが上がった!"
      puts "#{Hero.name}は、#{Skill.fetch}を覚えた。"
      puts
      wait_a_moment
      puts
      puts "そして、"
      puts "かくかくしかじかで、山あり谷ありの冒険が続いたが割愛。"
      puts
      wait_a_moment
    end
    puts "#{Hero.name}は、また、レベルが上がった!"
    puts "#{Hero.name}は、ふと空しさを覚えた。"
  end
end

Game.run(ARGV)