Python3: help - script over complicated - python

Ive just started python yesterday and tried to make a randomized fighting game for bets with friends. I think this script could be alot shorter, but i dont know how. Ive just googled everything. plr1_hit and plr2_hit are mostly the same. Is there a way to make it just one thing? Like i said, im fairly new to python. Any help would be very appreciated. Thanks in advance!
import random
import time
dmg = random.randrange(1, 40)
plr2_health = 100
plr1_health = 100
plr2_live = True
plr1_live = True
def plr1_hit():
global plr1_health
global plr2_health
global plr2_live
dmg = random.randrange(1, 40)
plr2_health = plr2_health - dmg
if plr2_health < 1:
plr2_live = False
print("-----------------")
print("PLAYER 1 LAYS DOWN THE FINAL PUNCH")
print("Player 2 had died. Player 1 wins!")
else:
print("-----------------")
if dmg < 30:
if dmg < 15:
if dmg < 5:
hit = "pokes"
else:
hit = "slaps"
else:
hit = "punches"
else:
hit = "DESTROYS"
print("player 1 " + hit + " player 2")
print("--")
print("--")
print("Damage done:")
print(dmg)
print("--")
print("Player 1 health:")
print(plr1_health)
print("Player 2 health:")
print(plr2_health)
print("-----------------")
def plr2_hit():
global plr1_health
global plr2_health
global plr1_live
dmg = random.randrange(1, 40)
plr1_health = plr1_health - dmg
if plr1_health < 1:
plr1_live = False
print("-----------------")
print("PLAYER 2 LAYS DOWN THE FINAL PUNCH")
print("Player 1 had died. Player 2 wins!")
else:
print("-----------------")
if dmg < 30:
if dmg < 15:
if dmg < 5:
hit = "pokes"
else:
hit = "slaps"
else:
hit = "punches"
else:
hit = "DESTROYS"
print("player 2 " + hit + " player 1")
print("--")
print("--")
print("Damage done:")
print(dmg)
print("--")
print("Player 1 health:")
print(plr1_health)
print("Player 2 health:")
print(plr2_health)
print("-----------------")
while plr1_live == True and plr2_live == True:
plr1_hit()
time.sleep(5)
if plr1_live == True:
plr2_hit()
time.sleep(5)

This seems like a pretty natural place to use a class for your players. If you've not learned anything about Object Oriented Programming yet, this may seem complicated, but it's not really all that bad (though you might want to read a tutorial). A class is a convenient way to store some data (like the health of a player) along with some methods, which are functions that operate on that data.
Here's how I'd modify your plrX_hit functions so they were implemented by a single hit method of a Player class:
class Player:
def __init__(self, name): # the __init__ method sets up an instance
self.name = name
self.health = 100 # save the player stats as attributes, rather than globals
self.alive = True
def hit(self, hitby): # this works like your functions, self gets hit by hitby
dmg = random.randrange(1, 40)
self.health -= dmg
if self.health < 1:
self.alive = False
print("-----------------")
print(f"{hitby.name} LAYS DOWN THE FINAL PUNCH") # use the name variables
print(f"{self.name} has died. {hitby.name} wins!") # rather than hardcoding
else:
print("-----------------")
if dmg < 30:
if dmg < 15:
if dmg < 5:
hit_descr = "pokes"
else:
hit_descr = "slaps"
else:
hit_descr = "punches"
else:
hit_descr = "DESTROYS"
print(f"{hitby.name} {hit_desc} {self.name}")
print("--")
print("--")
print("Damage done:")
print(dmg)
print("--")
# the last part of the function, printing the health of both players,
# is moved out of the method, because it was always in a specific order
# (not related to who hit who).
player1 = Player("Player 1") # here we create two instances of the Player class
player2 = Player("Player 2")
while player1.alive and player2.alive: # you never need == True, just use the value
player1.hit(player2)
print("Player 1 health:")
print(player1.health)
print("Player 2 health:")
print(player2.health)
print("-----------------")
time.sleep(5)
if player1.alive:
player2.hit(player1)
print("Player 1 health:")
print(player1.health)
print("Player 2 health:")
print(player2.health)
print("-----------------")
time.sleep(5)

There are many possible ways to write this shorter and a bit more elegant.
You said yourself, that the two methods are mostly the same, so that's a clear sign, that you could combine them. All you need for a general hit_func() is a parameter you send when the function is called. With this parameter you could specify which player is hit.
And when you call the function, you do this alternating with player1, then with player2, etc.
Maybe something like this:
activePlayer = player1
while plr1_live == True and plr2_live == True:
hit(activePlayer)
time.sleep(5)
# change the player that is hit
if activeplayer == player1:
activeplayer = player2
else:
activeplayer = player1
In the hit function you then could use the passed in activeplayer to decide which health to reduce and what messages to print, etc.
Even more convenient you could write it with a player class and two objects for the players. Then every player object could have a hit method and the necessary variables like health, alive, etc. With this approach you could get rid of your global variables easily, which usually should be avoided.
class Player:
def __init__(self, health):
self.health = health
self.alive = True
def hit(self):
#generate random damage and reduce the health of self accordingly, etc
Then you can create two player instances and hit them as before in a loop like this:
player1 = Player(100)
player2 = Player(100)
while player1.alive and player2.alive:
#call the hit method alternating for both players
I hope that helps and gives you some ideas for improvements and new things to learn.

Related

local variable assigned in enclosing line referenced before assignment

I have this bit of code
health = 12
manager_health = 10
def manager_boss_fight_used_for_backup():
sleep(1)
print("manager health is ",manager_health)
print("your health is ",health)
sleep(1)
print("the boss gets first attack")
manager_boss_damage = random.randint(1,8)
print(manager_boss_damage)
print(health)
health = health - manager_boss_damage
sleep(1)
print("the boss did",manager_boss_damage,"damage")
sleep(1)
print("your health is now",health)
if health <= 0:
sleep(1)
print("you have died")
sleep(1)
print("better luck next time")
exit()
sleep(1)
print("your turn to attack")
sleep(1)
heal_or_attack = input("Do you wish to heal or attack?(1/2)")
if heal_or_attack == "1":
healing = random.randint(1,7)
print("you have healed by",healing)
health = health + healing
manager_boss_fight_used_for_backup()
if heal_or_attack == "2":
print("you attack the boss")
sleep(1)
attack_damage = random.randint(1,6)
print("You did",attack_damage,"damage")
manager_health = manager_health - attack_damage
if manager_health <= 0:
sleep(1)
print("You have killed the boss")
sleep(1)
if manager_health > 0:
manager_boss_fight_used_for_backup()
and in the parts of code where for example health = health - manager_boss_damage it will error out. I have fiddled with global variables and all that but I cant get it to work so I came here. Any answers appreciated!
As probably everyone will tell you, using global variables is pretty bad. Why don't you use OOP instead? You can create a class Manager: and a class Player: with health as property and damage and take_damage as a method of the class, for example:
class Manager:
def __init__(self):
self.health = 10
def attack(self):
damage = ramdom.randint(1,8)
return damage
def take_damage(self, damage):
self.health -= damage
if (self.health <= 0):
self.health = 0
return self.health
then the Player class would be more or less the same:
class Player:
def __init__(self):
self.health = 12
def attack(self):
damage = ramdom.randint(1,8)
return damage
def take_damage(self, damage):
self.health -= damage
if (self.health <= 0):
self.health = 0
return self.health
def heal(self, amount):
self.health += amount
return self.health
Ideally of course, you can create one class called "Actor" or "Enemy" and derive those from there, but let's stick with this for now:
# Instantiate the classes like this:
manager = Manager()
player = Player()
# and then just invoke the methods from the classes:
sleep(1)
print("manager health is ",manager.health)
print("your health is ",player.health)
sleep(1)
print("the boss gets first attack")
damage = manager.Attack()
print("You were hit by {} points".format(damage))
player.take_damage(damage)
print("Your Health is {}".format(player.health))
Etc.
Whenever you make an assignment to health inside the manager_fight_used_for_backup() function, Python attempts to assign a new variable called health by default.
The fastest fix is to use the global keyword at the beginning of the function body, to make it clear that you're referring to the health variable outside of the function:
global health
This will tell Python that you're referring to the health variable outside of the function. However, the best solution is to refactor this code so that functions don't modify a shared global state.

Problem developing a turn-based battle system

So, I have recently got into coding and I am currently developing a small turn-based RPG, but I have encountered some real issue with the battle system. I am still learning and I never thought about asking questions here. Anyway, after getting many things done correctly, I have encountered this issue where using the defend command rises the player hp for some reason. Here is the code:
import random
import sys
import os
class Entity():
def __init__(self, hp, atk, dif):
self.hp = hp
self.atk = atk
self.dif = dif
class Battle():
def Attack(self, attacker, defender):
damage = attacker.atk - defender.dif
defender.hp -= damage
def Combat(self, player, enemy):
turns = []
while True:
while len(turns) < 5:
turn = random.randint(1, 2)
if turn == 1:
turns.append("player")
else:
turns.append("enemy")
for current_turn in turns:
print(player.hp, enemy.hp)
if current_turn == "player":
print(f"TURNS: \n{current_turn}\n{turns[turns.index(current_turn)+ 1:]}")
choice = input("1. Attack\n2. Defend\n")
if choice == "1":
self.Attack(player, enemy)
player.dif = 20
elif choice == "2":
player.dif *= 2
else:
print("Lost your chance!")
elif current_turn == "enemy":
print("Enemy turn!")
print(f"Next turns are: {turns}")
enemy_choice = random.randint(1, 2)
if enemy_choice == 2:
print("He attacks you!")
self.Attack(enemy, player)
enemy.dif = 0
else:
print("He defends himself.")
enemy.dif = 10
os.system("clear")
turns.pop(0)
if player.hp <= 0:
print("You died!")
sys.exit(0)
elif enemy.hp <= 0:
print("YOU WON!")
sys.exit(0)
break
charachter = Entity(100, 15, 15)
boss = Entity(300, 40, 0)
testbattle = Battle()
testbattle.Combat(charachter, boss)
In your Battle.Attack method:
def Attack(self, attacker, defender):
damage = attacker.atk - defender.dif
defender.hp -= damage
If attacker.atk - defender.dif is a negative number, then you'll be subtracting a negative number from defender.hp, increasing it.
If I'm the player, and my defense starts out as 15, and then I defend, my defense will become 30 because of player.dif *= 2. If I defend again the next turn, my defense will be 60, which is greater than the boss' attack of 40. So, if the boss then attacks me in Battle.Attack, we would get damage = 40 - 60, which is damage = -20, effectively healing the player by twenty points.
The entity will be healed if the attackers attack value is less than the defender's defence value. Perhaps instead of subtracting the 2 values you could use a defence divisor/ multiplier

how can I subtract the amount Randint generates in the while loop from enemies HP without getting 2 different numbers?

If you run the code you should see that the print says "14" for example, but It retracts something else from enemies HP.
Calculating attack damage for each "spell":
from random import randint
import time
class Player(object):
def __init__(self, health):
self.health = health
#staticmethod
def use_heal():
return randint(9, 21)
#staticmethod
def attack_slice():
return randint(5, 29)
#staticmethod
def attack_bash():
return randint(11, 18)
class Enemy(object):
def __init__(self, health):
self.health = health
#staticmethod
def enemy_attack():
return randint(9, 19)
For setting HP:
player = Player(100)
enemy = Enemy(100)
The loop that is the "game":
while True:
print(f"Your hp: {player.health}\nEnemy hp: {enemy.health}\n")
print("(1) Bash _ (2) Slice _ (3) Heal")
attack_choice = int(input(">>"))
if attack_choice == 1:
print(f"You hit for {Player.attack_bash()}")
enemy.health -= Player.attack_bash()
elif attack_choice == 2:
print(f"You hit for {Player.attack_slice()}")
enemy.health -= Player.attack_slice()
elif attack_choice == 3:
print(f"You heal for {Player.use_heal()}")
player.health += Player.use_heal()
when it calls Player.attack_* it returns a random value to print, and then calls it a second time to actualy damage the enemy so it is likely a defarent value. what it should do is call it once, store it in a variable and use the variable
while True:
print(f"Your hp: {player.health}\nEnemy hp: {enemy.health}\n")
print("(1) Bash _ (2) Slice _ (3) Heal")
attack_choice = int(input(">>"))
if attack_choice == 1:
damage = Player.attack_bash()
print(f"You hit for {damage}")
enemy.health -= damage
elif attack_choice == 2:
damage = Player.attack_slice()
print(f"You hit for {damage}")
enemy.health -= damage
elif attack_choice == 3:
damage = Player.use_heal()
print(f"You heal for {damage}")
player.health += damage
The problem is you are generating two random numbers for each case: The one that gets print and the one that gets subtracted/added.
...
print(f"You hit for {Player.attack_bash()}") # Generates a random number
enemy.health -= Player.attack_bash() # Generates another random number
...
You need to use a temporary variable so you can use the same value twice:
...
damage = Player.attack_bash()
print(f"You hit for {damage}")
enemy.health -= damage
...

Returning Value from Function to Variable

So I'm currently having some trouble with working out how to basically create a "count" function work within my Rock, Paper, Scissors game. I'm new to Python and this is my first foray into the use of multiple functions to execute game logic. Here's my code...
import random
from random import choice
cpu_score = 0
player_score = 0
# dictionary from which gestures are pulled
gestures = ["rock","paper","scissors"]
n_rounds = int(input("How many rounds would you like to play? "))
while n_rounds % 2 == 0 or n_rounds <= -1:
print("Number of rounds must be an odd number! Select an odd number!")
n_rounds = input("How many rounds?")
break
print("Lets play",n_rounds,"rounds!")
rounds_to_win = ((n_rounds + 1)//2)
rounds_to_win = round(rounds_to_win)
print("You must win",rounds_to_win,"rounds to beat the game!")
def computer_choice():
"""Movement made by the computer"""
comp = random.choice(gestures)
return comp
def player_gesture():
"""Movement by player"""
player = input("Please select, rock, paper or scissors")
if player not in gestures:
print("That's not an option! Please try again.")
player = input("Please select, rock, paper or scissors")
return player
def who_won_round(comp, player):
'''Who is the winner of the round.'''
winner = 0
if ((player == "rock") and (comp == "paper")) or \
((player == "paper") and (comp == "scissors")) or \
((player == "scissors") and (comp == "rock")):
winner = 1
elif ((comp == "rock") and (player == "paper")) or \
((comp == "paper") and (player == "scissors")) or \
((comp == "scissors") and (player == "rock")):
winner = 2
else:
winner = 0
return winner
def win_counter(winner, cpu_score,player_score):
rounds = 1
if winner == 1:
player_score += 1
print("This is round",rounds)
rounds += 1
return player_score
if winner == 2:
cpu_score += 1
print("This is round",rounds)
rounds += 1
return cpu_score
def count_round(winner, player, comp, cpu_score, player_score):
if winner == 0:
print("It's a tie!")
elif winner == 1:
print("The player chose",player)
print("The computer chose",comp)
print("The computer won the round!")
print("The computer has won",cpu_score,"rounds!")
elif winner == 2:
print("The player chose",player)
print("The computer chose",comp)
print("The player won the round!")
print("The player has won",player_score,"rounds!")
def game_structure():
while rounds_to_win < n_rounds:
comp = computer_choice()
player = player_gesture()
winner = who_won_round(comp,player)
win_count = win_counter(winner, cpu_score,player_score)
count = count_round(winner, player, comp, cpu_score, player_score)
game_structure()
Basically I'm having issues returning the variables in order to keep count of the scores of the "number of rounds" and "cpu_score" and "player_score". I prefer to not declare global variables as I realise they can be messy to use, but I'm not quite sure how to avoid this.
If you must avoid the use of global variables, you should take an object oriented approach. That way you can store the variables in the object.
So basically you do something like this:
newgame = mytictactoe()
while True #infinite loop
input("wanna play?")
if input == yes:
newgame.start
else:
break
From what I see, the cpu_score and player_score are never updated. You only have the newest result in win_count, that is not assigned to cpu_score or player_score at anytime. It could be something like:
win_count = win_counter(winner, cpu_score,player_score)
if winner == 1:
cpu_score = win_count
if winner == 2:
player_score = win_count
rounds_to_win = max(player_score, cpu_score)
count_round(winner, player, comp, cpu_score, player_score)
I did not run it, but this modifications should do the trick. There are better ways of doing it; maybe using a list to keep the scores and use winner as the index, or an object approach as someone else said, but I do not want to change too much the code.
Also, bear in mind that the round variable in win_counter does nothing.

calling a function in Python?

I don't know how to call the functions below I listed a box with what I want to do but don't know how to.
import random
import time
startingP_health = 30
startingE_health = 30
def player_attack():
global startingE_health
time.sleep(1)
print ("What ability would you like to use? (free speach(fs), capatalism(c), or punch(p)")
ability_choice = input()
if(ability_choice == "fs"):
enemy_health = startingE_health-3
enemy_heath = int(enemy_health)
elif(ability_choice == "c"):
enemy_health = startingE_health-(random.randint(1,6))
enemy_heath = int(enemy_health)
elif(ability_choice == "p"):
enemy_health = startingE_health-(random.randint(2,4))
enemy_heath = int(enemy_health)
else:
print("you fell.")
time.sleep(1)
print ("Enemie's health is now: ",enemy_health)
print("")
return int(enemy_health)
def enemy_attack():
global startingP_health
time.sleep(1)
print ("Enemy kicks you")
print("")
player_health = startingP_health - (random.randint(1,3)
player_health = int(player_health)
time.sleep(1)
print ("Your health is now ",player_health)
print ("")
return int(player_health)
def battle_trotsky():
global player_health
print ("Enemy appears")
print ("")
time.sleep(1)
while player_health > 0 and enemy_health > 0:
##############################
#call function player_attack
#call enemy_attack
##############################
if player_health <=0:
break
if enemy_health <= 0:
time.sleep(1)
print ("You have killed the enemy")
if player_health <= 0:
print("Sorry you failed the mission you must restart the mission"))
################################
#initate function sequence
################################
import random
import time
You need to declare enemy_health and player_health as globals so they persist:
startingP_health = 30
startingE_health = 30
enemy_health = startingE_health
player_health = startingP_health
This function doesn't have to return anything, since you declare global enemy_health and then assign it the new value:
def player_attack():
global enemy_health
time.sleep(1)
print ("What ability would you like to use? (free speech(fs), capitalism(c), or punch(p)")
ability_choice = input()
This wasn't working because you were doing enemy_health = starting health - 3, which means every round it would start again from the starting health. Instead here, -= is subtract from the current value: enemy_health = enemy_health - 3:
if(ability_choice == "fs"):
enemy_health -= 3
elif(ability_choice == "c"):
enemy_health -= random.randint(1,6)
elif(ability_choice == "p"):
enemy_health -= random.randint(2,4)
else:
print("you fell.")
time.sleep(1)
print ("Enemy's health is now: ",enemy_health)
print("")
def enemy_attack():
global player_health
time.sleep(1)
print ("Enemy kicks you")
print ("")
player_health -= random.randint(1,3)
time.sleep(1)
print ("Your health is now ",player_health)
print ("")
def battle_trotsky():
global player_health
global enemy_health
print ("Enemy appears")
print ("")
time.sleep(1)
while player_health > 0 and enemy_health > 0:
Functions are called via name(), as seen:
player_attack()
enemy_attack()
if enemy_health <= 0:
time.sleep(1)
print ("You have killed the enemy")
if player_health <= 0:
print("Sorry you failed the mission you must restart the mission")
battle_trotsky()
This is a not a good question for this site as it is not specific, but I feel compelled to fixed this code by a deep power. I'm not sure why you're doing the sleeps, the print("") statements can be replaced with a '\n' in the previous prints, and you are continuously reassigning player health to the starting health - number, which will never reach 0, so you must make the healths global.
Here's a working example
import random
player_health = 30
enemy_health = 30
def player_attack():
global enemy_health
ability_choice = input("What ability would you like to use? (free speach(fs), capatalism(c), or punch(p)")
if(ability_choice == "fs"):
enemy_health -= 3
elif(ability_choice == "c"):
enemy_health -= random.randint(1,6)
elif(ability_choice == "p"):
enemy_health -= random.randint(2,4)
else:
print("you fell.")
print ("Enemy's health is now: "+str(enemy_health)+'\n')
def enemy_attack():
global player_health
print ("Enemy kicks you")
player_health -= random.randint(1,3)
print ("Your health is now "+str(player_health)+"\n")
def battle_trotsky():
print ("Enemy appears\n")
while player_health > 0 and enemy_health > 0:
player_attack()
if check_game_over():
return
enemy_attack()
if check_game_over():
return
def check_game_over():
global player_health, enemy_health
if enemy_health <= 0:
print ("You have killed the enemy\n")
return True
elif player_health <= 0:
print("Sorry you failed the mission you must restart the mission")
return True
else:
return False
if __name__=='__main__':
battle_trotsky()

Categories