patternMinor
Dice Calculation Dialect
Viewed 0 times
calculationdicedialect
Problem
As a sample of dialects and teaching myself
Where the word!s are defined as integers (
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
I was looking for improvements in the handling of the variables in parsing to make it more elegant than requiring the
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.Solution
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:
So concretely:
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:
I wanted to write:
You could use get-word for this form of non-dice substitution:
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
Yet consider a possible alternative, more like the substitutions in parse, where brackets would be optional:
Which you might then allow as substitutions, maybe:
Further, I'd think that the dialect (which you might call
Other quick notes. If you know b is a block, say so in the function specification:
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
Especially if you go with my suggestion to make
>> parse [d12] [copy die word! (probe die)]
[d12]
== true
>> parse [d12] [set die word! (probe die)]
d12
== trueSo 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.Code Snippets
>> parse [d12] [copy die word! (probe die)]
[d12]
== true
>> parse [d12] [set die word! (probe die)]
d12
== truedice-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)
]
]dice: [d2 20 d100 d10 5 d6]x: 20
y: 5
dice: [d2 x d100 d10 y d6]dice: [d2 :x d100 d10 :y d6]Context
StackExchange Code Review Q#55929, answer score: 3
Revisions (0)
No revisions yet.