ðŸŽ‰ Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io ðŸŽ‰

# glennj's solution

## to D&D Character in the Tcl Track

Published at Sep 22 2020 · 0 comments
Instructions
Test suite
Solution

#### Note:

This exercise has changed since this solution was written.

For a game of Dungeons & Dragons, 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.

## Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

## Running the tests

To run the test suite, execute one of the following commands:

``````tclsh dnd-character.test            # Will stop testing after the first failure.
RUN_ALL=1 tclsh dnd-character.test  # Will run all tests and report all failures.
``````

## Feedback, Issues, Pull Requests

The exercism/tcl repository on GitHub is the home for all of the Tcl exercises on Exercism.

If you have feedback about an exercise, or want to help implementing a new one, head over there and create an issue. We'll do our best to help you!

## Source

Simon Shine, Erik Schierboom https://github.com/exercism/problem-specifications/issues/616#issuecomment-437358945

### dnd-character.test

``````#!/usr/bin/env tclsh
set version 1.1.0
package require tcltest
namespace import ::tcltest::*
source "dnd-character.tcl"

proc fail_fast {} {
return [expr {
![info exists ::env(RUN_ALL)]
|| [string is boolean -strict \$::env(RUN_ALL)]
&& !\$::env(RUN_ALL)
}]
}

proc failed {} {
return [expr {\$::tcltest::numTests(Failed) > 0}]
}

if {[fail_fast]} {
proc test args {
if {[failed]} {::tcltest::configure -skip *}
uplevel [list ::tcltest::test {*}\$args]
}
}

proc cleanupTests {} {
set failed [failed]
uplevel 1 ::tcltest::cleanupTests
if {\$failed} {exit 1}
}

if {\$::argv0 eq [info script]} {

set cases {
dnd-1 "ability modifier for score 3 is -4" 3 -4
dnd-2 "ability modifier for score 4 is -3" 4 -3
dnd-3 "ability modifier for score 5 is -3" 5 -3
dnd-4 "ability modifier for score 6 is -2" 6 -2
dnd-5 "ability modifier for score 7 is -2" 7 -2
dnd-6 "ability modifier for score 8 is -1" 8 -1
dnd-7 "ability modifier for score 9 is -1" 9 -1
dnd-8 "ability modifier for score 10 is 0" 10 0
dnd-9 "ability modifier for score 11 is 0" 11 0
dnd-10 "ability modifier for score 12 is +1" 12 1
dnd-11 "ability modifier for score 13 is +1" 13 1
dnd-12 "ability modifier for score 14 is +2" 14 2
dnd-13 "ability modifier for score 15 is +2" 15 2
dnd-14 "ability modifier for score 16 is +3" 16 3
dnd-15 "ability modifier for score 17 is +3" 17 3
dnd-16 "ability modifier for score 18 is +4" 18 4
}

foreach {name description score modifier} \$cases {
test \$name \$description -body {
dnd modifier \$score
} -returnCodes ok -result \$modifier
}

test dnd-17 "random ability is within range" -body {
set result true
for {set i 0} {\$i < 10000} {incr i} {
set a [dnd ability]
if {\$a < 3 || \$score > 18} {
set result false
break
}
}
set result
} -returnCodes ok -result true

test dnd-18 "a character has the correct attributes" -body {
set character [dnd character]
lsort [dict keys \$character]
} -returnCodes ok -result {charisma constitution dexterity hitpoints intelligence strength wisdom}

test dnd-19 "characteristics have the correct values" -body {
set character [dnd character]
dict with character {
expr {
3 <= \$charisma && \$charisma <= 18 &&
3 <= \$constitution && \$constitution <= 18 &&
3 <= \$dexterity && \$dexterity <= 18 &&
3 <= \$intelligence && \$intelligence <= 18 &&
3 <= \$strength && \$strength <= 18 &&
3 <= \$wisdom && \$wisdom <= 18 &&
\$hitpoints == 10 + [dnd modifier \$constitution]
}
}
} -returnCodes ok -result 1

cleanupTests
}``````
``````namespace eval dnd {
namespace export character ability modifier
namespace ensemble create

variable characteristics {
strength
dexterity
constitution
intelligence
wisdom
charisma
}

proc modifier {score} {
# floor() rounds towards -Inf
expr {int(floor((\$score - 10) / 2.0))}
}

proc d6 {} {
expr {1 + int(6 * rand())}
}

proc ability {} {
set dice [list [d6] [d6] [d6] [d6]]
set sum [::tcl::mathop::+ {*}\$dice]
set min [::tcl::mathfunc::min {*}\$dice]
expr {\$sum - \$min}
}

proc hitpoints {constitution} {
expr {10 + [modifier \$constitution]}
}

proc character {} {
variable characteristics
set character [dict create]
foreach c \$characteristics {
dict set character \$c [ability]
}
dict set character hitpoints \
[hitpoints [dict get \$character constitution]]
return \$character
}
}``````