Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python Object-Oriented Python Dice Roller RPG Roller

Unable to create a roll method in Hand

I am having trouble getting this method to work. It would be great if I could get some pointers on how to solve this issue.

dice.py
import random


class Die:
    def __init__(self, sides=2):
        if sides < 2:
            raise ValueError("Can't have fewer than two sides")
        self.sides = sides
        self.value = random.randint(1, sides)

    def __int__(self):
        return self.value

    def __add__(self, other):
        return int(self) + other

    def __radd__(self, other):
        return self + other

class D20(Die):
    def __init__(self,size=20):
        super().__init__(size)
hands.py
from dice import D20

class Hand(list):
    @property
    def total(self):
        return sum(self)

    def __init__(self, size=0, die_class=None, *args, **kwargs):
        if not die_class:
            raise ValueError("You must provide a die class")
        super().__init__()

        for _ in range(size):
            self.append(die_class())
        self.sort()

    def roll(self,size=2):
        return self.__init__(size,D20())

It should automatically have 20 sides not size

I made the change from size to sides in the dice.py class but I am still unable to get the roll method to work.

Zachary Canter
Zachary Canter
20,530 Points

Yeah man the directions for this one are sort of confusing . Hoping for some help myself. Kenneth Love

Zachary Canter
Zachary Canter
20,530 Points
 class Hand(list):
    @property
    def total(self):
        return sum(self)

    def __init__(self, size=0, die_class=None, *args, **kwargs):
        if not die_class:
            raise ValueError("You must provide a die class")
        super().__init__()

        for _ in range(size):
            self.append(die_class())

    @classmethod
     def roll(cls, size=2):
        return cls(size=size, die_class=D20)

14 Answers

Can anyone provide a method for thinking through this problem? All throughout this course I have had issues with the code challenges because I cannot seem to come up with the methodology to approach these problems based on what I learned in the videos. I usually just de-construct some else's answer and try to understand how they come up with it. This case is no different: I cannot even begin to think about how to approach this!

Ryan Cross
Ryan Cross
5,742 Points

There's gaps for sure. I've found that sometimes I'll get what he means and sometimes no luck. I head to other outside resources like a book or youtube and will get it. On return to the course after I reach understanding I'll rewatch the video and often say, well of course i didn't get it here... there's problems! Nobody's perfect and I imagine that its quite a stretch for someone very advanced to completely see it from the perspective of a stone cold noob.

Assma Al-Adawi This took me a while, but I finally figured it out.

Let's break down the problem. It states: "I'm going to use code similar to Hand.roll(2) and I want to get back an instance of Hand with two D20s rolled in it. I should then be able to call .total on the instance to get the total of the two dice."

The first thing to do is carefully read the problem and restate it to make sure you understand what is being asked. For example, "I need to write a method called roll() which takes a number and creates that many D20 dice. The total() method uses sum() to add up the dice values. Sum() takes an iterable, like a list. So the roll() method will create a list of dice, which makes sense because Hand is a subclass of list." Something like that.

The clue that Kenneth gave was that you're going to call it on Hand (the class), not an instance of Hand. What kind of method do you call directly on the class?

The books.py example from earlier in the course was helpful. Take a look at that and try to solve it before reading further.

class Bookcase:
    def __init(self, books=None):
        self.books = books

    @classmethod
    def create_bookcase(cls, book_list):
        books = []
        for title, author in book_list:
            books.append(Book(title, author))
        return cls(books)

As others already pointed out, it's a class method, so you put the @classmethod decorator above the class declaration. Class methods don't take self, but cls as the first argument, because you're calling it on the class, not an instance (self). Then you would put in the other argument, which is the number of dice you're creating. You can call that size.

Then, for that many (you can call range() on size), you're going to create a D20 die. So you'll need a for loop and inside the loop you'll create a die. How do you create a D20 die?

Since the function needs to return a list, the dice will need to be appended to a list. You can initialize an empty list and then append to it inside the for loop.

Finally, you'll need to return the list of dice and call cls() on the list to convert it into a Hand. (Is that correct Kenneth Love ? I couldn't find the explanation in the Python docs.)

To test the code, you can try:

Hand.roll(2).total
Kenneth Love
STAFF
Kenneth Love
Treehouse Guest Teacher

Read the instructions again. My example code was Hand.roll(2). What isn't being created in the example? What kind of method does roll need to be?

Zachary Canter
Zachary Canter
20,530 Points

Thanks a ton for getting back so quickly. Really stuck!! Can't figure it out :( Could we get another hint?

Kenneth Love
Kenneth Love
Treehouse Guest Teacher

Answer my question :) Talk through this. I'm sure you can get it.

"Read the instructions again" is pretty poor in terms of response when one of the major issues is the instructions being written in a terribly confusing way.

Ankoor Bhagat
Ankoor Bhagat
1,098 Points

One of the worst challenges ever. My solution works on Jupyter Notebook but here I get Bummer! Can't get the length of a 'Hand'. This OOP course sucks! Here is my solution that works on Jupyter Notebook:

from dice import D20

class Hand(list):
    def __init__(self, size=0, die_class=None, *args, **kwargs):
        if not die_class:
            raise ValueError("die_class must be provided.")
        super().__init__()

        for _ in range(size):
            self.append(die_class()) # Creating instances of die_class

        # Sorting
        self.sort()

    @classmethod
    def roll(cls, size, die_class=D20):
        return cls(size=size, die_class=die_class)

    @property
    def total(self):
        return sum(self)

h = Hand.roll(2)
print('Length: ', len(h))
print('Hand: ', h)
print('Total: ', h.total)

>>> Length:  2
>>> Hand:  [14, 18]
>>> Total:  32
keith rezendes
keith rezendes
4,459 Points

Agreed. course was great till this OOP portion. Almost gave up, just want to get this done. Not sure what is it, just too much info?

Taking this now, totally agree. I came into Python already knowing OOP JavaScript and having a decent programming foundation already. This course has managed to confuse me non-stop. Information is presented poorly, little context in terms of real-world usage, unclear explanation, I could go on. I hate to say it because I know Kenneth is a smart and accomplished guy and I have a lot of respect for him, but I think this course could have been done a lot better. I was thinking of repeating the entire course once finished, but instead I am gonna get a good book on Python and read the OOP chapter(s).

Here is my solution, I only post this as reference for people stuck on the challenge. There are other approaches in this thread but they are either too clunky or use concepts I'm not very strong on (classmethods). Happy learning!

'''python

from dice import D20

class Hand(list): @property def total(self): return sum(self)

def __init__(self, size = 0, *args, **kwargs):  //We dont really care about initialisation because the roll function is important here
    super().__init__()  


def roll(self, size):
    for x in range(size):
        self.append(int(D20())) // appending with the value of each dice to the Hand Object

'''

I just used your code and added the following lines:

h = Hand.roll(2) t = h.total

and it worked fine for me. Thanks for your help in resolving this issue Zac.

Paul Bentham
Paul Bentham
24,090 Points

Zachary, in your explanation I think it would be clearer if you set size = 0 in your roll class method... If my understanding is correct if you have 2 it means the default for roll would always be 2 so if you called Hand.roll() you'd always get that answer.

Thanks for plugging away at this though, it had me stuck for a while!

I tried the following code and was unable to get this working:

from dice import D20

class Hand(list): @property def total(self): return sum(self)

def __init__(self, sides=0, die_class=None, *args, **kwargs):
    if not die_class:
        raise ValueError("You must provide a die class")
    super().__init__()

    for _ in range(sides):
        self.append(die_class())

@classmethod
 def roll(cls, sides=2):
    return cls(sides=sides, die_class=D20)

h = Hand.roll(2) t = h.total

Could someone please tell me what I am missing with this code?

Zachary Canter
Zachary Canter
20,530 Points

Ya idk what to tell you man. Works find for me. Here is my entire hands file

from dice import D20

class Hand(list):

    def __init__(self, size=0, die_class=D20):
        super().__init__()
        for _ in range(size):
            self.append(die_class())

    @classmethod
    def roll(cls, size=2):
        return cls(size=size)

    @property
    def total(self):
        return sum(self)

After a crazy amount of frustration, I figured it out.

from dice import D20

class Hand(list):
    def __init__(self, size=0, die_class=D20, *args, **kwargs):
        super().__init__()
        for _ in range(size):
            self.append(die_class())

    @classmethod
    def roll(cls, size=2):
        return cls(size=size)

    @property
    def total(self):
        return sum(self)
Zachary Canter
Zachary Canter
20,530 Points

You were right. I forgot about classmethods. Thank you for all you do here at Treehouse! Your courses are excellent.

Terribly taught course. Need to redo Treehouse.

I thought it was pretty good tbh. I had to research on my own sometimes. But that's how programming works.

To be fair though, the challenges are wack, and sometimes pointless. So what I advise you to do, after every course create a project using the tools that you had learnt from that course.

Here what I read found working thanks to community here, I have some knowledge about Java and OOP but this course not helpfull much.

from dice import D20

class Hand(list):

def __init__(self, size=0, die_class=D20):
    super().__init__()
    for _ in range(size):
        self.append(die_class())

@classmethod
def roll(cls, size=2):
    return cls(size=size)

@property
def total(self):
    return sum(self)
mourad marzouk
mourad marzouk
5,560 Points

Hey guys, not sure who this is still a problem for. But I just looked through all of this and and using the last bit of code I cam up for the answer.

@zachary looked like he had it right.

Only thing I changed is in def roll, I only passes size as is. So like this

def roll(cls, size): return cls(size)

Everything else is the same.

I didn't think of setting the die_class=D20 in the init, I was doing it in the roll with a super like the video,

Also not sure why I didnt use cls as a parameter in the roll().

I'm actually still not sure why that's there.

Christopher Ransom
Christopher Ransom
11,602 Points
from dice import D20
class Hand(list):
    @property
    def total(self):
        return sum(self)
    @classmethod
    def roll(cls, size):
        empty = []
        for i in range(size):
            i = D20()
            empty.append(i)
        return cls(empty)

This is the solution, ursaminor really helped me out with calling cls() on the list at the end. I still dont understand what that does, but the rest of the thinking makes sense to me.

This course is pretty bad IMO. That's why you don't understand what it does. class methods are actually very helpful and you'll use them in many projects in the future. Anyways, check out Real Python. They have one of the best explanations for this concept.

from dice import D20

class Hand(list):
    def __init__(self, size=0, die_class=None, *args, **kwargs):
        self.size = size
        super().__init__()
        for _ in range(size):
            self.append(die_class()) # Creating instances of die_class

    @classmethod
    def roll(cls, size, die_class=D20):
        return cls(size=size, die_class=die_class)

    @property
    def total(self):
        return sum(self)