Ćwiczenie — dodawanie zachowania do gry
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:
Powinien istnieć plik
rock-paper-scissor.py
. Jeśli nie, wykonaj następujące czynności: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.
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ę.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.
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
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.
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.
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.
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.
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ę.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.
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)
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
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()
Zapisz plik.
Uruchom polecenie
python3 rock-paper-scissor.py
, aby przetestować program:python3 rock-paper-scissor.py
Wybierz dane wejściowe
rock
ipaper
, 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.