Tuesday, February 14, 2012

Dice Statistics

A change of pace: dice statistics. I am in the midst of rolling up a Dungeons and Dragons character. The DM said she would allow a method I was not previously familiar with:
Method VIII: The player assigns 24d6 among a character's six ability scores. Each ability score must have at least 3d6, but no more 6d6, devoted to it. If the player desires a character with a high Strength, he could devote 4d6, 5d6, or even 6d6 to that ability. Next, the appropriate number of dice are rolled for each ability, and the total of the three highest results become the score. Any and all other dice rolled for that ability are discarded.
The obvious action is for me to find the statistical distribution for 3d6, 4d6, 5d6, and 6d6. My brute force answer is to write a Perl script that generates all combinations of dice results, sums the three highest rolls, and records the distributions. I stuck the code at the end of this post. 

The two figures summarize the results in graphical form. In both graphs, the y-axis is percent (%). This is a nice demonstration of the difference between "most likely" and median. When rolling 6d6 and summing the best three, 15 is the most likely result but 14 is the median. 







The code is straight-forward. I couldn't think of a way to make the number of for loops variable, which kept the code easy to follow but not as general as it could be. This the first time I have found a use for a format statement. 



#!/usr/bin/perl

# high3.pl
#
# A D&D character creation method is quoted below
# Method VIII: The player assigns 24d6 among a character's six ability scores.
# Each ability score must have at least 3d6, but no more 6d6, devoted to it.
# If the player desires a character with a high Strength, he could devote 4d6,
# 5d6, or even 6d6 to that ability. Next, the appropriate number of dice are
# rolled for each ability, and the total of the three highest results become
# the score. Any and all other dice rolled forthat ability are discarded.
#
# This program find the distribution and cumulative distribution function for 
# rolling 3d6, 4d6, 5d6, and 6d6. 
#
# All possible combinations 1-6 are generated six times. For 3d6 only the 
# first three rolls are considered, for 4d6 only the first four rolls are 
# considered, etc. 
# The rolls are sorted and the highest three dice summed and recorded. 
#
# Bruce McLaren
# 11 Feb 12

use   warnings;
use   strict;

my $debug = 1;

my @roll     = ();  # [0-5] array containing the die rolls
# array of results. results[5]=number of times 5 came up
# [0-2] will remain 0
my @results3 = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
my @results4 = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
my @results5 = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
my @results6 = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
my $total    =  0;    # total possibilities = 6**6

for ($roll[0] = 1; $roll[0]<=6; $roll[0]++)
{
   for ($roll[1] = 1; $roll[1]<=6; $roll[1]++)
   {
      for ($roll[2] = 1; $roll[2]<=6; $roll[2]++)
      {
         for ($roll[3] = 1; $roll[3]<=6; $roll[3]++)
         {
            for ($roll[4] = 1; $roll[4]<=6; $roll[4]++)
            {
               for ($roll[5] = 1; $roll[5]<=6; $roll[5]++)
               {
                  $total++;

                  my @sorted3 = reverse (sort (@roll[(0..2)]));
                  my $result3 = $sorted3[0] + $sorted3[1] + $sorted3[2];
                  $results3[$result3]++;

                  my @sorted4 = reverse (sort (@roll[(0..3)]));
                  my $result4 = $sorted4[0] + $sorted4[1] + $sorted4[2];
                  $results4[$result4]++;

                  my @sorted5 = reverse (sort (@roll[(0..4)]));
                  my $result5 = $sorted5[0] + $sorted5[1] + $sorted5[2];
                  $results5[$result5]++;

                  my @sorted6 = reverse (sort (@roll[(0..5)]));
                  my $result6 = $sorted6[0] + $sorted6[1] + $sorted6[2];
                  $results6[$result6]++;
              }
            }
         }
      }
   }
}

if($debug >3)
{
   print "@results3[(3..18)]\n";
   print "@results4[(3..18)]\n";
   print "@results5[(3..18)]\n";
   print "@results6[(3..18)]\n";
   print "$total\t",6**6,"\n";
}

my $outindex = 0;
my $result3  = 0;
my $result4  = 0;
my $result5  = 0;
my $result6  = 0;
my $cdf3     = 0;
my $cdf4     = 0;
my $cdf5     = 0;
my $cdf6     = 0;

# All results are percentages taken to one decimal point
format STDOUT =
@>  @#.# @##.#  @#.# @##.#  @#.# @##.#  @#.# @##.#
$outindex, $result3, $cdf3, $result4, $cdf4, $result5, $cdf5, $result6, $cdf6
.

print " n   3d  3dcdf    4d 4dcdf    5d 5dcdf    6d 6dcdf\n";
for ($outindex=3; $outindex<=18; $outindex++)
{
   # results are multiplied by 100 to make them percentages
   $result3 = $results3[$outindex]/$total*100;
   $cdf3 += $result3;
   $result4 = $results4[$outindex]/$total*100;
   $cdf4 += $result4;
   $result5 = $results5[$outindex]/$total*100;
   $cdf5 += $result5;
   $result6 = $results6[$outindex]/$total*100;
   $cdf6 += $result6;

   write;
}

Here are the raw results (after passing through a StarOffice spread sheet).


n 3d 3dcdf 4d 4dcdf 5d 5dcdf 6d 6dcdf
3 0.5 0.5 0.1 0.1 0.0 0.0 0.0 0.0
4 1.4 1.9 0.3 0.4 0.1 0.1 0.0 0.0
5 2.8 4.6 0.8 1.2 0.2 0.3 0.0 0.1
6 4.6 9.3 1.6 2.8 0.5 0.8 0.2 0.2
7 6.9 16.2 2.9 5.7 1.2 2.0 0.4 0.7
8 9.7 25.9 4.8 10.5 2.2 4.1 1.0 1.6
9 11.6 37.5 7.0 17.5 3.8 7.9 2.0 3.6
10 12.5 50.0 9.4 26.9 6.0 14.0 3.6 7.2
11 12.5 62.5 11.4 38.3 8.6 22.5 5.8 13.0
12 11.6 74.1 12.9 51.2 11.3 33.9 8.9 21.8
13 9.7 83.8 13.3 64.5 13.6 47.4 12.1 33.9
14 6.9 90.7 12.3 76.9 14.9 62.3 15.1 49.1
15 4.6 95.4 10.1 87.0 14.3 76.6 16.7 65.7
16 2.8 98.1 7.3 94.2 12.0 88.6 16.2 81.9
17 1.4 99.5 4.2 98.4 7.8 96.5 11.9 93.8
18 0.5 100.0 1.6 100.0 3.5 100.0 6.2 100.0


Next week I may yet post the trailer LED circuit and implementation. Although my interview notes on bandgap voltage references are more likely.

Bruce McLaren