最初的灵感来自于这个Python初学者,它促使我用我的天赋和我的Python经验重写了一堆东西:第一次尝试: Python摇滚剪刀。
好吧,所以我看了上面的帖子,觉得无聊,需要在工作中消磨一个小时。所以我杀了一个小时-我拿他们的RPS游戏,把它变成一个班级,使它看起来不那么邪恶/丑陋。
虽然这绝不是一个完全成熟的程序,我已经创建了干净和真正彻底的测试,这是我至少可以要求投入的东西。
运行非常干净,并使用了许多字符串,这是最初的灵感帖子的操作。但是,它也有很多文档字符串。整个游戏驻留在一个类中,并通过类之类的方式调用。
。
rps.py:
import random
class RockPaperScissors:
"""
Class to handle an instance of a Rock-Paper-Scissors game
with unlimited rounds.
"""
def __init__(self):
"""
Initialize the variables for the class
"""
self.wins = 0
self.losses = 0
self.ties = 0
self.options = {'rock': 0, 'paper': 1, 'scissors': 2}
def random_choice(self):
"""
Chooses a choice randomly from the keys in self.options.
:returns: String containing the choice of the computer.
"""
return random.choice(list(self.options.keys()))
def check_win(self, player, opponent):
"""
Check if the player wins or loses.
:param player: Numeric representation of player choice from self.options
:param opponent: Numeric representation of computer choice from self.options
:return: Nothing, but will print whether win or lose.
"""
result = (player - opponent) % 3
if result == 0:
self.ties += 1
print("The game is a tie! You are a most worthy opponent!")
elif result == 1:
self.wins += 1
print("You win! My honor demands a rematch!")
elif result == 2:
self.losses += 1
print("Haha, I am victorious! Dare you challenge me again?")
def print_score(self):
"""
Prints a string reflecting the current player score.
:return: Nothing, just prints current score.
"""
print(f"You have {self.wins} wins, {self.losses} losses, and "
f"{self.ties} ties.")
def run_game(self):
"""
Plays a round of Rock-Paper-Scissors with the computer.
:return: Nothing
"""
while True:
userchoice = input("Choices are 'rock', 'paper', or 'scissors'.\n"
"Which do you choose? ").lower()
if userchoice not in self.options.keys():
print("Invalid input, try again!")
else:
break
opponent_choice = self.random_choice()
print(f"You've picked {userchoice}, and I picked {opponent_choice}.")
self.check_win(self.options[userchoice], self.options[opponent_choice])
if __name__ == "__main__":
game = RockPaperScissors()
while True:
game.run_game()
game.print_score()
while True:
continue_prompt = input('\nDo you wish to play again? (y/n): ').lower()
if continue_prompt == 'n':
print("You are weak!")
exit()
elif continue_prompt == 'y':
break
else:
print("Invalid input!\n")
continue欢迎任何建议和意见,因为这是一个粗略的尝试。:)
发布于 2019-11-02 22:59:17
userchoice -> user_choice (考虑到您有opponent_choice)continue_prompt -> user_choice (在使用它的上下文中,它实际上是用户对继续提示符的选择/响应,而不是继续提示本身)random_choice的docstring可以改进。与其逐字重复代码(实现)中正在发生的事情,不如以一种方式记录它,这样读者就不需要阅读实现就可以知道该方法将做什么:
def random_choice(self) -> str:
"""
Randomly chooses rock, paper, or scissors.
:return: 'rock', 'paper', or 'scissors'
"""您已经对用户输入调用了lower(),这是很好的,但是您也应该在它上调用strip()。否则,带有前导或尾随空格的用户选择将被视为无效输入(例如,‘摇滚乐,摇滚乐,摇滚乐)
每次对random_choice的调用都会调用self.options字典上的list(),这将在每个调用上重新创建相同的选项列表。考虑只在__init__中创建一次列表:
def __init__(self):
...
self.options = {'rock': 0, 'paper': 1, 'scissors': 2}
self.choices = list(self.options.keys())然后我们可以在random_choice中使用它:
def random_choice(self):
return random.choice(self.choices)在验证用户选择“rock”、“纸张”或“剪刀”的输入时:
if user_choice in self.choices:
...由于您的类已经在处理交互式用户输入,所以我认为您提示用户再玩一轮的代码应该驻留在类中。那么,任何想要利用你的班级发起一个互动的多轮摇滚剪刀游戏,只需做game.run_game()。
出于同样的原因,对print_score()的调用应该在类内的游戏协调逻辑中;您类的客户端不应该直接调用它。
我认为,如果您将用户输入的交互式提示和检索提取到他们自己的方法中,将更容易阅读。
def player_choice(self) -> str:
"""
Prompts player for choice of rock, paper, or scissors.
:return: 'rock', 'paper', or 'scissors'
"""
while True:
user_choice = input("Choices are 'rock', 'paper', or 'scissors'.\n"
"Which do you choose? ").lower().strip()
if user_choice in self.choices:
return user_choice
print("Invalid input, try again!")def player_wants_to_play_again(self) -> bool:
"""
Prompts player to play again.
:return: True if the player wants to play again.
"""
prompt = "\nDo you wish to play again? (y/n): "
valid_choices = {'y', 'n'}
while True:
user_choice = input(prompt).lower().strip()
if user_choice in valid_choices:
return user_choice == 'y'
print("Invalid input!")那么您的主要游戏方法可能如下所示:
def run_one_round(self):
user_choice = self.player_choice()
opponent_choice = self.random_choice()
print(f"You've picked {user_choice}, and I picked {opponent_choice}.")
self.check_win(self.options[user_choice],
self.options[opponent_choice])
self.print_score()
def run_game(self):
while True:
self.run_one_round()
if not self.player_wants_to_play_again():
print("You are weak!")
break通过这样的结构,我们不再需要调用exit() (它退出Python解释器)来突破主游戏循环。请注意,使用exit()处理程序流中的非异常场景通常被认为是不好的形式,也就是说,如果允许程序正常终止而不必求助于exit(),则应该这样做。
Enum提高清晰度
在最初的程序中,隐式约定是精确的字符串rock、paper和scissors表示每个玩家可以做出的选择,因此是特殊的。您可以通过查看字典self.options来观察这一点,它将上面的字符串映射到整数,因此我们可以在以后使用check_win中的模块化算法对它们进行比较。这听起来像是一种情况,即使用自定义enum.Enum类型可能有助于使事情更加明确。
让我们定义一个名为Enum的Choice,它可以接受三个值中的一个:ROCK、PAPER或SCISSORS。最酷的是,我们可以让Choice负责以下所有工作:
str转换到Choice (如果不能转换提供的字符串,抛出一个异常)Choice定义一个规范的字符串表示,例如“岩石”、“纸”和“剪刀”(从Choice到str的转换)Choices具有可比性,这样,如果您有两个Choices X和Y,您可以比较它们以确定哪一个会赢守则:
from enum import Enum
class Choice(Enum):
ROCK = 0
PAPER = 1
SCISSORS = 2
@classmethod
def from_str(cls, s: str) -> "Choice":
try:
return {
"r": cls.ROCK,
"rock": cls.ROCK,
"p": cls.PAPER,
"paper": cls.PAPER,
"s": cls.SCISSORS,
"scissors": cls.SCISSORS
}[s.strip().lower()]
except KeyError:
raise ValueError(f"{s!r} is not a valid {cls.__name__}")
def __str__(self) -> str:
return self.name.lower()
def beats(self, other: "Choice") -> bool:
return (self.value - other.value) % 3 == 1互动会议显示了它的行动:
>>> list(Choice)
[<Choice.ROCK: 0>, <Choice.PAPER: 1>, <Choice.SCISSORS: 2>]
>>> Choice.from_str('rock')
<Choice.ROCK: 0>
>>> Choice.from_str('paper')
<Choice.PAPER: 1>
>>> Choice.from_str('scissors')
<Choice.SCISSORS: 2>
>>> print(Choice.ROCK)
rock
>>> print(Choice.PAPER)
paper
>>> print(Choice.SCISSORS)
scissors
>>> Choice.ROCK == Choice.ROCK
True
>>> Choice.ROCK.beats(Choice.SCISSORS)
True
>>> Choice.PAPER.beats(Choice.ROCK)
True
>>> Choice.SCISSORS.beats(Choice.PAPER)
True让我们在RockPaperScissors中使用它来看看它的外观。这里是__init__:
def __init__(self):
self.wins = 0
self.losses = 0
self.ties = 0
self.choices = list(Choice)现在,random_choice和player_choice都返回Choice而不是str,这使得这些方法的类型签名更具表现力:
def random_choice(self) -> Choice:
return random.choice(self.choices)
def player_choice(self) -> Choice:
prompt = ("\nChoices are 'rock', 'paper', or 'scissors'.\n"
"Which do you choose? ")
while True:
try:
return Choice.from_str(input(prompt))
except ValueError:
print("Invalid input, try again!")当我们从上述两种方法返回字符串时,有必要在文档中澄清三条字符串中只有一条将被返回:“rock”、“纸张”或“剪刀”。使用Choice,我们不再需要这样做,因为所有这些信息都在其定义中显式地列出。
类似地,check_win现在采用两个Choices而不是两个ints作为参数。
def check_win(self, player_choice: Choice, opponent_choice: Choice):
if player_choice == opponent_choice:
self.ties += 1
print("The game is a tie! You are a most worthy opponent!")
elif player_choice.beats(opponent_choice):
self.wins += 1
print("You win! My honor demands a rematch!")
else:
self.losses += 1
print("Haha, I am victorious! Dare you challenge me again?")使用Choice的完整代码可以找到在这个要点里。
发布于 2019-11-02 19:56:13
我认为使用字典存储胜负值是有意义的:
def __init__(self):
"""
Initialize the variables for the class
"""
self.options = {'rock': 0, 'paper': 1, 'scissors': 2}
self.outcome_count = {
"tie": 0,
"win": 0,
"loss": 0,
}这使check_win更加“机械”,因为您现在可以通过名称和在静态数据中查找结果来引用结果,而不需要一堆if/else:
def check_win(self, player, opponent):
"""
Check if the player wins or loses.
:param player: Numeric representation of player choice from self.options
:param opponent: Numeric representation of computer choice from self.options
:return: Nothing, but will print whether win or lose.
"""
result = ["tie", "win", "loss"][(player - opponent) % 3]
self.outcome_count[result] += 1
outcome_message = {
"tie": "The game is a tie! You are a most worthy opponent!",
"win": "You win! My honor demands a rematch!",
"loss": "Haha, I am victorious! Dare you challenge me again?",
}
print(outcome_message[result])当然,它最终使print_score变得不那么容易解释了:
def print_score(self):
"""
Prints a string reflecting the current player score.
:return: Nothing, just prints current score.
"""
wins = self.outcome_count["win"]
losses = self.outcome_count["loss"]
ties = self.outcome_count["tie"]
print(f"You have {wins} wins, {losses} losses, and {ties} ties.")最后,我认为通过编写run_game循环可以稍微清晰一些。
while True:
userchoice = input("Choices are 'rock', 'paper', or 'scissors'.\nWhich do you choose? ").lower()
if userchoice in self.options.keys():
break
print("Invalid input, try again!")我发现一个没有else的明确的“早期退出”更容易遵循请注意,这种情况不是倒过来的,在这种情况下,我认为这有助于提高清晰度。,不过如果它不是常规的大代码库,这可能会让人不快。
https://codereview.stackexchange.com/questions/231706
复制相似问题