Dice Roller (#61)


#1

The three rules of Ruby Q.:

  1. Please do not post any solutions or spoiler discussion for this quiz
    until
    48 hours have passed from the time on this message.

  2. Support Ruby Q. by submitting ideas as often as you can:

http://www.rubyquiz.com/

  1. Enjoy!

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

by Matthew D Moss

Time to release your inner nerd.

The task for this Ruby Q. is to write a dice roller. You should write
a
program that takes two arguments: a dice expression followed by the
number of
times to roll it (being optional, with a default of 1). So to calculate
those
stats for your AD&D character, you would do this:

> roll.rb "3d6" 6
72  64  113  33  78  82

Or, for something more complicated:

> roll.rb "(5d5-4)d(16/d4)+3"
31

[NOTE: You’ll usually want quotes around the dice expression to hide
parenthesis
from the shell, but the quotes are not part of the expression.]

The main code of roll.rb should look something like this:

d = Dice.new(ARGV[0])
(ARGV[1] || 1).to_i.times { print "#{d.roll}  " }

The meat of this quiz is going to be parsing the dice expression (i.e.,
implementing Dice.new). Let’s first go over the grammar, which I present
in a
simplified BNF notation with some notes:

<expr> := <expr> + <expr>
       |  <expr> - <expr>
       |  <expr> * <expr>
       |  <expr> / <expr>
       |  ( <expr> )
       |  [<expr>] d <expr>
       |  integer

* Integers are positive; never zero, never negative.
* 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

[NOTE: The BNF above is simplified here for clarity and space. If
requested, I
will make available the full BNF description I’ve used in my own
solution, which
incorporates the association and precedence rules.]

A few more things… Feel free to either craft this by hand or an
available
lexing/parsing library. Handling whitespace between integers and
operators is
nice. Some game systems use d100 quite often, and may abbreviate it as
“d%”
(but note that ‘%’ is only allowed immediately after a ‘d’).


#2

On Sat, Jan 07, 2006 at 03:56:47AM +0900, Ruby Q. wrote:
[…]
} [NOTE: The BNF above is simplified here for clarity and space. If
} requested, I will make available the full BNF description I’ve used in
my
} own solution, which incorporates the association and precedence
rules.]

I would appreciate the full BNF, please.

–Greg


#3

On 1/6/06, Ruby Q. removed_email_address@domain.invalid wrote:

Or, for something more complicated:

    > roll.rb "(5d5-4)d(16/d4)+3"
    31

I assume integer arithmetic? So if, for example, a 3 comes up on your
d4, 16/d4 would be 5?

Jacob F.


#4

Please don’t take this the wrong way, but I’ve never played D&D.
Would someone mind explaining the math that went into the command
below to generate it’s result?

~ ryan ~


#5

On 06/01/06, J. Ryan S. removed_email_address@domain.invalid wrote:

Please don’t take this the wrong way, but I’ve never played D&D.
Would someone mind explaining the math that went into the command
below to generate it’s result?

I suspect user error.

The correct answer will always be between 3 and 18 for 3d6.

-austin


#6

On Jan 6, 2006, at 1:29 PM, J. Ryan S. wrote:

The task for this Ruby Q. is to write a dice roller…

roll.rb “3d6” 6
72 64 113 33 78 82

Hmm, that example looks wrong now that you mention it. It should be
6 numbers between 3 and 18 (the roll of 3 six-sided dice).

James Edward G. II


#7

Sticking with typical integer division (ie, round-down) is fine.

If you wanted to extend the syntax to support round-up division (using
‘’
perhaps) or other options, feel free. Extra credit.

A lot of extra credit if you add syntax to support some RPGs/home rules
where you might want 3d6, but you’ll actually roll 4d6 and toss the
lowest.


#8

Ha ha… Must have copied the wrong line when writing up the quiz
description.

That should look like this:

roll.rb “3d6” 6
18 18 18 18 18 18

=)


#9

On 06/01/06, Matthew M. removed_email_address@domain.invalid wrote:

Ha ha… Must have copied the wrong line when writing up the quiz
description.

That should look like this:

roll.rb “3d6” 6
18 18 18 18 18 18

Just don’t tell me that the first one is 18/00.

-austin


#10

On 1/6/06, Matthew M. removed_email_address@domain.invalid wrote:

Ha ha… Must have copied the wrong line when writing up the quiz
description.

That should look like this:

roll.rb “3d6” 6
18 18 18 18 18 18

Actually 3d6 means roll a 6 sided die 3 times so you would have a result
of 3-18

so this:

roll.rb “3d6” 6

Would actully be: (RND = Random)

RND(3-18) RND(3-18) RND(3-18) RND(3-18) RND(3-18) RND(3-18)

Below is 3d6 from the DnD Dice Roller on Wizards.com. The +0 would be
a modifier from depending if it was an attack roll or a defense roll.
For our purposes you would remove the +0

Roll(3d6)+0:
1,6,6,+0
Total:13

DnD Dice Roller:
http://www.wizards.com/default.asp?x=dnd/dnd/20040517a

Will


Will S. ( willshattuck.at.gmail.com )
Home Page: http://www.thewholeclan.com/will

When you get to your wit’s end, you’ll find God lives there.


#11

On 1/6/06, Will S. removed_email_address@domain.invalid wrote:

Actually 3d6 means roll a 6 sided die 3 times so you would have a result of 3-18
Actually, you’re right, but actually my post was a half-joke. The
munchkin players seem to roll 18’s every time. :wink:


#12

I guess that must be a D&D inside half-joke because I’m totally
confused.

Don’t worry about explaining it as I just needed to know what that
command, roll.rb “3d6” 6, did.

~ ryan ~

On Jan 6, 2006, at 2:39 PM, Matthew M. wrote:

roll.rb “3d6” 6
18 18 18 18 18 18

On Jan 6, 2006, at 2:49 PM, Austin Z. wrote:

Just don’t tell me that the first one is 18/00.

On Jan 6, 2006, at 2:53 PM, James Edward G. II wrote:

They all were, of course.


#13

On Jan 6, 2006, at 2:21 PM, J. Ryan S. wrote:

I guess that must be a D&D inside half-joke because I’m totally
confused.

18 was the best stat a starting character could have. If you got
one, they let you roll d% and put it after the slash (the higher the
better). 00 == 100. So characters with 18/00 had some damn lucky
die rolls. :slight_smile:

James Edward G. II


#14

On 06/01/06, James Edward G. II removed_email_address@domain.invalid wrote:

On Jan 6, 2006, at 2:21 PM, J. Ryan S. wrote:

I guess that must be a D&D inside half-joke because I’m totally
confused.
18 was the best stat a starting character could have. If you got
one, they let you roll d% and put it after the slash (the higher the
better). 00 == 100. So characters with 18/00 had some damn lucky
die rolls. :slight_smile:

In most versions of D&D/AD&D, this was also limited to Strength
attributes only.

This may have changed recently. :wink:

-austin


#15

On Jan 6, 2006, at 1:49 PM, Austin Z. wrote:

On 06/01/06, Matthew M. removed_email_address@domain.invalid wrote:

Ha ha… Must have copied the wrong line when writing up the quiz
description.

That should look like this:

roll.rb “3d6” 6
18 18 18 18 18 18

Just don’t tell me that the first one is 18/00.

They all were, of course.

James Edward G. II


#16

On Jan 6, 2006, at 2:29 PM, Austin Z. wrote:

attributes only.

This may have changed recently. :wink:

Ah, yeah, you’re right. It’s been too long.

Actually, I believe 3rd Edition and up did away with the extra
percentile roll altogether.

James Edward G. II


#17

I would appreciate the full BNF, please.

Okay, this is what I’ve done in my current version that takes care of
basic
precedence and associativity.

INTEGER = /[1-9][0-9]*/

expr: fact
| expr ‘+’ fact
| expr ‘-’ fact

fact: term
| fact ‘*’ term
| fact ‘/’ term

term: unit
| [term] ‘d’ dice

dice: ‘%’
| unit

unit: ‘(’ expr ‘)’
| INTEGER

Actually, this is slightly different than my current version, which
after
reexamining to extract this BNF, I found a minor error (in handling of
the
term rules and handling of the optional arg). My own code has a morphed
version of this BNF in order to code up a recursive descent parser, but
this
BNF shows one way to handle the precedence/association rules.


#18

forgive my ignorance… BNF?

http://www.garshol.priv.no/download/text/bnf.html
http://en.wikipedia.org/wiki/Backus-Naur_form

HTH,

Bill


#19

forgive my ignorance… BNF?

w

On 1/6/06, Matthew M. removed_email_address@domain.invalid wrote:

unit: ‘(’ expr ‘)’
| INTEGER

Actually, this is slightly different than my current version, which after
reexamining to extract this BNF, I found a minor error (in handling of the
term rules and handling of the optional arg). My own code has a morphed
version of this BNF in order to code up a recursive descent parser, but this
BNF shows one way to handle the precedence/association rules.


Will S. ( willshattuck.at.gmail.com )
Home Page: http://www.thewholeclan.com/will

When you get to your wit’s end, you’ll find God lives there.


#20

On Jan 6, 2006, at 12:56 PM, Ruby Q. wrote:

72 64 113 33 78 82

Ok, I’m still a little confused. This should have output something like:

rand(16)+3 rand(16)+3 rand(16)+3

Or, for something more complicated:

roll.rb “(5d5-4)d(16/d4)+3”
31

What is the -4 and the /d4 do?

Does the +3 apply to (5d5-4)d(16/d4) or to (16/d4) only, assuming it
matters
since I don’t know what this stuff does.

A few more things… Feel free to either craft this by hand or an
available
lexing/parsing library. Handling whitespace between integers and
operators is
nice. Some game systems use d100 quite often, and may abbreviate
it as “d%”
(but note that ‘%’ is only allowed immediately after a ‘d’).

So d100 == d% == d00

and

100 == 00

correct?