From 791f617acbb3a189ffdf64833669859e0b403dcb Mon Sep 17 00:00:00 2001 From: Nick Chambers Date: Sun, 25 Jul 2021 20:39:37 -0500 Subject: [PATCH] bash/roller: D&D-style dice roller --- bash/roller | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100755 bash/roller diff --git a/bash/roller b/bash/roller new file mode 100755 index 0000000..4299551 --- /dev/null +++ b/bash/roller @@ -0,0 +1,116 @@ +#!/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