giving turns for attacks in mini game (python 2.7) - python

I've been learning python and I was creating a mini-game and did good so far. I need ideas to give each player a turn to attack, First here is the code:
from random import randint
class Dragon:
def __init__(self,name,health):
self.name = name
self.health = health
def kill(self,target):
while target.health >= 0:
if target.health <= 50:
hit = randint(1,20)
target.health -= hit
print '%s hits %s for %s' %(self.name,target.name,hit)
elif target.health <= 0:
print '%s has killed %s' %(self.name,target.name)
else:
hit = randint(1,50)
if hit >= 40:
target.health -= hit
print "%s critically hits %s for %s" %(self.name,target.name,hit)
else:
target.health -= hit
print "%s hits %s for %s" %(self.name,target.name, hit)
def Dead(self):
if self.health <= 0:
print "%s is dead" %self.name
class Hero:
def __init__(self,name,health):
self.name = name
self.health = health
Diet = Dragon('Diet',200)
Pizza = Hero('Pizza', 100)
if __name__ == '__main__':
Diet.kill(Pizza)
I will add more methods to the hero class once I figure out how to give turns to each player and my second problem is that the block that prints out the death of a player is not working, I've tried to nest it under each if function and it worked. But I think I shouldn't repeat the same code over and over and there must be a better way to do it.

Take the kill method out of a while loop, make it so each time the method is called it is just a single attack, rather than going till the other person is dead.
Then stick the entire game in a while loop, with some sentinel value for the end condition, e.g.:
GameIsRunning = True
while GameIsRunning:
#heros action
#enemies action
if EndConditionIsMet():
GameIsRunning = False
At the end of each loop check if the game is over, e.g. if all enemies are dead or the hero is dead, and set GameIsRunning to false.
Edit:
Dead option 1
As is, the Dead method just prints a message. You can't use this for much other than showing the user that so-and-so is dead. But if you want it like this, call it somewhere in either the attackers kill method. Or in the game loop. If you want this to get automatically called when the instance dies, then I would wrap up any damage dealing in a method:
def DealDamage(self, damage):
self.health -= damage
self.Dead()
Then use
def Kill(self, target):
#damage picking logic stays the same
target.DealDamage(damage)
Dead option 2
If you want said Dead method to mean that thing do or don't happen, it ought to have a boolean signature, e.g.:
def IsDead(self):
# Stick in a display message here if you like, or let the game do it
if self.health <= 0:
return True
else:
return False
Then you can let the game decide what to do about the fact that this person is dead. If you want to start adding multiple enemies then (as most games ultimately will), you might consider sticking them in a collection (a list or a dictionary), looping through this to take each enemies' turn if they're still alive, and removing them if they're dead. Then you're end condition can just be
if Hero.IsDead() or len(Enemies) == 0:
return True
This way gives you an easy way to spawn more enemies mid-game as well.

Related

How to reduce the amount of lines needed for this logic?

I have a simple Python game where two players deal some damage to each other in turns until one player has 0 or less than 0 HP. I don't like how much I've repeated myself but I'm unsure how to fix this. I thought about using a list of players but then I'm unsure how to index the next player when it's their turn, without running into the list index out of range error.
If this kind of question is not allowed please let me know and I will remove it.
Below is my game logic to determine when a player has won. I may not have pasted in the exact code I have but it runs how it's expected locally.
def check_win_state(current_player):
if current_player.get_player_health() > 0:
return True
elif current_player.get_player_health() <= 0:
return False
def main():
player1 = player.Player("Me")
player2 = player.Player("You")
while True:
if check_win_state(player1):
take_turn(player1, player2)
else:
print(f"\n{player2.get_player_name()} ({player2.get_player_health()} HP) wins! {player1.get_player_name()} has {player1.get_player_health()} HP left.\n")
break
if check_win_state(player2):
take_turn(player2, player1)
else:
print(f"\n{player1.get_player_name()} ({player1.get_player_health()} HP) wins! {player2.get_player_name()} has {player2.get_player_health()} HP left.\n")
break
The easiest approach to reduce code duplication in situations like these is to use a secondary variable to hold the primary variable. Here, instead of having different code for player1 and player2, we instead have just one code, and use the variables current_player and opposing_player to hold player1 and player2, and swap with every iteration.
def main():
player1 = player.Player("Me")
player2 = player.Player("You")
current_player = player1
opposing_player = player2
while True:
if check_win_state(current_player):
take_turn(current_player, opposing_player)
else:
print(f"\n{opposing_player.get_player_name()} ({opposing_player.get_player_health()} HP) wins! {current_player.get_player_name()} has {current_player.get_player_health()} HP left.\n")
break
current_player, opposing_player = opposing_player, current_player
If you have more than two players, then a more extensible approach might be to have a list of players, and have an index rotate through the list with each iteration to specify which player is the 'current' one.
also, you can simplify check_win_state():
def check_win_state(current_player):
return current_player.get_player_health() > 0
because the check a > b returns a boolean anyway.
You can make the first function short using this logic:-
def check_win_state(current_player):
return current_player.get_player_health() > 0:
And now you can create another function to do the work of inside while loop
def work_in_while(pl, another_pl):
if check_win_state(pl):
take_turn(pl, another_pl)
else:
print(f"\n{another_pl.get_player_name()} ({another_pl.get_player_health()} HP) wins! {pl.get_player_name()} has {pl.get_player_health()} HP left.\n")
return "kill"
And use it like:-
while True:
if work_in_while(player1, player2) == "kill":
break
if work_in_while(player2, player1) == "kill":
break

Call a class function in a self in another function so that the attributes only include one and the variable not included but is self function?

This code is in the main function:
Player and Computer are lists in this format:
player = ["name", 10, 20, 30]
10 is the health, 20 is the strength, and 30 is the dexterity
# Get the character for the user and the computer.
player = character_choices.get_and_remove_character(choice)
computer = character_choices.get_random_character()
# Preparation for the battle
print ("You have picked a " + player.name)
print ("The computer picked " + computer.name)
print ("Let's battle!\n")
# Battle Loop
rnd = 1
while (player.hit_points > 0 and computer.hit_points > 0):
print ("Round: " + str(rnd))
player.attack (computer)
computer.attack (player)
This is the function:
def attack (self, enemy):
self.player = [self]
if self.player[3] == enemy[3]:
I do not know how to call the player variable within the attack function in this format I can not change the attributes and I do not know how to get the player list items to compare to the enemies list items and fight.
self is a variable commonly related to objects in python by convention.
In your case, to make use of self, you should read up on python object oriented programming.
https://realpython.com/python3-object-oriented-programming/
Go through the tutorial in the link and once you are done, you will have a better understanding of what you want to achieve using your code. Have fun!

python global name not defined after transition to classes

I've found a few versions of this question on the site, but none of the answers quite give me an answer I understand (this question is the closest, but the 'already answered' answer seemed to go off in a different direction).
I'm working my way through the learn python the hard way book and have gotten to the point where I'm trying to build a simple combat system for a game. The good news is that it seems to work when I leave it as a stand alone program. The bad news is that it breaks as soon as I try and add it as a class. I can add the full code if it is helpful, but I think the question is essentially related to code that looks like this:
class Room1(Scene):
def kick():
#things happen here
def start():
move = raw_input("> ")
if move == "kick":
kick()
start()
This worked fine when it was just a standalone set of defs, but now that I've added classes it throws up a global name error when move == kick. What am I missing?
Thanks in advance, sorry if there is an obvious answer that I'm missing.
Thanks to everyone for the quick responses! It looks like it may be helpful for me to add the entire code. Just to be clear, this is part of a larger game modeled on example 43 from Learn Python the Hard Way. I very much appreciate the suggestions on improving the structure, but my sense right now is that I want to figure out why this doesn't work with the structure I almost understand before moving on to change more things. Of course, I'm more than willing to accept an answer of "what you are trying to do does not fit in the structure you are trying to use."
When I run the code below as part of a larger structure (in the interest of space I won't paste the entire thing, but the game engine structure is linked to above) I get the error I described. I tried adding 'self.start()' or 'Room1.start()' I get errors that name 'self' or name 'Room1' is not defined.
class Room1(Scene):
gothon_power = 500
move_points = 10
damage = 0
def kick(self):
global gothon_power
global move_points
global damage
damage = randint(10,201)
gothon_power = gothon_power - damage
move_points = move_points - 2
result()
def punch(self):
global gothon_power
global move_points
global damage
damage = randint(1, 101)
gothon_power = gothon_power - damage
move_points = move_points -1
result()
def result(self):
if gothon_power > 0 and move_points > 1:
print "You did %s damage." % damage
print "The Gothon is down to %s health points." % gothon_power
print "You are down to %s move points." % move_points
print "\n"
print "What's your next move?"
move = raw_input("> ")
if move == "kick":
kick()
elif move == "punch":
punch()
else:
print "This isn't going to go anywhere unless you type 'kick' or 'punch'"
print "\n"
result()
elif gothon_power > 0 and move_points == 1:
print "You did %s damage." % damage
print "The Gothon is down to %s health points." % gothon_power
print "You are down to %s move points." % move_points
print "\n"
print "What's your next move? Remember, you only have 1 move point."
move = raw_input("> ")
if move == "kick":
print "You don't have enough move points for a kick."
print "\n"
result()
elif move == "punch":
punch()
else:
print "This isn't going to go anywhere unless you type 'kick' or 'punch'"
print "\n"
result()
elif gothon_power < 1 and move_points > 0:
print "Congratuations, you killed the Gothon!"
return 'room2'
else:
print "The Gothon still has health but you don't have moves."
print "You know what that means."
print "\n"
print "The Gothon killed you."
return 'death'
def start(self):
print "It is time to fight the Gothon"
print "First, let's pause to explain the fighting rules."
print "\n"
print "The Gothon has 500 health points."
print "You have 10 move points."
print "\n"
print "Kicks cost 2 move points and do between 10 and 200 points of damage."
print "Punches cost 1 move opint and do between 1 and 100 points of damage."
print "\n"
print "If you get rid of all 500 Gothon health points before you run out of"
print "move points you win. If you run out of move points before the Gothon"
print "moves out of health points you lose."
print "\n"
print "What's your first move?"
move = raw_input("> ")
if move == "kick":
kick()
elif move == "punch":
punch()
else:
print "This isn't going to go anywhere unless you type 'kick' or 'punch'"
start()
start()
A proper set of methods on a class will look like:
class Room1(Scene):
def kick(self):
#things happen here
def start(self):
move = raw_input("> ")
if move == "kick":
self.kick()
Room1().start()
But you might want to rethink your design a little bit. It really doesn't make sense for a Scene to query for input. You'll have to replicate that code in every room in your game.
Think about this kind of top-level driver for a minute:
game = Game()
starting_room = FrontPorch()
game.move_to(starting_room)
while not game.is_over():
move = raw_input("> ")
cmd, args = move.split(None, 1)
game.current_room.do_command(cmd, args)
Then let each room process the commands that it does specially. At the base level Room class, you can implement commands that are common to most rooms, like "GOTO", "LOOK", "WHERE", etc. Then rooms that allow kicking would override do_command and include logic like you have now.
Here is a presentation I gave at PyCon '06 on writing a text adventure. Skip over the parsing stuff, and go to the part where the game/room/items design is described. You don't have to follow this design verbatim, but you might get some ideas about how your objects and classes will interact. Think about that before actually diving in to write a lot of code.
You can't execute instance methods of a class, without creating an instance. For example, you could instead write:
class Room1(Scene):
def kick(self):
#things happen here
def start(self):
move = raw_input("> ")
if move == "kick":
self.kick()
room = Room1()
room.start()
However, I don't recommend using a class at all in this case. Classes are meant to give a way to represent your own custom objects with state. For example, you could have a Monster class with attributes damage, name, etc.
For this example that you show, it's probably better to just use a function for Room1.
To call kick() as a method you need to use the self.<method Name> syntax and add self as the first argument to it:
def kick(self):
#things happen here
print('kicking')
#call method
self.kick()
or additionally make kick() a static method by calling simply
A.kick()

Python New to Object Oriented Programming

I was wondering how to fix this problem I am having with my first piece of OOP code. The problem lies within the attack method of the Snake class. I have two snakes in the game and am trying to get the snake to attack the other one. At the moment I am using two variables to note which Snake's turn it is, then use this to try and attack the other snake. However this is not working. Anyone have any idea how to solve this? Thank you so much.
class Snake:
hp=100
attack=25
defense=1
def set_name(self, name):
self.name=name
def shed(self):
self.defense=self.defense+1
def attack(self, opposite, current):
opposite.hp=opposite.hp-(current.attack-opposite.defense)
def eat(self):
self.attack=self.attack+5
print(str(self.name) + " eats a rat!")
print(str(self.name) + "'s attack dmg is now " + str(self.attack))
def sleep(self):
print (str(self.name) + " goes to sleep")
self.hp=self.hp+10
if self.hp>100:
self.hp=100
print (str(self.name) + " wakes up with " + str(self.hp) + "hp")
##initialises the snakes
alpha=Snake()
beta=Snake()
## gives the snakes names of the user's choice
alpha_name=raw_input("What would you like to name your snake? ")
alpha.set_name(alpha_name)
beta_name=raw_input("What would you like to name the other snake? ")
beta.set_name(beta_name)
##starts the game
turn=True
while alpha.hp>0 and beta.hp>0:
while turn==True:
opposite="beta"
current="alpha"
action=raw_input("attack, sleep, eat or shed? ")
try:
if action=="attack":
alpha.attack(opposite, current)
if action=="sleep":
alpha.sleep()
if action=="eat":
alpha.eat()
if action=="shed":
alpha.shed()
turn=False
except IOError:
print("Please chose only one action, exaclty how it is typed")
while turn==False:
opposite="alpha"
current="beta"
if beta.hp<15:
beta.sleep()
elif alpha.hp>75:
beta.attack()
else:
index=random.randint(1, 3)
if index==1:
beta.shed()
elif index==2:
beta.eat()
else:
beta.attack(opposite, current)
turn=True
in "attack" you try to access "opposite.hp", but this method is called with a string instead of an object:
opposite="alpha"
current="beta"
=> change this to
opposite=alpha
current=beta
also, there is a field and a method with the same name in the class: attack. I suggest renaming the field to "attackpoints" or something.
additionaly, you call "beta.attack()". you forgot the method arguments there.
I see two problems. The first is you're passing the name of the variable instead of the variable itself.
change this:
while alpha.hp>0 and beta.hp>0:
while turn==True:
opposite="beta"
current="alpha"
action=raw_input("attack, sleep, eat or shed? ")
try:
if action=="attack":
alpha.attack(opposite, current)
to this:
while alpha.hp>0 and beta.hp>0:
while turn==True:
opposite=beta
current=alpha
action=raw_input("attack, sleep, eat or shed? ")
try:
if action=="attack":
alpha.attack(opposite, current)
Additionally, you have the attack field defined twice in the Snake class.
class Snake:
attack=25
def attack(self, opposite, current):
Here's what I came up with after playing with your code:
import random
class Snake:
hp=100
attack_skill=25
defense=1
def set_name(self, name):
self.name=name
def shed(self):
self.defense=self.defense+1
def attack(self, opposite):
opposite.hp = opposite.hp - (self.attack_skill - opposite.defense)
def eat(self):
self.attack_skill += 5
print(str(self.name) + " eats a rat!")
print(str(self.name) + "'s attack dmg is now " + str(self.attack_skill))
def sleep(self):
print (str(self.name) + " goes to sleep")
self.hp=self.hp+10
if self.hp>100:
self.hp=100
print (str(self.name) + " wakes up with " + str(self.hp) + "hp")
##initialises the snakes
alpha=Snake()
beta=Snake()
## gives the snakes names of the user's choice
alpha_name=raw_input("What would you like to name your snake? ")
alpha.set_name(alpha_name)
beta_name=raw_input("What would you like to name the other snake? ")
beta.set_name(beta_name)
##starts the game
turn=True
while alpha.hp>0 and beta.hp>0:
while turn==True:
opposite="beta"
current="alpha"
action=raw_input("attack, sleep, eat or shed? ")
try:
if action=="attack":
alpha.attack(beta)
if action=="sleep":
alpha.sleep()
if action=="eat":
alpha.eat()
if action=="shed":
alpha.shed()
turn=False
except IOError:
print("Please chose only one action, exaclty how it is typed")
while turn==False:
opposite="alpha"
current="beta"
if beta.hp<15:
beta.sleep()
elif alpha.hp>75:
beta.attack(alpha)
else:
index=random.randint(1, 3)
if index==1:
beta.shed()
elif index==2:
beta.eat()
else:
beta.attack(alpha)
turn=True
When you beta attack, you are calling the attack() method without any parameters. I assume you want beta.attack(alpha,beta)
But you could probably refactor the method to only require the opponent as a parameter (since you know who is attacking (it's the object calling the attack method))
def attack(self, opposite):
opposite.hp -= self.attack-opposite.defense

Simple way to decrease values without making a new attribute?

I'm making a program where you're firing a 'blaster', and I have 5 ammo. I'm blasting an alien who has 5 health. At the end I instantiate the player and make him blast 6 times to check that the program works correctly. But the way I've done it makes it so that the amount won't decrease. Is there an easy fix to this, or do I just have to make a new attribute for ammo and health? Here's what I have:
class Player(object):
""" A player in a shooter game. """
def blast(self, enemy, ammo=5):
if ammo>=1:
ammo-=1
print "You have blasted the alien."
print "You have", ammo, "ammunition left."
enemy.die(5)
else:
print "You are out of ammunition!"
class Alien(object):
""" An alien in a shooter game. """
def die(self, health=5):
if health>=1:
health-=1
print "The alien is wounded. He now has", health, "health left."
elif health==0:
health-=1
print "The alien gasps and says, 'Oh, this is it. This is the big one. \n" \
"Yes, it's getting dark now. Tell my 1.6 million larvae that I loved them... \n" \
"Good-bye, cruel universe.'"
else:
print "The alien's corpse sits up momentarily and says, 'No need to blast me, I'm dead already!"
# main
print "\t\tDeath of an Alien\n"
hero = Player()
invader = Alien()
hero.blast(invader)
hero.blast(invader)
hero.blast(invader)
hero.blast(invader)
hero.blast(invader)
hero.blast(invader)
raw_input("\n\nPress the enter key to exit.")
Think about it: the amount of ammunition available is part of a player's state. An object's state is best represented as instance variables of that object. So you should not have ammo as an argument to blast -- it should be self.ammo in that method, initialized to 5 or whatever in the __init__ you forgot to code;-).
It's not a matter of seeking for fancy workarounds to hide and stash that state somewhere else -- it's a matter of doing things in the simplest, most straightforward, most effective way. Why would you ever want anything but such a way?!
You need to keep track of the alien's health. All you're doing now is decrementing the "health" local variable in the Alien.die function.
Here's a little snippet that should help get you going in the right direction:
class Alien(object):
def __init__(self):
self.health = 5
def do_damage(self, amount):
self.health -= amount
Similar tracking required for player's ammo.
I just modify the above program by making 2 attributes named ammo and health.
I think they make program so easy. Try it for various outcomes by changing attributes' initial values.
class Player(object):
""" A player in a shooter game. """
def __init__(self, ammo):
self.ammo = ammo
def blast(self, enemy):
if enemy.health > 0:
if self.ammo > 0:
print "The player has blasted the alien.\n"
print "The player has", self.ammo, "ammunition left."
enemy.die()
elif self.ammo == 0:
print "The player can't blast the alien because he is out of ammunition.\n"
self.ammo -= 1
class Alien(object):
""" An alien in a shooter game. """
def __init__(self, health):
self.health = health
def die(self):
if self.health > 0:
self.health -= 1
print "The alien is wounded. He now has", self.health, "health left.\n"
if self.health == 0:
self.health -= 1
print "The alien gasps and says, 'Oh, this is it. This is the big one. \n"\
"Yes, it's getting dark now. Tell my 1.6 million larvae that I "\
"loved them...\nGood-bye, cruel universe.'\n"
elif self.health < 0:
print "The alien's corpse sits up momentarily and says, 'No need to blast me, I'm dead already!"
print "\t\tDeath of an Alien\n"
hero = Player(6)
invader = Alien(3)
blast = int(raw_input("How many times do you want to blast the alien? "))
for cnt in range(blast - 1):
hero.blast(invader)
Probably you want to do something like this:
class Player:
def __init__(self, ammo=5):
self.ammo=ammo
def blast(self, enemy):
if self.ammo>0:
self.ammo-=1
enemy.die(5)
else:
print "U are out of ammunition!"
You need to use self.health for the alien as well.
In class Player in the method named blast() your references to to ammo are incorrect. You want to refer to self.ammo in all cases within that function/method definition.
Furthermore you should probably (almost certainly) define an __init__ method to set the starting ammunition value to 5 (and perhaps later provide a reload() method to set self.ammo back up to 5 ... and so on.

Categories

Resources