Skip to content

Commit 3156d5f

Browse files
colinleachBethanyG
andauthored
[D&D Character] Approaches (#3666)
* [D&D Character] draft approaches * Added 4 approaches as examples of possible solutions based on the intro. * Added snippets for the 4 new approaches. * Modifications to introduction and config json. * Update exercises/practice/dnd-character/.approaches/loop-and-setattr-in-init/content.md * Update exercises/practice/dnd-character/.approaches/introduction.md * Update exercises/practice/dnd-character/.approaches/stand-alone-dice-roll-function/content.md --------- Co-authored-by: BethanyG <BethanyG@users.noreply.github.com>
1 parent e8aa6a6 commit 3156d5f

File tree

10 files changed

+603
-0
lines changed

10 files changed

+603
-0
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Use the `ability()` Method to Generate and Return the Dice Rolls
2+
3+
4+
```python
5+
from random import sample
6+
7+
class Character:
8+
def __init__(self):
9+
self.strength = self.ability()
10+
self.dexterity = self.ability()
11+
self.constitution = self.ability()
12+
self.intelligence = self.ability()
13+
self.wisdom = self.ability()
14+
self.charisma = self.ability()
15+
16+
self.hitpoints = 10 + modifier(self.constitution)
17+
18+
def ability(self):
19+
values = sample(range(1, 7), 4)
20+
return sum(values) - min(values)
21+
22+
def modifier(constitution):
23+
return (constitution - 10)//2
24+
```
25+
26+
27+
This approach uses a single `ability()` method to calculate the dice rolls and return an ability value.
28+
`ability()` is then called in `__init__()` to populate the listed-out character attributes.
29+
`self.hitpoints` calls the stand-alone `modifier()` function, adding it to 10 for the character's hitpoints attribute.
30+
31+
This approach is valid and passes all the tests.
32+
However, it will trigger an analyzer comment about there being "too few public methods", since there are no methods for this class beyond the one that calculates attribute values.
33+
34+
35+
The "too few" rule encourages you to think about the design of the class: is it worth the effort to create the class if it only holds attribute values for a character?
36+
What other functionality should this class hold?
37+
Should you separate dice rolls from ability values?
38+
Is the class better as a [dataclass][dataclass], with dice roll as a utility function?
39+
40+
None of these (_including the analyzer complaint about too few methods_) is a hard and fast rule or requirement - all are considerations for the class as you build out a larger program.
41+
42+
43+
[dataclass]: https://docs.python.org/3/library/dataclasses.html
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
...
2+
def __init__(self):
3+
for ability_type in self.ABILITIES:
4+
setattr(self, ability_type, self.ability())
5+
6+
def ability(self):
7+
values = sample(range(1, 7), 4)
8+
return sum(values) - min(values)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"introduction": {
3+
"authors": ["colinleach",
4+
"BethanyG"],
5+
"contributors": []
6+
},
7+
"approaches": [
8+
{
9+
"uuid": "952835d1-e9d1-4dc3-b2c2-aad703e8db2e",
10+
"slug": "ability-method",
11+
"title": "Ability Method",
12+
"blurb": "Use one method to generate dice rolls and return ability values.",
13+
"authors": ["bethanyg"]
14+
},
15+
{
16+
"uuid": "5022c80c-8ca1-45e4-aa5f-d91bb3f53441",
17+
"slug": "dice-roll-static-method",
18+
"title": "Dice Roll Static Method",
19+
"blurb": "Use a separate static method to conduct dice rolls.",
20+
"authors": ["bethanyg"]
21+
},
22+
{
23+
"uuid": "98af6c1d-5ab4-476d-9041-30b8f55e13eb",
24+
"slug": "stand-alone-dice-roll-function",
25+
"title": "Stand-alone Dice Roll Function",
26+
"blurb": "Create a dice roll function outside the Character class.",
27+
"authors": ["bethanyg"]
28+
},
29+
{
30+
"uuid": "5dd9d9b0-bfa5-43b2-8e95-787425349fc4",
31+
"slug": "loop-and-setattr-in-init",
32+
"title": "Loop and setattr in init",
33+
"blurb": "Use a tuple of attributes, a loop, and setattr in init to assign ability values.",
34+
"authors": ["bethanyg"]
35+
}
36+
]
37+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Move Dice Rolls Into a Static Method Separate from the `ability` Method
2+
3+
4+
```python
5+
from math import floor
6+
from random import choice
7+
8+
class Character:
9+
def __init__(self):
10+
self.strength = Character.dice_rolls()
11+
self.dexterity = Character.dice_rolls()
12+
self.constitution = Character.dice_rolls()
13+
self.intelligence = Character.dice_rolls()
14+
self.wisdom = Character.dice_rolls()
15+
self.charisma = Character.dice_rolls()
16+
17+
self.hitpoints = 10 + modifier(self.constitution)
18+
19+
def ability(self):
20+
return choice([*vars(self).values()])
21+
22+
@staticmethod
23+
def dice_rolls():
24+
values = sorted(choice(range(1,7)) for dice in range(4))[::-1]
25+
return sum(values[:-1])
26+
27+
def modifier(constitution):
28+
return floor((constitution - 10)/2)
29+
```
30+
31+
This approach separates the `ability()` method from a [`static method`][staticmethod] that calculates dice rolls.
32+
`ability()` returns the value of a randomly chosen character ability using [`random.choice`][random-choice] but does not roll dice or calculate values.
33+
Instead, `dice_rolls()` handles the rolls/values using [`random.choice`][random-choice] for selection.
34+
35+
The argument for this is that the logic/functionality of rolling dice 4 times and summing the top three values is not really related to a DnD character or their abilities - it is independent and likely useful across a wider scope than just the character class.
36+
However, it might be tidier to include it in the character class, rather than "clutter" the program or module with an additional stand-alone function.
37+
Declaring `dice_rolls()` as a static method allows other callers to use the function with or without instantiating a new `Character` object.
38+
It also makes it cleaner to maintain, should the method or number of the dice rolls change.
39+
40+
`dice_rolls()` is then called in `__init__()` to populate the listed-out character attributes.
41+
Note that it needs to be called with the class name: `Character.dice_rolls()`.
42+
`self.hitpoints` then calls the second stand-alone `modifier()` function, adding it to 10 for the character's `hitpoints` attribute.
43+
`modifieer()` in this example uses [`math.floor`][math-floor] for calculating the `hitpoints` value.
44+
45+
[math-floor]: https://docs.python.org/3/library/math.html#math.floor
46+
[random-choice]: https://docs.python.org/3/library/random.html#random.choice
47+
[staticmethod]: https://docs.python.org/3/library/functions.html#staticmethod
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
...
2+
def ability(self):
3+
return choice([*vars(self).values()])
4+
5+
@staticmethod
6+
def dice_roll():
7+
values = sample(range(1, 7), 4)
8+
return sum(sorted(values, reverse=True)[:-1])

0 commit comments

Comments
 (0)