I wonder if you’d have any advice, I spent some time at it, but am quite new to Python (not to programming.)
Maybe any styles or pattern that you find uncommon or not optimal, or styling suggestions.
import random
import sys
from typing import TypedDict
def request_valid_input(msg:str, allowed_values:list[str], max_tries:int)->str:
"""
Gives the user a certain number of tries in case it types the wrong letter.
returns: valid user input
"""
user_input = input(msg).strip()
while user_input not in allowed_values:
max_tries-=1
if max_tries <=0:
raise Exception(f"Max tries reached. Input must be one of {allowed_values.join(',')}")
user_input = input(msg).strip()
return user_input
class Score(TypedDict):
# total user score
user:int
# computer/python score
py:int
class LastRound(TypedDict):
"""
a summary of what happened in the last played game.
"""
user_score:int
# python/computer score.
py_score:int
user_letter:str
# python/computer letter.
py_letter:str
class RPS():
"""
Represents a simple Rock Paper Scissors game
"""
def __init__(self):
"""
Configuration and state of the game
"""
self.total_score:Score = {"user":0, "py":0}
self.last_round:LastRound|None = None
self.welcome = """
Welcome to the ultimate, deadly, emboldened Rock, Paper and Scissors game.
Possible values: r, p, s, q.
Enter r for ROCK, p for PAPER, s for SCISSORS, q to quit the game.
"""
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=set('r', 's', 'p', 'q')
def random_letter(self)->str:
"""
Generates r, s or p used as the computer's input value / choice.
"""
rps_letter=self.allowed_values[0,-1]
our_val = random.randint(0,2)
return rps_letter[our_val]
def report_result(self)->None:
"""
Print a formatted string telling who wins.
"""
user_selection = self.letter_mapping.get(self.last_round.get("user_letter"))
py_selection = self.letter_mapping.get(self.last_round.get("py_letter"))
print(f"You chose {user_selection}, Python chose {py_selection}")
if self.last_round.get("user_score") > self.last_round.get("py_score"):
print("User wins.")
elif self.last_round.get("py_score") > self.last_round.get("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'
while keep_going=='y':
input = request_valid_input(msg=game.welcome, 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)
# if there are second tries it will use this message.
msg = "Type your new choice (r, p, s or q): "
print(f"\n\nEnd of the game. \n\nUser Score: {game.total_score.get('user')},\nPython Score: {game.total_score.get('py')}")
run_game()