------ art_81463_5176244.1136752140088
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline
Here is my submission. Yes, this is my first completed Ruby Quiz ;)
Thanks to Eric Mahurin's syntax.rb for making this work. I've attached
it as well, because it's not easily accessible otherwise ;)
-austin
------ art_81463_5176244.1136752140088
Content-Type: application/octet-stream; name=roll.rb
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="roll.rb"
#! /usr/bin/env ruby
require 'syntax'
# Ruby Quiz #61 by Matthew D Moss
# Submission by Austin Ziegler
#
# > roll.rb "3d6" 6
# 12 7 13 16 11 17
#
# Or, for something more complicated:
#
# > roll.rb "(5d5-4)d(16/d4)+3"
# 31
#
# The main code of roll.rb should look something like this:
#
# d ice.new(ARGV[0])
# (ARGV[1] || 1).to_i.times { print "#{d.roll} " }
#
# I've implemented it with a modified BNF and Eric Mahurin's syntax.rb.
#
# integer : "1" - "9" [ "0" - "9" ]*
# white : [ " " | "\t" | "\n" ]*
# unit : "(" expr ")" | integer
# dice : "%" | unit
# term : unit? [ "d" dice ]*
# fact : term [ "*" | "/" term ]*
# expr : fact [ "+" | "-" fact ]*
#
# I have also modified the core function as:
#
# e RGV[0]
# c ARGV[1] || 1).to_i
# d ice.new(e)
# puts d.roll(c).join(" ")
NULL yntax::NULL
INF 1.0 / 0.0
LOOP0 0 .. INF)
LOOP1 1 .. INF)
class Dice
def initialize(dice)
@dice ice
@dice_n #{@dice}\n"
integer (("1" .. "9") * 1) + ("0" .. "9") * LOOP0).qualify do |m|
m.to_s.to_i
end
white (" " | "\t" | "\n") * LOOP0).qualify { TRUE }
expr yntax::Pass.new
unit "(" + expr + ")").qualify { |m| m[1] } |
integer.qualify { |m| m }
dice %".qualify { |m| 100 } | unit.qualify { |m| m }
term (unit | NULL) + (white + "d" + white + dice) * LOOP0).qualify do |m|
sum
if m[1].nil?
rep
xpr [0]
elsif m[1].empty?
sum [0]
xpr [1]
else
rep [0]
xpr [1]
end
xpr.each do |mm|
case mm[0]
when "d": sum 1..rep).inject(sum) { |s, i| s + (rand(mm[1]) + 1) }
else
sum + ep
end
end
sum
end
fact term + (white + ("*" | "/") + white + term) * LOOP0).qualify do |m|
prod [0]
m[1].each do |mm|
case mm[0]
when "*": prod * m[1]
when "/": prod / m[1]
end
end
prod
end
expr << (white + fact + (white + ("+" | "-") + white + fact) * LOOP0).qualify do |m|
sum [0]
m[1].each do |mm|
case mm[0]
when "+": sum + m[1]
when "-": sum - m[1]
end
end
sum
end
@die_expr xpr
end
def roll(times )
(1 .. times).map { @die_expr RandomAccessStream.new(@dice_n) }
end
def inspect
@dice
end
end
expr RGV[0]
count ARGV[1] || 1).to_i
if expr
d ice.new(expr)
puts d.roll(count).join(' ')
else
require 'test/unit'
class TestDice < Test::Unit::TestCase
def test_simple
assert (1..4).include?(Dice.new("d4").roll)
assert (1..6).include?(Dice.new("d6").roll)
assert (1..8).include?(Dice.new("d8").roll)
assert (1..10).include?(Dice.new("d10").roll)
assert (1..12).include?(Dice.new("d12").roll)
assert (1..20).include?(Dice.new("d20").roll)
assert (1..30).include?(Dice.new("d30").roll)
assert (1..100).include?(Dice.new("d100").roll)
assert (1..100).include?(Dice.new("d%").roll)
end
def test_3d6
assert (3..18).include?(Dice.new("3d6").roll)
end
def test_complex
assert (5..25).include?(Dice.new("5d5").roll)
assert (1..21).include?(Dice.new("5d5-4").roll)
assert [4, 5, 8, 16].include?(Dice.new("16/d4").roll)
assert (1..336).include?(Dice.new("(5d5-4)d(16/d4)").roll)
assert (4..339).include?(Dice.new("(5d5-4)d(16/d4)+3").roll)
end
end
end
------ art_81463_5176244.1136752140088
Content-Type: application/octet-stream; name=syntax.rb
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="syntax.rb"
# Classes in this module allow one to define BNF-like grammar directly in
# Ruby
# Author: Eric Mahurin
# License: free, but you are at your own risk if you use it
module Syntax
# base class where common operators are defined
class Base
def |(other)
Alteration.new(self,other)
end
def +(other)
Sequence.new(self,other)
end
def *(multiplier)
Repeat.new(self,multiplier)
end
def +@
Positive.new(self)
end
def -@
Negative.new(self)
end
def qualify(*args,&code)
Qualify.new(self,*args,&code)
end
end
# just passes the syntax through - needed for recursive syntax
class Pass < Base
def initialize(syntax LL)
@syntax if (syntax.kind_of?Base)
syntax
else
Verbatim.new(syntax)
end
end
def <<(syntax)
initialize(syntax)
end
def (stream)
@syntax stream
end
end
# generic code matches to the stream (first arg to the code)
# [] operator allows additional arguments to be passed to the code
class Code < Base
def initialize(*args,&code)
@args rgs
@code ode
end
def (stream,*args) # passing args here will bypass creating a new object
(match code[stream,*(@args+args)]) ||
stream.buffered || raise(Error.new(stream,"a semantic error"))
match
end
def [](*args)
self.class.new(*(@args+args),&@code)
end
end
# qualify the match with some code that takes the match
# [] operator allows additional arguments to be passed to the code
class Qualify < Base
def initialize(syntax LL,*args,&code)
@syntax if (syntax.kind_of?Base)
syntax
else
Verbatim.new(syntax)
end
@args rgs
@code ode
end
def (stream,*args) # passing args here will bypass creating a new object
(match @syntax stream)) || (return match)
(match code[match,*(@args+args)]) ||
stream.buffered || raise(Error.new(stream,"a semantic qualification error"))
match
end
def [](*args)
self.class.new(@syntax,*(@args+args),&@code)
end
end
# sequence of syntaxes
class Sequence < Base
def initialize(*syntaxes)
@syntax yntaxes.collect do |syntax|
if (syntax.kind_of?Base)
syntax
else
Verbatim.new(syntax)
end
end
end
def +(other)
self.class.new(*(@syntax+[other])) # pull in multiple sequence items
end
def <<(other)
@syntax << ((other.kind_of?Base)?other:Verbatim.new(other))
end
def (stream)
matches ]
@syntax.each do |syntax|
match syntax stream)
if (!match)
matches L
break
end
matches << match if match! UE
end
matches
end
end
# alternative syntaxes
class Alteration < Base
def initialize(*syntaxes)
@syntax yntaxes.collect do |syntax|
if (syntax.kind_of?Base)
syntax
else
Verbatim.new(syntax)
end
end
end
def |(other)
self.class.new(*(@syntax+[other])) # pull in multiple alteration items
end
def <<(other)
@syntax << ((other.kind_of?Base)?other:Verbatim.new(other))
end
def (stream)
match il
@syntax.detect do |syntax|
match tream.buffer { |stream| syntax stream }
end
match || stream.buffered || raise(Error.new(stream,nil,"an alteration"))
match
end
alias
end
# repeating syntax
class Repeat < Base
def initialize(syntax,multiplier)
@syntax if (syntax.kind_of?Base)
syntax
else
Verbatim.new(syntax)
end
@repeat if (multiplier.kind_of?Proc)
multiplier
else
lambda do |matches|
compare multiplier<