Ejercicio: Adición de comportamiento al juego

Completado

Ahora que ha agregado clases y datos al juego "Piedra, papel, tijeras", es el momento de agregar el último elemento: el comportamiento. Agregará el comportamiento en forma de métodos. Agregará métodos a las clases y el resultado final será un juego "Piedra, papel, tijeras" funcional.

Implementación del comportamiento

Ha aprendido que para compilar un programa en el estilo de la programación orientada a objetos (OOP) primero se modela y después se programa. Como resultado, el modelado ha generado una tabla que representa los objetos, los datos y el comportamiento que supuestamente componen el programa. Esta es la misma tabla de nuevo.

Fase Actor Comportamiento data
Entrada Participante Elige el símbolo Símbolo guardado como opción en Participant(choice)
Processing GameRound Compara las opciones con las reglas del juego Resultado inspeccionado
Processing GameRound Se conceden puntos según el valor del resultado Se agregan puntos al objeto Participant(point) ganador
Processing Juego Se comprueba la respuesta para continuar Si la respuesta es true, se continúa; de lo contrario, se sale
Output Juego Nueva ronda del juego o crédito final del juego

Esta vez se centrará en la columna Behavior y la rellenará con métodos que se agregarán a las clases. Además, agregará código a esos métodos para que funcionen de la manera prevista.

Este es el código escrito hasta ahora. Se ampliará en los pasos siguientes:

  1. Debe tener un archivo rock-paper-scissor.py. De lo contrario, siga estos pasos:

  2. Cree el archivo rock-paper-scissor y abra el editor:

    touch rock-paper-scissor.py
    code .
    

    y agregue el código siguiente:

    class Participant:
        def __init__(self):
            self.points = 0
            self.choice = ""
    
    class GameRound:
    
    class Game:
        def __init__(self):
            self.endGame = False
            self.participant = Participant()
            self.secondParticipant = Participant()
    

Iniciar un juego

La primera parte del juego implica su configuración, lo que significa que debe crear una instancia del propio juego y llevarlo a un punto en el que espere a que los participantes actúen.

  1. Reemplace el contenido de rock-paper-scissor.py por este código y guarde el archivo:

    class Participant:
        def __init__(self, name):
            self.name = name
            self.points = 0
            self.choice = ""
        def choose(self):
            self.choice = input("{name}, select rock, paper or scissor: ".format(name= self.name))
            print("{name} selects {choice}".format(name=self.name, choice = self.choice))
    
    class GameRound:
        def __init__(self, p1, p2):
            p1.choose()
            p2.choose()
        def compareChoices(self):
            print("implement")
        def awardPoints(self):
            print("implement")
    
    class Game:
        def __init__(self):
            self.endGame = False
            self.participant = Participant("Spock")
            self.secondParticipant = Participant("Kirk")
        def start(self):
            game_round = GameRound(self.participant, self.secondParticipant)
    
        def checkEndCondition(self):
            print("implement")
        def determineWinner(self):
            print("implement")
    
    game = Game()
    game.start()
    

    Ha agregado los métodos de la tabla a cada objeto. Los cambios que ha realizado se pueden expresar en una tabla para que sea más fácil ver qué comportamiento ha provocado que se agregue cada método.

    Comportamiento Método Actor
    Elige el símbolo choose() Participante
    Compara las opciones compareChoices() GameRound
    Concede puntos awardPoints() GameRound
    Se comprueba la respuesta para continuar checkEndCondition() Juego
    Crédito final del juego determineWinner() Juego

    La mayor parte del comportamiento de la tabla anterior se corresponde a métodos con nombres similares. La excepción es Crédito final del juego, que se convierte en determineWinner(). La razón es que, como parte del final de un juego, es interesante comprobar quién ha ganado e imprimir esa información. Es decisión suya si quiere asignar otro nombre a este método.

  2. Invoque python3 para ejecutar el código:

    python3 rock-paper-scissor.py
    

    El programa genera una salida como la siguiente:

    Spock, select rock, paper or scissor:
    

    Esa salida significa que el programa está a la espera de que actúe.

  3. Para asegurarse de que el programa funciona, seleccione rock y después presione Entrar. Seleccione paper y vuelva a presionar Entrar:

    La salida será similar a este texto:

    Spock, select rock, paper or scissor: rock
    Spock selects rock
    Kirk, select rock, paper or scissor: paper
    Kirk selects paper
    

Nota:

Puede encontrar la solución para este ejercicio en Iniciar un juego: código de la solución.

Implementación de reglas

En la descripción del problema, ha leído que unas opciones ganan a otras. Por ejemplo, la piedra gana a las tijeras, las tijeras ganan al papel, etc. Es tentador escribir código como el siguiente:

if choice1 == "rock" and choice2 == "scissor":
    return 1
elif choice1 == "paper" and choice2 == "scissor":
    return -1
else:
    # something else

# and so on

Como resultado, se escribe una cantidad considerable de código y se hace difícil de manejar. ¿Qué ocurre si el juego necesita expandir su conjunto de reglas, lo que podría dificultar todavía más el mantenimiento del código?

Afortunadamente, existe una solución mejor. Un mejor enfoque consiste en pensar en las reglas como una matriz. La idea de usar una matriz es para expresar qué combinación gana a las demás. Un movimiento ganador obtiene un 1, un empate obtiene un 0 y un movimiento perdedor obtiene un -1. La matriz siguiente es para piedra, papel y tijeras:

Opción Piedra Documento Tijeras
Piedra 0 -1 1
Documento 1 0 -1
Tijeras -1 1 0

Puede implementar la tabla anterior en Python mediante una matriz multidimensional, como la siguiente:

rules = [
    [0, -1, 1],
    [1, 0, -1],
    [-1, 1, 0]
]

rules[0][1] # Rock vs Paper = -1, Paper wins over Rock
  1. Busque la clase Participant y agregue el método toNumericalChoice():

        def toNumericalChoice(self):
            switcher = {
                "rock": 0,
                "paper": 1,
                "scissor": 2
            }
            return switcher[self.choice]
    

    El método anterior convertirá la entrada de cadena de línea de comandos en un entero. De esta forma será más sencillo determinar el ganador de una ronda.

    Sugerencia

    Asegúrese de colocar el cursor para aplicar sangría correctamente en el código.

  2. Busque la clase GameRound y actualice el método compareChoices():

        def compareChoices(self, p1, p2):
            return self.rules[p1.toNumericalChoice()][p2.toNumericalChoice()]
    

    El código le permitirá comparar dos opciones y determinar el ganador.

  3. En la misma clase, agregue el método getResultAsString():

        def getResultAsString(self, result):
            res = {
                0: "draw",
                1: "win",
                -1: "loss"
            }       
            return res[result]
    

    Este método le ayudará a determinar el resultado e imprimirá en la pantalla un texto fácil de entender.

  4. Todavía en la misma clase, reemplace el contenido de __init__() por este código:

        def __init__(self, p1, p2):
            self.rules = [
                [0, -1, 1],
                [1, 0, -1],
                [-1, 1, 0]
            ]
    
            p1.choose()
            p2.choose()
            result = self.compareChoices(p1,p2)
            print("Round resulted in a {result}".format(result = self.getResultAsString(result) ))
    

    El código anterior presenta el campo rules, que contiene una implementación de las reglas para "Piedra, papel, tijeras". Además, la llamada a self.compareChoices()compara las dos opciones realizadas. Por último, hay una fila que imprime en la pantalla resultados sencillos de leer.

Nota:

Puede encontrar la solución para este ejercicio en Implementación de reglas: código de la solución.

Puntuación del juego

La puntuación del juego consiste en asignar puntos al jugador correcto una vez que ha finalizado la partida. El jugador ganador obtiene un punto, un empate o ningún punto en caso de perder.

  1. Busque la clase Participant y agregue el método incrementPoint():

        def incrementPoint(self):
            self.points += 1
    

    Sugerencia

    La introducción de un método para cambiar un atributo es el primer paso hacia la encapsulación. Si quiere, intente cambiar el campo points por __points__ para ocultar el miembro al exterior y practicar con la encapsulación.

  2. Busque la clase GameRound y agregue el código siguiente al final del método __init()__:

            if result > 0:
               p1.incrementPoint()
            elif result < 0:
               p2.incrementPoint()
    

Nota:

Puede encontrar la solución para este ejercicio en Puntuación del juego: código de la solución.

Adición de una consulta de continuación

Una consulta de continuación es una pregunta al final de la ronda del juego en la que se le pregunta al jugador si quiere continuar. Si el usuario decide no continuar, sería recomendable generar los resultados actuales y el ganador, en caso de que lo haya.

  1. Busque la clase Game y actualice el método determineWinner():

        def determineWinner(self):
            resultString = "It's a Draw"
            if self.participant.points > self.secondParticipant.points:
                resultString = "Winner is {name}".format(name=self.participant.name)
            elif self.participant.points < self.secondParticipant.points:
                resultString = "Winner is {name}".format(name=self.secondParticipant.name)
            print(resultString)
    
  2. En la misma clase, actualice el método checkEndCondition():

        def checkEndCondition(self):
            answer = input("Continue game y/n: ")
            if answer == 'y':
                GameRound(self.participant, self.secondParticipant)
                self.checkEndCondition()
            else:
                print("Game ended, {p1name} has {p1points}, and {p2name} has {p2points}".format(p1name = self.participant.name, p1points= self.participant.points, p2name=self.secondParticipant.name, p2points=self.secondParticipant.points))
                self.determineWinner()
                self.endGame = True
    
  3. En la misma clase, reemplace el contenido del método start() por este código:

        def start(self):
            while not self.endGame:
                GameRound(self.participant, self.secondParticipant)
                self.checkEndCondition()
    
  4. Guarde el archivo.

  5. Ejecute el comando python3 rock-paper-scissor.py para probar el programa:

    python3 rock-paper-scissor.py
    

    Seleccione rock y paper como entradas, y escriba n cuando se le pregunte si quiere continuar.

    El resultado tendrá un aspecto similar al texto siguiente:

    Spock, select rock, paper or scissor: rock
    Spock selects rock
    Kirk, select rock, paper or scissor: paper
    Kirk selects paper
    Round resulted in a loss
    Continue game y/n: n
    Game ended, Spock has 0, and Kirk has 1
    Winner is Kirk
    

Nota:

Puede encontrar la solución para este ejercicio en Consulta de continuación: código de la solución.

Enhorabuena, lo ha conseguido. Ha implementado una versión de OOP del juego "Piedra, papel, tijeras".