Ćwiczenie — dodawanie zachowania do gry

Ukończone

Po dodaniu klas i danych do gry w kamień, papier, nożyce przyszedł czas na dodanie ostatniego elementu, czyli zachowania. Dodasz zachowanie w postaci metod. Po dodaniu metod do klas otrzymasz efekt końcowy w postaci działającej gry w kamień, papier, nożyce.

Wdrażanie zachowania

Wiesz już, że w celu utworzenia programu w stylu programowania obiektowego (OOP), najpierw modelujesz, a następnie kodujesz. Modelowanie wygenerowało dane wyjściowe tabeli reprezentujące obiekty, dane i zachowanie, z których wydaje się składać program. Poniżej znajduje się ta sama tabela.

Faza Aktor Zachowanie Data
Dane wejściowe Uczestnik Wybiera symbol Symbol zapisany jako choice (wybór) w obiekcie Participant(choice)
Trwa przetwarzanie GameRound Porównuje wybory z regułami gry Wynik do zbadania
Trwa przetwarzanie GameRound Przydzielenie punktów na podstawie wyniku Punkty dodane do obiektu zwycięzcy Participant(point)
Trwa przetwarzanie Gra Sprawdzenie odpowiedzi na pytanie, czy kontynuować Jeśli odpowiedź to „tak”, kontynuuj, w innym przypadku przerwij
Wyjście Gra Nowa runda lub podsumowanie wyniku gry

Tym razem skupisz się na kolumnie Behavior i wypełnisz ją metodami, które zostaną dodane do klas. Następnie dodasz kod do tych metod, aby działały zgodnie z oczekiwaniami.

Tak wygląda kod do tej pory. Rozszerzymy go w kolejnych krokach:

  1. Powinien istnieć plik rock-paper-scissor.py. Jeśli nie, wykonaj następujące czynności:

  2. Utwórz plik rock-paper-scissor i otwórz edytor:

    touch rock-paper-scissor.py
    code .
    

    i dodaj poniższy kod:

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

Rozpoczynanie gry

Pierwszą częścią gry jest przygotowanie, czyli utworzenie wystąpienia samej gry i dojście do momentu, w którym gra oczekuje na działanie graczy.

  1. Zastąp zawartość rock-paper-scissor.py tym kodem i zapisz plik:

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

    Do każdego obiektu zostały dodane metody z tabeli. Wprowadzone zmiany mogą być wyrażane przez tabelę, dzięki czemu łatwiej jest zobaczyć, jakie zachowanie doprowadziło do dodania metody.

    Zachowanie Metoda Aktor
    Wybiera symbol choose() Uczestnik
    Porównuje wybory compareChoices() GameRound
    Przyznaje punkty awardPoints() GameRound
    Sprawdzenie odpowiedzi na pytanie, czy kontynuować checkEndCondition() Gra
    Podsumowanie wyniku gry determineWinner() Gra

    Większość zachowań z powyższej tabeli odpowiada metodom o podobnych nazwach. Wyjątkiem jest Podsumowanie wyniku gry, które odpowiada metodzie determineWinner() (określenie zwycięzcy). Nazwano ją tak, ponieważ w ramach zakończenia gry dobrze jest sprawdzić, kto wygrał, i wyświetlić tę informację. Jeśli chcesz, możesz nadać tej metodzie inną nazwę.

  2. Uruchom kod, wywołując polecenie python3:

    python3 rock-paper-scissor.py
    

    Program wygeneruje dane wyjściowe podobne do następujących:

    Spock, select rock, paper or scissor:
    

    Te dane wyjściowe oznaczają, że program czeka na Twoje działanie.

  3. Upewnij się, że program działa, wybierając kamień (rock), a następnie naciskając klawisz Enter. Wybierz papier (paper), a następnie ponownie naciśnij klawisz Enter:

    Dane wyjściowe będą wyglądać podobnie do tego tekstu:

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

Uwaga

Rozwiązanie tego ćwiczenia można znaleźć na stronie Rozpocznij grę — kod rozwiązania.

Implementowanie reguł

Z opisu problemu wiesz, że niektóre wybory wygrywają z innymi. Na przykład kamień wygrywa z nożycami, nożyce z papierem itd. To zachęca do napisania kodu podobnego do następującego:

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

# and so on

W ten sposób trzeba by napisać sporo kodu, który nie jest zbyt wygodny w obsłudze. Co się stanie, jeśli zestaw reguł gry zostanie rozszerzony, co jeszcze bardziej skomplikuje utrzymanie kodu?

Na szczęście istnieje lepszy sposób. Lepiej potraktować reguły jako macierz. Założeniem macierzy jest określenie, jaka kombinacja pokonuje inne kombinacje. Wygrywający ruch otrzymuje wartość 1, remis 0, a ruch przegrywający -1. Poniżej znajduje się macierz dla gry w kamień, papier, nożyce:

Opcja wyboru Kamień Dokument Nożyce
Kamień 0 -1 1
Dokument 1 0 -1
Nożyce -1 1 0

Powyższą tabelę można zaimplementować w języku Python przy użyciu tablicy wielowymiarowej, takiej jak ta:

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

rules[0][1] # Rock vs Paper = -1, Paper wins over Rock
  1. Znajdź klasę Participant i dodaj metodę toNumericalChoice():

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

    Powyższa metoda przekonwertuje ciąg wprowadzony w wierszu polecenia na liczbę całkowitą. Ułatwi to określenie, kto wygrał rundę.

    Napiwek

    Pamiętaj, aby umieścić kursor w celu poprawnego wcięcia kodu.

  2. Znajdź klasę GameRound i zaktualizuj metodę compareChoices() :

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

    Ten kod umożliwia porównanie dwóch wyborów i określenie zwycięzcy.

  3. W tej samej klasie dodaj metodę getResultAsString():

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

    Ta metoda pomoże określić wynik i wyświetlić zrozumiały tekst na ekranie.

  4. W tej samej klasie zastąp zawartość funkcji __init__() tym kodem:

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

    Powyższy kod wprowadza pole rules (reguły), które zawiera implementację reguł gry w kamień, papier, nożyce. Ponadto wywołanie , aby porównać self.compareChoices() dwie dokonane opcje. Na końcu znajduje się wiersz wyświetlający czytelny komunikat z wynikami na ekranie.

Uwaga

Rozwiązanie tego ćwiczenia można znaleźć w sekcji Implementowanie reguł — kod rozwiązania.

Punktacja gry

Po zakończeniu rozgrywki należy przyznać punkty właściwemu graczowi. Możliwe wyniki to jeden punkt dla zwycięzcy, remis lub zero punktów w przypadku przegranej.

  1. Znajdź klasę Participant i dodaj metodę incrementPoint():

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

    Napiwek

    Wprowadzenie metody w celu zmiany atrybutu to pierwszy krok do hermetyzacji danych. Jeśli chcesz, możesz zmienić pole points na __points__, aby ukryć ten element członkowski przed obiektami zewnętrznymi, czyli zastosować hermetyzację.

  2. Znajdź klasę GameRound i dodaj następujący kod na końcu metody __init()__:

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

Uwaga

Rozwiązanie tego ćwiczenia można znaleźć na stronie Score game - solution code (Ocenianie gry — kod rozwiązania).

Dodawanie zapytania o kontynuację

Zapytanie o kontynuację oznacza pytanie, czy kontynuować, wyświetlane graczowi po zakończeniu rundy. Jeśli użytkownik zdecyduje się nie kontynuować, może wyświetlić bieżące wyniki i zwycięzcę, jeśli istnieje.

  1. Znajdź klasę Game i zaktualizuj metodę 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. W tej samej klasie zaktualizuj metodę 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. W tej samej klasie zastąp kod metody start() tym kodem:

        def start(self):
            while not self.endGame:
                GameRound(self.participant, self.secondParticipant)
                self.checkEndCondition()
    
  4. Zapisz plik.

  5. Uruchom polecenie python3 rock-paper-scissor.py, aby przetestować program:

    python3 rock-paper-scissor.py
    

    Wybierz dane wejściowe rock i paper, a po wyświetleniu pytania, czy kontynuować, wprowadź n.

    Dane wyjściowe będą podobne do poniższego tekstu:

    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
    

Uwaga

Rozwiązanie tego ćwiczenia można znaleźć w sekcji Kontynuacja zapytania — kod rozwiązania.

Gratulacje! Masz wersję gry w kamień, papier, nożyce zaimplementowaną przy użyciu programowania obiektowego.