Python 中的剪刀石头布抽象

Rock Paper Scissors abstraction in Python

我做了一个高级 Python 后端工程师面试,其中涉及实现剪刀石头布游戏。结果并不漂亮。他们的工程师说我到处都有笨拙的实施。之后我做了升级,但仍然有一个问题让我感到困惑。他们希望我将游戏规则的原始实现更改为更好的抽象。

我原来的实现是:

def _outcome(self, player_move, ai_move):
    """Determine the outcome of current round.

    Paper beats (wraps) rock.
    Rock beats (blunts) scissors.
    Scissors beats (cuts) paper.

    Parameters
    ----------
    player_move : MoveChoice
        Player's move for current round.

    ai_move : MoveChoice
        AI's move for current round.

    Returns
    -------
    outcome : Outcome
        Outcome of the current round.
    """
    if player_move == 1 and ai_move == 2 or \
        player_move == 2 and ai_move == 3 or \
        player_move == 3 and ai_move == 1:
        return self.computer.name
    elif player_move == 1 and ai_move == 3 or \
        player_move == 2 and ai_move == 1 or \
        player_move == 3 and ai_move == 2:
        return self.player.name
    else:
        return 'Draw'

有映射:

MOVE_CHOICE = {
    1: 'rock',
    2: 'paper',
    3: 'scissors',
}

我在别处定义了 role.Playerrole.Computer class。他们的批评是:

他们的建议是:

到目前为止,我已经为移动和结果创建了枚举:

class MoveChoice(Enum):
    ROCK = auto()
    PAPER = auto()
    SCISSORS = auto()


class Outcome(Enum):
    WIN = auto()
    LOSE = auto()
    DRAW = auto()

但我无法按照他们的要求抽象出游戏规则。我该怎么做?

根据你展示的代码和他们给你的建议,我会这样做:

from enum import Enum, auto

class MoveChoice(Enum):
    ROCK = auto()
    PAPER = auto()
    SCISSORS = auto()

class Outcome(Enum):
    WIN = auto()
    LOSE = auto()
    DRAW = auto()

WIN_MAPPING = {
    MoveChoice.ROCK: MoveChoice.SCISSORS,
    MoveChoice.SCISSORS: MoveChoice.PAPER,
    MoveChoice.PAPER: MoveChoice.ROCK,
}

def outcome(player_move, ai_move):
    if player_move == ai_move:
        return Outcome.DRAW
    elif WIN_MAPPING[player_move] == ai_move:
        return Outcome.WIN
    else:
        return Outcome.LOSE

请注意,我已将您的方法更改为函数。这不是对整体程序结构的建议,只是将逻辑呈现为一个更独立的示例。


Luke Nelson pointed out in the comments, the interviewers suggested that the win mapping map each move to a list of moves it beats. I missed this detail. While it doesn't make any practical difference in Rock Paper Scissors, it is more generalized, and would make it easier if they then ask you to expand the rule set to something like Rock Paper Scissors Lizard Spock.

唯一会改变映射的是将每个值变成一个单元素列表:

WIN_MAPPING = {
    MoveChoice.ROCK: [MoveChoice.SCISSORS],
    MoveChoice.SCISSORS: [MoveChoice.PAPER],
    MoveChoice.PAPER: [MoveChoice.ROCK],
}

然后 outcome 函数,而不是检查计算机的移动是否 等于 值,必须检查它是否 值(因为值现在是一个列表)。

def outcome(player_move, ai_move):
    if player_move == ai_move:
        return Outcome.DRAW
    # elif WIN_MAPPING[player_move] == ai_move:
    # Above line is replaced with:
    elif ai_move in WIN_MAPPING[player_move]:
        return Outcome.WIN
    else:
        return Outcome.LOSE