NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

## All the Perl that's Practical to Extract and Report

### Journal of brian_d_foy (44)

Sunday September 10, 2006
11:21 AM

[ #30954 ]

Going through some old papers yesterday, I found a statistics problem I had to prove:

This bar game uses three standard six-sided dice. The player rolls all three dice at once. If he rolls three 4s, the bar pays him \$3. If he rolls two 4s, the bar pays him \$2. If he rolls a single 4, the bar pays him \$1. If he rolls no 4s, he pays the bar \$1.

Prove that the average loss per roll is about 7 cents for a large number of rolls.

I had done all the math and so on, but I wanted to write a little simulation. The Games::Dice::Advanced can handle all sorts of die things.

#!/usr/bin/perl

my \$total = 0;

my \$iterations = \$ARGV[0] || 100_000;

print "Roll  | Dice  | Win    Total    Win/Roll \n" .
"------|-------|---------------------------\n";

foreach ( 1 .. \$iterations )
{
my @n = map { Games::Dice::Advanced->roll() } 1 .. 3;

my( \$change ) = pay_up( \@n );

\$total += \$change;

printf "%5d | %d %d %d |  %2d   %5d     % 5.3f\n",
\$_,
@n, \$change, \$total, \$total / \$_
if \$_ % ( \$iterations / 10 ) == 0;
}

sub pay_up
{
my \$count = grep { \$_ == 4 } @{ \$_[0] };

# return the payout based on the number of rolled 4s
if(    \$count == 3 ) {  3 }
elsif( \$count == 2 ) {  2 }
elsif( \$count == 1 ) {  1 }
else                 { -1 }
}

The "large number of rolls" tends to be pretty large. If you want to stick around a bar to roll dicce 4,000 times, you probably deserve to lose all of your money. Remember, you lose seven cents per roll.

Roll  | Dice  | Win    Total    Win/Roll
------|-------|---------------------------
1000 | 1 1 2 |  -1       9      0.009
2000 | 1 4 1 |   1     -74     -0.037
3000 | 3 5 6 |  -1    -132     -0.044
4000 | 1 3 6 |  -1    -251     -0.063
5000 | 2 2 4 |   1    -345     -0.069
6000 | 5 2 1 |  -1    -433     -0.072
7000 | 2 2 3 |  -1    -463     -0.066
8000 | 1 2 3 |  -1    -584     -0.073
9000 | 1 2 2 |  -1    -618     -0.069
10000 | 4 2 3 |   1    -761     -0.076

I figured that if I was going to play this at any Perl Mongers meeting I was likely to run into unusual dice, so I wanted to see how much I'd get out of people using twenty sided dice.

my \$total      = 0;
my \$sides      = \$ARGV[0] || 6;
my \$iterations = \$ARGV[1] || 100_00;

print "Roll  | Dice     | Win    Total    Win/Roll \n" .
"------|----------|---------------------------\n";

foreach ( 1 .. \$iterations )
{
my @n = map { Games::Dice::Advanced->roll( "d\$sides" ) } 1 .. 3;

my( \$change ) = pay_up( \@n );

\$total += \$change;

printf "%5d | %2d %2d %2d |  %2d   %5d     % 5.3f\n",
\$_,
@n, \$change, \$total, \$total / \$_
if \$_ % ( \$iterations / 10 ) == 0;
}
}

Doing this, I discovered a bug in Games::Dice::Advanced. There's a closing paren in the wrong place such that die with more than nine sides don't work correctly:

@@ -132,7 +132,7 @@
if(\$recipe !~ /\D/) {                       # constant
# \$self = eval("sub { \$recipe * \$mul }");
\$self = sub { \$recipe * \$mul };
-           } elsif(\$recipe =~ /^d(\d)+\$/) {            # dINT
+           } elsif(\$recipe =~ /^d(\d+)\$/) {            # dINT
# \$self = eval("sub { (1 + int(rand(\$1))) * \$mul }");
my \$faces = \$1;
\$self = sub { (1 + int(rand(\$faces))) * \$mul };

With the little change, I figure I'd win about 75 cents per throw. Of course, if I do this with the London Perl Mongers, I'd win even more because of the exchange rate (they'd have to play with pounds).

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.

Full
Abbreviated
Hidden