Making a method generate a new number every time it is called? - python

goal: I'm trying to make it so when self.damage is called it generates a random number between 1 and the argument "damage". However this random number is only being picked the first time, and then used throughout the entire code. My current code looks like this:
import random
class PC(object):
def __init__(self, health, damage):
self.health = health
self.damage = random.randint(1,damage)
Player_character = PC(10,5)
Spider = PC(2,3)
print(Player_character.health)
def fightspider(spiderturn):
rounds = 0
while spiderturn == 1 and rounds < 2:
Spider.damage
Player_character.health = Player_character.health - Spider.damage
rounds += 1
print(Spider.damage)
print(Player_character.health)
def fightplayer(playerturn):
if playerturn == 1:
Spider.damage
fightspider(1)
When I say Spider.damage inside of the while loop I want it to select a new random number, currently it is only using the first one and not picking any new ones.
I'm not sure why this is happening or how to fix, can I get any leads on where to go with this?
This is my first time using classes so I'm still only halfway sure on how exactly they work!
Thanks for any help.

The damage is only be called in the initalizer of the class random.randint(1, damage) (in the __init__). I recommend you only initialize the max_damage in your initalizer then create a new function that will generate the damage for example:
import random
class PC(object):
def __init__(self, health, damage):
self.health = health
self.max_damage = max_damage
def get_damage():
return random.randint(1, self.max_damage)
Then instead of Spider.damage you can use Spider.get_damage() to get the damage generated everytime. Hope this makes sense, good luck on your programming endeavors!

You've tagged this question with "python-3.x", so I presume you're using 3.x
You're usingSpider.damage in two different ways:
You're using it as an expression that returns the current value stored in the damage attribute.
You're also expectng it to act as a function call that will somehow call random.randint() and re-assign a new random number to the damage attribute.
The first usage is ok. The second won't work.
I suggest you write a method called set_damage(), that will internally call
random.randint(), and update the damage attribute.

Related

Function vs if-statement: Function is not working, but the code in the function will work when outside a function?

I was working on building a randomized character generator for Pathfinder 3.5 and got stuck.
I am using the Populate_Skills(Skill_String, Draw, Skill_List, Class_Skill): function to populate a randiomized list of skills with their class based points total, class bonus, and point buy. So modelling the action of a player picking skills for their character.
As an example below, Wizards.
I pick Knowledge_Arcana as a skill and spend one of my skill point pool (Calculated by taking my intelligence modifier +2) on it. So that skill now equals my intelligence modifier(+1 in this case), class skill bonus as a wizard (+3), plus the point I spent(+1) for a total of 5.
The problem is while the function prints the correct result of 5, the outstanding variables do not populate with the final total. To continue our example I'd run the function on Knowledge_Arcana, get a +5, and then check the Knowledge_Arcana after the function call and get just +1. Conversely, if I write out the function as just an if statement it works. Example is next to the function for comparison.
Does anyone know why Im getting the different result?
## Creating the lists and breaking into two separate sections
Int_Mod = 1
Skill_Ranks = 3
Rand_Class = 'Wizard'
Knowledge_Arcana = Int_Mod
Knowledge_Dungeoneering = Int_Mod
Wizard_Class_Top_Skills = ["Knowledge_Arcana"]
Wizard_Class_Less_Skills = ["Knowledge_Dungeoneering"]
Class_Skill = 3
Important_Skills_Weighted = .6
Less_Important_Skills_Weighted = .4
Important_Skills_Total_Weighted = round(Skill_Ranks*Important_Skills_Weighted)
Less_Skill_Total_Weighted = round(Skill_Ranks*Less_Important_Skills_Weighted)
Wiz_Draw =['Knowledge_Arcana', 'Knowledge_Dungeoneering']
def Populate_Skills(Skill_String, Draw, Skill_List, Class_Skill):
if Skill_String in Draw:
Skill_List = Skill_List + Class_Skill + Draw.count(Skill_String)
print(Skill_String, Skill_List)
else:
print('Nuts!')
## Function Calls
Populate_Skills('Knowledge_Arcana', Wiz_Draw, Knowledge_Arcana, Class_Skill)
Populate_Skills('Knowledge_Dungeoneering', Wiz_Draw, Knowledge_Dungeoneering, Class_Skill)
print(Knowledge_Arcana,Knowledge_Dungeoneering)
Edited to be a MRE, I believe. Sorry folks, Im new.
You are passing in a reference to a list and expect the function to modify it; but you are reassigning the variable inside the function which creates a local variable, which is then lost when the function is exited. You want to manipulate the same variable which the caller passed in, instead.
def Populate_Skills(Skill_String, Draw, Skill_List, Class_Skill):
if Skill_String in Draw:
Skill_List.extend(Class_Skill + Draw.count(Skill_String))
print(Skill_String, Skill_List)
else:
print('Nuts!')
Alternatively, have the function return the new value, and mandate for the caller to pick it up and assign it to the variable.
def Populate_Skills(Skill_String, Draw, Skill_List, Class_Skill):
if Skill_String in Draw:
Skill_List = Skill_List + Class_Skill + Draw.count(Skill_String)
print(Skill_String, Skill_List)
else:
print('Nuts!')
return Skill_List
Skill_List = Populate_Skills('Knowledge_Arcana', Wiz_Draw, Knowledge_Arcana, Class_Skill)
# etc
You should probably also rename your variables (capital letters should be used for classes and globals; regular Python functions and variables should use snake_case) and avoid using global variables at all. The entire program looks like you should probably look into refactoring it into objects, but that's far beyond the scope of what you are asking.

Can you make a variable in a list and then call it?

My friend and I are making a python RPG game and I’m currently working on weapons. I’ve set up a system that allows you to roll for damage and chance to hit like in dnd. I’m using a list to make the weapons and then using print(exampleWeapon[0]) for the damage and changing the 0 to a 1 for the chance to hit. I’ve tried using a variable so I can print the damage output a lot easier with print(handAxe[dmg]) but I can’t seem to make a variable in the list, is there a way to do this or do have to stick with how I was doing.
Here’s my script
handAxe = [
random.randint(1,6),
random.randint(1,20),
]
print(handAxe[0])
print(handAxe[1])```
You can use a dictionnary for this:
handAxe = {"dmg": random.randint(1, 6), "hit": random.randint(1, 20)}
You would then access them by handAxe["dmg"] and handAxe["hit"] respectively.
However, this means that you would have to redefine this variable each time. Otherwise, even though the value for the damage would be determined randomly when you define handAxe, it would stay at the same value thereafter.
A cleaner way to do it may be to use Object Oriented Programming and in particular properties.
class HandAxe:
#property
def damage(self):
return random.randint(1, 6)
#property
def hit(self):
return random.randint(1, 20)
weapon = HandAxe()
print(weapon.damage) # Prints 2
print(weapon.damage) # Prints 1
print(weapon.hit) # Prints 13
An even cleaner way to do it may be to use static properties, so that you won't have to instantiate the HandAxe class.
You could use dictionaries:
hand_axe = {'damage': random.randint(1, 6),
'hit_chance': random.randint(1, 20)}
print(hand_axe['damage'], hand_axe['hit_chance'])
Or you could use namedtuples:
from collections import namedtuple
weapon = namedtuple('Weapon', ['damage', 'hit_chance'])
hand_axe = weapon(damage=random.randint(1, 6),
hit_chance=random.randint(1, 20))
print(hand_axe.damage, hand_axe.hit_chance)
Or you could write a simple class ...

I need some help starting of this radio program

What I need help on is getting the stations to change to the next station in the list if the user presses 3.
class Radio:
def __init__(self):
self.stations=["STATIC","97.2", "99.6", "101.7", "105.3", "108.5"]
self.stationStart=self.stations[0]
def seekNext(self):
self.stationsStart
It starts at static but I want it to change every single one and then start over again. I tried something like this:
stations=["STATIC","97.2", "99.6", "101.7", "105.3", "108.5"]
a =input("enter 3 to seek next")
while a !="0":
if a =="3":
print(stations[-1])
I only end up getting the last station cannot figure out how to list the rest of the stations.
There are a couple of reasonable ways to do what you want.
The easiest would be to make your class store an index into your list, rather than an list item directly. That way you can increment the index and wrap it around using the % modulus operator:
class Radio:
def __init__(self):
self.stations=["STATIC","97.2", "99.6", "101.7", "105.3", "108.5"]
self.station_index = 0
def seek(self):
print("Currently tuned to", self.stations[self.station_index])
print("Seeking...")
self.station_index = (self.station_index + 1) % len(self.stations)
print("Now tuned to", self.stations[self.station_index])
A "fancier", and possibly more Pythonic way to solve the problem would be to use the cycle generator from the itertools module in the Python standard library. It returns an iterator that yields the values from your list, starting over when it reaches the end. Though you usually only deal with iterators in for loops, it's easy to use the iterator protocol by hand too. In our case, we just want to call next on the iterator to get the next value:
import itertools
class Radio:
def __init__(self):
self.stations = itertools.cycle(["STATIC","97.2", "99.6", "101.7", "105.3", "108.5"])
self.current_station = next(self.stations)
def seek(self):
print("Currently tuned to", self.current_station)
print("Seeking...")
self.current_station = next(self.stations)
print("Now tuned to", self.current_station)
index = 0
if a=="3":
index = (index+1)%6
print(stations[index])
Define a variable in init with the actual position "self.pos = 0", and call this function when you required
def seekNext(self):
if(self.pos == (len(self.stations)-1)):
self.pos = 0
else:
self.pos += 1
print(self.stations[self.pos])

Python - Passing variables between a class function incorrectly?

The program is rather self-explanatory. I've begun playing around with the basics of Python, and am really lost on this one. I am used to C++ and the wonderful ability to pass things by reference. But, in this, the class variable I'm trying to change (Fighter.statHEALTH) won't change, and I've read that it's because integers are immutable and it just creates a new object locally. So how in the world can I make the change apply to the original variable? I've Googled and Googled, but to no avail. I don't want to perform some ugly manuever like making a list and passing it if I don't have to.
#python 3.2.2
# Create a small test project to have combat between two entities. #
# Combat should include 3 different stats: statATK, statDEF, and statHEALTH. #
# The two entities should be of the same class. #
class Fighter:
def __init__(self):
self.statHEALTH = 10
self.statATK = 3
self.statDEF = 3
def attack(self, enemyhealth):
enemyhealth = (enemyhealth - self.statATK)
return enemyhealth
def defend(self):
statDEF += 1
def main():
James = Fighter()
Keaton = Fighter()
while James.statHEALTH > 0:
print("Do you wish to attack or defend?")
print("1. Attack")
print("2. Defend")
choice = input()
if choice == "1":
James.attack(Keaton.statHEALTH)
print("You did", James.statATK, "damage!")
Keaton.attack(James.statHEALTH)
print("Keaton has", Keaton.statHEALTH, "health left.")
print("Keaton did", Keaton.statATK, "damage!")
print("You have", James.statHEALTH, "health left.")
#elif choice == "2":
#James.defend()
#Keaton.attack(James.statHEALTH)
main()
def attack(self, enemyhealth):
enemyhealth = (enemyhealth - self.statATK)
return enemyhealth
This would actually work if you changed your call to be
Keaton.statHEALTH = James.attack(Keaton.statHEALTH)
.. since you return the damage from the attack. Obviously this is ugly; repeating yourself is not good. Instead you can make attack look like:
def attack(self, other):
other.statHEALTH -= self.statATK
And then just do
James.attack(Keaton)
when you call it.
Maybe you could think of it differently. In your example, Fighter.attack() just returns the value of the enemies health after the attack. So really, it should be a method call on the enemy's object. You could add a method that decreases the fighter's health when they get attacked:
def attack(self, enemy):
enemy.getAttacked(self.statATK)
def getAttacked(self, ATK):
self.statHEALTH -= ATK
Try doing:
while James.statHEALTH > 0:
#print statements
if choice == "1":
the_attack = James.attack(Keaton)
Then define your classes as:
class Fighter(object):
def __init__(self):
self.statHEALTH = 10
self.statATK = 3
self.statDEF = 3
def attack(self,target):
attack_details = target.takedamage(self.statATK)
return attack_details
def takedamage(self,dmg):
modified_dmg = dmg-self.statDEF
self.statHEALTH -= modified_dmg
return modified_dmg
This has the added benefit of being easily expandable, e.g. you can add a hit table (for i in random.randint(1,100) if i < 20: #miss; elif 20 <= i < 80: #hit; elif 80<= i: #crit) or resistances for certain elements or add a flag that allows your defender to counterattack in their takedamage function (perhaps calling a new function getcountered to prevent infinite looping).
The problem isn't really that you can't pass things by references in Python. In fact, you always pass values by reference; Python never copies values unless you ask it to.
Or, more accurately, that whole C-based terminology is misleading in Python.
Anyway, when you do this:
James.attack(Keaton.statHEALTH)
This doesn't make a copy of the value in Keaton.statHEALTH, it passes a reference to the exact same value. So, when attack starts, your enemyhealth variable is a name for that value.
And then you do this:
enemyhealth = (enemyhealth - self.statATK)
… the enemyhealth - self.statATK returns a new value, and then you bind that new value to the enemyhealth name. This has no effect on any other names for the old value.
There are two ways to solve this.
First, you don't actually need to mutate anything here. You're already doing return enemyhealth at the end. That means the caller can get the new value that you calculated. So why not just use that?
Keaton.statHEALTH = James.attack(Keaton.statHEALTH)
And then everything works.
Of course in C++, you can mutate integer values. This seems a little silly when you think about it—turning the number 24 into the number 19 would break a lot of math, and probably make the universe stop working, and Python just isn't that powerful.
But you can easily build a type that can be sensibly mutated. In fact, you've already built one: a Fighter holds an integer health value, but can be changed to hold a different one instead. So, you could write this:
def attack(self, enemy):
enemy.health = enemy.health - self.statATK
And then:
James.attack(Keaton)
Just as calling the old method with Keaton.health made enemyhealth into another reference to the same number, calling the new method with Keaton makes enemy into a reference to the same Fighter. If you just reassigned a new value to enemy, that wouldn't have any effect on the old value, the one that Keaton refers to. But if you change the value in-place, obviously Keaton is still referring to that now-changed value.

Calculate score in a pyramid score system

I am trying to calculate gamescores for a bunch over users and I haven't really got it yet. It is a pyramid game where you can invite people, and the people you invite is placed beneth you in the relations tree.
So if i invite X and X invites Y i get kickback from both of them. Let's say 10%^steps...
So from X i get 10% of his score and 1% from Y, and X get 10% from Y.
So to calculate this i was thinking that each "player" had a function that calculated his total score. This function had to be recursive and "know" how far in the tree it was so that it would kick back the right values.
def get_score(player):
if children:
score = player.points
for child in children:
score += child.points*math.pow(.1, get_ancestors(child))
score += get_score(child)
return score
else:
return player.points
But this doesnt work proper, it gives what i believe is the right values in some levels but not in others. So think my function is broken. Anybody got an idea on how to solve this?
I doubt these two lines
score += child.points*math.pow(.1, get_ancestors(child))
score += get_score(child)
this is a simple recursive structure so i think something like below will suffice
score += get_score(child)*.1
and recursive beauty will take care of itself
you also do not need 'if children:' check
so does it help
def get_score(player):
score = player.points
for child in children:
score += get_score(child)*.1
return score
This can have very different implementations, depending on the way the score must be calculating :
Do you need to propagate the result of each gain, in real time ? In that case , you start from the bottom of the pyramid and give the feedback until the top.
Can you afford the result to be calculated at the end of the game for everybody ? In that cas you can just set a method on each player and only call the one at the top.
E.G for the second option
You used a functional approach. While this is valid, I am more into OO so I'll go this way :
class Player(object) :
def __init__(self) :
self.score = 0;
self.children = []
def updateScore(self) :
self.score = self.score + sum(((children.score * 10 / 100) for children in self.children))
class Pyramid(object) :
def __init__(self) :
self.top_child = Player()
def updateScore(self, player = None) :
if player == None :
player = self.top_child
for child in player.children :
self.updateScore(child)
child.updateScore()
You may want to use itertools to make it less CPU and memory intensive.

Categories

Resources