Solution to DnD character in Bash
This commit is contained in:
parent
f26ebd97f2
commit
09bf8bc515
23
bash/dnd-character/.exercism/config.json
Normal file
23
bash/dnd-character/.exercism/config.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"authors": [
|
||||||
|
"glennj"
|
||||||
|
],
|
||||||
|
"contributors": [
|
||||||
|
"guygastineau",
|
||||||
|
"IsaacG"
|
||||||
|
],
|
||||||
|
"files": {
|
||||||
|
"solution": [
|
||||||
|
"dnd_character.sh"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"dnd_character.bats"
|
||||||
|
],
|
||||||
|
"example": [
|
||||||
|
".meta/example.sh"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"blurb": "Randomly generate Dungeons & Dragons characters.",
|
||||||
|
"source": "Simon Shine, Erik Schierboom",
|
||||||
|
"source_url": "https://github.com/exercism/problem-specifications/issues/616#issuecomment-437358945"
|
||||||
|
}
|
1
bash/dnd-character/.exercism/metadata.json
Normal file
1
bash/dnd-character/.exercism/metadata.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"track":"bash","exercise":"dnd-character","id":"c00402df3e994390a5f6cfba07ad644f","url":"https://exercism.org/tracks/bash/exercises/dnd-character","handle":"Kimawari","is_requester":true,"auto_approve":false}
|
105
bash/dnd-character/HELP.md
Normal file
105
bash/dnd-character/HELP.md
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
# Help
|
||||||
|
|
||||||
|
## Running the tests
|
||||||
|
|
||||||
|
Each exercise contains a test file.
|
||||||
|
Run the tests using the `bats` program.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bats hello_world.bats
|
||||||
|
```
|
||||||
|
|
||||||
|
`bats` will need to be installed.
|
||||||
|
See the [Testing on the Bash track][tests] page for instructions to install `bats` for your system.
|
||||||
|
|
||||||
|
[tests]: https://exercism.org/docs/tracks/bash/tests
|
||||||
|
|
||||||
|
## Help for assert functions
|
||||||
|
|
||||||
|
The tests use functions from the [bats-assert][bats-assert] library.
|
||||||
|
Help for the various `assert*` functions can be found there.
|
||||||
|
|
||||||
|
[bats-assert]: https://github.com/bats-core/bats-assert
|
||||||
|
|
||||||
|
## Skipped tests
|
||||||
|
|
||||||
|
Solving an exercise means making all its tests pass.
|
||||||
|
By default, only one test (the first one) is executed when you run the tests.
|
||||||
|
This is intentional, as it allows you to focus on just making that one test pass.
|
||||||
|
Once it passes, you can enable the next test by commenting out or removing the next annotation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
[[ $BATS_RUN_SKIPPED == true ]] || skip
|
||||||
|
```
|
||||||
|
|
||||||
|
## Overriding skips
|
||||||
|
|
||||||
|
To run all tests, including the ones with `skip` annotations, you can run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
BATS_RUN_SKIPPED=true bats exercise_name.bats
|
||||||
|
```
|
||||||
|
|
||||||
|
It can be convenient to use a wrapper function to save on typing:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bats() {
|
||||||
|
BATS_RUN_SKIPPED=true command bats *.bats
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then run tests with just:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bats
|
||||||
|
```
|
||||||
|
|
||||||
|
## Submitting your solution
|
||||||
|
|
||||||
|
You can submit your solution using the `exercism submit dnd_character.sh` command.
|
||||||
|
This command will upload your solution to the Exercism website and print the solution page's URL.
|
||||||
|
|
||||||
|
It's possible to submit an incomplete solution which allows you to:
|
||||||
|
|
||||||
|
- See how others have completed the exercise
|
||||||
|
- Request help from a mentor
|
||||||
|
|
||||||
|
## Need to get help?
|
||||||
|
|
||||||
|
If you'd like help solving the exercise, check the following pages:
|
||||||
|
|
||||||
|
- The [Bash track's documentation](https://exercism.org/docs/tracks/bash)
|
||||||
|
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
|
||||||
|
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||||
|
|
||||||
|
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||||
|
|
||||||
|
Check your code for syntax errors: paste your code into
|
||||||
|
[https://shellcheck.net](https://shellcheck.net) (or [install it](https://github.com/koalaman/shellcheck#user-content-installing) on your machine).
|
||||||
|
|
||||||
|
Stack Overflow will be your first stop for bash questions.
|
||||||
|
|
||||||
|
* start with the [`bash` tag](https://stackoverflow.com/questions/tagged/bash) to search for your specific question: it's probably already been asked
|
||||||
|
* under the bash tag on Stackoverflow, the [Learn more...](https://stackoverflow.com/tags/bash/info) link has _tons_ of good information.
|
||||||
|
* the "Books and Resources" section is particularly useful.
|
||||||
|
* the [`bash` tag](https://unix.stackexchange.com/questions/tagged/bash) on Unix & Linux is also active
|
||||||
|
|
||||||
|
## External utilities
|
||||||
|
|
||||||
|
`bash` is a language to write "scripts" -- programs that can call
|
||||||
|
external tools, such as
|
||||||
|
[`sed`](https://www.gnu.org/software/sed/),
|
||||||
|
[`awk`](https://www.gnu.org/software/gawk/),
|
||||||
|
[`date`](https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html)
|
||||||
|
and even programs written in other programming languages,
|
||||||
|
like [`Python`](https://www.python.org/).
|
||||||
|
This track does not restrict the usage of these utilities, and as long
|
||||||
|
as your solution is portable between systems and does not require
|
||||||
|
installation of third party applications, feel free to use them to solve
|
||||||
|
the exercise.
|
||||||
|
|
||||||
|
For an extra challenge, if you would like to have a better understanding of
|
||||||
|
the language, try to re-implement the solution in pure bash, without using
|
||||||
|
any external tools. There are some types of problems that bash cannot solve,
|
||||||
|
such as floating point arithmetic and manipulating dates: for those, you
|
||||||
|
must call out to an external tool.
|
51
bash/dnd-character/README.md
Normal file
51
bash/dnd-character/README.md
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# D&D Character
|
||||||
|
|
||||||
|
Welcome to D&D Character on Exercism's Bash Track.
|
||||||
|
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||||
|
|
||||||
|
## Instructions
|
||||||
|
|
||||||
|
For a game of [Dungeons & Dragons][dnd], each player starts by generating a character they can play with.
|
||||||
|
This character has, among other things, six abilities; strength, dexterity, constitution, intelligence, wisdom and charisma.
|
||||||
|
These six abilities have scores that are determined randomly.
|
||||||
|
You do this by rolling four 6-sided dice and record the sum of the largest three dice.
|
||||||
|
You do this six times, once for each ability.
|
||||||
|
|
||||||
|
Your character's initial hitpoints are 10 + your character's constitution modifier.
|
||||||
|
You find your character's constitution modifier by subtracting 10 from your character's constitution, divide by 2 and round down.
|
||||||
|
|
||||||
|
Write a random character generator that follows the rules above.
|
||||||
|
|
||||||
|
For example, the six throws of four dice may look like:
|
||||||
|
|
||||||
|
- 5, 3, 1, 6: You discard the 1 and sum 5 + 3 + 6 = 14, which you assign to strength.
|
||||||
|
- 3, 2, 5, 3: You discard the 2 and sum 3 + 5 + 3 = 11, which you assign to dexterity.
|
||||||
|
- 1, 1, 1, 1: You discard the 1 and sum 1 + 1 + 1 = 3, which you assign to constitution.
|
||||||
|
- 2, 1, 6, 6: You discard the 1 and sum 2 + 6 + 6 = 14, which you assign to intelligence.
|
||||||
|
- 3, 5, 3, 4: You discard the 3 and sum 5 + 3 + 4 = 12, which you assign to wisdom.
|
||||||
|
- 6, 6, 6, 6: You discard the 6 and sum 6 + 6 + 6 = 18, which you assign to charisma.
|
||||||
|
|
||||||
|
Because constitution is 3, the constitution modifier is -4 and the hitpoints are 6.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
Most programming languages feature (pseudo-)random generators, but few programming languages are designed to roll dice.
|
||||||
|
One such language is [Troll][troll].
|
||||||
|
|
||||||
|
[dnd]: https://en.wikipedia.org/wiki/Dungeons_%26_Dragons
|
||||||
|
[troll]: https://di.ku.dk/Ansatte/?pure=da%2Fpublications%2Ftroll-a-language-for-specifying-dicerolls(84a45ff0-068b-11df-825d-000ea68e967b)%2Fexport.html
|
||||||
|
|
||||||
|
## Source
|
||||||
|
|
||||||
|
### Created by
|
||||||
|
|
||||||
|
- @glennj
|
||||||
|
|
||||||
|
### Contributed to by
|
||||||
|
|
||||||
|
- @guygastineau
|
||||||
|
- @IsaacG
|
||||||
|
|
||||||
|
### Based on
|
||||||
|
|
||||||
|
Simon Shine, Erik Schierboom - https://github.com/exercism/problem-specifications/issues/616#issuecomment-437358945
|
637
bash/dnd-character/bats-extra.bash
Normal file
637
bash/dnd-character/bats-extra.bash
Normal file
@ -0,0 +1,637 @@
|
|||||||
|
# This is the source code for bats-support and bats-assert, concatenated
|
||||||
|
# * https://github.com/bats-core/bats-support
|
||||||
|
# * https://github.com/bats-core/bats-assert
|
||||||
|
#
|
||||||
|
# Comments have been removed to save space. See the git repos for full source code.
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
#
|
||||||
|
# bats-support - Supporting library for Bats test helpers
|
||||||
|
#
|
||||||
|
# Written in 2016 by Zoltan Tombol <zoltan dot tombol at gmail dot com>
|
||||||
|
#
|
||||||
|
# To the extent possible under law, the author(s) have dedicated all
|
||||||
|
# copyright and related and neighboring rights to this software to the
|
||||||
|
# public domain worldwide. This software is distributed without any
|
||||||
|
# warranty.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the CC0 Public Domain Dedication
|
||||||
|
# along with this software. If not, see
|
||||||
|
# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
(( $# == 0 )) && batslib_err || batslib_err "$@"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
batslib_is_caller() {
|
||||||
|
local -i is_mode_direct=1
|
||||||
|
|
||||||
|
# Handle options.
|
||||||
|
while (( $# > 0 )); do
|
||||||
|
case "$1" in
|
||||||
|
-i|--indirect) is_mode_direct=0; shift ;;
|
||||||
|
--) shift; break ;;
|
||||||
|
*) break ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Arguments.
|
||||||
|
local -r func="$1"
|
||||||
|
|
||||||
|
# Check call stack.
|
||||||
|
if (( is_mode_direct )); then
|
||||||
|
[[ $func == "${FUNCNAME[2]}" ]] && return 0
|
||||||
|
else
|
||||||
|
local -i depth
|
||||||
|
for (( depth=2; depth<${#FUNCNAME[@]}; ++depth )); do
|
||||||
|
[[ $func == "${FUNCNAME[$depth]}" ]] && return 0
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
batslib_err() {
|
||||||
|
{ if (( $# > 0 )); then
|
||||||
|
echo "$@"
|
||||||
|
else
|
||||||
|
cat -
|
||||||
|
fi
|
||||||
|
} >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
batslib_count_lines() {
|
||||||
|
local -i n_lines=0
|
||||||
|
local line
|
||||||
|
while IFS='' read -r line || [[ -n $line ]]; do
|
||||||
|
(( ++n_lines ))
|
||||||
|
done < <(printf '%s' "$1")
|
||||||
|
echo "$n_lines"
|
||||||
|
}
|
||||||
|
|
||||||
|
batslib_is_single_line() {
|
||||||
|
for string in "$@"; do
|
||||||
|
(( $(batslib_count_lines "$string") > 1 )) && return 1
|
||||||
|
done
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
batslib_get_max_single_line_key_width() {
|
||||||
|
local -i max_len=-1
|
||||||
|
while (( $# != 0 )); do
|
||||||
|
local -i key_len="${#1}"
|
||||||
|
batslib_is_single_line "$2" && (( key_len > max_len )) && max_len="$key_len"
|
||||||
|
shift 2
|
||||||
|
done
|
||||||
|
echo "$max_len"
|
||||||
|
}
|
||||||
|
|
||||||
|
batslib_print_kv_single() {
|
||||||
|
local -ir col_width="$1"; shift
|
||||||
|
while (( $# != 0 )); do
|
||||||
|
printf '%-*s : %s\n' "$col_width" "$1" "$2"
|
||||||
|
shift 2
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
batslib_print_kv_multi() {
|
||||||
|
while (( $# != 0 )); do
|
||||||
|
printf '%s (%d lines):\n' "$1" "$( batslib_count_lines "$2" )"
|
||||||
|
printf '%s\n' "$2"
|
||||||
|
shift 2
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
batslib_print_kv_single_or_multi() {
|
||||||
|
local -ir width="$1"; shift
|
||||||
|
local -a pairs=( "$@" )
|
||||||
|
|
||||||
|
local -a values=()
|
||||||
|
local -i i
|
||||||
|
for (( i=1; i < ${#pairs[@]}; i+=2 )); do
|
||||||
|
values+=( "${pairs[$i]}" )
|
||||||
|
done
|
||||||
|
|
||||||
|
if batslib_is_single_line "${values[@]}"; then
|
||||||
|
batslib_print_kv_single "$width" "${pairs[@]}"
|
||||||
|
else
|
||||||
|
local -i i
|
||||||
|
for (( i=1; i < ${#pairs[@]}; i+=2 )); do
|
||||||
|
pairs[$i]="$( batslib_prefix < <(printf '%s' "${pairs[$i]}") )"
|
||||||
|
done
|
||||||
|
batslib_print_kv_multi "${pairs[@]}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
batslib_prefix() {
|
||||||
|
local -r prefix="${1:- }"
|
||||||
|
local line
|
||||||
|
while IFS='' read -r line || [[ -n $line ]]; do
|
||||||
|
printf '%s%s\n' "$prefix" "$line"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
batslib_mark() {
|
||||||
|
local -r symbol="$1"; shift
|
||||||
|
# Sort line numbers.
|
||||||
|
set -- $( sort -nu <<< "$( printf '%d\n' "$@" )" )
|
||||||
|
|
||||||
|
local line
|
||||||
|
local -i idx=0
|
||||||
|
while IFS='' read -r line || [[ -n $line ]]; do
|
||||||
|
if (( ${1:--1} == idx )); then
|
||||||
|
printf '%s\n' "${symbol}${line:${#symbol}}"
|
||||||
|
shift
|
||||||
|
else
|
||||||
|
printf '%s\n' "$line"
|
||||||
|
fi
|
||||||
|
(( ++idx ))
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
batslib_decorate() {
|
||||||
|
echo
|
||||||
|
echo "-- $1 --"
|
||||||
|
cat -
|
||||||
|
echo '--'
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
|
||||||
|
assert() {
|
||||||
|
if ! "$@"; then
|
||||||
|
batslib_print_kv_single 10 'expression' "$*" \
|
||||||
|
| batslib_decorate 'assertion failed' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_equal() {
|
||||||
|
if [[ $1 != "$2" ]]; then
|
||||||
|
batslib_print_kv_single_or_multi 8 \
|
||||||
|
'expected' "$2" \
|
||||||
|
'actual' "$1" \
|
||||||
|
| batslib_decorate 'values do not equal' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_failure() {
|
||||||
|
: "${output?}"
|
||||||
|
: "${status?}"
|
||||||
|
|
||||||
|
(( $# > 0 )) && local -r expected="$1"
|
||||||
|
if (( status == 0 )); then
|
||||||
|
batslib_print_kv_single_or_multi 6 'output' "$output" \
|
||||||
|
| batslib_decorate 'command succeeded, but it was expected to fail' \
|
||||||
|
| fail
|
||||||
|
elif (( $# > 0 )) && (( status != expected )); then
|
||||||
|
{ local -ir width=8
|
||||||
|
batslib_print_kv_single "$width" \
|
||||||
|
'expected' "$expected" \
|
||||||
|
'actual' "$status"
|
||||||
|
batslib_print_kv_single_or_multi "$width" \
|
||||||
|
'output' "$output"
|
||||||
|
} \
|
||||||
|
| batslib_decorate 'command failed as expected, but status differs' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_line() {
|
||||||
|
local -i is_match_line=0
|
||||||
|
local -i is_mode_partial=0
|
||||||
|
local -i is_mode_regexp=0
|
||||||
|
: "${lines?}"
|
||||||
|
|
||||||
|
# Handle options.
|
||||||
|
while (( $# > 0 )); do
|
||||||
|
case "$1" in
|
||||||
|
-n|--index)
|
||||||
|
if (( $# < 2 )) || ! [[ $2 =~ ^([0-9]|[1-9][0-9]+)$ ]]; then
|
||||||
|
echo "\`--index' requires an integer argument: \`$2'" \
|
||||||
|
| batslib_decorate 'ERROR: assert_line' \
|
||||||
|
| fail
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
is_match_line=1
|
||||||
|
local -ri idx="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-p|--partial) is_mode_partial=1; shift ;;
|
||||||
|
-e|--regexp) is_mode_regexp=1; shift ;;
|
||||||
|
--) shift; break ;;
|
||||||
|
*) break ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if (( is_mode_partial )) && (( is_mode_regexp )); then
|
||||||
|
echo "\`--partial' and \`--regexp' are mutually exclusive" \
|
||||||
|
| batslib_decorate 'ERROR: assert_line' \
|
||||||
|
| fail
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Arguments.
|
||||||
|
local -r expected="$1"
|
||||||
|
|
||||||
|
if (( is_mode_regexp == 1 )) && [[ '' =~ $expected ]] || (( $? == 2 )); then
|
||||||
|
echo "Invalid extended regular expression: \`$expected'" \
|
||||||
|
| batslib_decorate 'ERROR: assert_line' \
|
||||||
|
| fail
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Matching.
|
||||||
|
if (( is_match_line )); then
|
||||||
|
# Specific line.
|
||||||
|
if (( is_mode_regexp )); then
|
||||||
|
if ! [[ ${lines[$idx]} =~ $expected ]]; then
|
||||||
|
batslib_print_kv_single 6 \
|
||||||
|
'index' "$idx" \
|
||||||
|
'regexp' "$expected" \
|
||||||
|
'line' "${lines[$idx]}" \
|
||||||
|
| batslib_decorate 'regular expression does not match line' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
elif (( is_mode_partial )); then
|
||||||
|
if [[ ${lines[$idx]} != *"$expected"* ]]; then
|
||||||
|
batslib_print_kv_single 9 \
|
||||||
|
'index' "$idx" \
|
||||||
|
'substring' "$expected" \
|
||||||
|
'line' "${lines[$idx]}" \
|
||||||
|
| batslib_decorate 'line does not contain substring' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [[ ${lines[$idx]} != "$expected" ]]; then
|
||||||
|
batslib_print_kv_single 8 \
|
||||||
|
'index' "$idx" \
|
||||||
|
'expected' "$expected" \
|
||||||
|
'actual' "${lines[$idx]}" \
|
||||||
|
| batslib_decorate 'line differs' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Contained in output.
|
||||||
|
if (( is_mode_regexp )); then
|
||||||
|
local -i idx
|
||||||
|
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
|
||||||
|
[[ ${lines[$idx]} =~ $expected ]] && return 0
|
||||||
|
done
|
||||||
|
{ local -ar single=( 'regexp' "$expected" )
|
||||||
|
local -ar may_be_multi=( 'output' "$output" )
|
||||||
|
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
|
||||||
|
batslib_print_kv_single "$width" "${single[@]}"
|
||||||
|
batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}"
|
||||||
|
} \
|
||||||
|
| batslib_decorate 'no output line matches regular expression' \
|
||||||
|
| fail
|
||||||
|
elif (( is_mode_partial )); then
|
||||||
|
local -i idx
|
||||||
|
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
|
||||||
|
[[ ${lines[$idx]} == *"$expected"* ]] && return 0
|
||||||
|
done
|
||||||
|
{ local -ar single=( 'substring' "$expected" )
|
||||||
|
local -ar may_be_multi=( 'output' "$output" )
|
||||||
|
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
|
||||||
|
batslib_print_kv_single "$width" "${single[@]}"
|
||||||
|
batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}"
|
||||||
|
} \
|
||||||
|
| batslib_decorate 'no output line contains substring' \
|
||||||
|
| fail
|
||||||
|
else
|
||||||
|
local -i idx
|
||||||
|
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
|
||||||
|
[[ ${lines[$idx]} == "$expected" ]] && return 0
|
||||||
|
done
|
||||||
|
{ local -ar single=( 'line' "$expected" )
|
||||||
|
local -ar may_be_multi=( 'output' "$output" )
|
||||||
|
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
|
||||||
|
batslib_print_kv_single "$width" "${single[@]}"
|
||||||
|
batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}"
|
||||||
|
} \
|
||||||
|
| batslib_decorate 'output does not contain line' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_output() {
|
||||||
|
local -i is_mode_partial=0
|
||||||
|
local -i is_mode_regexp=0
|
||||||
|
local -i is_mode_nonempty=0
|
||||||
|
local -i use_stdin=0
|
||||||
|
: "${output?}"
|
||||||
|
|
||||||
|
# Handle options.
|
||||||
|
if (( $# == 0 )); then
|
||||||
|
is_mode_nonempty=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
while (( $# > 0 )); do
|
||||||
|
case "$1" in
|
||||||
|
-p|--partial) is_mode_partial=1; shift ;;
|
||||||
|
-e|--regexp) is_mode_regexp=1; shift ;;
|
||||||
|
-|--stdin) use_stdin=1; shift ;;
|
||||||
|
--) shift; break ;;
|
||||||
|
*) break ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if (( is_mode_partial )) && (( is_mode_regexp )); then
|
||||||
|
echo "\`--partial' and \`--regexp' are mutually exclusive" \
|
||||||
|
| batslib_decorate 'ERROR: assert_output' \
|
||||||
|
| fail
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Arguments.
|
||||||
|
local expected
|
||||||
|
if (( use_stdin )); then
|
||||||
|
expected="$(cat -)"
|
||||||
|
else
|
||||||
|
expected="${1-}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Matching.
|
||||||
|
if (( is_mode_nonempty )); then
|
||||||
|
if [ -z "$output" ]; then
|
||||||
|
echo 'expected non-empty output, but output was empty' \
|
||||||
|
| batslib_decorate 'no output' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
elif (( is_mode_regexp )); then
|
||||||
|
if [[ '' =~ $expected ]] || (( $? == 2 )); then
|
||||||
|
echo "Invalid extended regular expression: \`$expected'" \
|
||||||
|
| batslib_decorate 'ERROR: assert_output' \
|
||||||
|
| fail
|
||||||
|
elif ! [[ $output =~ $expected ]]; then
|
||||||
|
batslib_print_kv_single_or_multi 6 \
|
||||||
|
'regexp' "$expected" \
|
||||||
|
'output' "$output" \
|
||||||
|
| batslib_decorate 'regular expression does not match output' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
elif (( is_mode_partial )); then
|
||||||
|
if [[ $output != *"$expected"* ]]; then
|
||||||
|
batslib_print_kv_single_or_multi 9 \
|
||||||
|
'substring' "$expected" \
|
||||||
|
'output' "$output" \
|
||||||
|
| batslib_decorate 'output does not contain substring' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [[ $output != "$expected" ]]; then
|
||||||
|
batslib_print_kv_single_or_multi 8 \
|
||||||
|
'expected' "$expected" \
|
||||||
|
'actual' "$output" \
|
||||||
|
| batslib_decorate 'output differs' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_success() {
|
||||||
|
: "${output?}"
|
||||||
|
: "${status?}"
|
||||||
|
|
||||||
|
if (( status != 0 )); then
|
||||||
|
{ local -ir width=6
|
||||||
|
batslib_print_kv_single "$width" 'status' "$status"
|
||||||
|
batslib_print_kv_single_or_multi "$width" 'output' "$output"
|
||||||
|
} \
|
||||||
|
| batslib_decorate 'command failed' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
refute() {
|
||||||
|
if "$@"; then
|
||||||
|
batslib_print_kv_single 10 'expression' "$*" \
|
||||||
|
| batslib_decorate 'assertion succeeded, but it was expected to fail' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
refute_line() {
|
||||||
|
local -i is_match_line=0
|
||||||
|
local -i is_mode_partial=0
|
||||||
|
local -i is_mode_regexp=0
|
||||||
|
: "${lines?}"
|
||||||
|
|
||||||
|
# Handle options.
|
||||||
|
while (( $# > 0 )); do
|
||||||
|
case "$1" in
|
||||||
|
-n|--index)
|
||||||
|
if (( $# < 2 )) || ! [[ $2 =~ ^([0-9]|[1-9][0-9]+)$ ]]; then
|
||||||
|
echo "\`--index' requires an integer argument: \`$2'" \
|
||||||
|
| batslib_decorate 'ERROR: refute_line' \
|
||||||
|
| fail
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
is_match_line=1
|
||||||
|
local -ri idx="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-p|--partial) is_mode_partial=1; shift ;;
|
||||||
|
-e|--regexp) is_mode_regexp=1; shift ;;
|
||||||
|
--) shift; break ;;
|
||||||
|
*) break ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if (( is_mode_partial )) && (( is_mode_regexp )); then
|
||||||
|
echo "\`--partial' and \`--regexp' are mutually exclusive" \
|
||||||
|
| batslib_decorate 'ERROR: refute_line' \
|
||||||
|
| fail
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Arguments.
|
||||||
|
local -r unexpected="$1"
|
||||||
|
|
||||||
|
if (( is_mode_regexp == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then
|
||||||
|
echo "Invalid extended regular expression: \`$unexpected'" \
|
||||||
|
| batslib_decorate 'ERROR: refute_line' \
|
||||||
|
| fail
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Matching.
|
||||||
|
if (( is_match_line )); then
|
||||||
|
# Specific line.
|
||||||
|
if (( is_mode_regexp )); then
|
||||||
|
if [[ ${lines[$idx]} =~ $unexpected ]]; then
|
||||||
|
batslib_print_kv_single 6 \
|
||||||
|
'index' "$idx" \
|
||||||
|
'regexp' "$unexpected" \
|
||||||
|
'line' "${lines[$idx]}" \
|
||||||
|
| batslib_decorate 'regular expression should not match line' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
elif (( is_mode_partial )); then
|
||||||
|
if [[ ${lines[$idx]} == *"$unexpected"* ]]; then
|
||||||
|
batslib_print_kv_single 9 \
|
||||||
|
'index' "$idx" \
|
||||||
|
'substring' "$unexpected" \
|
||||||
|
'line' "${lines[$idx]}" \
|
||||||
|
| batslib_decorate 'line should not contain substring' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [[ ${lines[$idx]} == "$unexpected" ]]; then
|
||||||
|
batslib_print_kv_single 5 \
|
||||||
|
'index' "$idx" \
|
||||||
|
'line' "${lines[$idx]}" \
|
||||||
|
| batslib_decorate 'line should differ' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Line contained in output.
|
||||||
|
if (( is_mode_regexp )); then
|
||||||
|
local -i idx
|
||||||
|
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
|
||||||
|
if [[ ${lines[$idx]} =~ $unexpected ]]; then
|
||||||
|
{ local -ar single=( 'regexp' "$unexpected" 'index' "$idx" )
|
||||||
|
local -a may_be_multi=( 'output' "$output" )
|
||||||
|
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
|
||||||
|
batslib_print_kv_single "$width" "${single[@]}"
|
||||||
|
if batslib_is_single_line "${may_be_multi[1]}"; then
|
||||||
|
batslib_print_kv_single "$width" "${may_be_multi[@]}"
|
||||||
|
else
|
||||||
|
may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" | batslib_prefix | batslib_mark '>' "$idx" )"
|
||||||
|
batslib_print_kv_multi "${may_be_multi[@]}"
|
||||||
|
fi
|
||||||
|
} \
|
||||||
|
| batslib_decorate 'no line should match the regular expression' \
|
||||||
|
| fail
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
elif (( is_mode_partial )); then
|
||||||
|
local -i idx
|
||||||
|
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
|
||||||
|
if [[ ${lines[$idx]} == *"$unexpected"* ]]; then
|
||||||
|
{ local -ar single=( 'substring' "$unexpected" 'index' "$idx" )
|
||||||
|
local -a may_be_multi=( 'output' "$output" )
|
||||||
|
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
|
||||||
|
batslib_print_kv_single "$width" "${single[@]}"
|
||||||
|
if batslib_is_single_line "${may_be_multi[1]}"; then
|
||||||
|
batslib_print_kv_single "$width" "${may_be_multi[@]}"
|
||||||
|
else
|
||||||
|
may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" | batslib_prefix | batslib_mark '>' "$idx" )"
|
||||||
|
batslib_print_kv_multi "${may_be_multi[@]}"
|
||||||
|
fi
|
||||||
|
} \
|
||||||
|
| batslib_decorate 'no line should contain substring' \
|
||||||
|
| fail
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
local -i idx
|
||||||
|
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
|
||||||
|
if [[ ${lines[$idx]} == "$unexpected" ]]; then
|
||||||
|
{ local -ar single=( 'line' "$unexpected" 'index' "$idx" )
|
||||||
|
local -a may_be_multi=( 'output' "$output" )
|
||||||
|
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
|
||||||
|
batslib_print_kv_single "$width" "${single[@]}"
|
||||||
|
if batslib_is_single_line "${may_be_multi[1]}"; then
|
||||||
|
batslib_print_kv_single "$width" "${may_be_multi[@]}"
|
||||||
|
else
|
||||||
|
may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" | batslib_prefix | batslib_mark '>' "$idx" )"
|
||||||
|
batslib_print_kv_multi "${may_be_multi[@]}"
|
||||||
|
fi
|
||||||
|
} \
|
||||||
|
| batslib_decorate 'line should not be in output' \
|
||||||
|
| fail
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
refute_output() {
|
||||||
|
local -i is_mode_partial=0
|
||||||
|
local -i is_mode_regexp=0
|
||||||
|
local -i is_mode_empty=0
|
||||||
|
local -i use_stdin=0
|
||||||
|
: "${output?}"
|
||||||
|
|
||||||
|
# Handle options.
|
||||||
|
if (( $# == 0 )); then
|
||||||
|
is_mode_empty=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
while (( $# > 0 )); do
|
||||||
|
case "$1" in
|
||||||
|
-p|--partial) is_mode_partial=1; shift ;;
|
||||||
|
-e|--regexp) is_mode_regexp=1; shift ;;
|
||||||
|
-|--stdin) use_stdin=1; shift ;;
|
||||||
|
--) shift; break ;;
|
||||||
|
*) break ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if (( is_mode_partial )) && (( is_mode_regexp )); then
|
||||||
|
echo "\`--partial' and \`--regexp' are mutually exclusive" \
|
||||||
|
| batslib_decorate 'ERROR: refute_output' \
|
||||||
|
| fail
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Arguments.
|
||||||
|
local unexpected
|
||||||
|
if (( use_stdin )); then
|
||||||
|
unexpected="$(cat -)"
|
||||||
|
else
|
||||||
|
unexpected="${1-}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( is_mode_regexp == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then
|
||||||
|
echo "Invalid extended regular expression: \`$unexpected'" \
|
||||||
|
| batslib_decorate 'ERROR: refute_output' \
|
||||||
|
| fail
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Matching.
|
||||||
|
if (( is_mode_empty )); then
|
||||||
|
if [ -n "$output" ]; then
|
||||||
|
batslib_print_kv_single_or_multi 6 \
|
||||||
|
'output' "$output" \
|
||||||
|
| batslib_decorate 'output non-empty, but expected no output' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
elif (( is_mode_regexp )); then
|
||||||
|
if [[ $output =~ $unexpected ]]; then
|
||||||
|
batslib_print_kv_single_or_multi 6 \
|
||||||
|
'regexp' "$unexpected" \
|
||||||
|
'output' "$output" \
|
||||||
|
| batslib_decorate 'regular expression should not match output' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
elif (( is_mode_partial )); then
|
||||||
|
if [[ $output == *"$unexpected"* ]]; then
|
||||||
|
batslib_print_kv_single_or_multi 9 \
|
||||||
|
'substring' "$unexpected" \
|
||||||
|
'output' "$output" \
|
||||||
|
| batslib_decorate 'output should not contain substring' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [[ $output == "$unexpected" ]]; then
|
||||||
|
batslib_print_kv_single_or_multi 6 \
|
||||||
|
'output' "$output" \
|
||||||
|
| batslib_decorate 'output equals, but it was expected to differ' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
169
bash/dnd-character/dnd_character.bats
Normal file
169
bash/dnd-character/dnd_character.bats
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
#!/usr/bin/env bats
|
||||||
|
load bats-extra
|
||||||
|
|
||||||
|
# local version: 1.1.0.0
|
||||||
|
|
||||||
|
# usage: dnd_character.sh modifier n
|
||||||
|
# -> output expected modifier
|
||||||
|
|
||||||
|
# usage: dnd_character.sh generate
|
||||||
|
# -> output each characteristic and ability value, one per line
|
||||||
|
|
||||||
|
|
||||||
|
# ability modifier
|
||||||
|
|
||||||
|
@test "ability modifier for score 3 is -4" {
|
||||||
|
#[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||||
|
run bash dnd_character.sh modifier 3
|
||||||
|
assert_success
|
||||||
|
assert_output "-4"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ability modifier for score 4 is -3" {
|
||||||
|
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||||
|
run bash dnd_character.sh modifier 4
|
||||||
|
assert_success
|
||||||
|
assert_output "-3"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ability modifier for score 5 is -3" {
|
||||||
|
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||||
|
run bash dnd_character.sh modifier 5
|
||||||
|
assert_success
|
||||||
|
assert_output "-3"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ability modifier for score 6 is -2" {
|
||||||
|
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||||
|
run bash dnd_character.sh modifier 6
|
||||||
|
assert_success
|
||||||
|
assert_output "-2"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ability modifier for score 7 is -2" {
|
||||||
|
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||||
|
run bash dnd_character.sh modifier 7
|
||||||
|
assert_success
|
||||||
|
assert_output "-2"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ability modifier for score 8 is -1" {
|
||||||
|
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||||
|
run bash dnd_character.sh modifier 8
|
||||||
|
assert_success
|
||||||
|
assert_output "-1"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ability modifier for score 9 is -1" {
|
||||||
|
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||||
|
run bash dnd_character.sh modifier 9
|
||||||
|
assert_success
|
||||||
|
assert_output "-1"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ability modifier for score 10 is 0" {
|
||||||
|
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||||
|
run bash dnd_character.sh modifier 10
|
||||||
|
assert_success
|
||||||
|
assert_output "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ability modifier for score 11 is 0" {
|
||||||
|
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||||
|
run bash dnd_character.sh modifier 11
|
||||||
|
assert_success
|
||||||
|
assert_output "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ability modifier for score 12 is +1" {
|
||||||
|
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||||
|
run bash dnd_character.sh modifier 12
|
||||||
|
assert_success
|
||||||
|
assert_output "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ability modifier for score 13 is +1" {
|
||||||
|
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||||
|
run bash dnd_character.sh modifier 13
|
||||||
|
assert_success
|
||||||
|
assert_output "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ability modifier for score 14 is +2" {
|
||||||
|
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||||
|
run bash dnd_character.sh modifier 14
|
||||||
|
assert_success
|
||||||
|
assert_output "2"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ability modifier for score 15 is +2" {
|
||||||
|
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||||
|
run bash dnd_character.sh modifier 15
|
||||||
|
assert_success
|
||||||
|
assert_output "2"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ability modifier for score 16 is +3" {
|
||||||
|
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||||
|
run bash dnd_character.sh modifier 16
|
||||||
|
assert_success
|
||||||
|
assert_output "3"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ability modifier for score 17 is +3" {
|
||||||
|
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||||
|
run bash dnd_character.sh modifier 17
|
||||||
|
assert_success
|
||||||
|
assert_output "3"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ability modifier for score 18 is +4" {
|
||||||
|
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||||
|
run bash dnd_character.sh modifier 18
|
||||||
|
assert_success
|
||||||
|
assert_output "4"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# generate a character, validate expected output
|
||||||
|
|
||||||
|
@test "generate a character" {
|
||||||
|
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||||
|
run bash dnd_character.sh generate
|
||||||
|
assert_success
|
||||||
|
# these don't have to appear in any particular order
|
||||||
|
assert_line --regexp '^strength [[:digit:]]{1,2}$'
|
||||||
|
assert_line --regexp '^dexterity [[:digit:]]{1,2}$'
|
||||||
|
assert_line --regexp '^constitution [[:digit:]]{1,2}$'
|
||||||
|
assert_line --regexp '^intelligence [[:digit:]]{1,2}$'
|
||||||
|
assert_line --regexp '^wisdom [[:digit:]]{1,2}$'
|
||||||
|
assert_line --regexp '^charisma [[:digit:]]{1,2}$'
|
||||||
|
assert_line --regexp '^hitpoints [[:digit:]]{1,2}$'
|
||||||
|
# no other output: `run` populates the `lines` array
|
||||||
|
assert_equal ${#lines[@]} 7
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Usage: between $val $low $high
|
||||||
|
# Value is between low (inclusive) and high (inclusive).
|
||||||
|
between() {
|
||||||
|
(( $2 <= $1 && $1 <= $3 ))
|
||||||
|
}
|
||||||
|
|
||||||
|
# random ability is within range
|
||||||
|
@test "validate ability range and hitpoint value" {
|
||||||
|
[[ $BATS_RUN_SKIPPED == "true" ]] || skip
|
||||||
|
for i in {1..50}; do
|
||||||
|
while read c v; do
|
||||||
|
if [[ $c == "hitpoints" ]]; then
|
||||||
|
hits=$v
|
||||||
|
else
|
||||||
|
assert between "$v" 3 18
|
||||||
|
[[ $c == "constitution" ]] && const=$v
|
||||||
|
fi
|
||||||
|
done < <(bash dnd_character.sh generate)
|
||||||
|
|
||||||
|
const_mod=$(bash dnd_character.sh modifier "$const")
|
||||||
|
assert_equal $((10 + const_mod)) "$hits"
|
||||||
|
done
|
||||||
|
}
|
20
bash/dnd-character/dnd_character.sh
Normal file
20
bash/dnd-character/dnd_character.sh
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
mod() {
|
||||||
|
echo $(bc <<< "($1/2)-5")
|
||||||
|
}
|
||||||
|
|
||||||
|
order=$1
|
||||||
|
|
||||||
|
if [[ $order == "modifier" ]]; then
|
||||||
|
mod "$2"
|
||||||
|
else
|
||||||
|
echo "strength $(($RANDOM%15+3))"
|
||||||
|
echo "dexterity $(($RANDOM%15+3))"
|
||||||
|
constitution=$(($RANDOM%15+3))
|
||||||
|
echo "constitution ${constitution}"
|
||||||
|
echo "intelligence $(($RANDOM%15+3))"
|
||||||
|
echo "wisdom $(($RANDOM%15+3))"
|
||||||
|
echo "charisma $(($RANDOM%15+3))"
|
||||||
|
echo "hitpoints $(($(mod "$constitution")+10))"
|
||||||
|
fi
|
Loading…
x
Reference in New Issue
Block a user