--------------050407040609060308020901
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit
I've decided to bite the bullet and post my overlong solution.
It's got a few extras in there:
$ ./dice.rb "(5d5-4)d(16/d4)+3"
45
$ ./dice.rb "3d6" 6
11 7 10 13 9 14
$ ./dice.rb -dist "2d5 + 1dd12"
Distribution:
3 0.0103440355940356
4 0.0276987734487735
5 0.0503975468975469
6 0.0773292448292448
7 0.107660533910534
8 0.120036676286676
9 0.120568783068783
10 0.112113997113997
11 0.096477873977874
12 0.07495670995671
13 0.0588945406445407
14 0.0457661135161135
15 0.0345793650793651
16 0.0250565175565176
17 0.0171049783549784
18 0.0107247474747475
19 0.00596632996632997
20 0.00290909090909091
21 0.00113636363636364
22 0.000277777777777778
Check total: 1.0
Mean 9.75 std. dev 3.37782803325187
$ ./dice.rb -cheat "2d5 + 1dd12" 19
19 : D5 D5PD12 D12 p 000277777777777778
$ ./dice.rb -cheat "2d5 + 1dd12" 25
Cannot get 25
I'm getting to grips with block-passing as a routine technique - the
evaluate() method "yield"s its result so that it can provide multiple
results - tens of thousands if you ask it to try every possible roll
involving lots of dice. The roll_dice method had to be written
recursively for that to work:
def roll_dice( numdice, sides )
if ( numdice 0 )
yield null_value
else
roll_one(sides) do |first|
roll_dice( numdice-1, sides ) do |rest|
yield( first + rest )
end
end
end
end
Depending on how roll_one has been overridden, "first" and "rest" can be
integers, frequency distributions, or objects representing the history
of every roll to get to this state. In the last case, roll_one will
yield "sides" times, to give you every possible roll
I'm not quite comfortable with things like redefining Integer#+
If I had done that, I could have avoided a lot of kind_of? calls in
stuff like this:
def eval_binop( force_same_type rue )
subtree(:left).evaluate do |l|
subtree(:right).evaluate do |r|
if force_same_type
if r.kind_of?( Roll_stat ) and ! l.kind_of?( Roll_stat )
l oll_stat.new(l)
end
end
yield(l,r)
end
end
end
The whole thing can generate the probability distributions reasonably
quickly - I had ideas of approximating large numbers of dice (100d6
and so on) with the appropriate normal distribution ("clipped" by max
and min values).
The exhaustive output is very large, though. It would be worth
optimising that by taking out permutations.
I've shoved it on
http://homepage.ntlworld.com/a.mcguinness/files/dice.rb and attached it.
--------------050407040609060308020901
Content-Type: text/plain;
name
ice.rb"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename
ice.rb"
#!/usr/bin/env ruby
in
A frequency table, stored as an array of (value,probability) pairs
Can be constructed empty, as a fixed integer value with a probability
of one, or as a uniform distribution from 1 to n
ßÅ
class Freq_dist
class Freq
attr_accessor :value, :p
def initialize( v, p )
@value
@p
end
def <