rustedの日記 このページをアンテナに追加 RSSフィード

ruby勉強するよ。
 | 

2006-09-20

[][]Dice Roller (#61) 23:29 Dice Roller (#61) - rustedの日記 を含むブックマーク はてなブックマーク - Dice Roller (#61) - rustedの日記

Time to release your inner nerd.

ダイス表現から値を生成する問題(http://www.rubyquiz.com/quiz61.html)。2d6(6面ダイスを2回振る)とかだね。

今回、メイン部分はすでに用意されているのでした。

d = Dice.new(ARGV[0])
(ARGV[1] || 1).to_i.times { print "#{d.roll} " }

今回の補足。

A few more things... Feel free to either craft this by hand or an available lexing/parsing library. Handling whitespace between integers and operators is nice. Some game systems use d100 quite often, and may abbreviate it as "d%" (but note that '%' is only allowed immediately after a 'd').

相変わらず英語に自信がないけど、

  • パーサの使用は自由
  • 演算子回りに空白を入れることができるとナイス
  • いくつかのゲームではd100をd%と略すことがあるよ

こんな感じかな。最後のやつは初めて聞いたよ。これまでパーサを使ったことがないので、パーサを使ってみることにする。racc(http://www.loveruby.net/ja/projects/racc/)をチョイス。

ソース

class DiceParser
prechigh
  left 'd'
  left '*' '/'
  left '+' '-'
preclow

rule
expr : '(' expr ')' { result = val[1] }
     |  expr '+' expr { result += val[2] }
     |  expr '-' expr { result -= val[2] }
     |  expr '*' expr { result *= val[2] }
     |  expr '/' expr { result /= val[2] }
     | expr 'd' expr { result = parse_d(val[0], val[2]) }
     | expr 'd' '%' { result = parse_d(val[0], 100) }
     | 'd' expr { result = parse_d(1, val[1]) }
     | 'd' '%' { result = parse_d(1, 100) }
     | NUMBER
     ;
end
---- header ----

def parse_d(x, y)
  r = 0
  i = 0
  while i < x
    r += (rand * y + 1).to_i
    i += 1
  end
  return r
end

---- inner ----

def parse(str)
  @q = []
  while str.size > 0
    case str
    when /\A\s+/o # 空白を読み飛ばす
    when /\A\d+/o
      @q.push [:NUMBER, $&.to_i]
    when /\A./o # 空白、数字以外の1文字
      s = $&
      @q.push [s, s]
    end
    str = $'
  end
  @q.push [false, $end]
  do_parse  
end

def next_token
  @q.shift
end

---- footer ----

class Dice
  def initialize(s)
    @str = s
    @parser = DiceParser.new
  end

  def roll
    begin
      val = @parser.parse(@str)
    rescue
      p $!
    end
  end
end  

d = Dice.new(ARGV[0])
(ARGV[1] || 1).to_i.times { print "#{d.roll} " }

raccに投入して実行!

$ racc -o dice.rb dice.y
$ ruby dice.rb "(((d(3 + d2)) * 10d3) - (10d2) / 2)" 10
33 11 27 36 7 28 10 12 27 31 
$ ruby dice.rb "(((2 * (3 + 2)) * 3) - (12 + 3) / 2)"
23 

できた!パーサ便利だなあ。

ゲスト



トラックバック - http://rubyist.g.hatena.ne.jp/rusted/20060920
 |