Skip to content

Commit

Permalink
Add dnd-character exercise
Browse files Browse the repository at this point in the history
  • Loading branch information
ErikSchierboom committed Jan 13, 2025
1 parent 25a34f0 commit aa23b32
Show file tree
Hide file tree
Showing 12 changed files with 344 additions and 0 deletions.
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,14 @@
"time"
]
},
{
"slug": "dnd-character",
"name": "D&D Character",
"uuid": "d6a7a17f-dd95-46cd-93a8-6b536b2ff4e4",
"practices": [],
"prerequisites": [],
"difficulty": 4
},
{
"slug": "octal",
"name": "Octal",
Expand Down
32 changes: 32 additions & 0 deletions exercises/practice/dnd-character/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# 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 recording 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 above rules.

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.

~~~~exercism/note
Most programming languages feature (pseudo-)random generators, but few programming languages are designed to roll dice.
One such language is [Troll][troll].
[troll]: https://di.ku.dk/Ansatte/?pure=da%2Fpublications%2Ftroll-a-language-for-specifying-dicerolls(84a45ff0-068b-11df-825d-000ea68e967b)%2Fexport.html
~~~~

[dnd]: https://en.wikipedia.org/wiki/Dungeons_%26_Dragons
10 changes: 10 additions & 0 deletions exercises/practice/dnd-character/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Introduction

After weeks of anticipation, you and your friends get together for your very first game of [Dungeons & Dragons][dnd] (D&D).
Since this is the first session of the game, each player has to generate a character to play with.
The character's abilities are determined by rolling 6-sided dice, but where _are_ the dice?
With a shock, you realize that your friends are waiting for _you_ to produce the dice; after all it was your idea to play D&D!
Panicking, you realize you forgot to bring the dice, which would mean no D&D game.
As you have some basic coding skills, you quickly come up with a solution: you'll write a program to simulate dice rolls.

[dnd]: https://en.wikipedia.org/wiki/Dungeons_%26_Dragons
19 changes: 19 additions & 0 deletions exercises/practice/dnd-character/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"authors": [
"erikschierboom"
],
"files": {
"solution": [
"src/dnd_character.clj"
],
"test": [
"test/dnd_character_test.clj"
],
"example": [
".meta/example.clj"
]
},
"blurb": "Randomly generate Dungeons & Dragons characters.",
"source": "Simon Shine, Erik Schierboom",
"source_url": "https://github.com/exercism/problem-specifications/issues/616#issuecomment-437358945"
}
19 changes: 19 additions & 0 deletions exercises/practice/dnd-character/.meta/example.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
(ns dnd-character
(:require [clojure.math :refer [floor]]))

(defn modifier [score]
(int (floor (/ (- score 10) 2))))

(defn- die [] (inc (rand-int 6)))

(defn ability []
(reduce + (rest (sort (repeatedly 4 die)))))

(defrecord DndCharacter [strength dexterity charisma wisdom intelligence constitution hitpoints])

(defn character []
(let [abilities (repeatedly 6 ability)
[strength dexterity charisma wisdom intelligence constitution] abilities
hitpoints (+ 10 (modifier constitution))]
(DndCharacter. strength dexterity charisma wisdom intelligence constitution hitpoints)))

16 changes: 16 additions & 0 deletions exercises/practice/dnd-character/.meta/generator.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
(ns dnd-character-generator)

(def random-abilities [:strength :dexterity :charisma :wisdom :intelligence :constitution])

(defn- transform-character-test-case [test-case]
(map
(fn [ability]
(assoc (update test-case :path #(conj % (name ability))) :ability ability)) random-abilities))

(defn- transform-test-case [test-case]
(if (= "character" (:property test-case))
(transform-character-test-case test-case)
[test-case]))

(defn transform [test-cases]
(mapcat #(transform-test-case %) test-cases))
29 changes: 29 additions & 0 deletions exercises/practice/dnd-character/.meta/generator.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
(ns dnd-character-test
(:require [clojure.test :refer [deftest testing is]]
dnd-character))
{{#test_cases.modifier}}
(deftest modifier_test_{{idx}}
(testing {{string description}}
(is (= {{expected}} (dnd-character/modifier {{input.score}})))))
{{/test_cases.modifier}}
(deftest ability_test_1
(testing "random ability is within range"
(is (<= 3 (dnd-character/ability) 18))))

(deftest ability_test_2
(testing "ability is generated randomly"
(is (>= (count (set (repeatedly 100 #(dnd-character/ability)))) 5))))
{{#test_cases.character}}
(deftest character_test_{{idx}}
(testing {{string description}}
(is (<= 3 (:{{ability}} (dnd-character/character)) 18))))
{{/test_cases.character}}
(deftest character_test_7
(testing "random character is valid - hitpoints"
(let [char (dnd-character/character)
expected (+ 10 (dnd-character/modifier (:constitution char)))]
(is (= expected (:hitpoints char))))))

(deftest character_test_8
(testing "random character is generated randomly"
(is (>= (count (set (repeatedly 100 #(dnd-character/character)))) 5))))
73 changes: 73 additions & 0 deletions exercises/practice/dnd-character/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# This is an auto-generated file.
#
# Regenerating this file via `configlet sync` will:
# - Recreate every `description` key/value pair
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
# - Preserve any other key/value pair
#
# As user-added comments (using the # character) will be removed when this file
# is regenerated, comments can be added via a `comment` key.

[1e9ae1dc-35bd-43ba-aa08-e4b94c20fa37]
description = "ability modifier -> ability modifier for score 3 is -4"

[cc9bb24e-56b8-4e9e-989d-a0d1a29ebb9c]
description = "ability modifier -> ability modifier for score 4 is -3"

[5b519fcd-6946-41ee-91fe-34b4f9808326]
description = "ability modifier -> ability modifier for score 5 is -3"

[dc2913bd-6d7a-402e-b1e2-6d568b1cbe21]
description = "ability modifier -> ability modifier for score 6 is -2"

[099440f5-0d66-4b1a-8a10-8f3a03cc499f]
description = "ability modifier -> ability modifier for score 7 is -2"

[cfda6e5c-3489-42f0-b22b-4acb47084df0]
description = "ability modifier -> ability modifier for score 8 is -1"

[c70f0507-fa7e-4228-8463-858bfbba1754]
description = "ability modifier -> ability modifier for score 9 is -1"

[6f4e6c88-1cd9-46a0-92b8-db4a99b372f7]
description = "ability modifier -> ability modifier for score 10 is 0"

[e00d9e5c-63c8-413f-879d-cd9be9697097]
description = "ability modifier -> ability modifier for score 11 is 0"

[eea06f3c-8de0-45e7-9d9d-b8cab4179715]
description = "ability modifier -> ability modifier for score 12 is +1"

[9c51f6be-db72-4af7-92ac-b293a02c0dcd]
description = "ability modifier -> ability modifier for score 13 is +1"

[94053a5d-53b6-4efc-b669-a8b5098f7762]
description = "ability modifier -> ability modifier for score 14 is +2"

[8c33e7ca-3f9f-4820-8ab3-65f2c9e2f0e2]
description = "ability modifier -> ability modifier for score 15 is +2"

[c3ec871e-1791-44d0-b3cc-77e5fb4cd33d]
description = "ability modifier -> ability modifier for score 16 is +3"

[3d053cee-2888-4616-b9fd-602a3b1efff4]
description = "ability modifier -> ability modifier for score 17 is +3"

[bafd997a-e852-4e56-9f65-14b60261faee]
description = "ability modifier -> ability modifier for score 18 is +4"

[4f28f19c-2e47-4453-a46a-c0d365259c14]
description = "random ability is within range"

[385d7e72-864f-4e88-8279-81a7d75b04ad]
description = "random character is valid"

[2ca77b9b-c099-46c3-a02c-0d0f68ffa0fe]
description = "each ability is only calculated once"
include = false

[dca2b2ec-f729-4551-84b9-078876bb4808]
description = "each ability is only calculated once"
reimplements = "2ca77b9b-c099-46c3-a02c-0d0f68ffa0fe"
include = false
6 changes: 6 additions & 0 deletions exercises/practice/dnd-character/deps.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{:aliases {:test {:extra-paths ["test"]
:extra-deps {io.github.cognitect-labs/test-runner
{:git/url "https://github.com/cognitect-labs/test-runner.git"
:sha "705ad25bbf0228b1c38d0244a36001c2987d7337"}}
:main-opts ["-m" "cognitect.test-runner"]
:exec-fn cognitect.test-runner.api/test}}}
4 changes: 4 additions & 0 deletions exercises/practice/dnd-character/project.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
(defproject dnd-character "0.1.0-SNAPSHOT"
:description "dnd-character exercise."
:url "https://github.com/exercism/clojure/tree/main/exercises/practice/dnd-character"
:dependencies [[org.clojure/clojure "1.11.1"]])
19 changes: 19 additions & 0 deletions exercises/practice/dnd-character/src/dnd_character.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
(ns dnd-character)

(defn modifier
"Calculate a score's modifier"
[score]
;; function body
)

(defn ability
"Generate a random ability"
[]
;; function body
)

(defn character
"Generate a random character"
[]
;; function body
)
109 changes: 109 additions & 0 deletions exercises/practice/dnd-character/test/dnd_character_test.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
(ns dnd-character-test
(:require [clojure.test :refer [deftest testing is]]
dnd-character))

(deftest modifier_test_1
(testing "ability modifier - ability modifier for score 3 is -4"
(is (= -4 (dnd-character/modifier 3)))))

(deftest modifier_test_2
(testing "ability modifier - ability modifier for score 4 is -3"
(is (= -3 (dnd-character/modifier 4)))))

(deftest modifier_test_3
(testing "ability modifier - ability modifier for score 5 is -3"
(is (= -3 (dnd-character/modifier 5)))))

(deftest modifier_test_4
(testing "ability modifier - ability modifier for score 6 is -2"
(is (= -2 (dnd-character/modifier 6)))))

(deftest modifier_test_5
(testing "ability modifier - ability modifier for score 7 is -2"
(is (= -2 (dnd-character/modifier 7)))))

(deftest modifier_test_6
(testing "ability modifier - ability modifier for score 8 is -1"
(is (= -1 (dnd-character/modifier 8)))))

(deftest modifier_test_7
(testing "ability modifier - ability modifier for score 9 is -1"
(is (= -1 (dnd-character/modifier 9)))))

(deftest modifier_test_8
(testing "ability modifier - ability modifier for score 10 is 0"
(is (= 0 (dnd-character/modifier 10)))))

(deftest modifier_test_9
(testing "ability modifier - ability modifier for score 11 is 0"
(is (= 0 (dnd-character/modifier 11)))))

(deftest modifier_test_10
(testing "ability modifier - ability modifier for score 12 is +1"
(is (= 1 (dnd-character/modifier 12)))))

(deftest modifier_test_11
(testing "ability modifier - ability modifier for score 13 is +1"
(is (= 1 (dnd-character/modifier 13)))))

(deftest modifier_test_12
(testing "ability modifier - ability modifier for score 14 is +2"
(is (= 2 (dnd-character/modifier 14)))))

(deftest modifier_test_13
(testing "ability modifier - ability modifier for score 15 is +2"
(is (= 2 (dnd-character/modifier 15)))))

(deftest modifier_test_14
(testing "ability modifier - ability modifier for score 16 is +3"
(is (= 3 (dnd-character/modifier 16)))))

(deftest modifier_test_15
(testing "ability modifier - ability modifier for score 17 is +3"
(is (= 3 (dnd-character/modifier 17)))))

(deftest modifier_test_16
(testing "ability modifier - ability modifier for score 18 is +4"
(is (= 4 (dnd-character/modifier 18)))))

(deftest ability_test_1
(testing "random ability is within range"
(is (<= 3 (dnd-character/ability) 18))))

(deftest ability_test_2
(testing "ability is generated randomly"
(is (>= (count (set (repeatedly 100 #(dnd-character/ability)))) 5))))

(deftest character_test_1
(testing "random character is valid - strength"
(is (<= 3 (:strength (dnd-character/character)) 18))))

(deftest character_test_2
(testing "random character is valid - dexterity"
(is (<= 3 (:dexterity (dnd-character/character)) 18))))

(deftest character_test_3
(testing "random character is valid - charisma"
(is (<= 3 (:charisma (dnd-character/character)) 18))))

(deftest character_test_4
(testing "random character is valid - wisdom"
(is (<= 3 (:wisdom (dnd-character/character)) 18))))

(deftest character_test_5
(testing "random character is valid - intelligence"
(is (<= 3 (:intelligence (dnd-character/character)) 18))))

(deftest character_test_6
(testing "random character is valid - constitution"
(is (<= 3 (:constitution (dnd-character/character)) 18))))

(deftest character_test_7
(testing "random character is valid - hitpoints"
(let [char (dnd-character/character)
expected (+ 10 (dnd-character/modifier (:constitution char)))]
(is (= expected (:hitpoints char))))))

(deftest character_test_8
(testing "random character is generated randomly"
(is (>= (count (set (repeatedly 100 #(dnd-character/character)))) 5))))

0 comments on commit aa23b32

Please sign in to comment.