Python 3: Why does my class' function run twice? - python

I have a class which contains this attack function:
def attack(self, victim):
strike = 0
if victim.strength > self.strength:
strike = 40
else:
strike = 70
successChance = randint(1,100)
if successChance > strike:
self.lives -= 1
return False
else:
victim.lives -= 1
return True
It is only supposed to run once each time a user presses a button, however it runs twice meaning that every button press counts as two. I know the error is in my class function because the error occurs during test runs of the class.
The only code within the class which calls the function is my test function which only runs internally. Yet the problem persists in my GUI code.
This is my class function:
class Player(object):
def __init__(self, name="", lives=DEFAULT_LIVES):
self._name = name
self.lives = lives
self.strength = randint(1,10)
if self._name== "Test":
self.lives = 1000
if self._name== "":
self._name = "John Smith"
def __str__(self):
return (self._name + " Life: " + str(self.lives) + " Strength: " + str(self.strength))
def getLives(self):
return self.lives
def getStrength(self):
self.strength = randint(1,10)
return self.strength
def getName(self):
return self._name
def isAlive(self):
if self.lives <= 0:
return False
return True
def attack(self, victim):
if victim.strength > self.strength:
strike = 40
else:
strike = 70
successChance = randint(1,100)
if successChance > strike:
print(successChance)
self.lives -= 1
return False
else:
print(successChance)
victim.lives -= 1
return True
def test():
player = Player("Tyler")
opponent = Player(choice(opponentList))
while player.isAlive() == True and opponent.isAlive() == True:
print(player)
print(opponent)
player.attack(opponent)
player.isAlive()
opponent.isAlive()
if not player.attack(opponent):
print("You lost")
else:
print("You won")
print("Game Over")
if __name__ == '__main__':
test()

Well if looks like you're actually calling the function twice in test():
#your old code:
while player.isAlive() == True and opponent.isAlive() == True:
print(player)
print(opponent)
player.attack(opponent) #called once here
player.isAlive()
opponent.isAlive()
if not player.attack(opponent):#called 2nd time here
print("You lost")
else:
print("You won")
print("Game Over")
I'd try this instead:
while player.isAlive() and opponent.isAlive():
print(player)
print(opponent)
player_attack_was_successful = player.attack(opponent)
#player.isAlive() #(does this line even do anything?)
#opponent.isAlive()
if player_attack_was_successful:
print("You won")
else:
print("You lost")
print("Game Over")

Related

How do I properly loop through a portion of code using methods in a class?

I am learning about classes in Python and tried to make a simple battle game. This code runs fine and as long as I print devin.battle() multiple times, but I want a while loop so that it will revert back to asking if the user would like to start a battle while also deducting the attack from the hit points.
start_battle = input("Would you like to start a battle? Y/N ===> ")
class People:
max_hit_points = 150
current_hit_points = 150
current_strength = 5
defence = 0
def __init__(self, name, current_hit_points):
self.name = name
self.current_hit_points
self.damage = 50
def battle(self):
if start_battle == "Y":
self.attacked()
elif start_battle == "N":
print("okay, nevermind then")
def attacked(self):
self.current_hit_points -= self.damage
print("You have been attacked")
if self.current_hit_points > 0:
print("Try again newb!")
else:
print("Your HP has reached 0, you are dead")
def __str__(self):
return f"{self. name} has {self.current_hit_points}HP remaining"
devin = People("Devin", 150)
devin.battle()
print(devin)
However, when I add one to the whole thing Python freezes up.
start_battle = input("Would you like to start a battle? Y/N ===> ")
class People:
max_hit_points = 150
current_hit_points = 150
current_strength = 5
defence = 0
def __init__(self, name, current_hit_points):
self.name = name
self.current_hit_points
self.damage = 50
while current_hit_points > 0:
def battle(self):
if start_battle == "Y":
self.attacked()
elif start_battle == "N":
print("okay, nevermind then")
def attacked(self):
self.current_hit_points -= self.damage
print("You have been attacked")
if self.current_hit_points > 0:
print("Try again newb!")
else:
print("Your HP has reached 0, you are dead")
def __str__(self):
return f"{self. name} has {self.current_hit_points}HP remaining"
continue
else:
print("You are dead")
devin = People("Devin", 150)
devin.battle()
print(devin)
If anyone can explain to me why its freezing up and how I can properly loop a battle, it would be greatly appreciated.
You have multiple issues here and its not super clear what you are trying to do but here is some direction according to your comment, make sure ot read the # code comments
class People:
max_hit_points = 150
current_hit_points = 150
current_strength = 5
defence = 0
def __init__(self, name, current_hit_points):
self.name = name
# you forgot to actually assign the hit points
self.current_hit_points = current_hit_points
self.damage = 50
def battle(self):
while self.current_hit_points > 0:
# you want to ask the user every round?
start_battle = input("Would you like to start a battle? Y/N ===> ")
if start_battle == "Y":
self.attacked()
# note that the user didnt get to attack back
# maybe print(self) here so the user can see his hp?
elif start_battle == "N":
print("okay, nevermind then")
break
else:
print("You are dead")
def attacked(self):
self.current_hit_points -= self.damage
print("You have been attacked")
if self.current_hit_points > 0:
print("Try again newb!")
else:
print("Your HP has reached 0, you are dead")
def __str__(self):
return f"{self. name} has {self.current_hit_points}HP remaining"
devin = People("Devin", 150)
devin.battle()
print(devin)

Python How to access class function variable

This was a Rock scissors paper game code ,its a humanplayer versus a reflectplayer. Reflectplayer who copy the last time humanplayer move and show the move. I tried to access the move2 from the play_round to ReflectPlayer . But How? Isn't game1.play_round.move2 work?
import random
moves = ['rock', 'paper', 'scissors']
class Player:
def move(self):
return 'rock'
def learn(self, my_move, their_move):
pass
class RandomPlayer:
def move(self):
all = ["rock","scissors","paper"]
ranint = random.randint(0,2)
return all[ranint]
def learn(self, my_move, their_move):
pass
class HumanPlayer:
def move(self):
ans = input("Rock Paper or Scissors?").lower()
return ans
def learn(self,my_move,their_move):
pass
class ReflectPlayer:
def move(self):
i = 1
RSP = ["rock","paper","scissors"]
print(self.move2)
if i == 1:
i += 1
return RSP[random.randint(0,2)]
elif game1.play_round.move2 == RSP[0]:
return RSP[0]
elif game1.play_round.move2 == RSP[1]:
return RSP[1]
elif game1.play_round.move2 == RSP[2]:
return RSP[2]
else:
print("Wrong input !")
pass
def learn(self,my_move,their_move):
pass
def beats(one, two):
return ((one == 'rock' and two == 'scissors') or
(one == 'scissors' and two == 'paper') or
(one == 'paper' and two == 'rock'))
class Game:
def __init__(self, p1, p2):
self.p1 = p1
self.p2 = p2
def play_round(self):
move1 = self.p1.move()
move2 = self.p2.move()
print(f"Player 1: {move1} Player 2: {move2}")
if beats(move1 , move2) == True:
print(f"This Round : Player 1 WIN")
elif beats(move2 , move1) == True:
print(f"This Round : Player 2 WIN")
elif move1 == move2:
print("This Round : TIE")
else:
print("Wrong Input !")
self.p1.learn(move1, move2)
self.p2.learn(move2, move1)
def play_game(self):
print("Game start!")
for round in range(3):
print(f"Round {round}:")
self.play_round()
print("Game over!")
if __name__ == '__main__':
game1 = Game(ReflectPlayer(),HumanPlayer())
game1.play_game()
Consider changing your implementation to pass the last moves played to the move() method. If your player needs to use the previous moves then you can access them easily. If not just discard them. I am assuming that you will have other types of players that can benefit from this.
rsp = ['rock', 'paper', 'scissors']
class ReflectPlayer:
def move(self, lastOpponentMove):
if lastOpponentMove == None:
return rsp[random.randint(0,2)]
elif lastOpponentMove == rsp[0]:
return rsp[0]
elif lastOpponentMove == rsp[1]:
return rsp[1]
elif lastOpponentMove == rsp[2]:
return rsp[2]
else:
print("Wrong input !")
pass
def learn(self,my_move,their_move):
pass
Possible adaption on the Game class:
class Game:
def __init__(self, p1, p2):
self.p1 = p1
self.p2 = p2
self.lastP1Move = None
self.lastP2Move = None
def play_round(self):
move1 = self.p1.move(lastP2Move)
move2 = self.p2.move(lastP1Move)
lastP1Move = move1
lastP2Move = move2
print(f"Player 1: {move1} Player 2: {move2}")
if beats(move1 , move2) == True:
print(f"This Round : Player 1 WIN")
elif beats(move2 , move1) == True:
print(f"This Round : Player 2 WIN")
elif move1 == move2:
print("This Round : TIE")
else:
print("Wrong Input !")
self.p1.learn(move1, move2)
self.p2.learn(move2, move1)
Some notes:
You defined rsp as global in the beginning so you can use that list instead of creating new ones.
The way you implemented the check to see if it was the first play would make it so that i would be 1 everytime. (I changed to see if you are receiving a None last move).
Check your indentation in the play_round method
Hope this helps

How can I get new results from randint while in a loop?

My code thus far:
from random import randint
Dice1 = randint(1,6)
Dice2 = randint(1,6)
Dice3 = randint(1,6)
DiceRoll2 = Dice1 + Dice2
DiceRoll3 = Dice1 + Dice2 + Dice3
class Item(object):
def __init__(self, name, value, desc):
self.name = name
self.value = value
self.desc = desc
sword = Item("Sword", 2, "A regular sword.")
class Monster(object):
def __init__(self, name, health, attack):
self.name = name
self.health = health
self.attack = attack
monster = Monster("Monster", 50, DiceRoll2)
Damage = DiceRoll3 + sword.value
NewHealth = monster.health - Damage
print("You see a monster!")
while True:
action = input("? ").lower().split()
if action[0] == "attack":
print("You swing your", sword.name, "for", Damage, "damage!")
print("The", monster.name, "is now at", NewHealth, "HP!")
elif action[0] == "exit":
break
The intension is that with every time you enter "attack" you get a random result of DiceRoll3 (three random numbers from 1 to 6, that three times), add the value of the sword and substract that from the monster's starting health. This goes well until I enter "attack" a second time, which results in the same damage and the same reduced health being printed instead of using a new value. How can I properly do this?
You need to make the call to randint everytime. Below is one way to do it
def Dice1(): return randint(1,6)
def Dice2(): return randint(1,6)
def Dice3(): return randint(1,6)
This isn't great though because of all the repeated code. Here is a better way
class Dice(object):
def roll_dice(self):
return randint(1,6)
Dice1 = Dice()
Dice2 = Dice()
Dice3 = Dice()
Now in your code where ever you call Dice1, Dice2 or Dice3; instead call Dice1.roll_dice(), Dice2.roll_dice(), Dice3.roll_dice(). This abstract away the dice implementation and you are free to change it later without having to change your code.
If you need have dice with different number of faces, you only need to change your dice class
class Dice(object):
def __init__ (self, num_faces=6):
self.faces = num_faces
def roll_dice(self):
return randint(1,self.faces)
Dice1 = Dice() # 6 faced die
Dice2 = Dice(20) # 20 faced die
Put your dice-rolling into a separate function and calculate Damage and NewHealth inside your loop. (Also, update your Monster's health. :))
from random import randint
def dice_roll(times):
sum = 0
for i in range(times):
sum += randint(1, 6)
return sum
class Item(object):
def __init__(self, name, value, desc):
self.name = name
self.value = value
self.desc = desc
sword = Item("Sword", 2, "A regular sword.")
class Monster(object):
def __init__(self, name, health, attack):
self.name = name
self.health = health
self.attack = attack
monster = Monster("Monster", 50, dice_roll(2))
print("You see a monster!")
while True:
action = input("? ").lower().split()
if action[0] == "attack":
Damage = dice_roll(3) + sword.value
NewHealth = max(0, monster.health - Damage) #prevent a negative health value
monster.health = NewHealth #save the new health value
print("You swing your", sword.name, "for", Damage, "damage!")
print("The", monster.name, "is now at", NewHealth, "HP!")
elif action[0] == "exit":
break
if monster.health < 1:
print("You win!")
break

How to add a while loop counter to this code

I believe that this code is fairly self-explanatory but I am confused as to how to implement a counter.
The code is for a guessing game
from random import randint
computer_guess = randint(0,10)
def endGame(computer_guess):
print("Cheerio")
finalOption = True
import sys
if 10/5 == 2:
sys.exit()
def menu(computer_guess):
finalOption = False
while not finalOption:
print("Welcome to this number guessing game sir")
print("You must see if you can guess my number...")
mainGame()
def mainGame():
anyInput = int(input('Give me your guess'))
if (anyInput == computer_guess):
print ("Well done, you got my number")
finalOption = True
endGame(computer_guess)
elif (computer_guess > anyInput):
print ("You need to go higher")
mainGame()
elif (anyInput > computer_guess):
print ("You need to go lower")
mainGame()
menu(computer_guess)
Apologies for the gaps in the code
try the following pattern
count = 1
while true:
guess = get input...
if guess > answer:
say something...
else if guess < answer:
say something...
else:
exit with count...
count++
I find that a class structure like
class MyGame:
def __init__(self, options):
# set up initial game state
def play(self):
# play one full game;
# return WIN or LOSE when the game is over
def one_round(self):
# play one round;
# return WIN, LOSE if the game is over,
# or CONTINUE to keep playing
makes it easier to think about a game. This becomes something like
from random import randint
LOSE, WIN, CONTINUE = 0, 1, 2
class GuessingGame:
def __init__(self, low=1, high=10, guesses=10):
self.low = low
self.high = high
self.prompt = "\nGive me your guess [{}-{}]: ".format(low, high)
self.turn = 0
self.guesses = guesses
self.target = randint(low, high)
def play(self):
print("Welcome to this number guessing game sir")
print("You must see if you can guess my number...")
while self.turn < self.guesses:
result = self.get_guess()
if result == WIN:
return WIN
# ran out of guesses!
print("Sorry, you lost! My number was {}.".format(self.target))
return LOSE
def get_guess(self):
self.turn += 1
# repeat until you get a valid guess
while True:
try:
num = int(input(self.prompt))
if self.low <= num <= self.high:
break
except ValueError:
print("It has to be an integer!")
# find the result
if num < self.target:
print("You need to go higher!")
return CONTINUE
elif num == self.target:
print("Well done, you guessed the number.")
return WIN
else:
print("You need to go lower!")
return CONTINUE
def main():
results = [0, 0]
while True:
result = GuessingGame().play()
results[result] += 1
print("\nYou have {} losses and {} wins.".format(*results))
yn = input("Do you want to play again [y/n]? ").strip().lower()
if yn in ["n", "no"]:
break
if __name__=="__main__":
main()

want to improve simple python code

i am new to python programming. This is code that i want to improve
# Critter Caretaker
# A virtual pet to care for
class Critter(object):
"""A virtual pet"""
def __init__(self, name, hunger = 0, boredom = 0):
self.name = name
self.hunger = hunger
self.boredom = boredom
def __pass_time(self):
self.hunger += 1
self.boredom += 1
#property
def mood(self):
unhappiness = self.hunger + self.boredom
if unhappiness < 5:
m = "happy"
elif 5 <= unhappiness <= 10:
m = "okay"
elif 11 <= unhappiness <= 15:
m = "frustrated"
else:
m = "mad"
return m
def talk(self):
print("I'm", self.name, "and I feel", self.mood, "now.\n")
self.__pass_time()
def eat(self, food = 4):
print("Brruppp. Thank you.")
self.hunger -= food
if self.hunger < 0:
self.hunger = 0
self.__pass_time()
def play(self, fun = 4):
print("Wheee!")
self.boredom -= fun
if self.boredom < 0:
self.boredom = 0
self.__pass_time()
def __str__(self):
rep = "Attribut value is\n"
rep += str(self.hunger) + "\n" + str(self.boredom) + "\n"
return rep
def main():
crit_name = input("What do you want to name your critter?: ")
crit = Critter(crit_name)
choice = None
while choice != "0":
print \
("""
Critter Caretaker
0 - Quit
1 - Listen to your critter
2 - Feed your critter
3 - Play with your critter
""")
choice = input("Choice: ")
print()
# exit
if choice == "0":
print("Good-bye.")
# listen to your critter
elif choice == "1":
crit.talk()
# feed your critter
elif choice == "2":
crit.eat()
# play with your critter
elif choice == "3":
crit.play()
# secret option
elif choice == "!":
print(crit)
# some unknown choice
else:
print("\nSorry, but", choice, "isn't a valid choice.")
main()
("\n\nPress the enter key to exit.")
You see it is a puppet that show his mood and search of you to feed and play with him. Every time puppet talk, eat or play counter pass_time add 2 level up. Attribute food and fun have always the same number that directly affects mood. I want to make that user can enter amount of food and time that can affect mood of puppet. Why i cant do this
def eat(self, food):
print("Brruppp. Thank you.")
self.hunger -= food
if self.hunger < 0:
self.hunger = 0
self.__pass_time()
def play(self, fun):
print("Wheee!")
self.boredom -= fun
if self.boredom < 0:
self.boredom = 0
self.__pass_time()
and this
choice = input("Choice: ")
print()
# exit
if choice == "0":
print("Good-bye.")
# listen to your critter
elif choice == "1":
crit.talk()
# feed your critter
elif choice == "2":
food = int(input("How much food?: "))
crit.eat()
# play with your critter
elif choice == "3":
fun = int(input("How much time?: "))
crit.play()
# secret option
elif choice == "!":
print(crit)
# some unknown choice
else:
print("\nSorry, but", choice, "isn't a valid choice.")
You seem to be almost there if I understand your question right. Lets use the eat function as an example. You want to pass the food variable to the class function to use. If you want you can also add validation to check if that value entered is a number.
elif choice == "2":
food = int(input("How much food?: "))
crit.eat(food) # pass food in as a parameter

Categories

Resources