I want to have a function in my parent class that when called by child class it will create another instance of that child class. I have an ugly solution but there must be better one. Here is my solution (this is in the function within the parent class):
if type(self) == MountainAsh:
trees.append (MountainAsh(seedlingLocation,0)) #Adds new seedling to trees list
elif type(self) == TeaTree:
trees.append (TeaTree(seedlingLocation,0)) #Adds new seedling to trees list
A little more code for context: (feel free to give other pointers, I am new to this, particularly OOP approaches)
class Tree(): #Tree parent class
def __init__(self, location, age):
self.location = location #tuple (x,y)
self.age = age
self.fertility = .5 #fertility betweeen 0 and 1
self.seedRange = 3 #Range from tree that seed can land
self.shade = 1 #Shade preventing new seedlings
self.lifeSpan =10 #tree dies at this age (no random currently)
def reproduce(self):
if random.uniform(0,1)<self.fertility: #Does it produce a seed?
distance = random.uniform(0,self.seedRange) #random range
angle = random.uniform(0,2*math.pi) #random sirection
#calculation of seed location
seedlingLocation = (self.location[0]+distance*math.cos(angle),self.location[0]+distance*math.sin(angle))
if seedlingLocation[0]>forestSize[0] or seedlingLocation[1]>forestSize[1] or min(seedlingLocation)<0:
return #checking if seed falls outside of forest therefore it doesn't grow
for i in trees:
if math.dist(i.location,seedlingLocation)<i.shade: #Checking if seed is outside of shade of existing tress
return
if type(self) == MountainAsh:
trees.append (MountainAsh(seedlingLocation,0)) #Adds new seedling to trees list
elif type(self) == TeaTree:
trees.append (TeaTree(seedlingLocation,0)) #Adds new seedling to trees list
#set up long lived tree
class MountainAsh(Tree):
def __init__(self, location, age):
super().__init__(location, age)
self.fertility = 0.3 #lower fertility
self.seedRange = 3 #larger
self.shade = 1
self.lifeSpan =15 #longer lived
#set up short lived tree
class TeaTree(Tree):
def __init__(self, location, age):
super().__init__(location, age)
self.fertility = 0.8 #high fertility
self.seedRange = 2 #smaller
self.shade = .5
self.lifeSpan =5 #Short lived
Related
I am creating a Lottery simulator, and am trying to initialize a Child class (called Checker) with a variable called win_ticket. To obtain this winning ticket, I attempt to call a method which itself belongs to a Root class of Checker.
On the creation of Checker() object: like
c = Checker(),
it appears to successfully initialize c.win_ticket and assign it a winning_ticket, but then it immediately "forgets" it. I have gone step by step through debugging and there is a moment where self.win_ticket does have the correct assignment, but then it forgets it!
It is then of Nonetype, but I can then invoke, c.draw_lottery() myself and only then does it successfully "remember" the variable c.win_ticket.
Could someone explain or point to the idea of why an object on creation appears to assign itself a variable on an init() and then forgets it? Thank you in advance.
class Checker(Ticket):
"""
Will check purchased tickets against the winning ticket drawn from root Class MegaMillion
TODO I can't seem to have self.winticket automatically draw and initialize!
"""
def __init__(self, cost=2) -> None:
super(Checker, self).__init__(cost)
# list of tuples (3,1), (0,1) to match with prize_dict
self.ticket_match_list = []
self.winnings = 0
self.win_ticket = self.draw_lottery()
It is trying to obtain the method from its Parent(or is it called grandparent?) class
class MegaMillions():
""""""
def __init__(self, cost=2) -> None:
self.win_ticket = None
self.cost = cost
def draw_numbers(self, times=1):
"""Return a list of numbers [(a, b, c, d, e), f ] """
number_list = []
for i in range(times):
numbers = (sorted(random.sample(list(range(1, 71)), k=5))
), (random.randint(1, 25))
number_list.append(numbers)
return number_list
def draw_lottery(self) -> list:
"""Draws and sets the lottery winning ticket"""
self.win_ticket = self.draw_numbers()
print(f"\n Tonight's winning ticket is ...: {self.win_ticket}")
logger.info(f"win_ticket is: {self.win_ticket}")
class Ticket(MegaMillions):
"""
Models purchasing tickets.
It can buy_tickets()
Can ask_number_tickets requested and buy them.
"""
def __init__(self, cost=2) -> None:
super().__init__(cost)
self.ticket_list = None
self.ticket_req = None
self.total_tickets_purchased = 0
...extra code...
class Checker(Ticket):
"""
Will check purchased tickets against the winning ticket drawn
from root Class MegaMillion
TODO initialize self.net
TODO I can't seem to have self.win_ticket automatically draw and initialize!
"""
def __init__(self, cost=2) -> None:
super(Checker, self).__init__(cost)
# list of tuples (3,1), (0,1) to match with prize_dict
self.ticket_match_list = []
self.winnings = 0
self.win_ticket = self.draw_lottery()
I made this program. What I want to do now, is to make a new method in the class Star_cluster that will count and return all planets in the star cluster that are in danger. In this case, a planet in danger is a planet with a star that has a short lifespan. A star has a short lifespan when it's total mass is more than 9.5 solar masses.
class Planet:
def __init__ (self, a_str1, a_str2, a_float):
self.name = a_str1
self.type = a_str2
self.orbital_period = a_float
class Star:
def __init__ (self, a_float, a_planet_list):
self.total_mass = a_float # in solar masses
self.orbiting_planets = a_planet_list # list of Planet-objects
class Star_cluster:
def __init__ (self):
self.star_list = [] # list of Star-objects
def add(self, Star):
self.star_list.append(Star)
My problem is that I don't get how I should combine the values in the different methods to make one for loop.
For example, I tried to make something like this:
class Star_cluster:
def planets_in_danger (self):
for i in self.orbiting_planets:
if i.total_mass > 9.5:
k = self.orbiting_planets.count(i)
return k
But I can't get this working. I want to understand how I should approach this problem, because I know what to do, but not entirely sure about how to do it the best way.
Especially whether to use self and the fact that there are multiple methods involved and just one for loop, confuses me. Could someone point me in the right direction?
Loop over all the stars in the given cluster. If a star's total mass exceeds 9.5, count the amount of planets that belong to that star to the amount of endangered planets.
def planets_in_danger(self):
in_danger = 0
for star in self.star_list:
if star.total_mass > 9.5:
in_danger += len(star.orbiting_planets)
return in_danger
Or shorter:
def planets_in_danger(self):
return sum(len(star.orbiting_planets) for star in self.star_list if star.total_mass > 9.5)
edit: returning the count of endangered planets and the planets:
def planets_in_danger(self):
in_danger = []
for star in self.star_list:
if star.total_mass > 9.5:
in_danger.extend(star.orbiting_planets)
return (len(in_danger), in_danger)
Call the function planets_in_danger in Star_Cluster
class Planet:
def __init__ (self, a_str1, a_str2, a_float):
self.name = a_str1
self.type = a_str2
self.orbital_period = a_float
class Star:
def __init__ (self, a_float, a_planet_list):
self.total_mass = a_float # in solar masses
self.orbiting_planets = a_planet_list # list of Planet-objects
class Star_cluster:
def __init__ (self):
self.star_list = [] # list of Star-objects
def add(self, Star):
self.star_list.append(Star)
def planets_in_danger(self, stars_list):
result = []
solar_mass = 1.989 * (10**30)
for i in stars_list:
if i.total_mass > 9.5 * solar_mass:
result.append(i)
return result
so I'm trying to create a program but I still have difficulties with classes. The program (which isn't finished of course) will print a random number of Villages. Each village(second Class) will have a random number of Clans(the first Class). Anyway my problem is the Village class. How do I make sure to add the areas and family size into the Village class? How do I insert the counter from the Clan class to the Village class? As you can see when I've randomized a number of Clans the areas and family sizes should add up into the Village class. What should I do? What is wrong with my Village class?
class Clan:
counter = 0
def __init__(self):
r = random.randrange(20, 101)
self.area = r
s = random.randrange(1, 6)
self.familySize = s
Clan.counter += 1
self.counter = Clan.counter
def getArea(self):
return self.area
def getFamilySize(self):
return self.familySize
class Village:
counter = 0
def __init__(self):
self.clan_list = []
for i in range(random.randrange(3, 7)):
self.clan_list += [Clan()]
def getclan_list(self):
return self.clan_list
def getArea(self):
return self.area
def getPopulation(self):
pass
So, you want the village class to calculate how many families are in the village?
families = 0
for clan in self.clan_list
families += clan.getFamilySize()
Since the values of area and population are dependent on the clans in clan_list you should compute these values each time they are needed. The alternative is much more complicated -- having to control how clans are added and removed from the village and how the area and family size of a clan can be changed and having those changes reflected in the village.
Below is an example of how you might compute both village area and population. The first using a getter method, and the second using a more python-esque property.
import random
class Clan:
counter = 0
def __init__(self):
r = random.randrange(20, 101)
self.area = r
s = random.randrange(1, 6)
self.familySize = s
Clan.counter += 1
self.counter = Clan.counter
# removed getters
# Unless your getter or setter is doing something special,
# just access the attribute directly.
class Village:
def __init__(self):
self.clan_list = []
for i in range(random.randrange(3, 7)):
self.clan_list.append(Clan())
# for a village, area is a computed value, so use a getter
def getArea(self):
total_area = 0
for clan in self.clan_list:
total_area += clan.area
return total_area
# the prefered alternative to getters (and setters) are properties
# note that the function is named as if it was an attribute rather than function
#property
def population(self):
# use sum and generator rather than a for loop
return sum(clan.familySize for clan in self.clan_list)
# How to use a village instance
v = Village()
# get area
print("area:", v.getArea())
# get population
print("population:", v.population)
# note how population is accessed as if it was an attribute rather than called
# like a function
I am trying to make a turn based RPG and I am currently working on the turn system.
To keep it simple for now I am trying to order each character/enemy by their speed and then highest speed goes first. Below is the start of my code for the turn system.
An example of what I want to do is read Order[0] (which will be the fasted character/enemy) and find out which character/enemy that relates to. Speed[0] to Speed[3] gives the speed of my 4 characters and enemySpeed[0] to enemySpeed[4] gives the speed of the 5 enemies.
def Turn():
Turn = [int(Speed[0]), int(Speed[1]), int(Speed[2]), int(Speed[3]), int(enemySpeed[0]), int(enemySpeed[1]),int(enemySpeed[2]), int(enemySpeed[3]), int(enemySpeed[4])]
Order = sorted(Turn, key = int, reverse = True)
Edit: Here is some information regarding player stats.
In main():
name = ['Ben','Ellis','Curt','Jen']
HP = [100,100,100,100]
exp = [0,0,0,0]
lvl = [1,1,1,1]
player1 = [name[0],HP[0],exp[0],lvl[0]]
player2 = [name[1],HP[1],exp[1],lvl[1]]
player3 = [name[2],HP[2],exp[2],lvl[2]]
player4 = [name[3],HP[3],exp[3],lvl[3]]
PLAYERS = [player1, player2, player3, player4]
REGION = 'start'
POS = '[x, y]'
ITEMS = None
SIGEVENTS = False
gameData = [PLAYERS, REGION, POS, ITEMS, SIGEVENTS]
Out of main():
def playerStats():
global Attack, Defense, Speed, MaxHP
Attack = [0,0,0,0]
Defense = [0,0,0,0]
Speed = [0,0,0,0]
MaxHP = [0,0,0,0]
for i in range(0,4):
Attack[i] = lvl[i] * 1
Defense[i] = lvl[i] * 2
Speed[i] = lvl[i] * 3
MaxHP[i] = lvl[i] * 4
return Attack, Defense, Speed, MaxHP
Instead of looking at just the speeds, look at your players and enemies, and devise a key to determine what speed goes with what player.
You didn't share any details on how you define players and speeds; I'll use a number here for now:
players_and_enemies = range(8)
fastest = max(players_and_enemies, key=lambda p: int(Speed[i] if i < 4 else enemySpeed[i - 4]))
It may well be that you can simplify this based on your data structures; if you have objects per player and enemy that have a speed attribute, for example, you can access that attribute in the key function instead.
max() returns the highest value by key; no need to sort all speeds if all you need is the fastest driver.
Martijn Pieters already pointed out the most important issues. Try to use objects. Try to remodel the objects of your game and don't manage their attributes seperately.
I will elaborate a bit on this and maybe I can give you so some ideas.
A Character, be it a NPC or a PC, is one object of a class that could look something like this:
class Character:
def __init__ (self, name, xp = 0, level = 1, hp = 4, speed = 3, atk = 1, def_ = 2):
self.name = name
self.xp = xp
self.level = level
self.hp = self.curHp = hp
self.speed = speed
self.atk = atk
self.def_ = def_
self.inventory = []
Now using this class you can instantiate your PCs and NPCs:
npcs = [Character (name) for name in ['Baldur', 'Lord Never', 'Ashyra'] ]
Adding methods to your class lets you interact with it, for example:
class Character:
[....]
def takeDamage (self, dmg):
self.curHp -= min (0, dmg - self.def_)
def gainXP (self, xp):
self.xp += xp
#check for level up
def takeItem (self, item):
self.inventory.append (item)
def useItem (self, item):
item.activate ()
self.inventory.remove (item)
Or in order to get all your characters ordered by their speed, you can use:
charactersBySpeed = sorted (characters, key = -character.speed)
And so further and so on. The idea is really to use object oriented programming for modelling the reality (even the made-up reality of your game) in your code.
Full disclosure, this is part of a homework assignment (though a small snippet, the project itself is a game playing AI).
I have this function built into a tree node class:
def recursive_score_calc(self):
current_score = self.board
for c in self.children:
child_score = c.recursive_score_calc()
if(turn_color == 1):
if(child_score > current_score):
current_score = child_score
else:
if(child_score < current_score):
current_score = child_score
self.recursive_score = current_score
return current_score
On a tree of depth 1 (a root and some children), it hits the Python recursion limit already. The function is designed to use dynamic programming to build a min-max tree from the bottom up. To be honest, I have no idea why this isn't working as intended, but I am fairly new to Python as well.
Good people of Stack Overflow: Why does this code give me a stack overflow?
The Entire Class in question:
from Numeric import *
class TreeNode:
children = []
numChildren = 0
board = zeros([8,8], Int)
turn_color = 0 # signifies NEXT to act
board_score = 0 # tally together board items
recursive_score = 0 # set when the recursive score function is called
def __init__(self, board, turn_color):
self.board = copy.deepcopy(board)
self.turn_color = turn_color
for x in range (0,7):
for y in range (0,7):
self.board_score = self.board_score + self.board[x][y]
def add_child(self, child):
self.children.append(child)
self.numChildren = self.numChildren + 1
def recursive_score_calc(self):
current_score = self.board # if no valid moves, we are the board. no move will make our score worse
for c in self.children:
child_score = c.recursive_score_calc()
if(turn_color == 1):
if(child_score > current_score):
current_score = child_score
else:
if(child_score < current_score):
current_score = child_score
self.recursive_score = current_score
return current_score
The function that interacts with this (Please Note, this is bordering on the edge of what is appropriate to post here, I'll remove this part after I accept an answer): [It didn't turn out to be the critical part anyways]
This bit of your code:
class TreeNode:
children = []
means that every instance of the class shares the same children list. So, in this bit:
def add_child(self, child):
self.children.append(child)
you're appending to the "class-global" list. So, of course, every node is a child of every other node, and disaster is guaranteed.
Fix: change your class to
class TreeNode(object):
numChildren = 0
board = zeros([8,8], Int)
turn_color = 0 # signifies NEXT to act
board_score = 0 # tally together board items
recursive_score = 0 # set when the recursive score function is called
def __init__(self, board, turn_color):
self.children = []
self.board = copy.deepcopy(board)
self.turn_color = turn_color
... etc, etc ...
the rest doesn't need to change to fix this bug (though there may be opportunities to improve it or fix other bugs, I have not inspected it deeply), but failing to assign self.children in __init__ is causing your current bug, and failing to inherit from object (unless you're using Python 3, but I'd hope you would mention this little detail if so;-) is just a bug waiting to happen.
It looks like self.children contains self.
EDIT:
The children property is being initialized to the same array instance for every instance of the TreeNode class.
You need to create a separate array instance for each TreeNode instance by adding self.children = [] to __init__.
The board array has the same problem.