From: Adam Shelly Date: 2006-01-10T02:22:29+09:00 Subject: Re: [SOLUTION] Dice Roller (#61) Here's my solution. I spent a few hours writing a BNF parser which was supposed to let me do this: -- begin buggy code -- CENT = BnfTerm.new(/(%)/ ) { '100' } INTEGER = /([1-9][0-9]*)/ DICE = BnfTerm.new(CENT,:|,INTEGER) term = BnfTerm.new() ROLL = BnfTerm.new(term, /d/, DICE) {|a,b| (1..a.to_i).inject(0){|s,i|s+rand(b.to_i)+1} } term.define(DICE, :|,ROLL) {|m| m} #... class Dice @@rule = DIEROLL def initialize expr @expr = expr end def roll @@rule.parse(@expr) end end -- end -- but it was too brittle, and it would go into endless recursion on a lot of valid inputs. So I switched to a quick,short simple solution: add a #d method to integer and let eval do the work: --- dice.rb -- class Integer def d n (0...self).inject(0){|s,i| s+rand(n)+1} end end class Dice def initialize str @rule= str.gsub(/%/,'100').gsub(/([^\d)]|^)d/,'\1 1d') # %->100 and bare d ->1d while @rule.gsub!(/([^.])d(\d+|\(.*\))/,'\1.d(\2)') # 'dX' -> '.d(X)' end #repeat to deal with nesting end def roll eval(@rule) end end d = Dice.new(ARGV[0]||'d6') (ARGV[1] || 1).to_i.times { print "#{d.roll} " } puts --- -Adam