Dice roller - Python OOP - python

I need to make a program where the user should be able to define
How many sides are on the dice
How many times the dice is rolled
How many dice there are
I'm trying to build the basic structure. I have started with the properties sides and rolled which will be in my main class Dice. I'm assuming here that I will always have 1 dice. Then I created a subclass called Dices( trying to make it plural) which will inherit the Dice class members.
However, I'm trying to introduce a new property called number_of_dice which I haven't set up in my main class, and it will take more than 1 dice. When I try to print print(input_more_dice.number_dice()) I get the following error:
in __init__
self.number_of_dice = number_of_dice
NameError: name 'number_of_dice' is not defined
I'm sure I'm not setting this up correctly. Here is my (Updated) code:
import random
# One dice result
class Dice:
sides = 0
rolled = 0
def __init__(self, sides, rolled):
self.sides = sides
self.rolled = rolled
def rolling_output(self):
if self.rolled == 1:
rolled_once = random.randint(0, self.sides)
return rolled_once
else:
list_of_results = [];
for i in range(self.rolled):
rolled_more = random.randint(0,self.sides)
list_of_results.append(rolled_more)
return list_of_results
# More than one Dice
class Dices(Dice):
number_of_dice = 0
def __init__(self, number_of_dice):
self.number_of_dice = number_of_dice
super().__init__(sides= self.sides, rolled= self.rolled, number_of_dice= self.number_of_dice)
def number_dice(self):
return self.number_of_dice
# input_one_dice = Dice(3, 3)
# print(input_one_dice.rolling_output())
input_more_dice = Dices(number_of_dice= 2)
print(input_more_dice.number_dice())
Why is my subclass not accepting a new property?

The number of parameters in the "init" in Dice class is 2(excluding self). But you tried to call the class Dice in Dices using 3(excluding self) parameters. Is it because of That? I am not that much familiar with Object Oriented programming. Also return doesn't work very well under loops.

Related

Inheriting class attributes without declaring attributes or better OOP

I am attempting to construct classes to play out a game of MTG (A card game). I have three relevant classes: MTGGame(...), MTGCard(...), and AbilityList(). An object of MTGGame has several attributes about the player (turn, mana,..., deck).
A player must have a deck of cards to play, so I create a list of MTGCard objects for each player that is a deck, and create an MTGGame object for each from the respective decks. The cards have abilities, and when creating the cards I store abilities as functions/params into each MTGCard. However, I need the abilities to inherit and access methods/attributes from MTGGame and update them, but if I use super().__init__, then I will need to call my deck as a parameter for AbilityList when making MTGCards, which I wouldn't have yet.
Can this be achieved? If not, any suggestions improving my OOP logic to achieve this task?
I am aware that I can do something like this:
class MTGGame():
def __init__(self, deck, turn = 0, mana = 0, lifeTotal = 20, cavalcadeCount = 0, hand = [], board = []):
self.turn = turn
self.mana = mana
self.lifeTotal = lifeTotal
...
def gainLife(self, lifeGained):
self.lifeTotal = self.lifeTotal +lifeGained
def combatPhase(self):
for card in self.board:
card.attackingAbility()
class MTGCard():
def __init__(self, name, CMC, cardType, power, toughness, castedAbility, attackingAbility, activatedAbility, canAttack = False):
....
self.attackingAbility = attackingAbility
Class abilityList():
def healersHawkAbility(self, lifeAmt):
MTGGame.gainLife(lifeAmt)
But this would affect all instances of MTGGame, not the specific MTGGame object this would've been called from. I'd like it to simply update the specific object in question. I'd like to do something like this but I don't know how abilityList methods could access MTGGame attributes/methods ('AbilityList' object has no attribute 'gainLife'):
Class abilityList():
def healersHawkAbility(self, lifeAmt):
#How do I access methods/attributes in MTGGame from here? self?
self.gainLife(lifeAmt)
aL = abilityList()
#One example card:
card1 = MTGCard("Healers Hawk",1,'Creature',1,1, aL.nullAbility(), aL.healersHawkAbility, aL.nullAbility())
whiteDeck = [list of constructed MTGCard() objects, card1, card2,...,cardLast]
player1 = MTGGame(whiteDeck)
...
#Call the ability in a method contained in MTGGame:
player1.combatPhase()
#Would call something like this inside
card.attackingAbility()
#Which may refer to card.healersHawkAbility() since we stored healersHawkAbility() as an attribute for that MTGCard,
#and would declare gainLife(), which refers to self.lifeTotal or player1.lifeTotal in this case.
This is an excellent start and clearly you have already thought a lot of this through. However, you haven't thought through the relationship between the classes.
First thing to note:
MTGGame.gainLife(lifeAmt) is a method call accessed via the class rather than an instance. This means that the self paramter is not actually filled in i.e. you will get an error becuase your method expects 2 arguments but only receive one.
What you perhaps meant to do is the following:
class MTGGame:
lifeTotal = 20 # Notice this is declared as a class variable
def __init__(self, ...):
...
#classmethod
def healersHawkAbility(cls, lifeGained):
cls.lifeTotal = cls.lifeTotal + lifeGained
However, this requires class variables which here defeats the point of having an instance.
Your naming throughout the program should suggest that your classes are a little off.
For instance player1 = MTGGame(). Is player a game? No, of course not. So actually you might want to rename your class MTGGame to Player to make it clear it refers to the player, not the game. A seperate class called MTGGame will probably need to be created to manage the interactions between the players e.g. whose turn it is, the stack holding the cards whilst resolving.
The main focus of your question: how to deal with the cards accessing the game/player object.
Cards should be able to access instances of the player and game classes, and if the player has a is_playing attribute, the card should not have this. The rule of thumb for inheritance is 'is a'. Since card 'is not a' player, it should not inherit from it or MTGGame. Instead, card should be like this for example:
game = RevisedMTGGame()
player1 = Player()
player2 = Player()
class Card:
def __init__(self, name, text, cost):
self.name = name
self.text = text
self.cost = cost
self.owner = None
self.game = None
class Creature(Card):
def __init__(self, name, text, cost, power, toughness):
super().__init__(self, name, text, cost)
self.power = power
self.toughness = toughness
def lifelink(self):
self.owner.heal(self.power) # NOTE: this is NOT how I would implement lifelink, it is an example of how to access the owner
healersHawk = Creature("Healer's Hawk", "Flying, Lifelink", 1, 1, 1)
healersHawk.game = game
healersHawk.owner = player1
You can see from this incomplete example how you can set up your cards easily, even with complex mechanics, and as the base classes have been defined you can avoid repitition of code. You might want to look into the event model in order to implement the lifelink mechanic, as an example. I wish you luck in continuing your game!

Is there a way to fix Name Error due to scope?

I have a function that creates a player object but when referencing the object, I get a NameError. I think it is happening due to local scope but global should fix it...
I just started out OOP and this code is working in the python shell but it is not working in script mode.
endl = lambda a: print("\n"*a)
class Score:
_tie = 0
def __init__(self):
self._name = ""
self._wins = 0
self._loses = 0
def get_name(self):
print
self._name = input().upper()
def inc_score(self, wlt):
if wlt=="w": self._wins += 1
elif wlt=="l": self._loses += 1
elif wlt=="t": _tie += 1
else: raise ValueError("Bad Input")
def player_num(): #Gets number of players
while True:
clear()
endl(10)
print("1 player or 2 players?")
endl(5)
pnum = input('Enter 1 or 2: '.rjust(55))
try:
assert int(pnum) == 1 or int(pnum) == 2
clear()
return int(pnum)
except:
print("\n\nPlease enter 1 or 2.")
def create_player(): #Creates players
global p1
p1 = Score()
yield 0 #stops here if there is only 1 player
global p2
p2 = Score()
def pr_(): #testing object
input(p1._wins)
input(p2._wins)
for i in range(player_num()):
create_player()
input(p1)
input(p1._wins())
pr_()
wherever I reference p1 I should get the required object attributes but I'm getting this error
Traceback (most recent call last):
File "G:/Python/TicTacTwo.py", line 83, in <module>
input(p1)
NameError: name 'p1' is not defined
Your issue is not with global but with the yield in create_player(), which turns the function into a generator.
What you could do:
Actually run through the generator, by executing list(create_player()) (not nice, but works).
But I suggest you re-design your code instead, e.g. by calling the method with the number of players:
def create_player(num): #Creates players
if num >= 1:
global p1
p1 = Score()
if num >= 2:
global p2
p2 = Score()
If you fix this issue, the next issues will be
1) input(p1) will print the string representation of p1 and the input will be lost, you probably want p1.get_name() instead.
2) input(p1._wins()) will raise TypeError: 'int' object is not callable
I will redesign the app to introduce really powerful python constructs that may help you when getting into OOP.
Your objects are going to represent players, then don't call them Score, call them Player.
Using _tie like that makes it a class variable, so the value is shared for all the players. With only two participants this may be true but this will come to hurt you when you try to extend to more players. Keep it as a instance variable.
I am a fan of __slots__. It is a class special variable that tells the instance variables what attributes they can have. This will prevent to insert new attributes by mistake and also improve the memory needed for each instance, you can remove this line and it will work but I suggest you leave it. __slots__ is any kind of iterable. Using tuples as they are inmutable is my recomendation.
Properties are also a really nice feature. They will act as instance attribute but allow you to specify how they behave when you get the value (a = instance.property), assign them a value (instance.property = value), or delete the value (del instance.property). Name seems to be a really nice fit for a property. The getter will just return the value stored in _name, the setter will remove the leading and trailing spaces and will capitalize the first letter of each word, and the deletter will set the default name again.
Using a single function to compute a result is not very descriptive. Let's do it with 3 functions.
The code could look like this:
# DEFAULT_NAME is a contant so that we only have to modify it here if we want another
# default name instead of having to change it in several places
DEFAULT_NAME = "Unknown"
class Player:
# ( and ) are not needed but I'll keep them for clarity
__slots__ = ("_name", "_wins", "_loses", "_ties")
# We give a default name in case none is provided when the instance is built
def __init__(self, name=DEFAULT_NAME):
self._name = name
self._wins = 0
self._loses = 0
self._ties = 0
# This is part of the name property, more specifically the getter and the documentation
#property
def name(self):
""" The name of the player """
return self._name
# This is the setter of the name property, it removes spaces with .strip() and
# capitalizes first letters of each word with .title()
#name.setter
def name(self, name):
self._name = name.strip().title()
# This is the last part, the deleter, that assigns the default name again
#name.deleter
def name(self):
self._name = DEFAULT_NAME
def won(self):
self._wins += 1
def lost(self):
self._loses += 1
def tied(self):
self._ties += 1
Now that's all we need for the player itself. The game should have a different class where the players are created.
class Game:
_min_players = 1
_max_players = 2
def __init__(self, players):
# Check that the number of players is correct
if not(self._min_players <= players <= self._max_players):
raise ValueError("Number of players is invalid")
self._players = []
for i in range(1, players+1):
self._players.append(Player(input("Insert player {}'s name: ".format(i))))
#property
def players(self):
# We return a copy of the list to avoid mutating the inner list
return self._players.copy()
Now the game would be created as follows:
def new_game():
return Game(int(input("How many players? ")))
After that you would create new methods for the game like playing matches that will call the players won, lost or tied method, etc.
I hope that some of the concepts introduced here are useful for you, like properties, slots, delegating object creation to the owner object, etc.

Accessing Classes via Variable

I am attempting to make a small game, and what I will be doing is using classes to dictate the stats of the user's preferred class. I need to make the classes so when it comes time to fight a monster (which will also be in it's own class), I will be able to have their stats callable.
This may be a "big picture" problem, but to avoid writing the code three times (for each class), I only want to call the variable "chosenClass.HP" instead of "Mage.HP" because if I did it that way, I would need to have a bunch of if statements for each class among the story. There must be a better way.
I have worked on this both ways, and I hate having to write
if userChoice is 'Mage':
HP = 150
I have looked around at the self.something, but I don't completely understand how that works. If that is the required or recommended solution, what is the best resource for learning?
print("Welcome to the game")
name = input("Choose your name\n")
print(name)
class Warrior:
HP = 100
ATK = 200
DEF = 0
class Mage:
HP = 100
ATK = 200
DEF = 0
class Rouge:
HP = 150
ATK = 250
DEF = 100
user_class = input("Choose a class between Warrior, Mage, or Rouge\n")
print(user_class)
while(user_class != 'Warrior' and user_class != 'Mage' and user_class != 'Rouge'):
print("You did not choose from the required classes, try again")
user_class = input()
theClass = user_class()
theClass.HP = 1000
My error that I get from this code is:
TypeError: 'str' object is not callable
I understand it's being given a String, but it's a variable. Why is it causing problems?
Any recommendations to make this program better?
user_class returns a string and not an actual Class that you've defined above. This is because input() returns a string and you store that in user_class. In order to create a new instance of the classes you created in your code need to instantiate the class using some sort of logic based on the player's input such as:
if user_class.lower() == 'warrior':
theClass = Warrior()
theClass.HP = 1000
Obviously this wouldn't be the best way but is just an example to show how to instantiate the class.
You need a mapping of strings to classes if you want to create an instance like this.
pc_classes = {'Mage': Mage, 'Warrior': Warrior, 'Rogue': Rogue}
while True:
try:
user_class = input("Enter Mage, Warrior, or Rogue)
if user_class in pc_classes:
break
pc = pc_classes[user_class]()

How do I pass variables around in Python?

I want to make a text-based fighting game, but in order to do so I need to use several functions and pass values around such as damage, weapons, and health.
Please allow this code to be able to pass "weapons" "damage" "p1 n p2" throughout my code. As you can see I have tried using parameters for p1 n p2, but I am a little bit a newbie.
import random
def main():
print("Welcome to fight club!\nYou will be fighting next!\nMake sure you have two people ready to play!")
p1=input("\nEnter player 1's name ")
p2=input("Enter player 2's name ")
print("Time to get your weapons for round one!\n")
round1(p1,p2)
def randomweapons(p1,p2):
weapon=["Stick","Baseball bat","Golf club","Cricket bat","Knife",]
p1weapon=random.choice(weapon)
p2weapon=random.choice(weapon)
print(p1 +" has found a "+p1weapon)
print(p2 +" has found a "+p2weapon)
def randomdamage():
damage=["17","13","10","18","15"]
p1damage=random.choice(damage)
p2damage=random.choice(damage)
def round1(p1,p2):
randomweapons(p1,p2)
def round2():
pass
def round3():
pass
def weaponlocation():
pass
main()
There are a few options.
One is to pass the values as parameters and return values from your various functions. You're already doing this with the names of the two players, which are passed as parameters from main to round1 and from there on to randomweapons. You just need to decide what else needs to be passed around.
When the information needs to flow the other direction (from a called function back to the caller), use return. For instance, you might have randomweapons return the weapons it chose to whatever function calls it (with return p1weapon, p2weapon). You could then save the weapons in the calling function by assigning the function's return value to a variable or multiple variables, using Python's tuple-unpacking syntax: w1, w2 = randomweapons(p1, p2). The calling function could do whatever it wants with those variables from then on (including passing them to other functions).
Another, probably better approach is to use object oriented programming. If your functions are methods defined in some class (e.g. MyGame), you can save various pieces of data as attributes on an instance of the class. The methods get the instance passed in automatically as the first parameter, which is conventionally named self. Here's a somewhat crude example of what that could be like:
class MyGame: # define the class
def play(self): # each method gets an instance passed as "self"
self.p1 = input("Enter player 1's name ") # attributes can be assigned on self
self.p2 = input("Enter player 2's name ")
self.round1()
self.round2()
def random_weapons(self):
weapons = ["Stick", "Baseball bat", "Golf club", "Cricket bat", "Knife"]
self.w1 = random.choice(weapons)
self.w2 = random.choice(weapons)
print(self.p1 + " has found a " + self.w1) # and looked up again in other methods
print(self.p2 + " has found a " + self.w2)
def round1(self):
print("Lets pick weapons for Round 1")
self.random_weapons()
def round2(self):
print("Lets pick weapons for Round 2")
self.random_weapons()
def main():
game = MyGame() # create the instance
game.play() # call the play() method on it, to actually start the game

Python elevator simulation problem

I have a homework assignment that's really baking my noodle. It involves an elevator simulation that takes user inputs for the number of floors and the number of people using the elevator. the people's starting floor and destination floors are random numbers within the floors.
I realize that my code is very sparse and that there's quite a few gaps, but I really don't know where to go from here.
I need help within the building class, such as how to make the run() and output() sections work. any other tips would be greatly appreciated and helpful. Note that i am not looking for someone to do the code for me, but to kind of hold my hand and tell me which way to go. Classes seem to be completely mystifying to me.
import random
floors=raw_input('Please enter the number of floors for the simulation:')
while floors.isalpha() or floors.isspace() or int(floors) <=0:
floors=raw_input('Please re enter a digit for number of floors:')
customers=raw_input('Please enter the number of customers in the building:')
while customers.isalpha() or customers.isspace() or int(customers) <0:
customers=raw_input('Please re enter a digit for number of customers:')
count = 1
class building:
def num_of_floors():
num_of_floors = floors
def customer_list():
customer_list = customers
def run(self):
def output(self):
print elevator.cur_floor
class elevator:
def num_of_floors():
building.num_of_floors
def register_list():
register_list = []
def cur_floor(building):
cur_floor = 1
def direction(self):
if elevator.cur_floor == 1:
direction = up
if elevator.cur_floor == floors:
direction = down
def move(self):
if elevator.direction == up:
cur_floor +=1
if elevator.direction == down:
cur_floor -=1
def register_customer(self, customer):
register_list.append(customer.ID)
def cancel_customer (self, customer):
register_list.remove(customer.ID)
class customer:
def cur_floor(customer):
cur_floor = random.randint(0,int(floors))
def dst_floor(customer):
dst_floor = random.randint(0,int(floors))
while dst_floor == cur_floor:
dst_floor = random.randint(0,int(floors))
def ID():
cust_id = count
count+=1
def cust_dict(cust_id,dst_floor):
cust_dict = {cust_id:dst_floor}
def in_elevator():
in_elevator = 0
if customer.ID in register_list:
in_elevator = 1
def finished():
if customer.ID not in register_list:
pass
You need to understand the self
parameter to all methods.
You need to understand __init__,
the constructor.
You need to understand self.varible
for your member variables.
You need to understand how to setup a
main function.
You need to understand how to
return a value from a function or
method.
You need to understand how to assign to global variables from within a function or method.
Maybe your building class should start like this.
class building:
def __init__(self, floors, customers):
self.num_of_floors = floors
self.customer_list = customers
self.elevator = elevator()
You should definately spend some time on Python Tutorial or Dive into Python.
The first parameter of every method is a reference to the object and is usually called self. You need it to reference instancemembers of an object.
Second, referencing global variables from inside a class is considered a bad idea. You can better pass them to a class via the constructor or parameters.

Categories