Another solution:
#! /usr/bin/ruby
# change this to some fixed value for reproducable results
def random(i)
# i
# FIXME: check rand's usabilty for throwing dices...
rand(i)+1
end
class DiceExpr
def initialize(rolls, sides)
@rolls, @sides = rolls, sides
end
def to_i
sides = @sides.to_i
(
[email protected]_i).inject(0) { | sum, i | sum += random(sides) }
end
def to_s
"(#{@rolls}d#{@sides})"
end
end
class Expr
def initialize(lhs, rhs, op)
@lhs, @rhs, @op = lhs, rhs, op
end
def to_i
@lhs.to_i.send(@op, @rhs.to_i)
end
def to_s
"(#{@lhs}#{@op}#{@rhs})"
end
end
class Dice
def initialize(expr)
@expr_org = @expr_str = expr
next_token
@expr = addend()
if @token
raise "parser error: tokens left: >#{@fulltoken}#{@expr_str}<"
end
end
# "lexer"
@@regex = Regexp.compile(/^\s*([()+\-*\/]|[1-9][0-9]*|d%|d)\s*/)
def next_token
@prev_token = @token
return @token = nil if @expr_str.empty?
match = @@regex.match(@expr_str)
if !match
raise "parser error: cannot tokenize input #{@expr_str}"
end
@expr_str = @expr_str[match.to_s.length, @expr_str.length]
@fulltoken = match.to_s # for "tokens left" error message only...
@token = match[1]
end
# "parser"
# bit lengthy but basically straightforward
def number() # number or parenthesized expression
raise "unexpeced >)<" if ( @token == ')' )
if ( @token == '(' )
next_token
val = addend
raise "parser error: parenthesis error, expected ) got #{@token}" if @token != ')'
next_token
return val
end
raise "parse error: number expected, got #{@token}" if @token !~ /^[0-9]*$/
next_token
@prev_token
end
def dice()
if ( @token == 'd' )
rolls = 1
else
rolls = number()
end
while ( @token == 'd' || @token == 'd%' )
if @token == 'd%'
rolls = DiceExpr.new(rolls, 100)
next_token
else
next_token
sides = number()
raise "parser error: missing sides expression" if !sides
rolls = DiceExpr.new(rolls, sides)
end
end
rolls
end
def factor()
lhs = dice()
while ( @token == '*' || @token == '/' )
op = @token
next_token
rhs = dice()
raise "parser error: missing factor" if !rhs
lhs = Expr.new(lhs, rhs, op)
end
lhs
end
def addend()
lhs = factor()
while ( @token == '+' || @token == '-' )
op = @token
next_token
rhs = factor()
raise "parser error: missing addend" if !rhs
lhs = Expr.new(lhs, rhs, op)
end
lhs
end
def to_s
"#{@expr_org} -> #{@expr.to_s}"
end
def roll
@expr.to_i
end
end
d = Dice.new(ARGV[0])
#puts d.to_s
(ARGV[1] || 1).to_i.times { print "#{d.roll} " }
puts