I have recently started my end of the year project in computer science (self proposed, also my first year taking it so only have about 4 months of python experience) and decided I wanted to do a simple Pokemon simulator in it.
I've recently run into a roadblock: Let's say the user chooses Pikachu as their starter. I don't want the user to input their health, attack, speed, and moves. Is their a way to create a Pikachu class that already has that data in it?
I tried something like this:
class Pikachu(object):
def __init__(self):
self.data = []
def health(self):
self.health = 22
print self.health
def attack(self):
self.attack = 7
def speed(self):
self.speed = 6
Pikachu.health()
When I do this (in an attempt to check and see if the health is stored and will be returned on demand), it gives the error:
TypeError: unbound method health() must be called with Pikachu instance as first argument (got nothing instead)
I'm confused as to what the error means as well. Usually this would work and you wouldn't need an argument in the parentheses, but in this instance it doesn't.
Is there some different way to create a class and store data in it? Is there some way to just create an object or do something like this:
def Pikachu:
health = 22
attack = 7
speed = 6
it is because your variable names are the same as your method names. and also why don't you initialise everything in the constructor:
class Pikachu(object):
def __init__(self):
self.data = []
self.attack = 7
self.health = 22
self.speed = 6
def get_health(self):
return self.health
def get_attack(self):
return self.attack
def get_speed(self):
return self.speed
if __name__ == '__main__':
p = Pikachu()
print p.get_health()
Related
I am currently developing a short text-based adventure so I can learn how to use Classes within Python. As part of this, I am trying to create a combat system where the player could choose an NPC to attack.
The aim is that the player can enter the name of the NPC and the weapon they want to use. A method in the target's class will then be called, to lose health based on the damage of the weapon.
My current code is below:
class npc:
def __init__(self, name, alliance):
self.name = name
self.alliance = alliance
def loseHealth(self, health, dmg):
self.dmg = dmg
self.health = self.health - dmg
def usePotion(self, health, pType):
if pType == "great":
self.health = min(self.health + 50,self.maxHealth)
elif pType == "normal":
self.health = min(self.health + 25,self.maxHealth)
else:
pass
def attack(self, target, weaponDmg):
if target in npcList:
target.loseHealth(self.health, weaponDmg)
class human(npc):
maxHealth = 100
health = 100
def __init__(self, name, alliance):
super().__init__(name, alliance)
class orc(npc):
maxHealth = 200
health = 200
def __init(self, name, alliance):
super().__init__(name, alliance)
weaponDmg = {'sword':10,'axe':20}
alice = human("alice","good")
bob = orc("bob","evil")
npcList = [alice, bob]
target = input("Enter Target:")
weapon = input("Enter weapon:")
for x in range(3):
alice.attack(target,weaponDmg[weapon]) #using alice temporarily until I have a person class sorted
print(target.health)
The simple and pythonic answer is to use a dict of NPCs keyed by name, the same way you’re already doing it with weapons:
npcs = {‘alice’: alice, ‘bob’: bob}
target = input("Enter Target:")
weapon = input("Enter weapon:")
for x in range(3):
alice.attack(npcs[target], weaponDmg[weapon])
print(target.health)
And if you want to look up the attacking NPC by user-supplied name as well as the attackee, you can do the same thing there:
npcs[attacker].attack(npcs[target], weaponDmg[weapon])
If you really want to do this inside the attack method you can keep passing in target as a name (string) and do this:
if target in npcs:
npcs[target].loseHealth(self.health, weaponDmg)
... but that probably isn’t a very good design. It means you’re sharing a global variable, and your NPC objects all “know” about that global dict and all the NPCs in it, which doesn’t seem like part of their responsibility.
You can make this a little less repetitive by creating the dict with a comprehension:
npcs = {npc.name: npc for npc in (alice, bob)}
... or by just creating them directly in the dict instead of in variables that you’re probably never going to otherwise use:
npcs = {}
npcs[‘alice’] = human("alice","good")
npcs[‘bob’] = orc("bob","evil")
You can call a method on an instance by using getattr, here is an example:
>>> class Test:
... def my_method(self, arg1, arg2):
... print(arg1, arg2)
...
>>> t = Test()
>>> getattr(t, 'my_method')('foo', 'bar')
foo bar
I'm creating a basic game to help understand classes, where by the Player() instance (Ben), owns a number of buildings and units. Each player, such as ben will have various buildings such as a Plane factory or Bot Factory.
class Player:
def __init__(self):
self.money = 200
self.score = 0
print('Created ')
def increase_money(self, amount):
self.money += amount
def decrease_money(self, amount):
self.money -= amount
def check_money(self):
print(self.money)
class Building(Player):
def __init__(self):
self.level = 0
self.upgrade_cost = 100
print('created building')
def upgrade_building(self):
self.level += 1
def check_building_level(self):
print(self.level)
ben = Player()
I have realised that if I call the Building() class to create an instance, it actually creates an building instance that inherit the properties of player (i.e. each building would have its own money).
How would I have each player contain various buildings and units with different properties? For example, Ben and Bill have different buildings. In this case, would I use a single Player() class and then use functions inside of Player()?
First of all, Building inheriting from Player does not make much sense, a is-a relationship between Building and Player seems weird and like a semantic error.
Just don't inherit from Player, i.e.:
class Building(object):
# code for the class
Now for your other question, if you want each Player to be able to aggregate multiple instances of Building objects, you could make each instance of Player have a collection attribute (a list or a set) to store the Building instances.
To achieve this, you can add the line
self.buildings = []
to the __init__ method of Player. Initially, this list will be empty. To add a building for a specific player, append to its buildings attribute, for example:
p = Player()
p.buildings.append(Building())
p.buildings.append(Building())
p.buildings[1].upgrade_building()
The player p now has two buildings, and the second one is upgraded once. This is just a general demo of how you can do it. I can't say much more without more information about the direction you want your program to take.
Your player constructions don't have an attribute to track buildings owned. Your buildings don't have an owner, either. So you need:
class Player:
def __init__(self):
self.money = 200
self.score = 0
self.buildings = []
print('Created ')
def increase_money(self, amount):
self.money += amount
def add_building(self, Building):
#Here's what to call when we construct a new building with the owner
self.buildings.append(Building):
def decrease_money(self, amount):
self.money -= amount
def check_money(self):
print(self.money)
class Building:
def __init__(self, Owner, Type)
#Constructor now requires an Owner and Type:
self.level = 0
self.owner = Owner
self.Type = Type
self.upgrade_cost = 100
print('created building')
Owner.add_building(self) #And now we will add this to the owner's buildings set
def upgrade_building(self):
self.level += 1
def check_building_level(self):
print(self.level)
To create a building, you'll call it like so:
P = player()
B1 = Building(P, "Building type")
and this will add to the player's list of owned buildings. In addition, you can look at each building for an owner ("How many Bot Factories are built?"). You can also look at the player for their buildings.
So this is answer is more for reference purposes - someone who may be trying to do the same sort of thing.
I actually found the best way to do it, and cleanest was to call the Building class from the Player class:
class Player:
def __init__(self):
self.money = 200
self.score = 0
print('Created ')
self.bot_factory = Building('Bot Factory') # [1]
self.vehicle_factory = Building('Vehicle Factory')
def increase_money(self, amount):
self.money += amount
def decrease_money(self, amount):
self.money -= amount
def check_money(self):
print(self.money)
class Building(Player):
def __init__(self):
self.level = 0
self.upgrade_cost = 100
print('created building')
def upgrade_building(self):
self.level += 1
def check_building_level(self):
print(self.level)
ben = Player()
This way, each player, such as Ben has their own versions of the bot_factory and vehicle_factory. I then intend to upgrade the buildings by a call such as ben.building_upgrade('bot','upgrade'), which calls the Building functions from the Player class.
I'm having trouble with understanding classes and the way their scopes work.
This is the code that I have now.
class Player():
def __init__(self):
self.wheatFields = 1 #line 3
self.wheatProduction = self.wheatFields * 10 #line 4
player = Player()
def main(player):
print('Wheat fields: ' +str(player.wheatFields))
print('Production: ' +str(player.wheatProduction))
print('What do you want to do?\n1) Add wheat field')
if input() == '1':
player.wheatFields += 1
Now I know this is wrong, but if someone can explain why I am wrong, but I would like to know why.
On line 3 and 4, they declare the variables wheatFields and wheatProduction. That means that I can use player.wheatFields and player.wheatProduction to call them. On line 11, it is saying to increase the wheatFields by one. This variable successfully changes and saves on the main menu? How come the wheatProduction variable doesn't change if it is equal to wheatFields * 10?
Is it because it's in the initializer and only runs once?
If that is the case, how can I make it to where it updates every time I add a wheat field to my player?
I tried doing this, too, but it said that the variable wasn't defined.
class Player():
def __init__(self):
self.wheatFields = 1
wheatProduction = wheatFields * 10
This tells me that wheatFields is not defined, but it is just in the line above it.
EDIT:
Thank you, dm03514, but I am still having some trouble. What your're saying makes sense, and I don't understand why this isn't working.
class Player():
def __init__(self):
self.wheatFields = 1
self.wheatProduction = self.wheatFields * 10
def wheatProduction(self):
return self.wheatFields * 10
player = Player()
def main(player):
print('Wheat fields: ' +str(player.wheatFields))
print('Production: ' + str(player.wheatProduction))
print('What do you want to do?\n1) Add wheat field')
if input() == '1':
player.wheatFields += 1
while 1:
main(player)
This still causes wheat fields to go up, but when I call player.wheatProduction, it is staying the same for some reason.
it doesn't change because it is only assigned 1 time when your class is initialized
There are a number of ways you can address it. If wheatproduction should always be based on wheatfields multiple you could turn it into a property so it is calculated every time you access it:
class Player():
#property
def wheatProduction(self):
return self.wheatFields * 10
I've made a simple game using pygame and livewires, where a sprite has to avoid falling mushrooms. The number of mushrooms falling at a certain time is meant to increase as the score increases. Here is what I mean:
from livewires import games,color
import random
games.init(screen_width=633,screen_height=479,fps=50)
class Stick_Man(games.Sprite):
def update(self):
self.x=games.mouse.x
if self.left<0:
self.left=0
if self.right>games.screen.width:
self.right=games.screen.width
self.check_collision()
def check_collision(self):
if self.overlapping_sprites:
self.over_message()
def over_message(self):
b=games.Message(value="Game Over", size=100, color=color.red,x=games.screen.width/2,y=games.screen.height/2,lifetime=250,after_death=games.screen.quit)
games.screen.add(b)
class Mushroom(games.Sprite):
score=0
start=200
score_required=100
level=1
total_score=0
speed=1
mushroom=games.load_image("mushroom.jpg")
x_position=random.randrange(640)
#staticmethod
def next_level():
indicate='Level ', + Mushroom.level, ' cleared'
message=games.Message(value=indicate,size=50,color=color.red,x=games.screen.width/2,y=games.screen.height/2, lifetime=150)
games.screen.add(message)
Mushroom().score_required+=50
Mushroom().score-=Mushroom.score_required
Mushroom().start-=150
Mushroom().speed+=5
Mushroom().level+=1
if Mushroom().start==20:
Mushroom().start+=10
def __init__(self):
super(Mushroom,self).__init__(image=Mushroom.mushroom,x=games.mouse.x,y=0)
def update(self):
self.dy=Mushroom.speed
self.check()
self.check2()
def check(self):
if self.bottom==games.screen.height:
self.destroy()
Mushroom.score+=50
Mushroom.total_score+=Mushroom.score
if Mushroom().score==Mushroom.score_required:
self.next_level()
def check2(self):
if self.top==Mushroom.start:
self.duplicate()
def duplicate(self):
new_mush=Mushroom()
games.screen.add(new_mush)
background_image=games.load_image("background.jpg", transparent=False)
games.screen.background=background_image
stickman_image=games.load_image("stickman.png", transparent=True)
stickman=Stick_Man(image=stickman_image,left=1,bottom=480)
games.screen.add(stickman)
games.mouse.is_visible=False
b=Mushroom()
c=Mushroom()
a=Mushroom()
games.screen.add(b)
games.screen.add(a)
games.screen.add(c)
games.screen.event_brab=True
games.screen.mainloop()
The code is pretty self explanatory and whenever one of the mushrooms is equal to start, then a new object is created thus meaning a new mushroom comes in. However, what happens is that code doesn't function properly a second time and the mushrooms don't get faster spawn much faster either. Also, when the game first starts, the minute the first mushroom hits the bottom it says level one cleared, when it should be after two mushrooms. The sprite is just a red mushroom and also a stickman which can be found on g images if you want to simulate.
So my question is how do i make the object's STATS carry on from where it left off whenever another mushroom appears and also display the message at the right time
Your problem is in all of the lines that look like this:
Mushroom().score_required+=50
There are a number of problems here, which all together add up to make this have no useful effect:
Mushroom() creates a new Mushroom instance (which goes away as soon as this line is done).
Assigning (including update-assigning) to an attribute through an instance always creates or updates an instance attribute, even if there was a class attribute of the same name.
The += operator doesn't mutate immutable values like integers in-place (because that would be impossible); a += b is effectively the same as a = a + b.*
So, when you put that together, what you're doing is creating a new value equal to Mushroom.score_required + 50, then assigning that value to a new instance attribute of a temporary instance (which immediately goes away). This has no effect on the class attribute, or on any of the other instances.
You have a related, but different, problem in the lines like this:
x_position=random.randrange(640)
Unless you want all of the mushrooms to have the same x_position, this should not be a class attribute, but an instance attribute, and you're going to run into all kinds of strange problems.
Storing game stats as class attributes of a random class is a strange thing to do. There are ways you could make that work, but there's no good reason to even try. Class attributes are useful for constants that all instances of the class might as well share, but they're not useful as a substitute for global variables.
A better design would be something like this:
class Game(object):
def __init__(self):
self.score = 0
self.start = 200
self.score_required = 100
self.level = 1
self.total_score = 0
def next_level(self):
indicate = 'Level ', + Mushroom.level, ' cleared'
message = games.Message(value=indicate, size=50, color=color.red,
x=games.screen.width/2, y=games.screen.height/2,
lifetime=150)
games.screen.add(message)
self.score_required += 50
self.score -= self.score_required
self.start -= 150
self.speed += 5
self.level += 1
if self.start == 20:
self.start += 10
def update_score(self, n):
game.score += n
game.total_score += game.score
if self.score == self.score_required:
self.next_level()
class Mushroom(games.Sprite):
mushroom=games.load_image("mushroom.jpg")
def __init__(self, game):
self.x_position=random.randrange(640)
self.game = game
super(Mushroom,self).__init__(image=Mushroom.mushroom,x=games.mouse.x,y=0)
def update(self):
self.dy=Mushroom.speed
self.check()
self.check2()
def check(self):
if self.bottom == games.screen.height:
self.destroy()
game.update_score(50)
def check2(self):
if self.top == Mushroom.start:
self.duplicate()
def duplicate(self):
games.screen.add(Mushroom(self.game))
game = Game()
games.screen.add(Mushroom(game))
games.screen.add(Mushroom(game))
games.screen.add(Mushroom(game))
games.screen.event_brab=True
* That's not completely true. In fact, a = a + b is equivalent to a = a.__add__(b), while a += b is equivalent to a = a.__iadd__(b) if such a method exists, falling back to __add__ only if it doesn't. For mutable objects like lists, this makes a big difference, because __iadd__ can change self in-place and then return it, meaning you end up assigning the same object back to a that was already there. But for immutable objects, there's no difference.
I just started learning python and I am hoping you guys can help me comprehend things a little better. If you have ever played a pokemon game for the gameboy you'll understand more as to what I am trying to do. I started off with a text adventure where you do simple stuff, but now I am at the point of pokemon battling eachother. So this is what I am trying to achieve.
Pokemon battle starts
You attack target
Target loses HP and attacks back
First one to 0 hp loses
Of course all of this is printed out.
This is what I have for the battle so far, I am not sure how accurate I am right now. Just really looking to see how close I am to doing this correctly.
class Pokemon(object):
sName = "pidgy"
nAttack = 5
nHealth = 10
nEvasion = 1
def __init__(self, name, atk, hp, evd):
self.sName = name
self.nAttack = atk
self.nHealth = hp
self.nEvasion = evd
def fight(target, self):
target.nHealth - self.nAttack
def battle():
print "A wild appeared"
#pikachu = Pokemon("Pikafaggot", 18, 80, 21)
pidgy = Pokemon("Pidgy", 18, 80, 21)
pidgy.fight(pikachu)
#pikachu.fight(pidgy)
Full code here: http://pastebin.com/ikmRuE5z
I am also looking for advice on how to manage variables; I seem to be having a grocery list of variables at the top and I assume that is not good practice, where should they go?
If I was to have fight as a instance method (which I'm not sure I would), I would probably code it up something like this:
class Pokemon(object):
def __init__(self,name,hp,damage):
self.name = name #pokemon name
self.hp = hp #hit-points of this particular pokemon
self.damage = damage #amount of damage this pokemon does every attack
def fight(self,other):
if(self.hp > 0):
print("%s did %d damage to %s"%(self.name,self.damage,other.name))
print("%s has %d hp left"%(other.name,other.hp))
other.hp -= self.damage
return other.fight(self) #Now the other pokemon fights back!
else:
print("%s wins! (%d hp left)"%(other.name,other.hp))
return other,self #return a tuple (winner,loser)
pikachu=Pokemon('pikachu', 100, 10)
pidgy=Pokemon('pidgy', 200, 12)
winner,loser = pidgy.fight(pikachu)
Of course, this is somewhat boring since the amount of damage does not depend on type of pokemon and isn't randomized in any way ... but hopefully it illustrates the point.
As for your class structure:
class Foo(object):
attr1=1
attr2=2
def __init__(self,attr1,attr2):
self.attr1 = attr1
self.attr2 = attr2
It doesn't really make sense (to me) to declare the class attributes if you're guaranteed to overwrite them in __init__. Just use instance attributes and you should be fine (i.e.):
class Foo(object):
def __init__(self,attr1,attr2):
self.attr1 = attr1
self.attr2 = attr2v
You don't need the variables up the top. You just need them in the init() method.
The fight method should return a value:
def fight(self, target):
target.nHealth -= self.nAttack
return target
You probably want to also check if someone has lost the battle:
def checkWin(myPoke, target):
# Return 1 if myPoke wins, 0 if target wins, -1 if no winner yet.
winner = -1
if myPoke.nHealth == 0:
winner = 0
elif target.nHealth == 0:
winner = 1
return winner
Hope I helped.
I am only going to comment on a few obvious aspects, because a complete code review is beyond the scope of this site (try codereview.stackexchange.com)
Your fight() method isn't saving the results of the subtraction, so nothing is changed. You would need to do something like this:
def fight(target, self):
target.nHealth -= self.nAttack
# check if target is dead now?
I might even recommend not imposing a modification on your target directly. It may be better if you can call an attack(power) on your target, and let it determine how much damage is done. You can then check if the target is dead yet. Ultimately I would think you would have some "dice" object that would determine the outcomes for you.
As for globals... just stop using them. It is a bad habit to have them unless you really have a good reason. Have functions that return results to the caller, which you then make use of:
def func(foo):
return 'bar'
You can however have a module of constants. These are a bunch of values that don't change for the life of the application. They are merely variables that provide common values. You might create a constants.py and have stuff like:
UP = "up"
DOWN = "down"
DEAD = 0
...
... And in your other modules you do:
from constants import *