--------------010008010306010505080102
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit

Well, here is my first solution to a quizz ^^
I tried to use racc for that ... so you need to generate the ruby script
using :

$ racc roll.y -o roll.rb

Otherwise, it is pretty simple ...

A small explanation is included within the file. If needed, I will post
the generated file.

Pierre

--------------010008010306010505080102
Content-Type: text/plain;
 nameoll.y"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filenameoll.y"

# -*- ruby -*-
# How to get the ruby program :
#    racc roll.y -o roll.rb
#
# Design :
#   The class Dice correspond to a very general dice. In my programme, "d6" is 
#   dice, but so are "3d6" and "(2d6-3)d(3d4)". A dice simply holds Proc which, 
#   when evaluated, return a random integer corresponding to a (simple or 
#   multiple) dice roll. The dices may be combined using standard arithmetic 
#   operators (+,-,*,/). These operator simply create Proc that roll the two 
#   argument dices. In the design, an integer is considered a d1. However the 
#   Proc for d1 don't use random ... (hopefully ;)
#
#   The tokenize method is pretty simple and the "%" is handled here, replaced 
#   by 100 automatically. Basically, it skips spaces (which flushes the char 
#   buffer however, so that "10 10" is invalid), gather consecutive figures 
#   into numbers and each char is considered an independant token.
#
#   Every grammar aspect is taken care by Racc ...
class DiceParser

  prechigh
    nonassoc UMINUS
    nonassoc SHORTD
    left 'd'
    left '*' '/'
    left '+' '-'
  preclow

rule
  target: exp
        | /* none */ { result  ice.new 0 }

  exp: exp '+' exp { result + al[2] }
     | exp '-' exp { result - al[2] }
     | exp '*' exp { result * al[2] }
     | exp '/' exp { result / al[2] }
     | '(' exp ')' { result  al[1] }
     | '-' NUMBER   MINUS { result  ice.new(-val[1]) }
     | NUMBER      { result  ice.new val[0] }
     | exp 'd' exp { result  ice.new val[0], val[2] }
     | 'd' exp  HORTD { result  ice.new 1, val[1] }

---- header

$debug_dice  alse
$debug_token  alse

class Dice

  attr_reader :size, :act

  def initialize(number, size  )
    @size  ize
    if number.respond_to? :to_proc
      puts "Initialize with proc" if $debug_dice
      @act  umber.to_proc
      @size  
    elsif size 1 then
      puts "Initialize with 1-sided dice(s)" if $debug_dice
      @act  ambda { number }
    else
      puts "Initialize with full dice(s)" if $debug_dice
      number  ice.new number unless number.respond_to? :roll # at that point, number must be a Dice
      @act  ambda do
        result  
        s  ize.roll
        n  umber.roll
        result  1..n.to_i).inject(0) { |sum,ii| sum + random(s) }
        puts "Rolling #{n}d#{s}  {result}" if $debug_dice
        result
      end
    end
  end

  def random(sides)
    rand(sides)+1
  end

  def roll
    act[]
  end

  def self.define_op(op)
    module_eval <<-EOF
    def #{op}(other)
      if (size 1) && (other.size 1) then
        Dice.new(self.roll #{op} other.roll)
      else
        Dice.new(lambda { self.roll #{op} other.roll })
      end
    end
    EOF
  end

  define_op :+
  define_op :*
  define_op :-
  define_op :/

end

---- inner

  attr_accessor :string, :result

  def parse( str )
    @result  ]
    #@yydebug  rue
    @string  tr
    yyparse( self, :tokens )
  end

  def tokens
    buffer  "
    string.each_byte do |b|
      print "bb}/#{b.chr}, buffer  #{buffer}'\n" if $debug_token
      case b
      when ?0..?9
        buffer << b.chr
        print "Added #{b.chr} to buffer #{buffer}\n" if $debug_token
      when [?\ ,?\t,?\n]
        yield :NUMBER, buffer.to_i unless buffer.empty?
        print "Pushing : #{buffer}\n" unless buffer.empty? if $debug_token
        buffer  "
      when ?%
        yield :NUMBER, buffer.to_i unless buffer.empty?
        print "Pushing : #{buffer}\n" unless buffer.empty? if $debug_token
        yield :NUMBER, 100
      else
        yield :NUMBER, buffer.to_i unless buffer.empty?
        print "Pushing : #{buffer}\n" unless buffer.empty? if $debug_token
        buffer  "
        yield b.chr, b.chr
        print "Pushing : #{b.chr}\n" if $debug_token
      end
    end
    yield :NUMBER, buffer.to_i unless buffer.empty?
    print "Pushing : #{buffer}\n" unless buffer.empty? if $debug_token
    yield false, '$end'
  end

---- footer

string  RGV[0]

# puts "Parsing : '#{string}'"
value  iceParser.new.parse( string )

num  RGV[1] || 1

num.to_i.times do
  print "#{value.roll} "
end
print "\n"


--------------010008010306010505080102--