Skip to content

Instantly share code, notes, and snippets.

@scottchiefbaker
Created March 1, 2018 16:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save scottchiefbaker/e721811255d27d99c288f4a1e1cc9a14 to your computer and use it in GitHub Desktop.
Save scottchiefbaker/e721811255d27d99c288f4a1e1cc9a14 to your computer and use it in GitHub Desktop.
#!/usr/bin/perl
###################################################
# Scott Baker - 2008-03-06
# Released under the GPLV3
#
# A simple script to roll dice, or multiple dice
# Example: Roll two d20 and add 10
# ./dice_roll.pl 2d20+10
#
# Roll a d6
# ./dice_roll.pl 1d6
#
# Roll three d10 subtract three (10 times)
# ./dice_roll.pl 3d10-3 10
###################################################
use Data::Dumper;
use List::Util qw[min max sum];
# Get the dice and times
my $dice = valid_dice($ARGV[0]);
my $times = int($ARGV[1]) || 1;
my $verbose = argv('v');
# Make sure we have something valid to process
if (!$dice) {
die(usage());
}
my ($res,$gt);
# Loop through for each roll
for (my $i = 0; $i < $times; $i++) {
# Roll the actual dice
my $roll = roll_me($dice);
# Get the total for summary later, and also update the grand total
my $tot = $roll->{'total'};
$gt += $tot;
# Store the total of the roll for stats later
$res->{$tot}++;
# Print the roll details
printf("Roll #%02d: %s\n", ($i + 1), $roll->{'textual'});
}
# If there is more than one roll show some summary stats
if ($times > 1) {
my $min = min(keys(%$res));
my $max = max(keys(%$res));
my $avg = sprintf("%.2f",$gt / $times);
print "\n";
print "Min: $min Max: $max\n";
print "Average roll: " . round($avg) . " ($avg)\n";
}
##########################################################
sub roll_me {
my $dice = shift();
$dice =~ /\s+/g;
# Get the appropriate pieces of the dice syntax
my ($mult,$sides,$neg,$addit) = $dice =~ /(\d+?)?d(\d+)([+\-])?(\d+)?/i;
$mult = int($mult) || 1; # Can't have a mult of zero
$addit = int($addit);
# If the +/- is negative, flip addit to negative
if ($neg eq '-') {
$addit = -$addit;
}
if ($verbose) {
print "\n* '$dice' means roll a $sides sided dice $mult times";
if ($addit) {
print " and add $addit";
}
print "\n";
}
my @roll = ();
# For each mult roll the dice and add it to the array
for (my $i = 0; $i < $mult; $i++) {
my $num = int(rand($sides)) + 1;
# 10 sided dice are weird and have numbers 0-9 (instead of 1-10)
# so subtract 1 to get the right number
if ($sides == 10) {
$num--;
}
push(@roll,$num);
}
# Get the total of all the dice rolls
my $total = sum(@roll) + $addit;
my $text;
# If there is an additional add that section at the end
if ($addit) {
$text = "(" . join(" + ", @roll) . ") + " . $addit . " = " . $total;
} else {
$text = "(" . join(" + ", @roll) . ") = " . $total;
}
# Build the big return hash
my $ret;
$ret->{'total'} = $total;
$ret->{'additional'} = $addit;
$ret->{'rolls'} = \@roll;
$ret->{'textual'} = $text;
return $ret;
}
# Only works for positive numbers (good enough)
sub round {
my $number = shift;
return int($number + .5);
}
sub usage {
my $ret .= "$0 [Dice] [Times]\n";
$ret .= "Example: $0 2d20+3 10\n";
}
sub valid_dice {
my $t = shift();
# At the minimum make sure there is a litteral d in the dice string
if ($t =~ /\d?d\d/i) {
return $t;
} else {
return 0;
}
}
sub argv {
my $ret = {};
for (my $i = 0; $i < scalar(@ARGV); $i++) {
# If the item starts with "-" it's a key
if ((my ($key) = $ARGV[$i] =~ /^--?([a-zA-Z_-]*\w)$/) && ($ARGV[$i] !~ /^-\w\w/)) {
# If the next item does not start with "--" it's the value for this item
if (defined($ARGV[$i + 1]) && ($ARGV[$i + 1] !~ /^--?\D/)) {
$ret->{$key} = $ARGV[$i + 1];
# Bareword like --verbose with no options
} else {
$ret->{$key}++;
}
}
}
# We're looking for a certain item
if ($_[0]) { return $ret->{$_[0]}; }
return $ret;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment