Bones is a highly-expressive functional dice rolling language. Bones is capable of handling any dice rolling mechanism which could reasonably be carried out by players of any game (plus many which couldn't). Every expression in Bones is either a scalar (an integer or variable) or a list of scalars.
Single die rolls may be made using the 'd
' operator, followed by the number of faces on the die to be rolled. E.g., d6
will roll a single six-sided die, and d2
will flip a coin. Expressions may be modified by the standard arithmetic operators. d10-1
will yield a value between 0 and 9, inclusive. In order to roll multiple dice of the same type, use the repetition operator '#
'. 2#d6
will roll two six-sided dice; this is not the same as 2*d6
, which rolls only a single die but multipies the result by two, or 2d6
which will cause a syntax error. In order to get the sum of two six-sided dice, do sum(2#d6)
.
Valid expressions in Bones are defined as follows:
<integer> ::=
-?[0-9]+
<variable> ::=
[A-Za-z]+
<scalar> ::=
<integer>
| <variable>
| -<scalar>
| <scalar> + <scalar>
| <scalar> - <scalar>
| <scalar> * <scalar>
| <scalar> / <scalar>
| <scalar> % <scalar>
| <scalar> ^ <scalar>
| <scalar> . <scalar>
| d<scalar>
| sum <expr>
| prod <expr>
| count <expr>
<list> ::=
<scalar> # <expr>
| <scalar> .. <scalar>
| <expr> , <expr>
| perm <expr>
| sort <expr>
| rev <expr>
| (drop|keep)? low <scalar> <expr>
| (drop|keep)? high <scalar> <expr>
| (drop|keep)? first <scalar> <expr>
| (drop|keep)? last <scalar> <expr>
| (drop|keep)? == <scalar> <expr>
| (drop|keep)? != <scalar> <expr>
| (drop|keep)? < <scalar> <expr>
| (drop|keep)? > <scalar> <expr>
| (drop|keep)? <= <scalar> <expr>
| (drop|keep)? >= <scalar> <expr>
| let <variable> = <expr> in <expr>
| while <variable> = <expr> do <expr>
| foreach <variable> in <expr> do <expr>
| if <expr> then <expr> else <expr>
<expr> ::=
<scalar>
| <list>
Whitespace between expressions is permitted, and required only between consecutive integers, variables, or alphabetic operators. Comments maybe be inserted into expressions by prepending double slashes ("//
") to them:
sum(10#d6) + // this is a comment 7
Comments run to the ends of the lines on which they appear, as in the programming language C++. Parentheses ("(
", ")
") may be used to enclose any expression as needed to indicate order of operations. 5+d(d10+1)
is properly parenthesized, while high(1 4#d6)
is not, because (1 4#d6)
is not a valid expression. An empty list may be specified with an empty pair of parentheses: ( )
. Any alphabetic string which does not conflict with a keyword may be used as a variable name. In particular, the lowercase 'd
' may not be used as a variable name, as it would conflict with the die roll operator.
+
-
*
/
^
5+7
, d6-1
, 2^10
-
-1
%
x % y
gives the remainder of x
divided by y
. Examples: 11%2
, d6%3
.
x . y
gives xy
, the concatenation of x
and y
. Examples: -10.9
, d6.d6
d
dn
gives the value of a single roll of an n
-sided die. Examples: d6
, 2#d6
sum
prod
e
is an expression, sum e
and prod e
give the sum of the members of e
and the product of the members of e
, respectively. Examples: sum(1..100)
, prod(3#d6)
count
e
is an expression, then count e
gives the number of members of e
. Examples: count(1,2,3)
, count(== 6 10#d6)
#
n
is a nonnegative scalar and e
is an expression, then n#e
is a list containing the results of n
evaluations of e
. Examples: 10#8
, 3#d10
..
x
and y
are scalars, then x..y
is a list consisting of the interval [x,y]
. If x>y
, then the resulting list is empty. Examples: 1..10
, 4..d10
,
v,u
gives the list consisting of all of the members of v
, followed by all of the members of u
. Examples: 1,2
, 4,(3#d6)
sort
sort e
sorts the list e
in ascending order. Examples: sort(10#d6)
perm
sort e
results in a random permutation of the list e
. Use perm
to shuffle a list. Examples: perm(1..52)
rev
rev e
results in a list with the same members as the list e
, but in reverse order. Examples: rev(1..10)
, rev sort(10#d8)
low
high
n
is a nonnegative scalar and e
is an expression, then low n e
gives the n
least members of e
, and high n e
gives the n
greatest members of e
. Examples: high 3 5#d6
first
last
n
is a nonnegtive scalar and e
is an expression, then first n e
gives the first n
members of e
, and last n e
gives the last n
members of e
. Examples: first 3 (1..10)
==
!=
<
>
<=
>=
x
is a scalar and e
is an expression, then == x e
gives the list of members of e
equal to x
; != x e
gives the list of members of e
not equal to x
; < x e
gives the list of members of e
less than x
; > x e
gives the list of members of e
greater than x
; <= x e
gives the list of members of e
less than or equal to x
; >= x e
gives the list of members of e
greater than or equal to x
. Examples: >= 3 5#d6
drop
keep
fop
is a filter operation on an expression e
, then keep fop e
has the same result as fop e
and drop fop e
evaluates to e
less keep fop e
. In other words, drop
negates filter conditions, and keep
affirms them. keep
is never necessary and exists only for symmetry. Examples: sum(drop low 1 4#d6)
let
x
is a variable and e
and f
are an expressions, then let x = e in f
gives the list which results from evaluating f
with the value of e
substituted for every occurance of x
in f
. Evaluation of e
is done prior to substitution. Examples: let x = d6 in x*x
foreach
x
is a variable and e
and f
are expressions, then foreach x in e do f
gives the list which results from assigning to x
each of the members of e
and evaluating f
. Examples: foreach x in c do x+1
while
x
is a variable and e
and f
are expressions, then while x = e do f
is the list v0,v1,...,vn
, where v0
is the result of evaluating e
and vi+1
is the result of assigning vi
to x
and evaluating f
, stopping at the first vi
which is empty. Examples: while x=d6 do ((count <6 x)#d6)
if
e
, f
, and g
are expressions, then if e then f else g
gives f
if e
is nonempty, and g
otherwise. Examples: if count(>4 2#d6) then 1 else 0
Precedence | Operator | Associativity |
---|---|---|
1 | d | right |
2 | ^ | right |
3 | # | right |
4 | sum prod count low high | none |
5 | - (unary) | right |
6 | * / % | left |
7 | + - . | left |
8 | == != < > <= >= | none |
9 | , | right |
10 | .. | none |
11 | in while do else | none |
count >7 5#d10
let c=5#d10 in (count >7 c)-(count ==1 c)
count (while x=d6 do ((count <6 x)#d6))
count (while x=(d6/6) do ((count <1 x)#(d6/6)))
6#sum(drop low 1 4#d6)
d6.d6
high 1 low 2 3#d20
sum(while x=3#d6 do ((count ==6 x)#d6))
let x = 7#d10 in high 1 (foreach y in 1..10 do sum (==y x))
Fn = Fn-1 + Fn-2
, with F1 = F2 = 1
. Calculate the first twenty Fibonacci numbers:
let n = 20 in let f = (1,1) in foreach i in 1..n do let f = (f,sum(high 2 f)) in if ==n i then f else ()
let a = 3#d6 in let b = 2#d6 in count( (<(high 1 a) high 1 b), (<(high 1 low 2 a) low 1 b))
let c=5#d10 in let succs = count >7 c in let ones = count ==1 c in if >0 succs then high 1 (0,succs-ones) else if >0 ones then -1 else 0
let x = 2#d8,d10 in (count >15 sum x)# let a = low 1 x in // low die let b = high 1 low 2 x in // middle die let c = high 1 x in // high die if ==a ==b c then a+b+c // all equal else if ==a <c b then c // two low equal else if >a ==c b then b+c // two high equal else b // all different
If you find a bug in Bones or have an idea for a feature, please email Joel Uckelman.
Bones is written by Joel Uckelman, based on the Roll language devised by Torben Mogensen. This documentation contains examples adapted from the Roll documentation.
Copyright © 2006 Joel Uckelman.
This is free software. You may redistribute copies of it under the terms of the GNU General Public License. There is NO WARRANTY, to the extent permitted by law.