#!/usr/bin/env bash die() { printf "invalid roll: $1\n" "${@:2}" >&2 exit 1 } debug() { if [[ -v opts[debug] ]]; then printf "DEBUG: $1\n" "${@:2}" fi } verbose() { if [[ -v opts[verbose] ]]; then printf "$1\n" "${@:2}" fi } is_integer() { if (( $# )); then [[ $1 =~ ^[0-9]+$ ]] else return 1 fi } roll_die() { if (( $# )); then printf %d "$(( (RANDOM % $1) + 1 ))" else printf 0 fi } declare -A opts while (( $# )); do case $1 in -v) opts[verbose]=1 ;; -d) opts[debug]=1 ;; -x) set -x ;; -*) die "unknown option '%s'" "$1" ;; *) break esac shift done if (( $# )); then IFS=d read -r quantity rest <<< "$1" if [[ -z $quantity ]]; then quantity=1 elif ! is_integer "$quantity"; then die "dice quantity is not a number" fi debug "quantity of dice is %d" "$quantity" while (( ${#rest} )) && [[ ${rest:0:1} != @(+|-) ]]; do sides+=${rest:0:1} rest=${rest:1} done if ! is_integer "$sides"; then die "not a good number of die sides" fi debug "die has %d side(s)" "$sides" total_mod=0 while (( ${#rest} )); do mod_type=${rest:0:1} rest=${rest:1} next_mod= while [[ -n $rest && ${rest:0:1} != @(+|-) ]]; do next_mod+=${rest:0:1} rest=${rest:1} done if ! is_integer "$next_mod"; then die "'%s' is not a proper modifier" "$next_mod" elif [[ $mod_type = + ]]; then (( total_mod += next_mod )) debug "adding %d to modifier for a total of %d" "$next_mod" "$total_mod" else (( total_mod -= next_mod )) debug "subtracting %d from modifier for a total of %d" "$next_mod" "$total_mod" fi done score=0 count=0 while (( quantity-- )); do roll=$(roll_die "$sides") (( score += roll )) debug "rolled a %d. score is now %d" "$roll" "$score" verbose "roll %d: %d" "$(( count++ ))" "$roll" done verbose "score before modifier: %d" "$score" (( score += total_mod )) verbose "score after modifier: %d" "$score" debug "score total after modifier is %d" "$score" if [[ ! -v opts[verbose] ]]; then printf '%d\n' "$score" fi fi