Very interesting, and different solutions, this time! Here's my recursive descent solution with histogram: =begin Ruby Quiz #61 by Matthew D Moss Solution by Christer Nilsson "3d6" gives 3..18 randomly "(5d5-4)d(16/d4)+3" Backus Naur Form: expr: term ['+' expr | '-' expr] term: fact ['*' term | '/' term] fact: [unit] 'd' dice unit: '(' expr ')' | integer dice: '%' | term integer: digit [integer] digit: /[0-9]/ * Integers are positive * The "d" (dice) expression XdY rolls a Y-sided die (numbered from 1 to Y) X times, accumulating the results. X is optional and defaults to 1. * All binary operators are left-associative. * Operator precedence: ( ) highest d * / + - lowest Some game systems use d100 quite often, and may abbreviate it as "d%" (but note that '%' is only allowed immediately after a 'd'). =end class String def behead return ['',''] if self == '' [self[0..0], self[1...self.size]] end end class Array def sum inject(0) {|sum,e| sum += e} end def histogram(header="") width = 100 each_index {|i| self[i]=0 if self[i].nil?} sum = self.sum max = self.max if max.nil? s = " " + header + "\n" each_with_index do |x,i| label = " " + format("%2.1f",100.0*x/sum)+"%" s += format("%2d",i) + " " + "*" * ((x-min) * width / (max-min)) + label + "\n" end s += "\n" end end class Dice def statistics(expr, n=1000) prob = [] n.times do value = evaluate(expr) prob[value]=0 if prob[value].nil? prob[value] += 1 end prob end def evaluate s @sym, @s = s.behead @stack = [] expr pop end def drop (pattern) raise 'syntax error: expected ' + pattern unless pattern === @sym @sym, @s = @s.behead end def push(x) @stack.push x end def top2() @stack[-2] end def top() @stack[-1] end def pop() @stack.pop end def calc value pop push value end def try symbol return nil unless @sym == symbol drop symbol case symbol when '+' then expr; calc top2 + pop when '-' then expr; calc top2 - pop when '*' then term; calc top2 * pop when '/' then term; calc top2 / pop when '%' then push 100 when '(' then expr; drop ')' #when 'd' then dice; calc top2 * pop # debug mode when 'd' # release mode dice sum = 0 sides = pop count = pop count.times {sum += rand(sides) + 1} push sum end end def expr term try('+') or try('-') end def term fact try('*') or try('/') end def fact @sym == 'd' ? push(1) : unit # implicit 1 try('d') end def dice #unit unless try('%')# if 5d6d7 is not accepted term unless try('%') # if 5d6d7 is accepted end def unit integer @sym.to_i unless try('(') end def integer(i) return if @sym == '' digit = /[0-9]/ drop(digit) digit === @sym ? integer( 10 * i + @sym.to_i ) : push(i) end end require 'test/unit' class TestDice < Test::Unit::TestCase def t (actual, expect) assert_equal expect, actual end def test_all t(/[0-9]/==="0", true) t(/[0-9]/==="a", false) t "abc".behead, ["a","bc"] t "a".behead, ["a",""] t "".behead, ["",""] dice = Dice.new() print dice.statistics("d6").histogram("d6") print dice.statistics("2d6").histogram("2d6") print dice.statistics("(d6)d6",10000).histogram("(d6)d6") #t dice.evaluate("(6)"), 6 #t dice.evaluate("12+34"), 46 #t dice.evaluate("3*4+2"), 14 #t dice.evaluate("5+6+7"), 18 #t dice.evaluate("5+6-7"), 4 #t dice.evaluate("(5+6)+7"), 18 #t dice.evaluate("5"), 5 #t dice.evaluate("5+(6+7)"), 18 #t dice.evaluate("(5+6+7)"), 18 #t dice.evaluate("5*6*7"), 210 #t dice.evaluate("2+3*4"), 14 #t dice.evaluate("12+13*14"), 194 #t dice.evaluate("(2+3)*4"), 20 #t dice.evaluate("(5d5-4)d(16/1d4)+3"), 45 #t dice.evaluate("(5d5-4)d(400/1d%)+3"), 87 #t dice.evaluate("1"), 1 #t dice.evaluate("1+2"),3 #t dice.evaluate("1+3*4"),13 #t dice.evaluate("1*2+4/8-1"), 1 #t dice.evaluate("d1"),1 #t dice.evaluate("1d1"),1 #t dice.evaluate("1d10"), 10 #t dice.evaluate("10d10"),100 #t dice.evaluate("d3*2"), 6 #t dice.evaluate("2d3+8"), 14 #t dice.evaluate("(2*(3+8))"),22 #t dice.evaluate("d3+d3"),6 #t dice.evaluate("d2*2d4"),16 #t dice.evaluate("2d%"),200 #t dice.evaluate("14+3*10d2"), 74 #t dice.evaluate("(5d5-4)d(16/d4)+3"),87 #t dice.evaluate("d10"), 10 #t dice.evaluate("d%"),100 #t dice.evaluate("d(2*2)+d4"),8 #t dice.evaluate("(5d6)d7"), 210 #t dice.evaluate("5d(6d7)"), 210 #t dice.evaluate("5d6d7)"), 210 #t dice.evaluate("12d13d14)"), 2184 #t dice.evaluate("12*d13)"), 156 #t dice.evaluate("12+d13)"), 25 end end -- Posted via http://www.ruby-forum.com/.