Skip to content

Commit dbad29c

Browse files
Add dnd-character exercise
1 parent c0b457b commit dbad29c

File tree

12 files changed

+350
-0
lines changed

12 files changed

+350
-0
lines changed

config.json

+10
Original file line numberDiff line numberDiff line change
@@ -1166,6 +1166,16 @@
11661166
"time"
11671167
]
11681168
},
1169+
{
1170+
"slug": "dnd-character",
1171+
"name": "D&D Character",
1172+
"uuid": "d6a7a17f-dd95-46cd-93a8-6b536b2ff4e4",
1173+
"practices": [],
1174+
"prerequisites": [
1175+
"numbers"
1176+
],
1177+
"difficulty": 4
1178+
},
11691179
{
11701180
"slug": "octal",
11711181
"name": "Octal",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Instructions
2+
3+
For a game of [Dungeons & Dragons][dnd], each player starts by generating a character they can play with.
4+
This character has, among other things, six abilities; strength, dexterity, constitution, intelligence, wisdom and charisma.
5+
These six abilities have scores that are determined randomly.
6+
You do this by rolling four 6-sided dice and recording the sum of the largest three dice.
7+
You do this six times, once for each ability.
8+
9+
Your character's initial hitpoints are 10 + your character's constitution modifier.
10+
You find your character's constitution modifier by subtracting 10 from your character's constitution, divide by 2 and round down.
11+
12+
Write a random character generator that follows the above rules.
13+
14+
For example, the six throws of four dice may look like:
15+
16+
- 5, 3, 1, 6: You discard the 1 and sum 5 + 3 + 6 = 14, which you assign to strength.
17+
- 3, 2, 5, 3: You discard the 2 and sum 3 + 5 + 3 = 11, which you assign to dexterity.
18+
- 1, 1, 1, 1: You discard the 1 and sum 1 + 1 + 1 = 3, which you assign to constitution.
19+
- 2, 1, 6, 6: You discard the 1 and sum 2 + 6 + 6 = 14, which you assign to intelligence.
20+
- 3, 5, 3, 4: You discard the 3 and sum 5 + 3 + 4 = 12, which you assign to wisdom.
21+
- 6, 6, 6, 6: You discard the 6 and sum 6 + 6 + 6 = 18, which you assign to charisma.
22+
23+
Because constitution is 3, the constitution modifier is -4 and the hitpoints are 6.
24+
25+
~~~~exercism/note
26+
Most programming languages feature (pseudo-)random generators, but few programming languages are designed to roll dice.
27+
One such language is [Troll][troll].
28+
29+
[troll]: https://di.ku.dk/Ansatte/?pure=da%2Fpublications%2Ftroll-a-language-for-specifying-dicerolls(84a45ff0-068b-11df-825d-000ea68e967b)%2Fexport.html
30+
~~~~
31+
32+
[dnd]: https://en.wikipedia.org/wiki/Dungeons_%26_Dragons
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Introduction
2+
3+
After weeks of anticipation, you and your friends get together for your very first game of [Dungeons & Dragons][dnd] (D&D).
4+
Since this is the first session of the game, each player has to generate a character to play with.
5+
The character's abilities are determined by rolling 6-sided dice, but where _are_ the dice?
6+
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!
7+
Panicking, you realize you forgot to bring the dice, which would mean no D&D game.
8+
As you have some basic coding skills, you quickly come up with a solution: you'll write a program to simulate dice rolls.
9+
10+
[dnd]: https://en.wikipedia.org/wiki/Dungeons_%26_Dragons
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"erikschierboom"
4+
],
5+
"files": {
6+
"solution": [
7+
"src/dnd_character.clj"
8+
],
9+
"test": [
10+
"test/dnd_character_test.clj"
11+
],
12+
"example": [
13+
".meta/example.clj"
14+
]
15+
},
16+
"blurb": "Randomly generate Dungeons & Dragons characters.",
17+
"source": "Simon Shine, Erik Schierboom",
18+
"source_url": "https://github.com/exercism/problem-specifications/issues/616#issuecomment-437358945"
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
(ns dnd-character
2+
(:require [clojure.math :refer [floor]]))
3+
4+
(defn score-modifier [score]
5+
(int (floor (/ (- score 10) 2))))
6+
7+
(defn- die [] (inc (rand-int 6)))
8+
9+
(defn rand-ability []
10+
(reduce + (rest (sort (repeatedly 4 die)))))
11+
12+
(defrecord DndCharacter [strength dexterity charisma wisdom intelligence constitution hitpoints])
13+
14+
(defn rand-character []
15+
(let [abilities (repeatedly 6 rand-ability)
16+
[strength dexterity charisma wisdom intelligence constitution] abilities
17+
hitpoints (+ 10 (score-modifier constitution))]
18+
(DndCharacter. strength dexterity charisma wisdom intelligence constitution hitpoints)))
19+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
(ns dnd-character-generator
2+
(:require [hbs.helper :refer [safe-str]]))
3+
4+
(def random-abilities [:strength :dexterity :charisma :wisdom :intelligence :constitution])
5+
6+
(defn- expand-character-test-case [test-case]
7+
(map
8+
(fn [ability]
9+
(-> test-case
10+
(update :path #(vector (str (last %) " >> " (name ability))))
11+
(assoc :ability (safe-str (str ability))))) random-abilities))
12+
13+
(defn- expand-test-case [test-case]
14+
(case (:property test-case)
15+
"character" (expand-character-test-case test-case)
16+
"modifier" [(update test-case :path #(take-last 1 %))]
17+
[test-case]))
18+
19+
(defn transform [test-cases]
20+
(mapcat #(expand-test-case %) test-cases))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
(ns dnd-character-test
2+
(:require [clojure.test :refer [deftest testing is]]
3+
dnd-character))
4+
{{#test_cases.modifier}}
5+
(deftest modifier_test_{{idx}}
6+
(testing {{description}}
7+
(is (= {{expected}} (dnd-character/score-modifier {{input.score}})))))
8+
{{/test_cases.modifier}}
9+
(deftest ability_test_1
10+
(testing "random ability is within range"
11+
(is (<= 3 (dnd-character/rand-ability) 18))))
12+
13+
(deftest ability_test_2
14+
(testing "ability is generated randomly"
15+
(is (>= (count (set (repeatedly 100 #(dnd-character/rand-ability)))) 5))))
16+
{{#test_cases.character}}
17+
(deftest character_test_{{idx}}
18+
(testing {{description}}
19+
(is (<= 3 ({{ability}} (dnd-character/rand-character)) 18))))
20+
{{/test_cases.character}}
21+
(deftest character_test_7
22+
(testing "random character is valid >> hitpoints"
23+
(let [character (dnd-character/rand-character)
24+
expected (+ 10 (dnd-character/score-modifier (:constitution character)))]
25+
(is (= expected (:hitpoints character))))))
26+
27+
(deftest character_test_8
28+
(testing "random character is generated randomly"
29+
(is (>= (count (set (repeatedly 100 #(dnd-character/rand-character)))) 5))))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[1e9ae1dc-35bd-43ba-aa08-e4b94c20fa37]
13+
description = "ability modifier -> ability modifier for score 3 is -4"
14+
15+
[cc9bb24e-56b8-4e9e-989d-a0d1a29ebb9c]
16+
description = "ability modifier -> ability modifier for score 4 is -3"
17+
18+
[5b519fcd-6946-41ee-91fe-34b4f9808326]
19+
description = "ability modifier -> ability modifier for score 5 is -3"
20+
21+
[dc2913bd-6d7a-402e-b1e2-6d568b1cbe21]
22+
description = "ability modifier -> ability modifier for score 6 is -2"
23+
24+
[099440f5-0d66-4b1a-8a10-8f3a03cc499f]
25+
description = "ability modifier -> ability modifier for score 7 is -2"
26+
27+
[cfda6e5c-3489-42f0-b22b-4acb47084df0]
28+
description = "ability modifier -> ability modifier for score 8 is -1"
29+
30+
[c70f0507-fa7e-4228-8463-858bfbba1754]
31+
description = "ability modifier -> ability modifier for score 9 is -1"
32+
33+
[6f4e6c88-1cd9-46a0-92b8-db4a99b372f7]
34+
description = "ability modifier -> ability modifier for score 10 is 0"
35+
36+
[e00d9e5c-63c8-413f-879d-cd9be9697097]
37+
description = "ability modifier -> ability modifier for score 11 is 0"
38+
39+
[eea06f3c-8de0-45e7-9d9d-b8cab4179715]
40+
description = "ability modifier -> ability modifier for score 12 is +1"
41+
42+
[9c51f6be-db72-4af7-92ac-b293a02c0dcd]
43+
description = "ability modifier -> ability modifier for score 13 is +1"
44+
45+
[94053a5d-53b6-4efc-b669-a8b5098f7762]
46+
description = "ability modifier -> ability modifier for score 14 is +2"
47+
48+
[8c33e7ca-3f9f-4820-8ab3-65f2c9e2f0e2]
49+
description = "ability modifier -> ability modifier for score 15 is +2"
50+
51+
[c3ec871e-1791-44d0-b3cc-77e5fb4cd33d]
52+
description = "ability modifier -> ability modifier for score 16 is +3"
53+
54+
[3d053cee-2888-4616-b9fd-602a3b1efff4]
55+
description = "ability modifier -> ability modifier for score 17 is +3"
56+
57+
[bafd997a-e852-4e56-9f65-14b60261faee]
58+
description = "ability modifier -> ability modifier for score 18 is +4"
59+
60+
[4f28f19c-2e47-4453-a46a-c0d365259c14]
61+
description = "random ability is within range"
62+
63+
[385d7e72-864f-4e88-8279-81a7d75b04ad]
64+
description = "random character is valid"
65+
66+
[2ca77b9b-c099-46c3-a02c-0d0f68ffa0fe]
67+
description = "each ability is only calculated once"
68+
include = false
69+
70+
[dca2b2ec-f729-4551-84b9-078876bb4808]
71+
description = "each ability is only calculated once"
72+
reimplements = "2ca77b9b-c099-46c3-a02c-0d0f68ffa0fe"
73+
include = false
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{:aliases {:test {:extra-paths ["test"]
2+
:extra-deps {io.github.cognitect-labs/test-runner
3+
{:git/url "https://github.com/cognitect-labs/test-runner.git"
4+
:sha "705ad25bbf0228b1c38d0244a36001c2987d7337"}}
5+
:main-opts ["-m" "cognitect.test-runner"]
6+
:exec-fn cognitect.test-runner.api/test}}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
(defproject dnd-character "0.1.0-SNAPSHOT"
2+
:description "dnd-character exercise."
3+
:url "https://github.com/exercism/clojure/tree/main/exercises/practice/dnd-character"
4+
:dependencies [[org.clojure/clojure "1.11.1"]])
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
(ns dnd-character)
2+
3+
(defn score-modifier
4+
"Calculate a score's modifier"
5+
[score]
6+
;; function body
7+
)
8+
9+
(defn rand-ability
10+
"Generate a random ability"
11+
[]
12+
;; function body
13+
)
14+
15+
(defn rand-character
16+
"Generate a random character"
17+
[]
18+
;; function body
19+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
(ns dnd-character-test
2+
(:require [clojure.test :refer [deftest testing is]]
3+
dnd-character))
4+
5+
(deftest modifier_test_1
6+
(testing "ability modifier for score 3 is -4"
7+
(is (= -4 (dnd-character/score-modifier 3)))))
8+
9+
(deftest modifier_test_2
10+
(testing "ability modifier for score 4 is -3"
11+
(is (= -3 (dnd-character/score-modifier 4)))))
12+
13+
(deftest modifier_test_3
14+
(testing "ability modifier for score 5 is -3"
15+
(is (= -3 (dnd-character/score-modifier 5)))))
16+
17+
(deftest modifier_test_4
18+
(testing "ability modifier for score 6 is -2"
19+
(is (= -2 (dnd-character/score-modifier 6)))))
20+
21+
(deftest modifier_test_5
22+
(testing "ability modifier for score 7 is -2"
23+
(is (= -2 (dnd-character/score-modifier 7)))))
24+
25+
(deftest modifier_test_6
26+
(testing "ability modifier for score 8 is -1"
27+
(is (= -1 (dnd-character/score-modifier 8)))))
28+
29+
(deftest modifier_test_7
30+
(testing "ability modifier for score 9 is -1"
31+
(is (= -1 (dnd-character/score-modifier 9)))))
32+
33+
(deftest modifier_test_8
34+
(testing "ability modifier for score 10 is 0"
35+
(is (= 0 (dnd-character/score-modifier 10)))))
36+
37+
(deftest modifier_test_9
38+
(testing "ability modifier for score 11 is 0"
39+
(is (= 0 (dnd-character/score-modifier 11)))))
40+
41+
(deftest modifier_test_10
42+
(testing "ability modifier for score 12 is +1"
43+
(is (= 1 (dnd-character/score-modifier 12)))))
44+
45+
(deftest modifier_test_11
46+
(testing "ability modifier for score 13 is +1"
47+
(is (= 1 (dnd-character/score-modifier 13)))))
48+
49+
(deftest modifier_test_12
50+
(testing "ability modifier for score 14 is +2"
51+
(is (= 2 (dnd-character/score-modifier 14)))))
52+
53+
(deftest modifier_test_13
54+
(testing "ability modifier for score 15 is +2"
55+
(is (= 2 (dnd-character/score-modifier 15)))))
56+
57+
(deftest modifier_test_14
58+
(testing "ability modifier for score 16 is +3"
59+
(is (= 3 (dnd-character/score-modifier 16)))))
60+
61+
(deftest modifier_test_15
62+
(testing "ability modifier for score 17 is +3"
63+
(is (= 3 (dnd-character/score-modifier 17)))))
64+
65+
(deftest modifier_test_16
66+
(testing "ability modifier for score 18 is +4"
67+
(is (= 4 (dnd-character/score-modifier 18)))))
68+
69+
(deftest ability_test_1
70+
(testing "random ability is within range"
71+
(is (<= 3 (dnd-character/rand-ability) 18))))
72+
73+
(deftest ability_test_2
74+
(testing "ability is generated randomly"
75+
(is (>= (count (set (repeatedly 100 #(dnd-character/rand-ability)))) 5))))
76+
77+
(deftest character_test_1
78+
(testing "random character is valid >> strength"
79+
(is (<= 3 (:strength (dnd-character/rand-character)) 18))))
80+
81+
(deftest character_test_2
82+
(testing "random character is valid >> dexterity"
83+
(is (<= 3 (:dexterity (dnd-character/rand-character)) 18))))
84+
85+
(deftest character_test_3
86+
(testing "random character is valid >> charisma"
87+
(is (<= 3 (:charisma (dnd-character/rand-character)) 18))))
88+
89+
(deftest character_test_4
90+
(testing "random character is valid >> wisdom"
91+
(is (<= 3 (:wisdom (dnd-character/rand-character)) 18))))
92+
93+
(deftest character_test_5
94+
(testing "random character is valid >> intelligence"
95+
(is (<= 3 (:intelligence (dnd-character/rand-character)) 18))))
96+
97+
(deftest character_test_6
98+
(testing "random character is valid >> constitution"
99+
(is (<= 3 (:constitution (dnd-character/rand-character)) 18))))
100+
101+
(deftest character_test_7
102+
(testing "random character is valid >> hitpoints"
103+
(let [character (dnd-character/rand-character)
104+
expected (+ 10 (dnd-character/score-modifier (:constitution character)))]
105+
(is (= expected (:hitpoints character))))))
106+
107+
(deftest character_test_8
108+
(testing "random character is generated randomly"
109+
(is (>= (count (set (repeatedly 100 #(dnd-character/rand-character)))) 5))))

0 commit comments

Comments
 (0)