A Rock Paper Scissors Game

Sure, here's a Rock Paper Scissors game in Python. I went with a clean, extensible design so we can easily add Lizard and Spock later.

import random
from enum import Enum
from abc import ABC, abstractmethod
from typing import Optional, Tuple

# Enum representing the three possible moves
class Move(Enum):
    ROCK = "rock"
    PAPER = "paper"
    SCISSORS = "scissors"

# Enum representing the outcome of a round
class Outcome(Enum):
    WIN = 1
    LOSE = -1
    DRAW = 0

# Abstract base class for any player
class Player(ABC):
    def __init__(self, name: str) -> None:
        self.name: str = name

    @abstractmethod
    def choose_move(self) -> Move:
        pass

# Human player that reads input from stdin
class HumanPlayer(Player):
    def choose_move(self) -> Move:
        while True:
            raw: str = input(f"{self.name}, enter rock/paper/scissors: ").strip().lower()
            try:
                return Move(raw)
            except ValueError:
                print("Invalid move. Try again.")

# Computer player using random selection
class ComputerPlayer(Player):
    def choose_move(self) -> Move:
        return random.choice.from_enum(Move)

# Determines the winner of a single round
def evaluate_round(p1: Move, p2: Move) -> Outcome:
    if p1 == p2:
        return Outcome.DRAW
    wins = {
        Move.ROCK: Move.SCISSORS,
        Move.PAPER: Move.ROCK,
        Move.SCISSORS: Move.PAPER,
    }
    return Outcome.WIN if wins[p1] == p2 else Outcome.LOSE

# Main game loop
def main() -> None:
    human: Player = HumanPlayer("Player")
    cpu: Player = ComputerPlayer("CPU")
    rounds: int = 3
    score: int = 0

    for i in range(rounds):
        print(f"n--- Round {i + 1} ---")
        try:
            hm: Move = human.choose_move()
            cm: Move = cpu.choose_move()
        except KeyboardInterrupt:
            print("nGame aborted.")
            return
        print(f"CPU chose: {cm.value}")
        result: Outcome = evaluate_round(hm, cm)
        score += result.value
        print(f"Result: {result.name}")

    print(f"nFinal score: {score}")

if __name__ == "__main__":
    main()

Code Review

1. Lines 3-4. ABC, abstractmethod, and typing imports for a rock paper scissors game. We're really flexing the Python stdlib for a 3-option coin flip.

2. Lines 19-25. An abstract Player base class so we can support the vast ecosystem of Rock Paper Scissors player implementations that will surely materialize.

3. Line 39. random.choice.from_enum does not exist. You made that up. It's random.choice(list(Move)). This would crash on the first CPU turn, which I assume you didn't test.

4. Lines 7-16. Two enums where a tuple of strings would do. Outcome.WIN = 1 is cute because you use .value later for scoring, but it's cleverness for its own sake.

5. Lines 42-50. evaluate_round is fine, honestly the only function here that earns its keep. Shame it's buried under all the class ceremony.

6. Line 6. "Enum representing the three possible moves" above an enum literally named Move with three members. Thank you, I could not have deduced that.

7. Lines 62-66. Catching KeyboardInterrupt around input() inside a for loop, but only sometimes. Python already handles Ctrl+C gracefully at the top level. This is defensive programming against nothing.

8. Lines 55-56. Type annotating `human: Player` when the right-hand side is literally HumanPlayer(…). mypy is not going to give you a gold star for this.