Exercício – adicionar comportamento ao seu jogo

Concluído

Agora que você adicionou classes e dados ao seu jogo de pedra, papel e tesoura, é hora de adicionar a última parte, que é o comportamento. Você adicionará o comportamento na forma de métodos. Você adicionará métodos às suas classes e o resultado final será um jogo de pedra, papel e tesoura funcional.

Implementar comportamento

Você já aprendeu que, para criar um programa em estilo OOP (programação orientada a objeto), primeiro você modela e depois codifica. A modelagem produziu a saída de uma tabela que representava por quais objetos, dados e comportamento o seu programa parece ser composto. Esta é a mesma tabela novamente.

Fase Ator Comportamento Dados
Entrada Participante Escolhe um símbolo Símbolo salvo como escolha em Participant(choice)
Processando GameRound Compara a escolha tendo em mente as regras do jogo Resultado inspecionado
Processando GameRound Recebe pontos com base no valor do resultado Pontos adicionados ao Participant(point) vencedor
Processando Game Verificar resposta continuar A resposta é true, continuar; caso contrário, sair
Saída Game Nova crédito de rodada do jogo ou fim do jogo

Desta vez, você se concentrará na coluna Behavior e a preencherá com métodos que serão adicionados às suas classes. Além disso, você adicionará código a esses métodos para que eles funcionem da maneira que deveriam.

Este é o código até o momento. Vamos estendê-lo nas seguintes etapas:

  1. Você deve ter um arquivo rock-paper-scissor.py. Se não tiver, siga estas etapas:

  2. Crie o arquivo rock-paper-scissor e abra o editor:

    touch rock-paper-scissor.py
    code .
    

    e adicione o código abaixo:

    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 um jogo

A primeira parte do jogo envolve configurá-lo, o que significa criar uma instância do próprio jogo e levar o jogo para um ponto em que ele fique aguardando a ação dos participantes.

  1. Substitua o conteúdo de rock-paper-scissor.py por este código e salve o arquivo:

    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()
    

    Você adicionou os métodos de sua tabela a cada objeto. As alterações feitas podem ser expressas por uma tabela para que seja mais fácil ver qual comportamento levou ao método que está sendo adicionado.

    Comportamento Método Ator
    Escolhe um símbolo choose() Participante
    Compara opções compareChoices() GameRound
    Recebe pontos awardPoints() GameRound
    Verificar resposta continuar checkEndCondition() Game
    Crédito final do jogo determineWinner() Game

    A maior parte do comportamento na tabela anterior corresponde a métodos com nomes semelhantes. A exceção é Crédito final do jogo, que se torna determineWinner(). O motivo é que, como parte do final de um jogo, convém verificar quem ganhou e imprimir essas informações. Fica a seu critério dar outro nome a esse método.

  2. Execute o código invocando python3:

    python3 rock-paper-scissor.py
    

    O programa produz uma saída como esta:

    Spock, select rock, paper or scissor:
    

    Essa saída significa que o programa está aguardando sua ação.

  3. Verifique se o programa funciona selecionando rock e, em seguida, selecionando Enter. Selecione paper e Enter novamente:

    A saída será semelhante a este texto:

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

Observação

Encontre a solução para este exercício em Iniciar um jogo – código de solução.

Implementar regras

Na descrição do problema, você lê que algumas escolhas vencem outras. Por exemplo, pedra vence tesoura, tesoura vence papel e assim por diante. É tentador escrever um código semelhante ao seguinte:

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

# and so on

Resulta em uma quantidade significativa de código escrito e torna-se um pouco difícil. E se o jogo precisar expandir o conjunto de regras, o que poderá tornar o código ainda mais difícil de ser mantido?

Felizmente, há uma forma melhor. Uma abordagem melhor é pensar nas regras como uma matriz. A ideia de usar uma matriz é expressar qual combinação ganha de outras combinações. Uma jogada vencedora recebe um 1, um empate recebe um 0 e uma jogada perdedora recebe um -1. A seguinte matriz destina-se a pedra, papel e tesoura:

Escolha Pedra Papel Tesoura
Pedra 0 -1 1
Papel 1 0 -1
Tesoura -1 1 0

Você pode implementar a tabela anterior em Python usando uma matriz multidimensional, desta forma:

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

rules[0][1] # Rock vs Paper = -1, Paper wins over Rock
  1. Localize a classe Participant e adicione o método toNumericalChoice():

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

    o método anterior converterá sua entrada de cadeia de caracteres de linha de comando em um inteiro. Isso facilitará a determinação de quem ganhou uma rodada.

    Dica

    Posicione o cursor para recuar o código corretamente.

  2. Localize a classe GameRound e atualize o método compareChoices():

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

    o código permitirá que você compare duas escolhas e determine o vencedor.

  3. Na mesma classe, adicione o método getResultAsString():

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

    esse método ajudará a determinar o resultado e imprimirá um texto fácil de entender na tela.

  4. Ainda na mesma classe, substitua o conteúdo 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) ))
    

    O código anterior introduz o campo rules, que contém uma implementação das regras para pedra, papel e tesoura. Além disso, a chamada a self.compareChoices()compara duas escolhas feitas. Por fim, há uma linha que imprime resultados amigáveis para leitores na tela.

Observação

Encontre a solução para este exercício em Implementar regras – código de solução.

Pontuar o jogo

A pontuação do jogo trata-se de atribuir pontos ao jogador correto após o término do jogo. O jogador vencedor recebe um ponto, um empate ou nenhum ponto caso perca.

  1. Localize a classe Participant e adicione o método incrementPoint():

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

    Dica

    A introdução de um método para alterar um atributo é a primeira etapa para o encapsulamento. Se desejar, tente alterar o campo points para __points__ a fim de ocultar o membro exterior para praticar o encapsulamento.

  2. Localize a classe GameRound e adicione o seguinte código ao final do método __init()__:

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

Observação

Encontre a solução para este exercício em Pontuar o jogo – código de solução.

Adicionar uma consulta de continuação

Uma consulta de continuação é perguntar, no final da rodada do jogo, se o jogador quer continuar. Se o usuário optar por não continuar, ele poderá gerar os resultados atuais e o vencedor, se houver.

  1. Localize a classe Game e atualize o 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. Na mesma classe, atualize o 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. Na mesma classe, substitua o código do método start() por este código:

        def start(self):
            while not self.endGame:
                GameRound(self.participant, self.secondParticipant)
                self.checkEndCondition()
    
  4. Salve o arquivo.

  5. Execute o comando python3 rock-paper-scissor.py para testar o seu programa:

    python3 rock-paper-scissor.py
    

    Selecione rock e paper como entradas e insira n quando for solicitado a continuar.

    A saída é semelhante ao seguinte texto:

    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
    

Observação

Encontre a solução para este exercício em Consulta de continuação – código de solução.

Parabéns, você concluiu! Você implementou uma versão OOP do jogo pedra, papel e tesoura.