Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Use the `ability()` Method to Generate and Return the Dice Rolls


```python
from random import sample

class Character:
def __init__(self):
self.strength = self.ability()
self.dexterity = self.ability()
self.constitution = self.ability()
self.intelligence = self.ability()
self.wisdom = self.ability()
self.charisma = self.ability()

self.hitpoints = 10 + modifier(self.constitution)

def ability(self):
values = sample(range(1, 7), 4)
return sum(values) - min(values)

def modifier(constitution):
return (constitution - 10)//2
```


This approach uses a single `ability()` method to calculate the dice rolls and return an ability value.
`ability()` is then called in `__init__()` to populate the listed-out character attributes.
`self.hitpoints` calls the stand-alone `modifier()` function, adding it to 10 for the character's hitpoints attribute.

This approach is valid and passes all the tests.
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.


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?
What other functionality should this class hold?
Should you separate dice rolls from ability values?
Is the class better as a [dataclass][dataclass], with dice roll as a utility function?

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.


[dataclass]: https://docs.python.org/3/library/dataclasses.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
...
def __init__(self):
for ability_type in self.ABILITIES:
setattr(self, ability_type, self.ability())

def ability(self):
values = sample(range(1, 7), 4)
return sum(values) - min(values)
37 changes: 37 additions & 0 deletions exercises/practice/dnd-character/.approaches/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"introduction": {
"authors": ["colinleach",
"BethanyG"],
"contributors": []
},
"approaches": [
{
"uuid": "952835d1-e9d1-4dc3-b2c2-aad703e8db2e",
"slug": "ability-method",
"title": "Ability Method",
"blurb": "Use one method to generate dice rolls and return ability values.",
"authors": ["bethanyg"]
},
{
"uuid": "5022c80c-8ca1-45e4-aa5f-d91bb3f53441",
"slug": "dice-roll-static-method",
"title": "Dice Roll Static Method",
"blurb": "Use a separate static method to conduct dice rolls.",
"authors": ["bethanyg"]
},
{
"uuid": "98af6c1d-5ab4-476d-9041-30b8f55e13eb",
"slug": "stand-alone-dice-roll-function",
"title": "Stand-alone Dice Roll Function",
"blurb": "Create a dice roll function outside the Character class.",
"authors": ["bethanyg"]
},
{
"uuid": "5dd9d9b0-bfa5-43b2-8e95-787425349fc4",
"slug": "loop-and-setattr-in-init",
"title": "Loop and setattr in init",
"blurb": "Use a tuple of attributes, a loop, and setattr in init to assign ability values.",
"authors": ["bethanyg"]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Move Dice Rolls Into a Static Method Separate from the `ability` Method


```python
from math import floor
from random import choice

class Character:
def __init__(self):
self.strength = Character.dice_rolls()
self.dexterity = Character.dice_rolls()
self.constitution = Character.dice_rolls()
self.intelligence = Character.dice_rolls()
self.wisdom = Character.dice_rolls()
self.charisma = Character.dice_rolls()

self.hitpoints = 10 + modifier(self.constitution)

def ability(self):
return choice([*vars(self).values()])

@staticmethod
def dice_rolls():
values = sorted(choice(range(1,7)) for dice in range(4))[::-1]
return sum(values[:-1])

def modifier(constitution):
return floor((constitution - 10)/2)
```

This approach separates the `ability()` method from a [`static method`][staticmethod] that calculates dice rolls.
`ability()` returns the value of a randomly chosen character ability using [`random.choice`][random-choice] but does not roll dice or calculate values.
Instead, `dice_rolls()` handles the rolls/values using [`random.choice`][random-choice] for selection.

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.
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.
Declaring `dice_rolls()` as a static method allows other callers to use the function with or without instantiating a new `Character` object.
It also makes it cleaner to maintain, should the method or number of the dice rolls change.

`dice_rolls()` is then called in `__init__()` to populate the listed-out character attributes.
Note that it needs to be called with the class name: `Character.dice_rolls()`.
`self.hitpoints` then calls the second stand-alone `modifier()` function, adding it to 10 for the character's `hitpoints` attribute.
`modifieer()` in this example uses [`math.floor`][math-floor] for calculating the `hitpoints` value.

[math-floor]: https://docs.python.org/3/library/math.html#math.floor
[random-choice]: https://docs.python.org/3/library/random.html#random.choice
[staticmethod]: https://docs.python.org/3/library/functions.html#staticmethod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
...
def ability(self):
return choice([*vars(self).values()])

@staticmethod
def dice_roll():
values = sample(range(1, 7), 4)
return sum(sorted(values, reverse=True)[:-1])
Loading
Loading