Updating local variables inside a function in Python - python

I'm learning python and about to complete a program to run a rock, paper, scissors game between two players for three rounds and update player scores at the end of each round and display it on screen:
import random
"""This program plays a game of Rock, Paper, Scissors between two Players,
and reports both Player's scores each round."""
moves = ['rock', 'paper', 'scissors']
"""#!/usr/bin/env python3
import random
"""This program plays a game of Rock, Paper, Scissors between two Players,
and reports both Player's scores each round."""
moves = ['rock', 'paper', 'scissors']
"""The Player class is the parent class for all of the Players
in this game"""
class Player:
def move(self):
return 'rock'
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 RandomPlayer(Player):
def move(self):
return random.choice(moves)
class Game:
def __init__(self, p1, p2):
self.p1 = p1
self.p2 = p2
def keep_p1_score(self, p1_Score):
return p1_Score
def play_round(self):
move1 = self.p1.move()
move2 = self.p2.move()
p1_Score = 0
p2_Score = 0
print(f"Player One played: {move1} Player Two played: {move2}")
if beats(move1, move2) == True:
print('**Player One wins**')
p1_Score += 1
elif beats(move2, move1) == True:
print('**Player Two wins**')
p2_Score += 1
else:
print('**TIE**')
print(f'Score: Player One: {p1_Score} Player Two: {p2_Score}')
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 + 1}:")
self.play_round()
print("Game over!")
if __name__ == '__main__':
game = Game(RandomPlayer(), RandomPlayer())
game.play_game()
The problem is when I run the code it doesn't update players scores. As you can see in this picture of the output even though player two won rounds two and three, his score at the end of run three is still 1 instead of 2. I know that's because for each iteration of play_round function in the for loop inside the play_game function the values of p1_score and p2_score resets to zero but I do not know how to stop that from happening. Any suggestion will be greatly appreciated.
program output

The problem is that p1_Score and p2_Score are local variables in play_round. So every time you call this function, you get a new instance of these variables. Moreover, you always set
p1_Score = 0
p2_Score = 0
in play_round. So at the end of the function no score will ever be larger than 1.
To rememdy this, use self.p1_Score and self.p2_Score to make these variables instance variables and also move the 0-initialization into the __init__ function of the game class. Alternatively, make the score a class variable of the Player class.

Related

Error in Python code for Rock Paper Scissors

I am a python newbie and this is my first question on stackoverflow so sorry if it's not correct. I am programming my own rock, paper scissors game in Python, which includes a computer class which remembers the previous turn of the player. However, I can only get it to return random.choice moves, and I am not sure where I am going wrong?
moves = ['rock', 'paper', 'scissors']
class Player:
def __init__(self):
self.my_move = None
self.their_move = None
def learn(self, my_move, their_move):
self.my_move = my_move
self.their_move = their_move
def beats(one, two):
return ((one == 'rock' and two == 'scissors') or
(one == 'scissors' and two == 'paper') or
(one == 'paper' and two == 'rock'))
class MemoryPlayer(Player):
def move(self):
if self.their_move in moves:
return self.their_move
else:
return random.choice(moves)
class Game:
def __init__(self, p1, p2):
self.p1 = p1
self.p2 = p2
self.scorep1 = 0
self.scorep2 = 0
# starting score of both players
def play_round(self):
firstmove = self.p1.move()
secondmove = self.p2.move()
print(f"Player 1: {firstmove} Player 2: {secondmove}")
# prints move of Player 1 and Player 2 after each round
if beats(firstmove, secondmove):
self.scorep1 += 1
print(f"Player 1 Wins. Score: {self.scorep1}"
f" to {self.scorep2}")
elif beats(secondmove, firstmove):
self.scorep2 += 1
print(f"Player 2 Wins. Score: {self.scorep2}"
f" to {self.scorep1}")
else:
print(f"Boring - TIE. Score: {self.scorep1}"
f" to {self.scorep2}")
# uses beats function in if and else statement to define score increase
# if player 1 move beats player 2 move and vice versa
def play_game(self):
print("Game start!")
self.rounds = 3
for round in range(self.rounds):
print(f"Round {round}:")
self.play_round()
print(f"Game over! Final score:\
Player 1: {self.scorep1}), Player 2: {self.scorep2}")
# prints final secores for Player 1 and Player 2
if self.scorep1 > self.scorep2:
print("Player 1 is Triumphant")
# if player 1 score is greater than player 2 - prints winning statement
elif self.scorep2 > self.scorep1:
print("Player 2 is Triumphant")
# if player 2 score is greater than player 1 - prints winning statement
else:
print("Boring - TIE")
if __name__ == '__main__':
game = Game(HumanPlayer(), MemoryPlayer())
# randomly chooses opposing NPC
game.play_game()

Cannot change a variable(score) dynamically in python

My program objective:
A dice game, two dice roll Everytime the player is ready. If the two numbers are equa, player gets +5 score. Else, -1 score.
My trouble: my program can't change the score. It is set to 0 initially. But Everytime it's only either -1 or +5. It has to keep decreasing or increasing. I also tried global variables.
Here is my code:
from random import randint
# this function returns two random numbers in list as dice result.
def roll_dice():
dice1 = randint(1, 7)
dice2 = randint(1, 7)
rolled_dice = [dice1, dice2]
return rolled_dice
# game function is all the game, if player is ready.
def game():
score = 0
rolled_dice = roll_dice()
print(rolled_dice)
if rolled_dice[0] != rolled_dice[1]:
score -= 1
elif rolled_dice[0] == rolled_dice[1]:
score += 5
print(f"score is {score}")
#also my code in pycharms, not asking if I want to continue game. but ignore it I it bothers you, I can figure it out.
#help here also if you can.. :)
conti = input("continue?")
if conti == 'y':
game()
else:
quit()
# this is the whole program.
def main():
ready = input("ready? (y/n)")
if ready == 'y':
game()
elif ready == 'n':
quit()
else:
print("type only y/n")
main()
I appreciate any help.
The reset happens because you keep calling your game() function each time a user types y to continue the game. You can change your game() function to be a loop and that will solve your problem:
def game():
score = 0
while True:
rolled_dice = roll_dice()
print(rolled_dice)
if rolled_dice[0] != rolled_dice[1]:
score -= 1
else: # you can change here to else, because being equals is the complement of the first if clause
score += 5
print(f"score is {score}")
conti = input("continue?")
if conti == 'n':
break

I Must add two Attributes

I am trying to create an attribute 'score' and 'learn' in my code.
I am getting these error:
AttributeError: 'HumanPlayer' object has no attribute 'score' and
'learn'.
See some parts of the codes below:
#Create random player class
class RandomPlayer:
def __init__(self):
Player.__init__(self)
def move(self):
self.move = RandomPlayer
#use imported random function
RandomPlayer = random.randint(1,3)
#Computer choice is either rock, paper, or scissors
if RandomPlayerMove == ("Rock"):
print("The computer choses ROCK")
elif RandomPlayerMove == ("Paper"):
print("The computer choses PAPER")
else:
print("The computer choses SCISSORS")
#return value
return RandomPlayer
#Create human player class
class HumanPlayer:
def __init__(self):
Player.__init__(self)
def move(self):
while True:
HumanPlayer = input("'Rock', 'Paper', or 'Scissors' ")
#Detect invalid entry
if HumanPlayer.lower() not in moves:
print('Please choose Paper, Rock or Scissors: ')
else:
break
return HumanPlayer
class Game:
def __init__(self, HumanPlayer, RandomPlayer):
self.player1 = HumanPlayer
self.player2 = RandomPlayer
self.player1_score = 0
self.player2_score = 0
def play_round(self):
move1 = self.player1.move()
move2 = self.player2.move()
print(f"Player 1: {move1} Player 2: {move2}")
self.player1.learn(move1, move2)
self.player2.learn(move2, move1)
if (move1 == move2):
print("it's a tie!")
elif beats(move1, move2) is True:
self.player1_score += 1
elif beats(move2, move1) is True:
self.player2_score += 1
print("It's Tie, Play again!")
print(f"Scores, HumanPlayer: {self.player1.score} RandomPlayer: {self.player2.score}")
print("Game over!")
I'm pretty sure this is due to a typo. Rather than self.player1.score in your next to last print statement, you probably want self.player1_score, since that's an attribute you've defined in __init__. Similarly, you probably want self.player2_score instead of self.player2.score later in the same format string.
While it doesn't appear to be the cause of your current issues, there is another suggestion I'd like to make: Avoid using the same name for different things. You have a whole bunch of places where you're using the same name for both an instance and a class. For instance, your Game class takes arguments named HumanPlayer and RandomPlayer, which hide the classes with those names. You're also using your class names as variable names in each of your move methods. This is going to make bug fixing a nightmare! Use unique names. Python convention is to use CapitalizedNames for classes, lowercase_names_with_underscores for most other variables (including function and method names), and ALL_CAPS for constants (in the rare cases you need them), but you can use your own style if you prefer (just be consistent!).

Unable to break a python while loop inside a function

This program is a snakes and ladders program for my GCSE computer science (don't worry, this is just practice), and I'm having trouble breaking a while loop with a function that exists inside the loop. Here's the code:
win = False
while win == False:
print("Player 1 goes first.")
def dice():
roll = random.randint(1,6)
return roll
roll = dice()
print("They roll a...",roll)
player1+=roll
def check():
if player1 in ladders or player2 in ladders:
print("A ladder has moved the player to a new position.")
elif player1 in snakes or player2 in snakes:
print("A snake has moved the player to a new position.")
elif player1 >= 36:
print("Congratulations. Player 1 has won the game.")
win = True
elif player2 >= 36:
print("Congratulations. Player 2 has won the game.")
win = True
check()
print("Player 1 is on square",player1)
It's obviously not finished yet, and that's not all the code. After that bit, it does the same but with player2. There's a tuple above it that the check function checks to see if the player landed on a snake or a ladder, but I haven't added the code which actually moves the player up/down the ladder/snake.
The error is that the while loop is an infinite loop.
I've tried changing the whole win = False or True thing to just while True and then using break where I say win = True, but then it returns an error of 'break outside loop' even though the break is obviously inside the loop. I wonder if it's because I need to return something from the function, but I'm not quite sure how to do that. Simply putting 'return win' below both win = True doesn't change anything, and the while loop still continues indefinitely.
I've looked here and here for answers but neither worked for me; I think my situation is slightly different.
Perhaps this is what you are looking for? Note how I took the functions out of the loop. Then I ditched using a boolean variable altogether, as there is cleaner ways around it. You can use while True and then simply break if a certain condition is met. If you want the loop go back to starting point when a certain condition is met, you can use continue.
def dice():
return random.randint(1,6)
def check():
if player1 in ladders or player2 in ladders:
print("A ladder has moved the player to a new position.")
elif player1 in snakes or player2 in snakes:
print("A snake has moved the player to a new position.")
elif player1 >= 36:
print("Congratulations. Player 1 has won the game.")
return True
elif player2 >= 36:
print("Congratulations. Player 2 has won the game.")
return True
while True:
print("Player 1 goes first.")
roll = dice()
print("They roll a...",roll)
player1 += roll
if check():
break
print("Player 1 is on square",player1)
I didn't really touch the logic but it would make sense to pass the player score into check.
It happened because when you are assign variable in function it used local variable. So, for a fast fix you can add global win in check function:
def check():
global win
if player1 in ladders or player2 in ladders:
print("A ladder has moved the player to a new position.")
elif player1 in snakes or player2 in snakes:
print("A snake has moved the player to a new position.")
elif player1 >= 36:
print("Congratulations. Player 1 has won the game.")
win = True
elif player2 >= 36:
print("Congratulations. Player 2 has won the game.")
win = True
You can read more about type of variables here - http://www.python-course.eu/python3_global_vs_local_variables.php
Also it's not a good idea to store function inside while because it will create function on each iteration which is not good. So better is define them outside of loop.

NIM game - re-running the game

I have to do a small game called NIM. The game is a human vs. computer game where each player removes a number of straws (1,2 or 3) and the player who removes the last straw looses. I got the game to work properly but the problem is that it doesn't want to re-run if the player wants to play again. Any help would be apprecieted. :)
import random
print("""************ NIM GAME ***********
************ Game Start ***********
************ The rules ***********
-----------------------------------------------------
You need to remove from 1 to 3 straws from the pile.
The player that removes the final straw is the loser.
-----------------------------------------------------""")
player1=str(input("Enter your name. "))
player2="Computer"
howMany=0
gameover=False
strawsNumber=random.randint(10,20)
if (strawsNumber%4)==1:
strawsNumber+=1
def removingStrawsComputer():
removedNumber=random.randint(1,3)
global strawsNumber
while removedNumber>strawsNumber:
removedNumber=random.randint(1,3)
strawsNumber-=removedNumber
return strawsNumber
def removingStrawsHuman():
global strawsNumber
strawsNumber-=howMany
return strawsNumber
def humanLegalMove():
global howMany
legalMove=False
while not legalMove:
print("It's your turn, ",player1)
howMany=int(input("How many straws do you want to remove?(from 1 to 3) "))
if howMany>3 or howMany<1:
print("Enter a number between 1 and 3.")
else:
legalMove=True
while howMany>strawsNumber:
print("The entered number is greater than a number of straws remained.")
howMany=int(input("How many straws do you want to remove?"))
return howMany
def checkWinner(player):
if strawsNumber==0:
print(player," wins.")
global gameover
gameover=True
return gameover
def resetGameover():
global gameover
gameover=False
return gameover
def game():
while gameover==False:
print("It's ",player2,"turn. The number of straws left: ",removingStrawsComputer())
checkWinner(player1)
if gameover==True:
break
humanLegalMove()
print("The number of straws left: ",removingStrawsHuman())
checkWinner(player2)
def playAgain():
answer=input("Do you want to play again?(y/n)")
resetGameover()
while answer=="y":
game()
else:
print("Thanks for playing the game")
game()
playAgain()
You forgot to reset the number of straws at the start of each game. After def game():, you should insert:
global strawsNumber
strawsNumber=random.randint(10,20)
Note: you also need to put answer=input("Do you want to play again?(y/n)") at the end of your while answer=="y": loop. This will ask the user for a replay every time, instead of just after the first game.

Categories

Resources