# Dice Calculation Dialect

As a sample of dialects and teaching myself parse with block! values, I wrote the following dice dialect with the following grammar:

[
word!
|
integer! word!
]


Where the word!s are defined as integers (d10: 10) so an expression of this dialect would look like:

dice: [d2 20 d100 d10 5 d6]


This would sum the totals of one 2-sided die roll, 20 hundred-sided dice, one 10-sided die and five 6-sided dice. I wrapped this in the function below

Rebol [
Title: "Dice Rolling Dialect"
]
random/seed now/precise

d2: 2
d4: 4
d6: 6
d8: 8
d10: 10
d12: 12
d20: 20
d100: 100

calculate: function [b] [
dice-rule: [
(total: 0)
some [
copy die word!
(total: total + roll 1 get die/1)
|
copy n integer!
copy die word!
(total: total + roll n/1 get die/1)
]
]

roll: func [times [integer!] die [integer!]] [
result: 0
repeat i times [result: result + random die]
result
]

parse b dice-rule
total
]

repeat i 400 [
print calculate [d100 10 d10 d2 4 d6]
]


I was looking for improvements in the handling of the variables in parsing to make it more elegant than requiring the get die/1 notation.

• Welcome to Code Review! Even though I understand squat of your code (as I don't speak rebol), it seems like you understand what this site is about. Welcome! – Simon Forsberg Jul 2 '14 at 20:50

To first answer your question about the die/1, you are using COPY to get a range of multiple values out of the source block. You probably want SET to get a single value:

>> parse [d12] [copy die word! (probe die)]
[d12]
== true

>> parse [d12] [set die word! (probe die)]
d12
== true


So concretely:

dice-rule: [
(total: 0)
some [
set die word!
(total: total + roll 1 get die)
|
set n integer!
set die word!
(total: total + roll n get die)
]
]


Broad dialect design concern: you've decided that a symbolic word that looks up to an integer will represent a dice roll of that integer value. You then have another meaning for literal integer values. This ties your hands a bit because it means you can't do ordinary variable substitution.

For instance, what if instead of:

dice: [d2 20 d100 d10 5 d6]


I wanted to write:

x: 20
y: 5
dice: [d2 x d100 d10 y d6]


You could use get-word for this form of non-dice substitution:

dice: [d2 :x d100 d10 :y d6]


But the general heuristic of coming up with dialects is to only dig out a small bit of keyword space, so such an "ugliness" isn't necessary most of the time. Most dialects pick out a finite set of keywords, but there's probably nothing universally wrong with taking all the d* words.

Yet consider a possible alternative, more like the substitutions in parse, where brackets would be optional:

dice: [
die 2
20 [die 100]
[die 10]
5 die 6
]


Which you might then allow as substitutions, maybe:

d2: [die 2]
d100: [die 100]
d10: [die 10]
d6: [die 6]

dice: [d2 20 d100 d10 5 d6]


Further, I'd think that the dialect (which you might call dice-roll) should provide all the values of the roll. It's easy enough to sum if that's what people want...but sometimes you might want the highest value, or lowest, or know if there were any doubles of the same value/etc.

Other quick notes. If you know b is a block, say so in the function specification:

calculate: function [b [block!]] [
...


That checks the type at the callsite, and in Red it offers more of an advantage for static typing in the compiler.

I'd define roll before I defined the dice-rule. Also, by using a func instead of a function you are allowing result to bleed into the calculate function body. If you don't have a good reason for that, I'd avoid it... func shouldn't be used for performance reasons, but rather when you really do not want to introduce a new context.

Especially if you go with my suggestion to make die a word in your dialect, I might use another word for the integer representing the number of possibilities on the die...perhaps something like radix.

• I really like the d6: [die 6] implementation. – kealist Jul 3 '14 at 14:03