Leo Marco Corpuz18,755 Points
Dice roll challenge part 2
I’m not sure if my code even makes sense. I’ve checked to see if anyone else was stuck on this challenge and I’ve read that the Hand.roll(2) is a class method. For the init function, are we just assigning two D20 class dice? I’m not sure where the randint() function comes in for this challenge.
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): super().__init__(sides=20)
import random class Hand(list): def __init__(self, size=None, die_class): for _ in range(size): self.append(die_class) self.sort() @property def total(self): return sum(self) @classmethod def roll(cls): return cls(size=2,die_class=D20)
Louise St. GermainTreehouse Moderator 15,568 Points
You're definitely not the only one to have struggled with this challenge, so I think it's normal. Kudos to you for sticking with it - you'll figure it out!
There are a few small problems with your code that are preventing it from working the way you want, so I'll go through those and hopefully you can move forward from there. Here's your original code. I added comments with numbers so it will be easier to reference the lines to talk about below.
import random # 1 class Hand(list): def __init__(self, size=None, die_class): # 2 # 3 for _ in range(size): self.append(die_class) # 4 self.sort() # 5 @property def total(self): return sum(self) @classmethod def roll(cls): # 6 return cls(size=2,die_class=D20) # 7
# 1 - Import
You mentioned that you're not sure where randint() fits in to this challenge, and basically, you don't need to worry about it. It's already there in dice.py to pick a random # from 1 to 6 when "rolling" the dice. It otherwise isn't used in this program. So in Hands.py, you don't need to import random.
However, you do need to import that D20 class you created in the other file. If you don't connect dice.py to Hands.py somehow, Hands.py has no clue that dice.py is a related file, so it will create an error if you try to reference D20 without importing it. So remove the import random line and replace it with:
from dice import D20
That way, you are importing the class you're going to need later, and Python will know where to find it. ("dice" is the name of the file containing your D20 definition, without the ".py" extension.)
# 2 - initializing Hand
This is pretty close to the right code. However, die_class will need a default value here, like you did with size right before it. Since the challenge explicitly says it should be D20 die, and that's the class you imported, you might as well default it to D20!
#3 - Initialize a List
In this challenge, the Hand class extends the list built-in type. Or in other words, a Hand is a special kind of list: we start with a plain list and give it some extras (in our case, a few rolled D20s). So the first thing we need to do inside our Hand initialization method is to create the list that we're going to add stuff to! If we don't have this empty list, we can create dice, but they will just be floating around - there's no list to append them to.
To create the list, this is where super() comes in. super() refers to the parent class (in this case, list), and we can use its own built-in initialization function:
# Make an empty list super().__init__()
#4 - Making a D20 die
The idea of this line is to create a die of type die_class, and append it to the (now initialized) list. You definitely have the right idea! However, to actually make the die, you need to call its init function through ().
for _ in range(size): # Add () after die_class to actually make something happen self.append(die_class())
This works because die_class contains a reference to a class, so when you run the code, Python will realize, hey, die_class is set to D20, so die_class() means D20()... OK, let's create a new D20 instance! Without the parentheses, Python will know you have a class reference but will not know it's supposed to do anything with it.
# 5 - self.sort()
This isn't needed, and won't work as written anyway - your list contains D20 objects, not plain numbers, and Python won't know how you want them sorted. So you can just delete this line!
#6 - roll class method definition
This needs an extra parameter, which Kenneth hints at in the challenge question:
I'm going to use code similar to Hand.roll(2) [...]
This can be confusing: a class method will have one more parameter than what the user is calling it with. When you set up a class method, it's always a given that it will have cls as a parameter, which gets the class reference that comes immediately before the method name. (In this case, Hand). Here's a quick illustration:
# User can call class method with no parameters: Hand.roll() # But the corresponding class method definition already has one parameter: cls @classmethod def roll(cls): # cls refers to Hand, which is from before the "." in the method call # User can call class method with 1 param: Hand.roll(3) # Corresponding class method definition needs 2 parameters: cls + one more def roll(cls, count): # cls is the class Hand # count contains that 1st user param: 3 # If user calls with 2 params: Hand.roll(3, 12) # Corresponding class method definition needs 3 parameters: cls + 2 more def roll(cls, count, something_else): # cls contains Hand class # count contains 3 # something_else contains 12 # etc!
Kenneth says he wants to call Hand.roll with one parameter (e.g., Hand.roll(2)), so it means that the roll class method actually has 2 parameters:
- cls (as always)
- another one to contain how many dice should be in the hand.
@classmethod def roll(cls, size=2): # Can initialize it to whatever... I just made it 2 to match his example. # It will get replaced when the user specifies the # of dice they want.
# 7 - returning the class instance
(Almost done! :-)) Now that we have the number of dice the user wants, we can pass that to the init method, instead of hard-coding 2 dice. Also, if you made D20 the default in the init method(on the line labeled #2), you don't need to pass it as a parameter here. So you can make this change:
@classmethod def roll(cls, size=2): # size will contain the hand size the user specifies. If not # specified, defaults to 2. Can remove die_class param # if you set the default to D20 up in the init method. return cls(size=size)
I know this is a lot, but I hope it was helpful!