I tried to implement what I understood from previous advices here.
- use more functions
- improve the logic of "play"
- allow wrong user input
- indent to 4 spaces and remove
()
Mind that I only have access to google colab so I cant benefit from formatting tools or tests.
I also added comments and types to the extent I could do it, and allowed a user to keep playing and accumulate the result.
I'd appreciate general advice in how to get a more readable and neat code, just do not go to extreme detail because I'm quite new to python.
import random
import sys
from typing import TypedDict
welcome='''
Possible values: r, p, s, q.
Enter r for ROCK, p for PAPER, s for SCISSORS, q to quit the game.
'''
def request_valid_input(msg:str, allowed_values:list[str], max_tries:int):
'''
Gives the user a certain number of tries in case it types the wrong letter.
returns: valid user input
'''
user_input = input(msg).strip()
if max_tries <=0:
raise Exception(f"Max tries reached. Input must be one of {allowed_values.join(',')}")
while user_input not in allowed_values:
max_tries-=1
request_valid_input(max_tries=max_tries)
return user_input
class Score(TypedDict):
user:int
py:int
class LastRound(TypedDict):
user_score:int
py_score:int
user_letter:str
py_letter:str
class RPS():
'''
Represents a simple Rock Paper Scissors game
'''
def __init__(self):
self.total_score:Score = {"user":0, "py":0}
self.last_round:LastRound|None = None
self.welcome = welcome
self.max_tries=3 # when user tries an invalid letter or input.
self.letter_mapping = {"r":"ROCK", "s":"SCISSORS", "p":"PAPER"} # for informing user
self.allowed_values=['r', 's', 'p', 'q']
def random_letter(self):
'''
Generates r, s or p used as the computer's input value / choice.
'''
letters=['r', 's', 'p']
our_val = random.randint(0,2)
return letters[our_val]
def report_result(self)->None:
'''
Print a formatted string telling who wins.
Returns: None
'''
user_selection = self.letter_mapping[self.last_round["user_letter"]]
py_selection = self.letter_mapping[self.last_round["py_letter"]]
print(f"You chose {user_selection}, Python chose {py_selection}")
if self.last_round["user_score"] > self.last_round["py_score"]:
print("User wins.")
elif self.last_round["py_score"] > self.last_round["user_score"]:
print("Python wins.")
else:
print("Ties.")
def play(self, user_input):
user_score=0
py_score=0
if user_input == "q":
sys.exit("Exiting game.")
user_val = user_input
our_val = self.random_letter()
# test tie separately.
if user_val==our_val:
pass
# case 1: user chooses ROCK
elif user_val=="r":
if our_val=="s":
user_score+=1
else:
py_score+=1
# case 2 user chooses PAPER
elif user_val=="p":
if our_val=="r":
user_score+=1
else:
py_score+=1
# case 3 user chooses SCISSORS
else:
if our_val=="r":
py_score+=1
else :
user_score+=1
# put the results in the state of the class
self.last_round:LastRound = {"user_score":user_score, "py_score":py_score, "user_letter":user_val, "py_letter":our_val}
self.total_score["user"]+=user_score
self.total_score["py"]+=py_score
def run_game(subsequent_message="remember: r for ROCK, s for SCISSORS, p for PAPER"):
game = RPS()
keep_going='y'
first_try=True
while(keep_going=='y'):
msg = game.welcome if first_try else "Type your new choice (r, p, s or q): "
input = request_valid_input(msg=msg, max_tries=game.max_tries, allowed_values=game.allowed_values)
game.play(input)
game.report_result()
keep_going = request_valid_input(msg="Would you like to play again? Type y for yes, n for no.", allowed_values=['n', 'y'], max_tries=2)
first_try=False
print(f"user score: {game.total_score['user']}, python score: {game.total_score['py']}")
run_game()