Zaimplementujemy zjadanie owoców przez węża w taki sposób, by znikały one z mapy.
Zastanówmy się, gdzie powinniśmy zacząć pisanie kodu. Teoretycznie czynność zjadania wykonywana jest przez węża, czyli właśnie w klasie Wąż powinniśmy ją obsługiwać. Jednak skąd w klasie Wąż ma znaleźć się informacja, że na danym polu jest owoc?
Informacje o wszystkich polach przechowuje klasa Plansza. Z diagramu klas wynika również, że klasa Wąż otrzymuje informacje od klasy Plansza.
Oznacza to, że jeśli chcemy wykonać program zgodnie z diagramem klas, przekazujemy referencję dotyczącą układu mapy z obiektu klasy Plansza do klasy obiektu Wąż, by ta następnie wykonywała na niej metodę zjedzOwoc().
Jednak klasa Wąż nie posiada zgodnie z założeniami żadnego atrybutu, który mógłby na stałe przechowywać referencję do obiektu reprezentującego planszę.
Problem możemy rozwiązać na kilka sposobów:
Zmodyfikować diagram klas, dopasowując go do oczekiwań – jednak celem jest zrealizowanie projektu, bez konieczności wprowadzania zmian na wcześniejszych etapach (i ponownego testowania wszystkich funkcji).
Przekazać jednej z metod obiektu klasy Wąż referencję do klasy Plansza, aby tam korzystać z metody zjedzOwoc().
Przekazać jednej z metod obiektu klasy Plansza referencję do obiektu klasy Wąż i – analogicznie jak w poprzednim podpunkcie – skorzystać w niej z metody zjedzOwoc().
Przekazywać obie referencje – do obiektów reprezentujących węża i planszę – w ramach metody wyświetlGrę(), dostępnej w ramach klasy WyświetlanieGrafiki.
Oczywiście, potencjalnych rozwiązań może być dużo więcej. Jednak nie wszystkie są poprawne. Przykładowo: rozwiązanie polegające na obsłudze zjadania owoców w klasie przeznaczonej do wyświetlania grafiki działałoby w sposób zawiły i nielogiczny. Tym samym znacząco utrudnilibyśmy analizę, serwisowanie i dalszą rozbudowę kodu. Dodatkowo rozwiązania takie są uważane za niezgodne z koncepcją programowania obiektowego, która zakłada, że klasy i obiekty reprezentują pewne sprecyzowane byty i ich zastosowania.
Język C++ i zjadanie owoców
We wszystkich trzech językach skorzystamy z drugiego z przedstawionych sposobów. Referencja zostanie przekazana do metody przesun(), która od tego momentu będzie obsługiwała (poza przesuwaniem węża) każdą potencjalną sytuację – np. zjadanie owoców czy przegranie gry.
Zdefiniowaliśmy wszystkie klasy w jednym pliku bez rozdzielania ich. W przypadku dużych projektów, które mają większą liczbę klas, dobrą praktyką jest umieszczanie każdej klasy w oddzielnym pliku źródłowym. Ułatwia to poruszanie się po projekcie i sprawia, że kod jest czytelny. Nie robimy tego, ponieważ omawiany projekt jest mały i zawiera niewiele klas. Pamiętajmy jednak, że kompilator przetwarza kod źródłowy od pierwszej linii do ostatniej. Dlatego jeżeli w jakiejś klasie wykorzystujemy inne klasy, muszą one zostać zdefiniowane wcześniej (wyżej) w kodzie.
Modyfikujemy kod w taki sposób, aby definicja klasy Plansza znajdowała się przed definicją klasy Waz.
Już wiesz
Deklaracja klasy polega na podaniu jej nazwy.
Definicja klasy polega na zadeklarowaniu jej metod oraz atrybutów. W ramach definicji klasy nie musimy od razu definiować metod, a jedynie je zadeklarować.
W omawianym przypadku najprościej będzie przenieść definicje klas Owoc i Plansza przed definicje klasy Waz, o ile wcześniej nie występowały w takiej kolejności.
Następnie modyfikujemy argumenty dostarczane do metody przesun().
Dwa pierwsze z argumentów będą przekazywały wskaźniki do zmiennych WatekWazPracuje i WatekGrafikaPracuje, które informują o tym, czy poszczególne wątki działają, czy też są zatrzymane. Trzeci argument będzie przekazywał wskaźnik do obiektu reprezentującego planszę.
Odpowiednie zmiany wprowadzamy w ramach funkcji main(), żeby w funkcji uruchamiającej wątek zgadzały się argumenty.
Po zweryfikowaniu, że przedstawione modyfikacje nie powodują błędów w programie, dopisujemy sprawdzenie, czy na danym polu znajduje się owoc.
Tworzymy:
Linia 1. void przesun otwórz nawias okrągły bool asterysk watekGrafikiPracuje przecinek bool asterysk watekWazPracuje przecinek Plansza asterysk plansza zamknij nawias okrągły.
Linia 2. otwórz nawias klamrowy.
Linia 3. while otwórz nawias okrągły 1 zamknij nawias okrągły.
Linia 4. otwórz nawias klamrowy.
Linia 5. while otwórz nawias okrągły asterysk watekGrafikiPracuje zamknij nawias okrągły.
Linia 6. otwórz nawias klamrowy.
Linia 7. std dwukropek dwukropek this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły std dwukropek dwukropek chrono dwukropek dwukropek milliseconds otwórz nawias okrągły 10 zamknij nawias okrągły zamknij nawias okrągły średnik.
Linia 8. zamknij nawias klamrowy.
Linia 9. asterysk watekWazPracuje znak równości true średnik.
Linia 10. if otwórz nawias okrągły kierunekRuchu znak równości znak równości polnoc zamknij nawias okrągły.
Linia 11. otwórz nawias klamrowy.
Linia 12. pozycjaWezaY minus znak równości 1 średnik.
Linia 13. zamknij nawias klamrowy.
Linia 14. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości poludnie zamknij nawias okrągły.
Linia 15. otwórz nawias klamrowy.
Linia 16. pozycjaWezaY plus znak równości 1 średnik.
Linia 17. zamknij nawias klamrowy.
Linia 18. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości zachod zamknij nawias okrągły.
Linia 19. otwórz nawias klamrowy.
Linia 20. pozycjaWezaX plus znak równości 1 średnik.
Linia 21. zamknij nawias klamrowy.
Linia 22. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości wschod zamknij nawias okrągły.
Linia 23. otwórz nawias klamrowy.
Linia 24. pozycjaWezaX minus znak równości 1 średnik.
Linia 25. zamknij nawias klamrowy.
Linia 26. asterysk watekWazPracuje znak równości false średnik.
Linia 27. Owoc owocNaPolu znak równości plansza minus zamknij nawias ostrokątny pole otwórz nawias kwadratowy pozycjaWezaY zamknij nawias kwadratowy otwórz nawias kwadratowy pozycjaWezaX zamknij nawias kwadratowy kropka owoc średnik.
Linia 28. std dwukropek dwukropek cout otwórz nawias ostrokątny otwórz nawias ostrokątny owocNaPolu kropka sprawdzPunkty otwórz nawias okrągły zamknij nawias okrągły otwórz nawias ostrokątny otwórz nawias ostrokątny std dwukropek dwukropek endl średnik.
Linia 29. std dwukropek dwukropek this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły std dwukropek dwukropek chrono dwukropek dwukropek milliseconds otwórz nawias okrągły 500 zamknij nawias okrągły zamknij nawias okrągły średnik.
Linia 30. zamknij nawias klamrowy.
Linia 31. zamknij nawias klamrowy.
Po zastosowaniu tej definicji metody wyświetli się liczba punktów danego pola na mapie.
Warto zwrócić uwagę, że wartość ta pokazuje się o jeden krok animacji przed momentem, gdy wąż faktycznie wejdzie na daną kratkę. Dzieje się tak, ponieważ jeden wątek obsługuje przesuwanie, a inny wyświetlanie grafiki.
RGpp7cgfP0kVR
Po przetestowaniu praktycznego działania programu, przejdźmy do implementacji zjadania owocu. Pod warunkiem jednak, że ten owoc znajduje się na danym polu:
Linia 1. void przesun otwórz nawias okrągły bool asterysk watekGrafikiPracuje przecinek bool asterysk watekWazPracuje przecinek Plansza asterysk plansza zamknij nawias okrągły.
Linia 2. otwórz nawias klamrowy.
Linia 3. while otwórz nawias okrągły 1 zamknij nawias okrągły.
Linia 4. otwórz nawias klamrowy.
Linia 5. while otwórz nawias okrągły asterysk watekGrafikiPracuje zamknij nawias okrągły.
Linia 6. otwórz nawias klamrowy.
Linia 7. std dwukropek dwukropek this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły std dwukropek dwukropek chrono dwukropek dwukropek milliseconds otwórz nawias okrągły 10 zamknij nawias okrągły zamknij nawias okrągły średnik.
Linia 8. zamknij nawias klamrowy.
Linia 9. asterysk watekWazPracuje znak równości true średnik.
Linia 10. if otwórz nawias okrągły kierunekRuchu znak równości znak równości polnoc zamknij nawias okrągły.
Linia 11. otwórz nawias klamrowy.
Linia 12. pozycjaWezaY minus znak równości 1 średnik.
Linia 13. zamknij nawias klamrowy.
Linia 14. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości poludnie zamknij nawias okrągły.
Linia 15. otwórz nawias klamrowy.
Linia 16. pozycjaWezaY plus znak równości 1 średnik.
Linia 17. zamknij nawias klamrowy.
Linia 18. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości zachod zamknij nawias okrągły.
Linia 19. otwórz nawias klamrowy.
Linia 20. pozycjaWezaX plus znak równości 1 średnik.
Linia 21. zamknij nawias klamrowy.
Linia 22. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości wschod zamknij nawias okrągły.
Linia 23. otwórz nawias klamrowy.
Linia 24. pozycjaWezaX minus znak równości 1 średnik.
Linia 25. zamknij nawias klamrowy.
Linia 26. asterysk watekWazPracuje znak równości false średnik.
Linia 27. Owoc asterysk owocNaPolu znak równości ampersant plansza minus zamknij nawias ostrokątny pole otwórz nawias kwadratowy pozycjaWezaY zamknij nawias kwadratowy otwórz nawias kwadratowy pozycjaWezaX zamknij nawias kwadratowy kropka owoc średnik.
Linia 28. if otwórz nawias okrągły owocNaPolu minus zamknij nawias ostrokątny sprawdzPunkty otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły.
Linia 29. otwórz nawias klamrowy.
Linia 30. zjedzOwoc otwórz nawias okrągły owocNaPolu zamknij nawias okrągły średnik.
Linia 31. zamknij nawias klamrowy.
Linia 32. std dwukropek dwukropek this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły std dwukropek dwukropek chrono dwukropek dwukropek milliseconds otwórz nawias okrągły 500 zamknij nawias okrągły zamknij nawias okrągły średnik.
Linia 33. zamknij nawias klamrowy.
Linia 34. zamknij nawias klamrowy.
Następnie implementujemy metodę zjedzOwoc().
Istotne jest, że klient na żadnym etapie prac nie wymagał, żeby po zjedzeniu owocu na planszy pojawiał się kolejny owoc. Nie zostało to też uwzględnione na żadnym diagramie.
Dlatego nie zaimplementujemy takiej opcji, przez co metoda zjedzOwoc() przyjmie postać:
Linia 1. void zjedzOwoc otwórz nawias okrągły Owoc asterysk owoc zamknij nawias okrągły.
Linia 2. otwórz nawias klamrowy.
Linia 3. liczbaPunktow plus znak równości owoc minus zamknij nawias ostrokątny sprawdzPunkty otwórz nawias okrągły zamknij nawias okrągły średnik.
Linia 4. owoc minus zamknij nawias ostrokątny ustawPunkty otwórz nawias okrągły 0 zamknij nawias okrągły średnik.
Linia 5. zamknij nawias klamrowy.
Dla zainteresowanych
Spróbuj samodzielnie zaimplementować pojawianie się owoców w losowych miejscach po zjedzeniu któregoś z nich. W tym celu przydatne może być przekazanie jako argumentu klasy Plansza do metody zjedzOwoc(). Nie umieszczaj owoców w miejscu, gdzie atrybut pola wartoscPola jest różny od 0, ponieważ wtedy na nim znajduje się owoc. Pamiętaj też, żeby nie umieszczać na sobie dwóch owoców.
W ramach tego materiału będziemy posługiwać się diagramami. Żadne z dalszych działań nie uwzględni faktu, że owoce będą się dodawały na bieżąco do mapy.
Dotychczas napisany kod źródłowy w języku C++ można pobrać, korzystając z linku:
Ridn3RcDeURsW
Ważne!
Upewnij się, że masz zainstalowany kompilator w wersji, która obsługuje standard języka C++11. W razie problemów instrukcję instalacji standardu znajdziesz w sieci.
Język Java i zjadanie owoców
Implementując zjadanie owoców w języku Java, odejdziemy od projektu programu i dodamy do klasy Waz informacje na temat planszy, poprzez dodanie odpowiedniego atrybutu. Nie jest to jedyne rozwiązanie, ale inne – w większym stopniu – niszczyłyby ład wcześniej opracowanych diagramów.
Na przykładzie problemu zjadania owocu widać, że nawet jeżeli diagram jest ogólny, to jego implementacja może się różnić w zależności od języka, mimo że w domyśle nie powinna. Omawiany problem udało się rozwiązać lepiej w przypadku języka C++, w którym podczas tworzenia wątku można dodać argument do metody przesun(), którą ten wątek wykorzysta (co jest zaprezentowane w sekcji przeznaczonej dla tego języka). Niestety w języku Java nie istnieje żaden prosty sposób na rozwiązanie problemu zjadania owocu.
Innym rozwiązaniem tego problemu potencjalnie mogłaby być zmiana w ramach metody wyswietlGre(), w której zaimplementowalibyśmy wszystkie operacje związane z usuwaniem owoców. Rozwiązanie takie byłoby jednak sprzeczne z dobrą praktyką, polegającą na dbaniu o schludność i łatwość analizy kodu. Umieszczanie fragmentu odpowiedzialnego za logiczne usuwanie owoców w metodzie, która służy do wyświetlania grafiki, utrudniałoby analizę kodu.
Kolejnym nie do końca odpowiednim rozwiązaniem byłoby dodanie do obiektu klasy Plansza referencji do obiektu klasy Waz. Byłby to zabieg niezgodny z wcześniej ustalonym diagramem, do czego z założenia wolimy nie dopuszczać. Warto w tym miejscu zwrócić uwagę, że już na etapie tworzenia diagramów można było się zastanowić, czy klasa Plansza nie powinna przechowywać referencji do obiektu klasy Waz, analogicznie jak w przypadku owoców. Jeżeli umieścilibyśmy referencję Waz w obiekcie klasy Plansza i z jej poziomu wywoływali jego metodę, to wtedy nie wąż byłby samodzielnym wątkiem, a plansza. Takie podejście również jest poprawne, jednak wymagałoby od nas przebudowania znacznej części kodu.
W projekcie do klasy Waz dodamy bezpośrednio z konstruktora parametr o nazwie planszy. Jest to rozwiązanie najprostsze i niewprowadzające do kodu chaosu, ani nie wymagające istotnych zmian w całej logice gry. Definicja klasy wygląda następująco:
Linia 1. public class Waz implements Runnable otwórz nawias klamrowy.
Linia 2. enum Kierunek otwórz nawias klamrowy polnoc przecinek wschod przecinek poludnie przecinek zachod zamknij nawias klamrowy.
Linia 3. private int liczbaPunktow znak równości 0 średnik.
Linia 4. private int pozycjaWezaX znak równości 0 średnik.
Linia 5. private int pozycjaWezaY znak równości 0 średnik.
Linia 6. private Kierunek kierunekRuchu znak równości Kierunek kropka polnoc średnik.
Linia 7. private Plansza plansza średnik.
Linia 8. kropka kropka kropka.
Z kolei kod klasy Main modyfikujemy w taki sposób:
Linia 1. public class Main otwórz nawias klamrowy.
Linia 3. public static void main otwórz nawias okrągły String otwórz nawias kwadratowy zamknij nawias kwadratowy args zamknij nawias okrągły otwórz nawias klamrowy.
Linia 5. File plik znak równości new File otwórz nawias okrągły cudzysłów config kropka ini cudzysłów zamknij nawias okrągły średnik.
Linia 6. Scanner sc znak równości null średnik.
Linia 7. try otwórz nawias klamrowy.
Linia 8. sc znak równości new Scanner otwórz nawias okrągły plik zamknij nawias okrągły średnik.
Linia 9. zamknij nawias klamrowy catch otwórz nawias okrągły FileNotFoundException e zamknij nawias okrągły otwórz nawias klamrowy.
Linia 10. e kropka printStackTrace otwórz nawias okrągły zamknij nawias okrągły średnik.
Linia 11. zamknij nawias klamrowy.
Linia 12. int rozmiar znak równości 0 średnik.
Linia 13. int pozycjaWezaX znak równości 0 średnik.
Linia 14. int pozycjaWezaY znak równości 0 średnik.
Linia 15. while otwórz nawias okrągły sc kropka hasNext otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy.
Linia 16. String nazwaParametru znak równości sc kropka next otwórz nawias okrągły zamknij nawias okrągły średnik.
Linia 17. switch otwórz nawias okrągły nazwaParametru zamknij nawias okrągły otwórz nawias klamrowy.
Linia 18. case cudzysłów rozmiar cudzysłów dwukropek.
Linia 19. rozmiar znak równości Integer kropka parseInt otwórz nawias okrągły sc kropka next otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły średnik.
Linia 20. break średnik.
Linia 21. case cudzysłów pozycjaWezaX cudzysłów dwukropek.
Linia 22. pozycjaWezaX znak równości Integer kropka parseInt otwórz nawias okrągły sc kropka next otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły średnik.
Linia 23. break średnik.
Linia 24. case cudzysłów pozycjaWezaY cudzysłów dwukropek.
Linia 25. pozycjaWezaY znak równości Integer kropka parseInt otwórz nawias okrągły sc kropka next otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły średnik.
Linia 26. break średnik.
Linia 27. zamknij nawias klamrowy.
Linia 28. zamknij nawias klamrowy.
Linia 30. Plansza plansza znak równości new Plansza otwórz nawias okrągły rozmiar zamknij nawias okrągły średnik.
Linia 31. Waz waz znak równości new Waz otwórz nawias okrągły pozycjaWezaX przecinek pozycjaWezaY przecinek Waz kropka Kierunek kropka polnoc przecinek plansza zamknij nawias okrągły średnik.
Linia 32. ObslugaWejscia wejscie znak równości new ObslugaWejscia otwórz nawias okrągły waz zamknij nawias okrągły średnik.
Linia 33. WyswietlanieGrafiki grafika znak równości new WyswietlanieGrafiki otwórz nawias okrągły waz przecinek plansza przecinek wejscie zamknij nawias okrągły średnik.
Linia 34. Thread watekGrafiki znak równości new Thread otwórz nawias okrągły grafika zamknij nawias okrągły średnik.
Linia 35. watekGrafiki kropka start otwórz nawias okrągły zamknij nawias okrągły średnik.
Linia 36. Thread watekWeza znak równości new Thread otwórz nawias okrągły waz zamknij nawias okrągły średnik.
Linia 37. watekWeza kropka start otwórz nawias okrągły zamknij nawias okrągły średnik.
Linia 38. zamknij nawias klamrowy.
Linia 39. zamknij nawias klamrowy.
Konstruktor klasy Waz prezentuje się teraz tak:
Linia 1. Waz otwórz nawias okrągły int pozycjaWezaX przecinek int pozycjaWezaY przecinek Kierunek kierunekRuchu przecinek Plansza plansza zamknij nawias okrągły otwórz nawias klamrowy.
Linia 2. this kropka pozycjaWezaX znak równości pozycjaWezaX średnik.
Linia 3. this kropka pozycjaWezaY znak równości pozycjaWezaY średnik.
Linia 4. this kropka kierunekRuchu znak równości kierunekRuchu średnik.
Linia 5. this kropka plansza znak równości plansza średnik.
Linia 6. zamknij nawias klamrowy.
Następnie w metodzie przesun() sprawdzamy, czy w miejscu, gdzie jest głowa węża, znajduje się owoc.
Linia 1. void przesun otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy.
Linia 2. if otwórz nawias okrągły kierunekRuchu znak równości znak równości Kierunek kropka polnoc zamknij nawias okrągły otwórz nawias klamrowy.
Linia 3. pozycjaWezaY minus znak równości 1 średnik.
Linia 4. zamknij nawias klamrowy.
Linia 5. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości Kierunek kropka poludnie zamknij nawias okrągły otwórz nawias klamrowy.
Linia 6. pozycjaWezaY plus znak równości 1 średnik.
Linia 7. zamknij nawias klamrowy.
Linia 8. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości Kierunek kropka zachod zamknij nawias okrągły otwórz nawias klamrowy.
Linia 9. pozycjaWezaX plus znak równości 1 średnik.
Linia 10. zamknij nawias klamrowy.
Linia 11. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości Kierunek kropka wschod zamknij nawias okrągły otwórz nawias klamrowy.
Linia 12. pozycjaWezaX minus znak równości 1 średnik.
Linia 13. zamknij nawias klamrowy.
Linia 14. Owoc owoc znak równości plansza kropka pole otwórz nawias kwadratowy pozycjaWezaY zamknij nawias kwadratowy otwórz nawias kwadratowy pozycjaWezaX zamknij nawias kwadratowy kropka owoc średnik.
Linia 15. if otwórz nawias okrągły owoc wykrzyknik znak równości null zamknij nawias okrągły.
Linia 16. System kropka out kropka println otwórz nawias okrągły owoc zamknij nawias okrągły średnik.
Linia 17. zamknij nawias klamrowy.
Po uruchomieniu modyfikacji i sprawdzeniu, czy wszystko działa, przechodzimy do zaimplementowania metody zjedzOwoc(), która powinna znaleźć się w klasie Waz.
Ponieważ na żadnym etapie projektowania i rozmów nie zostało ustalone, że owoce mają się ponownie pojawiać, nie zaimplementujemy tej opcji.
Dla zainteresowanych
Zaimplementuj pojawianie się owoców po ich zjedzeniu w metodzie zjedzOwoc(). Owoce nie mogą pojawiać się na w miejscu, w którym już są oraz w ogonie węża.
Zanim przystąpimy do implementacji metody zjedzOwoc(), tworzymy dwie metody, które powinny znaleźć się w definicji klasy Owoc: sprawdzPunkty() oraz ustawPunkty(). Pierwsza z nich zwraca liczbę punktów danego owocu. Druga natomiast służy do ustawienia liczby punktów owocu.
Ostatecznie uzyskujemy następującą postać klasy Owoc:
Linia 1. public class Owoc otwórz nawias klamrowy.
Linia 2. private int liczbaPunktow znak równości 0 średnik.
Linia 3. public void ustawPunkty otwórz nawias okrągły int liczbaPunktow zamknij nawias okrągły otwórz nawias klamrowy.
Linia 4. this kropka liczbaPunktow znak równości liczbaPunktow średnik.
Linia 5. zamknij nawias klamrowy.
Linia 6. public int sprawdzPunkty otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy.
Linia 7. return this kropka liczbaPunktow średnik.
Linia 8. zamknij nawias klamrowy.
Linia 9. zamknij nawias klamrowy.
Metoda zjedzOwoc() wygląda tak:
Linia 1. void zjedzOwoc otwórz nawias okrągły Owoc owoc zamknij nawias okrągły otwórz nawias klamrowy.
Linia 2. liczbaPunktow plus znak równości owoc kropka sprawdzPunkty otwórz nawias okrągły zamknij nawias okrągły średnik.
Linia 3. owoc kropka ustawPunkty otwórz nawias okrągły 0 zamknij nawias okrągły średnik.
Linia 4. zamknij nawias klamrowy.
Napotykamy tutaj dość istotny problem. W przypadku projektu w języku Java jeżeli nawet dla obiektu klasy Owoc w danym obiekcie klasy Pole ustawimy wartość punktów na 0, to owoc ten nie zniknie z danego pola.
W wymaganiach zapisano, że owoc ma znikać po zjedzeniu go przez węża.
Rozwiązania tego problemu są dwa. Możemy wyświetlać owoce, które mają wartości niezerowe. Przy założeniu, że nowe owoce się nie pojawiają, jest to rozwiązanie akceptowalne. Jednakże odrzucimy je, aby nie utrudniać ewentualnej późniejszej implementacji pojawiania się owocu.
Skorzystamy z drugiego rozwiązania, polegającego na zmianie budowy klasy zjedzOwoc(). Zamiast jako argument podawać referencje na obiekt klasy Owoc, będziemy podawali tam referencje na obiekt klasy Pole, które zawiera obiekt klasy Owoc.
Linia 1. void zjedzOwoc otwórz nawias okrągły Pole poleNaKtorymJestOwoc zamknij nawias okrągły otwórz nawias klamrowy.
Linia 2. liczbaPunktow plus znak równości poleNaKtorymJestOwoc kropka owoc kropka sprawdzPunkty otwórz nawias okrągły zamknij nawias okrągły średnik.
Linia 3. poleNaKtorymJestOwoc kropka owoc kropka ustawPunkty otwórz nawias okrągły 0 zamknij nawias okrągły średnik.
Linia 4. poleNaKtorymJestOwoc kropka owoc znak równości null średnik.
Linia 5. System kropka out kropka println otwórz nawias okrągły liczbaPunktow zamknij nawias okrągły średnik.
Linia 6. zamknij nawias klamrowy.
Przeprowadzamy też zmianę w ramach wywołania tej metody w metodzie przesun().
Linia 1. kropka kropka kropka.
Linia 2. Owoc owoc znak równości plansza kropka pole otwórz nawias kwadratowy pozycjaWezaY zamknij nawias kwadratowy otwórz nawias kwadratowy pozycjaWezaX zamknij nawias kwadratowy kropka owoc średnik.
Linia 3. if otwórz nawias okrągły owoc wykrzyknik znak równości null zamknij nawias okrągły.
Linia 4. zjedzOwoc otwórz nawias okrągły plansza kropka pole otwórz nawias kwadratowy pozycjaWezaY zamknij nawias kwadratowy otwórz nawias kwadratowy pozycjaWezaX zamknij nawias kwadratowy zamknij nawias okrągły średnik.
Możemy zauważyć, że wszystkie owoce mają wartość 0 punktów (a więc zebranie owocu nie powoduje dodania punktów graczowi). Jest to ewidentnie sytuacja niepożądana, która wymaga poprawek w kodzie.
Ustawianie punktów dla owoców dodamy w ramach metody losujPlansze() z klasy Plansza.
Linia 1. void losujPLansze otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy.
Linia 2. Random rd znak równości new Random otwórz nawias okrągły zamknij nawias okrągły średnik.
Linia 3. for otwórz nawias okrągły int i znak równości 0 średnik i otwórz nawias ostrokątny rozmiarYPlanszy średnik i plus plus zamknij nawias okrągły otwórz nawias klamrowy.
Linia 4. for otwórz nawias okrągły int j znak równości 0 średnik j otwórz nawias ostrokątny rozmiarXPlanszy średnik j plus plus zamknij nawias okrągły otwórz nawias klamrowy.
Linia 5. if otwórz nawias okrągły otwórz nawias okrągły rd kropka nextInt otwórz nawias okrągły 100 zamknij nawias okrągły plus 1 zamknij nawias okrągły znak równości znak równości 100 zamknij nawias okrągły otwórz nawias klamrowy.
Linia 6. pole otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias kwadratowy j zamknij nawias kwadratowy kropka owoc znak równości new Owoc otwórz nawias okrągły zamknij nawias okrągły średnik.
Linia 7. pole otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias kwadratowy j zamknij nawias kwadratowy kropka owoc kropka ustawPunkty otwórz nawias okrągły 1 zamknij nawias okrągły średnik.
Linia 8. zamknij nawias klamrowy.
Linia 9. zamknij nawias klamrowy.
Linia 10. zamknij nawias klamrowy.
Linia 11. zamknij nawias klamrowy.
Po uruchomieniu rozgrywki możemy zobaczyć, że wąż rzeczywiście przesuwa się po mapie, zjadając kolejne owoce i zbierając punkty.
Cały dotychczas napisany kod źródłowy w języku Java możesz pobrać, korzystając z linku:
RW5fkPncR21R6
Język Python i zjadanie owoców
W przypadku projektu w języku Python problem polega na tym, że w żadnym miejscu kodu nie generujemy obiektów reprezentujących owoce.
Aby wygenerować planszę z owocami, implementujemy metodę losujPlansze(), z użyciem funkcji randint(), dostępnej z biblioteki random.
Dla każdego pola na planszy szansa, że pojawi się jakiś owoc, wynosi 15% – dlatego kod generujący owoce na planszy wygląda w następujący sposób:
Linia 1. def losujPlansze otwórz nawias okrągły self zamknij nawias okrągły dwukropek.
Linia 2. import random.
Linia 3. for i in range otwórz nawias okrągły self kropka rozmiarYPlanszy zamknij nawias okrągły dwukropek.
Linia 4. for j in range otwórz nawias okrągły self kropka rozmiarXPlanszy zamknij nawias okrągły dwukropek.
Linia 5. if random kropka randint otwórz nawias okrągły 1 przecinek 100 zamknij nawias okrągły otwórz nawias ostrokątny znak równości 15 dwukropek.
Linia 6. self kropka pole otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias kwadratowy j zamknij nawias kwadratowy kropka owoc znak równości Owoc otwórz nawias okrągły zamknij nawias okrągły.
Linia 7. self kropka pole otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias kwadratowy j zamknij nawias kwadratowy kropka owoc kropka ustawPunkty otwórz nawias okrągły 1 zamknij nawias okrągły.
Linia 8. return.
A samą metodę wywołujemy w ramach konstruktora obiektu Plansza, czyli:
Linia 1. def podkreślnik podkreślnik init podkreślnik podkreślnik otwórz nawias okrągły self przecinek rozmiarXPlanszy przecinek rozmiarYPlanszy zamknij nawias okrągły dwukropek.
Linia 2. self kropka rozmiarXPlanszy znak równości rozmiarXPlanszy.
Linia 3. self kropka rozmiarYPlanszy znak równości rozmiarYPlanszy.
Linia 4. self kropka pole znak równości otwórz nawias kwadratowy otwórz nawias kwadratowy Pole otwórz nawias okrągły zamknij nawias okrągły for x in range otwórz nawias okrągły rozmiarXPlanszy zamknij nawias okrągły zamknij nawias kwadratowy for y in range otwórz nawias okrągły rozmiarYPlanszy zamknij nawias okrągły zamknij nawias kwadratowy.
Linia 5. self kropka losujPlansze otwórz nawias okrągły zamknij nawias okrągły.
Linia 6. return.
W tym miejscu możemy się zorientować, że nie zaimplementowaliśmy jeszcze metody ustawPunkty(), więc robimy to teraz.
Linia 1. def ustawPunkty otwórz nawias okrągły self przecinek punkty zamknij nawias okrągły dwukropek.
Linia 2. self kropka liczbaPunktow znak równości punkty.
Linia 3. return.
Po zaimplementowaniu tych podstawowych metod przechodzimy do czynności zjadania owocu. W przypadku języka Python korzystamy z drugiej z przedstawionych wcześniej możliwości.
Definicja metody przesun() zmienia się następująco:
Natomiast stworzenie wątku węża zmieniamy w taki sposób:
Linia 1. watekWeza znak równości threading kropka Thread otwórz nawias okrągły target znak równości Waz kropka przesun przecinek args znak równości otwórz nawias okrągły waz przecinek zamekWeza przecinek zamekWyswietlania przecinek plansza zamknij nawias okrągły zamknij nawias okrągły.
Następnie dopisujemy obsługę zjadania owocu w metodzie przesun().
Linia 1. owoc znak równości plansza kropka pole otwórz nawias kwadratowy self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy otwórz nawias kwadratowy self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy kropka owoc.
Linia 2. if owoc wykrzyknik znak równości None dwukropek.
Linia 3. self kropka zjedzOwoc otwórz nawias okrągły owoc zamknij nawias okrągły.
Jednak obiekt klasy Owoc będzie dalej w danym polu posiadał swoją referencję. Jest to problematyczne, ponieważ wyświetlamy owoc lub puste pole w zależności od tego, czy w danym miejscu obiekt klasy Owoc się znajduje. Należy te referencje usuwać, zamiast jedynie zerować ich wartości. Stąd wprowadzamy modyfikację w kodzie, polegającą na tym, że jako argument metody zjedzOwoc() przekażemy obiekt klasy Pole, z którego usuniemy referencje do danego obiektu klasy Owoc.
Dlatego też metoda zjedzOwoc() zmieni się w następujący sposób:
Linia 1. def zjedzOwoc otwórz nawias okrągły self przecinek pole zamknij nawias okrągły dwukropek.
Linia 2. print otwórz nawias okrągły cudzysłów PRINT dwukropek cudzysłów plus str otwórz nawias okrągły pole kropka owoc kropka sprawdzPunkty otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły zamknij nawias okrągły.
Linia 3. kratka self kropka liczbaPunktow plus znak równości pole kropka owoc kropka sprawdzPunkty otwórz nawias okrągły zamknij nawias okrągły.
Linia 4. pole kropka owoc znak równości None.
Analogicznie zmieniamy wywołanie tej metody, żeby jej parametry się zgadzały:
Po uruchomieniu programu możemy zaobserwować wyświetlanie komunikatu:
Linia 1. Print dwukropek None.
Błąd wynika z faktu, że nie zaimplementowaliśmy jeszcze metody sprawdzPunkty(). Zróbmy to:
Linia 1. def sprawdzPunkty otwórz nawias okrągły self zamknij nawias okrągły dwukropek.
Linia 2. return self kropka liczbaPunktow.
Po dodaniu tej zmiany widzimy, że komunikat jest poprawny, ponieważ pokazuje wartość liczbową:
Linia 1. Print dwukropek 1.
Dlatego też możemy ostatecznie zmodyfikować metodę zjedzOwoc(), by dodawała punkty do obiektu klasy Waz.
Linia 1. def zjedzOwoc otwórz nawias okrągły self przecinek pole zamknij nawias okrągły dwukropek.
Linia 2. self kropka liczbaPunktow plus znak równości pole kropka owoc kropka sprawdzPunkty otwórz nawias okrągły zamknij nawias okrągły.
Linia 3. pole kropka owoc znak równości None.
Po uruchomieniu programu możemy zaobserwować, że wąż rzeczywiście przesuwa się po planszy.
Dotychczas napisany kod źródłowy w języku Python możesz ściągnąć, korzystając z linku:
RA09NrNVVlN7w
Warunki zakończenia rozgrywki
Klient jako jedyny warunek zakończenia rozgrywki wymienił sytuację, w której wąż dotyka swojego ogona. Ponieważ zjedzone owoce się nie odnawiają, sytuację, w której na planszy nie będzie już żadnego owocu (wszystkie owoce zostały zjedzone), uznamy za drugi warunek zakończenia rozgrywki.
Ostatnią kwestią do rozwiązania jest granica mapy. Czy gra powinna się skończyć po tym, gdy wąż dotknie granicy mapy? Założymy, że tak, mimo że nie zostało to w początkowych wymaganiach sformułowane.
Dla zainteresowanych
Spróbuj zaimplementować grę w taki sposób, żeby po dotknięciu granicy mapy głowa węża przenosiła się na jej drugą stronę.
Sprawdzenie, czy na mapie są jeszcze jakieś owoce, można zaimplementować na co najmniej dwa sposoby:
podczas generowania obiektów reprezentujących owoce zliczać je w specjalnej zmiennej w ramach klasy Plansza, a podczas zjadania zmniejszać wartość tej zmiennej;
podczas zjadania owoców sprawdzać każde pole na mapie w poszukiwaniu owoców.
Wykorzystamy pierwszą metodę, ponieważ jest ona mniej złożona obliczeniowo, a pojedyncza zmienna nie zajmuje dużo pamięci.
Sprawdzenie, czy wąż dotyka krawędzi mapy, będziemy wykonywali w ramach metody przesuń().
Zakończenie rozgrywki napiszemy dopiero wtedy, gdy zaimplementujemy możliwość sprawdzenia wszystkich warunków porażki.
Ostatnim warunkiem zakończenia rozgrywki jest dotknięcie przez głowę węża jego własnego ogona. Sam ogon jednak nie został dotąd zaimplementowany – zrobimy to w dalszej kolejności, ponieważ jego działanie musi zostać szerzej omówione.
Warunki zakończenie rozgrywki w języku C++
Zaczynamy od dodania atrybutu liczbaOwocow do klasy Plansza, domyślnie jego wartość ustawiamy na zero.
Następnie modyfikujemy metodę zjedzOwoc(), żeby przyjmowała jako parametr dodatkowo obiekt klasy Pole. W ten sposób uzyskujemy możliwość modyfikowania atrybutów tego obiektu z poziomu metody zjedzOwoc().
Linia 1. void zjedzOwoc otwórz nawias okrągły Owoc asterysk owoc przecinek Plansza asterysk plansza zamknij nawias okrągły.
Linia 2. otwórz nawias klamrowy.
Linia 3. liczbaPunktow plus znak równości owoc minus zamknij nawias ostrokątny sprawdzPunkty otwórz nawias okrągły zamknij nawias okrągły średnik.
Linia 4. owoc minus zamknij nawias ostrokątny ustawPunkty otwórz nawias okrągły 0 zamknij nawias okrągły średnik.
Linia 5. plansza minus zamknij nawias ostrokątny liczbaOwocow minus minus średnik.
Linia 6. zamknij nawias klamrowy.
Przy okazji poprawiamy również wywołanie tej metody w metodzie przesun().
Piszemy fragment kodu odpowiedzialny za wychodzenie za krawędź mapy. Umieszczamy go w ramach metody przesun():
Linia 1. if otwórz nawias okrągły sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły otwórz nawias ostrokątny 0 kreska pionowa kreska pionowa sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias ostrokątny znak równości plansza minus zamknij nawias ostrokątny rozmiarYPlanszy.
Linia 2. kreska pionowa kreska pionowa sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły otwórz nawias ostrokątny 0 kreska pionowa kreska pionowa sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias ostrokątny znak równości plansza minus zamknij nawias ostrokątny rozmiarXPlanszy zamknij nawias okrągły.
Linia 3. otwórz nawias klamrowy.
Linia 4. break średnik.
Linia 5. zamknij nawias klamrowy.
Istotna jest kolejność wykonywania instrukcji. Przedstawiony fragment powinien się znaleźć po przesunięciu, jednak przed wywołaniem metody sprawdzPunkty(). Ponieważ po wyjściu z planszy dojdzie do próby wywołania metody z nieistniejącego obiektu lub – co gorsza – pobrania wartości z ujemnego indeksu tablicy, co w języku C++ nie może mieć miejsca.
W tym momencie metoda przesun() wygląda w taki sposób:
Linia 1. void przesun otwórz nawias okrągły bool asterysk watekGrafikiPracuje przecinek bool asterysk watekWazPracuje przecinek Plansza asterysk plansza zamknij nawias okrągły.
Linia 2. otwórz nawias klamrowy.
Linia 3. while otwórz nawias okrągły 1 zamknij nawias okrągły.
Linia 4. otwórz nawias klamrowy.
Linia 5. while otwórz nawias okrągły asterysk watekGrafikiPracuje zamknij nawias okrągły.
Linia 6. otwórz nawias klamrowy.
Linia 7. std dwukropek dwukropek this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły std dwukropek dwukropek chrono dwukropek dwukropek milliseconds otwórz nawias okrągły 10 zamknij nawias okrągły zamknij nawias okrągły średnik.
Linia 8. zamknij nawias klamrowy.
Linia 9. asterysk watekWazPracuje znak równości true średnik.
Linia 10. if otwórz nawias okrągły kierunekRuchu znak równości znak równości polnoc zamknij nawias okrągły.
Linia 11. otwórz nawias klamrowy.
Linia 12. pozycjaWezaY minus znak równości 1 średnik.
Linia 13. zamknij nawias klamrowy.
Linia 14. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości poludnie zamknij nawias okrągły.
Linia 15. otwórz nawias klamrowy.
Linia 16. pozycjaWezaY plus znak równości 1 średnik.
Linia 17. zamknij nawias klamrowy.
Linia 18. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości zachod zamknij nawias okrągły.
Linia 19. otwórz nawias klamrowy.
Linia 20. pozycjaWezaX plus znak równości 1 średnik.
Linia 21. zamknij nawias klamrowy.
Linia 22. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości wschod zamknij nawias okrągły.
Linia 23. otwórz nawias klamrowy.
Linia 24. pozycjaWezaX minus znak równości 1 średnik.
Linia 25. zamknij nawias klamrowy.
Linia 26. asterysk watekWazPracuje znak równości false średnik.
Linia 27. Owoc asterysk owocNaPolu znak równości ampersant plansza minus zamknij nawias ostrokątny pole otwórz nawias kwadratowy pozycjaWezaY zamknij nawias kwadratowy otwórz nawias kwadratowy pozycjaWezaX zamknij nawias kwadratowy kropka owoc średnik.
Linia 28. if otwórz nawias okrągły sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły otwórz nawias ostrokątny 0 kreska pionowa kreska pionowa sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias ostrokątny znak równości plansza minus zamknij nawias ostrokątny rozmiarYPlanszy.
Linia 29. kreska pionowa kreska pionowa sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły otwórz nawias ostrokątny 0 kreska pionowa kreska pionowa sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias ostrokątny znak równości plansza minus zamknij nawias ostrokątny rozmiarXPlanszy zamknij nawias okrągły.
Linia 30. otwórz nawias klamrowy.
Linia 31. break średnik.
Linia 32. zamknij nawias klamrowy.
Linia 33. if otwórz nawias okrągły owocNaPolu minus zamknij nawias ostrokątny sprawdzPunkty otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły.
Linia 34. otwórz nawias klamrowy.
Linia 35. zjedzOwoc otwórz nawias okrągły owocNaPolu przecinek plansza zamknij nawias okrągły średnik.
Linia 36. zamknij nawias klamrowy.
Linia 37. std dwukropek dwukropek this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły std dwukropek dwukropek chrono dwukropek dwukropek milliseconds otwórz nawias okrągły 500 zamknij nawias okrągły zamknij nawias okrągły średnik.
Linia 38. zamknij nawias klamrowy.
Linia 39. zamknij nawias klamrowy.
Zanim przejdziemy dalej, musimy jeszcze dodać, że w momencie generowania owoców inkrementujemy atrybut liczbaOwocow po każdym wygenerowanym owocu.
Możemy uzyskać ten efekt poprzez edytowanie metody losujPlansze(), gdzie losowane są również owoce. Po edycji wygląda ona następująco:
Linia 1. void losujPlansze otwórz nawias okrągły zamknij nawias okrągły.
Linia 2. otwórz nawias klamrowy.
Linia 3. srand otwórz nawias okrągły otwórz nawias okrągły int zamknij nawias okrągły time otwórz nawias okrągły NULL zamknij nawias okrągły zamknij nawias okrągły średnik.
Linia 5. for otwórz nawias okrągły int i znak równości 0 średnik i otwórz nawias ostrokątny rozmiarYPlanszy średnik i plus plus zamknij nawias okrągły.
Linia 6. otwórz nawias klamrowy.
Linia 7. for otwórz nawias okrągły int j znak równości 0 średnik j otwórz nawias ostrokątny rozmiarXPlanszy średnik j plus plus zamknij nawias okrągły.
Linia 8. otwórz nawias klamrowy.
Linia 9. if otwórz nawias okrągły otwórz nawias okrągły rand otwórz nawias okrągły zamknij nawias okrągły procent 50 zamknij nawias okrągły znak równości znak równości 0 zamknij nawias okrągły.
Linia 10. otwórz nawias klamrowy.
Linia 11. pole otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias kwadratowy j zamknij nawias kwadratowy kropka owoc kropka ustawPunkty otwórz nawias okrągły 1 zamknij nawias okrągły średnik.
Linia 12. liczbaOwocow plus plus średnik.
Linia 13. zamknij nawias klamrowy.
Linia 14. zamknij nawias klamrowy.
Linia 15. zamknij nawias klamrowy.
Linia 16. zamknij nawias klamrowy.
Warunki zakończenia rozgrywki w Javie
W przypadku języka Java zaczynamy od zmodyfikowania atrybutów klasy Plansza, a mianowicie od dodania atrybutu liczbaOwocow.
Atrybut ten nie był uwzględniony w początkowym diagramie klas. Co prawda powinniśmy starać się pisać program w sposób jak najbardziej zgodny z diagramem, jednak tylko przy założeniu, że został on poprawnie wykonany. W omawianym przypadku analityk nie uwzględnił, że taki atrybut będzie wymagany lub założył, że w inny sposób zostanie zweryfikowana liczba owoców.
Dodajemy więc następujący atrybut w ramach klasy Plansza:
Linia 1. public int liczbaOwocow znak równości 0 średnik.
Następnie edytujemy metodę losujPlansze() z klasy Plansza w ten sposób, żeby po wygenerowaniu owocu inkrementował się również atrybut liczbaOwocow.
Po edycji metoda losujPlansze() wygląda następująco:
Linia 1. void losujPlansze otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy.
Linia 2. Random rd znak równości new Random otwórz nawias okrągły zamknij nawias okrągły średnik.
Linia 3. for otwórz nawias okrągły int i znak równości 0 średnik i otwórz nawias ostrokątny rozmiarYPlanszy średnik i plus plus zamknij nawias okrągły otwórz nawias klamrowy.
Linia 4. for otwórz nawias okrągły int j znak równości 0 średnik j otwórz nawias ostrokątny rozmiarXPlanszy średnik j plus plus zamknij nawias okrągły otwórz nawias klamrowy.
Linia 5. if otwórz nawias okrągły otwórz nawias okrągły rd kropka nextInt otwórz nawias okrągły 100 zamknij nawias okrągły plus 1 zamknij nawias okrągły znak równości znak równości 100 zamknij nawias okrągły otwórz nawias klamrowy.
Linia 6. pole otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias kwadratowy j zamknij nawias kwadratowy kropka owoc znak równości new Owoc otwórz nawias okrągły zamknij nawias okrągły średnik.
Linia 7. pole otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias kwadratowy j zamknij nawias kwadratowy kropka owoc kropka ustawPunkty otwórz nawias okrągły 1 zamknij nawias okrągły średnik.
Linia 8. liczbaOwocow plus plus średnik.
Linia 9. zamknij nawias klamrowy.
Linia 10. zamknij nawias klamrowy.
Linia 11. zamknij nawias klamrowy.
Linia 12. zamknij nawias klamrowy.
Uwzględniamy również, że zjadanie owoców powoduje dekrementacje, robimy to w ramach metody zjedzOwoc(), dostępnej w klasie Waz.
Linia 1. void zjedzOwoc otwórz nawias okrągły Pole poleNaKtorymJestOwoc zamknij nawias okrągły otwórz nawias klamrowy.
Linia 2. liczbaPunktow plus znak równości poleNaKtorymJestOwoc kropka owoc kropka sprawdzPunkty otwórz nawias okrągły zamknij nawias okrągły średnik.
Linia 3. poleNaKtorymJestOwoc kropka owoc kropka ustawPunkty otwórz nawias okrągły 0 zamknij nawias okrągły średnik.
Linia 4. poleNaKtorymJestOwoc kropka owoc znak równości null średnik.
Linia 5. System kropka out kropka println otwórz nawias okrągły liczbaPunktow zamknij nawias okrągły średnik.
Linia 6. plansza kropka liczbaOwocow minus minus średnik.
Linia 7. zamknij nawias klamrowy.
Cały czas sprawdzamy, czy granice planszy zostały przez głowę węża przekroczone. W tym celu modyfikujemy klasę Waz, a w niej metodę run(), która wywołuje metodę przesun(), dodając następujące sprawdzenie:
Jednak analogiczne sprawdzenie musiałoby zostać dodane w ramach metody przesun(), tak żeby wątek nie próbował odczytać elementu tablicy spod ujemnego indeksu, co w języku Java nie może mieć miejsca.
Dlatego wprowadzimy w ramach klasy Waz metodę pomocniczą sprawdzCzyNaPlanszy(), która przyjmie następującą postać:
Linia 1. public void run otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy.
Linia 2. while otwórz nawias okrągły true zamknij nawias okrągły otwórz nawias klamrowy.
Linia 3. try otwórz nawias klamrowy.
Linia 4. Thread kropka sleep otwórz nawias okrągły 1000 zamknij nawias okrągły średnik.
Linia 5. zamknij nawias klamrowy catch otwórz nawias okrągły InterruptedException e zamknij nawias okrągły otwórz nawias klamrowy.
Linia 6. e kropka printStackTrace otwórz nawias okrągły zamknij nawias okrągły średnik.
Linia 7. zamknij nawias klamrowy.
Linia 8. przesun otwórz nawias okrągły zamknij nawias okrągły średnik.
Linia 9. if otwórz nawias okrągły wykrzyknik sprawdzCzyNaPlanszy otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy.
Linia 10. break średnik.
Linia 11. zamknij nawias klamrowy.
Linia 12. zamknij nawias klamrowy.
Linia 13. zamknij nawias klamrowy.
Metodę pomocniczą sprawdzCzyNaPlanszy() używamy również w metodzie przesun(), żebyśmy nie próbowali operować na nieprawidłowych indeksach planszy.
Linia 1. void przesun otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy.
Linia 2. if otwórz nawias okrągły kierunekRuchu znak równości znak równości Kierunek kropka polnoc zamknij nawias okrągły otwórz nawias klamrowy.
Linia 3. pozycjaWezaY minus znak równości 1 średnik.
Linia 4. zamknij nawias klamrowy.
Linia 5. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości Kierunek kropka poludnie zamknij nawias okrągły otwórz nawias klamrowy.
Linia 6. pozycjaWezaY plus znak równości 1 średnik.
Linia 7. zamknij nawias klamrowy.
Linia 8. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości Kierunek kropka zachod zamknij nawias okrągły otwórz nawias klamrowy.
Linia 9. pozycjaWezaX plus znak równości 1 średnik.
Linia 10. zamknij nawias klamrowy.
Linia 11. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości Kierunek kropka wschod zamknij nawias okrągły otwórz nawias klamrowy.
Linia 12. pozycjaWezaX minus znak równości 1 średnik.
Linia 13. zamknij nawias klamrowy.
Linia 14. if otwórz nawias okrągły sprawdzCzyNaPlanszy otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy.
Linia 15. Owoc owoc znak równości plansza kropka pole otwórz nawias kwadratowy pozycjaWezaY zamknij nawias kwadratowy otwórz nawias kwadratowy pozycjaWezaX zamknij nawias kwadratowy kropka owoc średnik.
Linia 16. if otwórz nawias okrągły owoc wykrzyknik znak równości null zamknij nawias okrągły.
Linia 17. zjedzOwoc otwórz nawias okrągły plansza kropka pole otwórz nawias kwadratowy pozycjaWezaY zamknij nawias kwadratowy otwórz nawias kwadratowy pozycjaWezaX zamknij nawias kwadratowy zamknij nawias okrągły średnik.
Linia 18. zamknij nawias klamrowy.
Linia 19. zamknij nawias klamrowy.
Wykonywanie identycznego sprawdzenia dwa razy nie jest jednak optymalnym sposobem na rozwiązanie tego problemu.
Dla zainteresowanych
Spróbuj zmodyfikować kod w ten sposób, żeby metoda sprawdzCzyNaPlanszy() wykonywała się tylko raz, przy jednoczesnym poprawnym działaniu kodu.
Warunki zakończenia rozgrywki w Pythonie
Zaczynamy od dodania warunku sprawdzającego, czy wąż znalazł się na granicy mapy w ramach metody przesun(). Początkowo robimy tak, żeby nieskończona pętla przerywała swoje działanie (w późniejszym kroku wykonamy działania związane z zakończeniem rozgrywki). Metoda po zmianie:
Linia 1. def przesun otwórz nawias okrągły self przecinek zamekWeza przecinek zamekGrafiki przecinek plansza zamknij nawias okrągły dwukropek.
Linia 2. import time.
Linia 3. while 1 dwukropek.
Linia 4. zamekWeza kropka acquire otwórz nawias okrągły zamknij nawias okrągły.
Linia 5. if self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka polnoc kropka value dwukropek.
Linia 6. self kropka pozycjaWezaY minus znak równości 1.
Linia 7. elif self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka poludnie kropka value dwukropek.
Linia 8. self kropka pozycjaWezaY plus znak równości 1.
Linia 9. elif self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka zachod kropka value dwukropek.
Linia 10. self kropka pozycjaWezaX minus znak równości 1.
Linia 11. elif self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka wschod kropka value dwukropek.
Linia 12. self kropka pozycjaWezaX plus znak równości 1.
Linia 14. if self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły otwórz nawias ostrokątny 0 or self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias ostrokątny znak równości plansza kropka rozmiarYPlanszy or self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły otwórz nawias ostrokątny 0 or self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias ostrokątny znak równości plansza kropka rozmiarXPlanszy dwukropek.
Linia 15. zamekGrafiki kropka release otwórz nawias okrągły zamknij nawias okrągły.
Linia 16. break.
Linia 17. else dwukropek.
Linia 18. owoc znak równości plansza kropka pole otwórz nawias kwadratowy self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy otwórz nawias kwadratowy self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy kropka owoc.
Linia 19. if owoc wykrzyknik znak równości None dwukropek.
Linia 20. self kropka zjedzOwoc otwórz nawias okrągły plansza kropka pole otwórz nawias kwadratowy self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy otwórz nawias kwadratowy self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy zamknij nawias okrągły.
Linia 21. time kropka sleep otwórz nawias okrągły 1 zamknij nawias okrągły.
Linia 22. zamekGrafiki kropka release otwórz nawias okrągły zamknij nawias okrągły.
Sprawdzenie, czy wąż znajduje się w ramach planszy, zapisane zostało w ramach następującego wyrażenia warunkowego:
Linia 1. if self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły otwórz nawias ostrokątny 0 or self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias ostrokątny znak równości plansza kropka rozmiarYPlanszy or self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły otwórz nawias ostrokątny 0 or self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias ostrokątny znak równości plansza kropka rozmiarXPlanszy dwukropek.
Dodajemy również w klasie Plansza atrybut o nazwie ileOwocow. Za jego pomocą będziemy sprawdzać, ile owoców znajduje się aktualnie na planszy. Liczba owoców będzie ustalana podczas ich generowania, tzn. po każdorazowym wygenerowaniu obiektu owocu będziemy inkrementowali wartość tego atrybutu, a po zjedzeniu owocu przez węża, wartość atrybutu będziemy zmniejszali.
Początkową wartość atrybutu ileOwocow ustawiamy na zero.
Oto jedna z możliwych postaci metody losujPlansze() po modyfikacji uwzględniającej zliczanie owoców:
Linia 1. def losujPlansze otwórz nawias okrągły self zamknij nawias okrągły dwukropek.
Linia 2. for i in range otwórz nawias okrągły self kropka rozmiarYPlanszy zamknij nawias okrągły dwukropek.
Linia 3. for j in range otwórz nawias okrągły self kropka rozmiarXPlanszy zamknij nawias okrągły dwukropek.
Linia 4. if random kropka randint otwórz nawias okrągły 1 przecinek 100 zamknij nawias okrągły otwórz nawias ostrokątny znak równości 15 dwukropek.
Linia 5. self kropka pole otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias kwadratowy j zamknij nawias kwadratowy kropka owoc znak równości Owoc otwórz nawias okrągły zamknij nawias okrągły.
Linia 6. self kropka pole otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias kwadratowy j zamknij nawias kwadratowy kropka owoc kropka ustawPunkty otwórz nawias okrągły 1 zamknij nawias okrągły.
Linia 7. self kropka ileOwocow znak równości self kropka ileOwocow plus 1.
Linia 8. return.
Brakuje nam jeszcze dekrementacji liczby owoców podczas ich zjadania. Implementujemy to w ramach metody zjedzOwoc() dostępnej w klasie Waz.
Metoda ta po zmianie:
Linia 1. def zjedzOwoc otwórz nawias okrągły self przecinek pole zamknij nawias okrągły dwukropek.
Linia 2. self kropka liczbaPunktow plus znak równości pole kropka owoc kropka sprawdzPunkty otwórz nawias okrągły zamknij nawias okrągły.
Linia 3. plansza kropka ileOwocow znak równości plansza kropka ileOwocow minus 1.
Linia 4. pole kropka owoc znak równości None.
Ogon
W oryginalnej grze Snake istotnym elementem rozgrywki jest ogon węża. Przyszedł moment, w którym dodamy go do gry, ponieważ bez niego nie będziemy w stanie zaimplementować wszystkich warunków porażki.
Ogon węża mógłby być osobną klasą, ale wcześniej uznaliśmy, że nie ma takiej potrzeby. Musimy jednak w jakiś sposób rozpoznać pola, w których powinien pojawić się ogon, tzn. miejsca, które odwiedził wąż, a następnie się przesunął.
W implementacji ogona wykorzystamy właśnie fakt, że podąża on bezpośrednio trasą węża. Problem rozwiążemy w następujący sposób: każde Pole posiada specjalny atrybut do tego przewidziany, tj. wartoscPola. Jeżeli atrybut ten będzie dodatni, to w danym miejscu pojawi się znak, który będziemy interpretowali jako ogon. Atrybut wartoscPola we wszystkich polach, gdzie jest on dodatni, będziemy dekrementowali, żeby symulować efekt przesuwania się ogona.
Ogon w języku C++
W przypadku implementacji gry w języku C++ rozpoczynamy od dopisaniu fragmentu kodu, który będzie odpowiedzialny za wyświetlanie się ogona. Za znak ogona przyjmiemy znak „x”, który powinien się wyświetlić w przypadku, gdy na danym polu nie znajdują się ani owoc, ani głowa węża oraz gdy wartość atrybutu wartoscPola będzie większa od zera.
Metoda wyswietlGre() --- po uwzględnieniu wspomnianych przed chwilą warunków wyświetlania znaku „x” – przyjmuje następującą postać:
Linia 1. prawy ukośnik prawy ukośnik at dwukropek głowa węża przecinek x dwukropek ogon węża przecinek $ dwukropek owoc przecinek plus dwukropek pole.
Linia 2. void wyswietlGre otwórz nawias okrągły Waz asterysk waz przecinek Plansza asterysk plansza przecinek bool asterysk watekGrafikiPracuje przecinek bool asterysk watekWazPracuje zamknij nawias okrągły.
Linia 3. otwórz nawias klamrowy.
Linia 4. while otwórz nawias okrągły true zamknij nawias okrągły.
Linia 5. otwórz nawias klamrowy.
Linia 6. while otwórz nawias okrągły asterysk watekWazPracuje zamknij nawias okrągły.
Linia 7. otwórz nawias klamrowy.
Linia 8. std dwukropek dwukropek this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły std dwukropek dwukropek chrono dwukropek dwukropek milliseconds otwórz nawias okrągły 10 zamknij nawias okrągły zamknij nawias okrągły średnik.
Linia 9. zamknij nawias klamrowy.
Linia 10. asterysk watekGrafikiPracuje znak równości true średnik.
Linia 11. system otwórz nawias okrągły cudzysłów CLS cudzysłów zamknij nawias okrągły średnik.
Linia 12. for otwórz nawias okrągły int i znak równości 0 średnik i otwórz nawias ostrokątny plansza minus zamknij nawias ostrokątny rozmiarYPlanszy średnik i plus plus zamknij nawias okrągły.
Linia 13. otwórz nawias klamrowy.
Linia 14. for otwórz nawias okrągły int j znak równości 0 średnik j otwórz nawias ostrokątny plansza minus zamknij nawias ostrokątny rozmiarXPlanszy średnik j plus plus zamknij nawias okrągły.
Linia 15. otwórz nawias klamrowy.
Linia 16. if otwórz nawias okrągły waz minus zamknij nawias ostrokątny sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły znak równości znak równości i ampersant ampersant waz minus zamknij nawias ostrokątny sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły znak równości znak równości j zamknij nawias okrągły.
Linia 17. std dwukropek dwukropek cout otwórz nawias ostrokątny otwórz nawias ostrokątny cudzysłów at cudzysłów średnik.
Linia 18. else if otwórz nawias okrągły plansza minus zamknij nawias ostrokątny pole otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias kwadratowy j zamknij nawias kwadratowy kropka owoc kropka sprawdzPunkty otwórz nawias okrągły zamknij nawias okrągły wykrzyknik znak równości 0 zamknij nawias okrągły.
Linia 19. std dwukropek dwukropek cout otwórz nawias ostrokątny otwórz nawias ostrokątny cudzysłów $ cudzysłów średnik.
Linia 20. else if otwórz nawias okrągły plansza minus zamknij nawias ostrokątny pole otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias kwadratowy j zamknij nawias kwadratowy kropka wartoscPola zamknij nawias ostrokątny 0 zamknij nawias okrągły.
Linia 21. std dwukropek dwukropek cout otwórz nawias ostrokątny otwórz nawias ostrokątny cudzysłów x cudzysłów średnik.
Linia 22. else.
Linia 23. std dwukropek dwukropek cout otwórz nawias ostrokątny otwórz nawias ostrokątny cudzysłów plus cudzysłów średnik.
Linia 24. zamknij nawias klamrowy.
Linia 25. std dwukropek dwukropek cout otwórz nawias ostrokątny otwórz nawias ostrokątny std dwukropek dwukropek endl średnik.
Linia 26. zamknij nawias klamrowy.
Linia 27. asterysk watekGrafikiPracuje znak równości false średnik.
Linia 28. std dwukropek dwukropek this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły std dwukropek dwukropek chrono dwukropek dwukropek milliseconds otwórz nawias okrągły 200 zamknij nawias okrągły zamknij nawias okrągły średnik.
Linia 29. zamknij nawias klamrowy.
Linia 30. zamknij nawias klamrowy.
Następnie dopisujemy kod w taki sposób, by na odpowiednich polach ogon zaczął się rzeczywiście pojawiać.
Przechodzimy do metody przesun(), gdzie odwołujemy się do obiektu Pole poprzez obiekt klasy Plansza.
Linia 1. plansza minus zamknij nawias ostrokątny pole otwórz nawias kwadratowy pozycjaWezaY zamknij nawias kwadratowy otwórz nawias kwadratowy pozycjaWezaX zamknij nawias kwadratowy.
W kolejnym kroku ustawiamy w danym polu wartość atrybutu wartoscPola na podstawie obecnego wyniku punktowego, czyli atrybutu liczbaPunktow dostępnego w ramach klasy Waz.
Atrybut wartoscPola modyfikujemy następująco:
Linia 1. plansza minus zamknij nawias ostrokątny pole otwórz nawias kwadratowy pozycjaWezaY zamknij nawias kwadratowy otwórz nawias kwadratowy pozycjaWezaX zamknij nawias kwadratowy znak równości liczbaPunktow średnik.
Cała metoda przesun():
Linia 1. void przesun otwórz nawias okrągły bool asterysk watekGrafikiPracuje przecinek bool asterysk watekWazPracuje przecinek Plansza asterysk plansza zamknij nawias okrągły.
Linia 2. otwórz nawias klamrowy.
Linia 3. while otwórz nawias okrągły 1 zamknij nawias okrągły.
Linia 4. otwórz nawias klamrowy.
Linia 5. while otwórz nawias okrągły asterysk watekGrafikiPracuje zamknij nawias okrągły.
Linia 6. otwórz nawias klamrowy.
Linia 7. std dwukropek dwukropek this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły std dwukropek dwukropek chrono dwukropek dwukropek milliseconds otwórz nawias okrągły 10 zamknij nawias okrągły zamknij nawias okrągły średnik.
Linia 8. zamknij nawias klamrowy.
Linia 9. asterysk watekWazPracuje znak równości true średnik.
Linia 10. if otwórz nawias okrągły kierunekRuchu znak równości znak równości polnoc zamknij nawias okrągły.
Linia 11. otwórz nawias klamrowy.
Linia 12. pozycjaWezaY minus znak równości 1 średnik.
Linia 13. zamknij nawias klamrowy.
Linia 14. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości poludnie zamknij nawias okrągły.
Linia 15. otwórz nawias klamrowy.
Linia 16. pozycjaWezaY plus znak równości 1 średnik.
Linia 17. zamknij nawias klamrowy.
Linia 18. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości zachod zamknij nawias okrągły.
Linia 19. otwórz nawias klamrowy.
Linia 20. pozycjaWezaX plus znak równości 1 średnik.
Linia 21. zamknij nawias klamrowy.
Linia 22. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości wschod zamknij nawias okrągły.
Linia 23. otwórz nawias klamrowy.
Linia 24. pozycjaWezaX minus znak równości 1 średnik.
Linia 25. zamknij nawias klamrowy.
Linia 26. asterysk watekWazPracuje znak równości false średnik.
Linia 27. Owoc asterysk owocNaPolu znak równości ampersant plansza minus zamknij nawias ostrokątny pole otwórz nawias kwadratowy pozycjaWezaY zamknij nawias kwadratowy otwórz nawias kwadratowy pozycjaWezaX zamknij nawias kwadratowy kropka owoc średnik.
Linia 28. if otwórz nawias okrągły sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły otwórz nawias ostrokątny 0 kreska pionowa kreska pionowa sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias ostrokątny znak równości plansza minus zamknij nawias ostrokątny rozmiarYPlanszy.
Linia 29. kreska pionowa kreska pionowa sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły otwórz nawias ostrokątny 0 kreska pionowa kreska pionowa sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias ostrokątny znak równości plansza minus zamknij nawias ostrokątny rozmiarXPlanszy zamknij nawias okrągły.
Linia 30. otwórz nawias klamrowy.
Linia 31. break średnik.
Linia 32. zamknij nawias klamrowy.
Linia 33. if otwórz nawias okrągły owocNaPolu minus zamknij nawias ostrokątny sprawdzPunkty otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły.
Linia 34. otwórz nawias klamrowy.
Linia 35. zjedzOwoc otwórz nawias okrągły owocNaPolu przecinek plansza zamknij nawias okrągły średnik.
Linia 36. zamknij nawias klamrowy.
Linia 37. plansza minus zamknij nawias ostrokątny pole otwórz nawias kwadratowy pozycjaWezaY zamknij nawias kwadratowy otwórz nawias kwadratowy pozycjaWezaX zamknij nawias kwadratowy kropka wartoscPola znak równości liczbaPunktow średnik.
Linia 38. std dwukropek dwukropek this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły std dwukropek dwukropek chrono dwukropek dwukropek milliseconds otwórz nawias okrągły 500 zamknij nawias okrągły zamknij nawias okrągły średnik.
Linia 39. zamknij nawias klamrowy.
Linia 40. zamknij nawias klamrowy.
Po uruchomieniu programu widzimy, że wąż rysuje za sobą nieskończony ogon. Żeby zasymulować jego ruch, implementujemy metodę zaaktualizujOgonWeza(), dostępną w klasie Plansza. Będziemy w niej iterować po wszystkich dostępnych obiektach klasy Pole, a następnie dekrementować atrybut wartoscPola – w przypadku, jeżeli wartość tego atrybutu jest większa niż zero.
Metodę tę wywołamy w końcowej części metody przesun(), jednak przed wywołaniem funkcji sleep_for().
Metoda zaaktualizujOgonWeza() przyjmuje następującą postać:
Linia 1. void zaaktualizujOgonWeza otwórz nawias okrągły zamknij nawias okrągły.
Linia 2. otwórz nawias klamrowy.
Linia 3. for otwórz nawias okrągły int i znak równości 0 średnik i otwórz nawias ostrokątny rozmiarYPlanszy średnik i plus plus zamknij nawias okrągły.
Linia 4. otwórz nawias klamrowy.
Linia 5. for otwórz nawias okrągły int j znak równości 0 średnik j otwórz nawias ostrokątny rozmiarXPlanszy średnik j plus plus zamknij nawias okrągły.
Linia 6. otwórz nawias klamrowy.
Linia 7. if otwórz nawias okrągły pole otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias kwadratowy j zamknij nawias kwadratowy kropka wartoscPola zamknij nawias ostrokątny 0 zamknij nawias okrągły.
Linia 8. otwórz nawias klamrowy.
Linia 9. pole otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias kwadratowy j zamknij nawias kwadratowy kropka wartoscPola minus minus średnik.
Linia 10. zamknij nawias klamrowy.
Linia 11. zamknij nawias klamrowy.
Linia 12. zamknij nawias klamrowy.
Linia 13. zamknij nawias klamrowy.
Po uruchomieniu gry możemy zaobserwować, że ogon węża pojawia się i porusza za głową węża dopiero po trzecim zjedzonym owocu.
Przykładowym prostym rozwiązaniem tego problemu byłoby ustawienie domyślnego wyniku punktowego węża w atrybucie liczbaPunktow na dwa. Wtedy ogon wyświetlałby się prawidłowo – nie jest to jednak idealne rozwiązanie.
Problem z ogonem bierze się stąd, że plansza jest aktualizowana zbyt szybko – gdy wąż ma jeden punkt (czyli atrybut liczbaPunktow jest równy jeden), atrybut wartoscPola również przyjmuje jeden. Jednak ponieważ Pole aktualizowane jest po ustawieniu wartości punktów, to od razu ten punkt jest kasowany - w linii nr 9 powyższego kodu metody zaktualizujOgonWeza(). Jeżeli zamienimy kolejnością aktualizowanie ogona oraz ustawianie wartości pola, to ogon będzie za krótki tylko o jedno pole, zamiast o dwa.
Oto przykład, w którym pokazujemy, jak najpierw aktualizujemy ogon, a dopiero potem ustawiamy wartość Pola:
Linia 1. plansza minus zamknij nawias ostrokątny zaaktualizujOgonWeza otwórz nawias okrągły zamknij nawias okrągły średnik.
Linia 2. plansza minus zamknij nawias ostrokątny pole otwórz nawias kwadratowy pozycjaWezaY zamknij nawias kwadratowy otwórz nawias kwadratowy pozycjaWezaX zamknij nawias kwadratowy kropka wartoscPola znak równości liczbaPunktow średnik.
Ta pozostała kratka opóźnienia bierze się stąd, że o ile rzeczywiście wartość Pola jest ustawiana w danej iteracji przed dekrementacją, o tyle wartości wszystkich pozostałych pól już są dekrementowane w metodzie zaktualizujOgonWeza(). A ponieważ fakt wykonania przesunięcia powoduje wykonanie tej metody, dekrementowany jest również atrybut wartoscPola pola dopiero co odwiedzonego.
Problem ten można rozwiązać, modyfikując instrukcję w następujący sposób:
Linia 1. plansza minus zamknij nawias ostrokątny pole otwórz nawias kwadratowy pozycjaWezaY zamknij nawias kwadratowy otwórz nawias kwadratowy pozycjaWezaX zamknij nawias kwadratowy kropka wartoscPola znak równości liczbaPunktow plus 1 średnik.
W ten sposób liczba punktów węża jest zgodna z długością jego ogona.
Na tym przykładzie widać, że nie zawsze pierwsze, najprostsze rozwiązanie jest dobre. Po przetestowaniu może okazać się, że jest ono błędne. Należy wtedy dokładnie przeanalizować powód błędnego działania kodu, zaproponować poprawione rozwiązanie i sprawdzić, czy ono działa w sposób prawidłowy.
Dotychczas napisany kod w języku C++ można pobrać za pomocą linku:
R1LKixBHyMwmI
Ogon w języku Java
W projekcie w języku Java mamy już zaimplementowane wyświetlanie ogona, który jest znakiem „x”. Dla przypomnienia: za wyświetlanie grafiki odpowiada metoda wyswietlGre() z klasy WyswietlanieGrafiki, która powinna wyglądać w następujący sposób:
Linia 1. public void wyswietlGre otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy.
Linia 2. grafikaTekstowa kropka setText otwórz nawias okrągły cudzysłów cudzysłów zamknij nawias okrągły średnik.
Linia 3. for otwórz nawias okrągły int i znak równości 0 średnik i otwórz nawias ostrokątny plansza kropka rozmiarYPlanszy średnik i plus plus zamknij nawias okrągły otwórz nawias klamrowy.
Linia 4. for otwórz nawias okrągły int j znak równości 0 średnik j otwórz nawias ostrokątny plansza kropka rozmiarXPlanszy średnik j plus plus zamknij nawias okrągły otwórz nawias klamrowy.
Linia 5. if otwórz nawias okrągły waz kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły znak równości znak równości i ampersant ampersant waz kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły znak równości znak równości j zamknij nawias okrągły otwórz nawias klamrowy.
Linia 6. grafikaTekstowa kropka append otwórz nawias okrągły cudzysłów at cudzysłów zamknij nawias okrągły średnik.
Linia 7. zamknij nawias klamrowy else if otwórz nawias okrągły plansza kropka pole otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias kwadratowy j zamknij nawias kwadratowy kropka owoc wykrzyknik znak równości null zamknij nawias okrągły otwórz nawias klamrowy.
Linia 8. grafikaTekstowa kropka append otwórz nawias okrągły cudzysłów $ cudzysłów zamknij nawias okrągły średnik.
Linia 9. zamknij nawias klamrowy else if otwórz nawias okrągły plansza kropka pole otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias kwadratowy j zamknij nawias kwadratowy kropka wartoscPola znak równości znak równości 0 zamknij nawias okrągły otwórz nawias klamrowy.
Linia 10. grafikaTekstowa kropka append otwórz nawias okrągły cudzysłów plus cudzysłów zamknij nawias okrągły średnik.
Linia 11. zamknij nawias klamrowy else otwórz nawias klamrowy.
Linia 12. grafikaTekstowa kropka append otwórz nawias okrągły cudzysłów x cudzysłów zamknij nawias okrągły średnik.
Linia 13. zamknij nawias klamrowy.
Linia 14. zamknij nawias klamrowy.
Linia 15. grafikaTekstowa kropka append otwórz nawias okrągły cudzysłów lewy ukośnik n cudzysłów zamknij nawias okrągły średnik.
Linia 16. zamknij nawias klamrowy.
Linia 17. zamknij nawias klamrowy.
Dlatego też możemy skupić się na implementacji ogona. Przechodzimy do metody przesun(), w której będziemy ustawiali atrybut wartoscPola, w obiektach klasy Pole, na podstawie atrybutu węża o nazwie liczbaPunktow.
W metodzie przesun() z klasy Waz po uruchomieniu programu możemy zobaczyć, że w sytuacji, gdy wąż zjadł przynajmniej jeden owoc, to rysuje on za sobą ślad.
Dzieje się tak, ponieważ nigdzie nie zmniejszamy jeszcze wartosci atrybutu wartoscPola.
Do modyfikowania tego atrybutu we wszystkich polach posłuży nam metoda zaaktualizujOgonWeza(), z klasy Plansza, której jeszcze nie zaimplementowaliśmy. Dopisujemy ją i iterujemy w niej wszystkie Pola, a w sytuacji, gdy wartoscPola będzie większa od 0 – zdekrementujemy ją.
Linia 1. void zaaktualizujOgonWeza otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy.
Linia 2. for otwórz nawias okrągły int i znak równości 0 średnik i otwórz nawias ostrokątny rozmiarYPlanszy średnik i plus plus zamknij nawias okrągły otwórz nawias klamrowy.
Linia 3. for otwórz nawias okrągły int j znak równości 0 średnik j otwórz nawias ostrokątny rozmiarXPlanszy średnik j plus plus zamknij nawias okrągły otwórz nawias klamrowy.
Linia 4. if otwórz nawias okrągły pole otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias kwadratowy j zamknij nawias kwadratowy kropka wartoscPola zamknij nawias ostrokątny 0 zamknij nawias okrągły otwórz nawias klamrowy.
Linia 5. pole otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias kwadratowy j zamknij nawias kwadratowy kropka wartoscPola minus minus średnik.
Linia 6. zamknij nawias klamrowy.
Linia 7. zamknij nawias klamrowy.
Linia 8. zamknij nawias klamrowy.
Linia 9. zamknij nawias klamrowy.
Wywołanie tej metody umieszczamy w ramach metody przesun(), po wykonaniu samego przesunięcia, ale przed zmianą atrybutu wartoscPola.
Dodatkowo, w celu uniknięcia ewentualnych błędów, aktualizacje wartości danego pola oraz wywołanie metody zaaktualizujOgonWeza() umieszczamy w wyrażeniu warunkowym sprawdzającym, czy wąż znajduje się na planszy.
Metoda przesun():
Linia 1. void przesun otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy.
Linia 2. if otwórz nawias okrągły kierunekRuchu znak równości znak równości Kierunek kropka polnoc zamknij nawias okrągły otwórz nawias klamrowy.
Linia 3. pozycjaWezaY minus znak równości 1 średnik.
Linia 4. zamknij nawias klamrowy.
Linia 5. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości Kierunek kropka poludnie zamknij nawias okrągły otwórz nawias klamrowy.
Linia 6. pozycjaWezaY plus znak równości 1 średnik.
Linia 7. zamknij nawias klamrowy.
Linia 8. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości Kierunek kropka zachod zamknij nawias okrągły otwórz nawias klamrowy.
Linia 9. pozycjaWezaX plus znak równości 1 średnik.
Linia 10. zamknij nawias klamrowy.
Linia 11. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości Kierunek kropka wschod zamknij nawias okrągły otwórz nawias klamrowy.
Linia 12. pozycjaWezaX minus znak równości 1 średnik.
Linia 13. zamknij nawias klamrowy.
Linia 14. if otwórz nawias okrągły sprawdzCzyNaPlanszy otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy.
Linia 15. Owoc owoc znak równości plansza kropka pole otwórz nawias kwadratowy pozycjaWezaY zamknij nawias kwadratowy otwórz nawias kwadratowy pozycjaWezaX zamknij nawias kwadratowy kropka owoc średnik.
Linia 16. if otwórz nawias okrągły owoc wykrzyknik znak równości null zamknij nawias okrągły.
Linia 17. zjedzOwoc otwórz nawias okrągły plansza kropka pole otwórz nawias kwadratowy pozycjaWezaY zamknij nawias kwadratowy otwórz nawias kwadratowy pozycjaWezaX zamknij nawias kwadratowy zamknij nawias okrągły średnik.
Linia 18. plansza kropka zaaktualizujOgonWeza otwórz nawias okrągły zamknij nawias okrągły średnik.
Linia 19. plansza kropka pole otwórz nawias kwadratowy pozycjaWezaY zamknij nawias kwadratowy otwórz nawias kwadratowy sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy kropka wartoscPola znak równości liczbaPunktow średnik.
Linia 20. zamknij nawias klamrowy.
Linia 21. zamknij nawias klamrowy.
Po uruchomieniu gry i zjedzeniu kilku owoców możemy zobaczyć, że ogon jest o jedną kratkę za krótki. W domyśle powinien on odpowiadać swoją długością liczbie zjedzonych owoców. Obecnie odpowiada liczbie zjedzonych owoców minus jeden, gdzie minimalna długość ogona to zero.
Sytuacja ta wynika z faktu, że o ile rzeczywiście ustawiamy wartość pola po użyciu metody zaaktualizujOgonWeza(), wartość jest jednak zbyt mała. Już w trakcie kolejnego przesunięcia się węża wartość jest błędnie modyfikowana. Jest ona bowiem zmniejszana, zanim w ogóle się pojawi.
Atrybut wartoscPola ustawimy następująco:
Linia 1. plansza kropka pole otwórz nawias kwadratowy pozycjaWezaY zamknij nawias kwadratowy otwórz nawias kwadratowy sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy kropka wartoscPola znak równości liczbaPunktow plus 1 średnik.
Wyświetlana w grze długość ogona jest już równa liczbie punktów zdobytych przez węża.
Cały dotychczas napisany kod źródłowy w języku Java możesz pobrać, korzystając z linku:
Rjiad0YCNXE0k
Ogon w języku Python
W języka Python mamy już zaimplementowane wyświetlanie się ogona w sytuacji, gdy atrybut wartoscPola klasy Pole jest większy od 0.
Metoda wyswietlGre():
Linia 1. def wyswietlGre otwórz nawias okrągły self przecinek plansza przecinek waz przecinek zamekWeza przecinek zamekGrafiki zamknij nawias okrągły dwukropek.
Linia 2. import os.
Linia 3. import time.
Linia 4. while 1 dwukropek.
Linia 5. zamekGrafiki kropka acquire otwórz nawias okrągły zamknij nawias okrągły.
Linia 6. os kropka system otwórz nawias okrągły apostrof CLS apostrof zamknij nawias okrągły.
Linia 7. for i in range otwórz nawias okrągły plansza kropka rozmiarYPlanszy zamknij nawias okrągły dwukropek.
Linia 8. for j in range otwórz nawias okrągły plansza kropka rozmiarXPlanszy zamknij nawias okrągły dwukropek.
Linia 9. if waz kropka pozycjaWezaX znak równości znak równości j and waz kropka pozycjaWezaY znak równości znak równości i dwukropek.
Linia 10. print otwórz nawias okrągły cudzysłów at cudzysłów przecinek end znak równości apostrof lewy ukośnik t apostrof zamknij nawias okrągły.
Linia 11. elif plansza kropka pole otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias kwadratowy j zamknij nawias kwadratowy kropka owoc wykrzyknik znak równości None dwukropek.
Linia 12. print otwórz nawias okrągły cudzysłów $ cudzysłów przecinek end znak równości apostrof lewy ukośnik t apostrof zamknij nawias okrągły.
Linia 13. elif plansza kropka pole otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias kwadratowy j zamknij nawias kwadratowy kropka wartoscPola zamknij nawias ostrokątny 0 dwukropek.
Linia 14. print otwórz nawias okrągły cudzysłów o cudzysłów przecinek end znak równości apostrof lewy ukośnik t apostrof zamknij nawias okrągły.
Linia 15. else dwukropek.
Linia 16. print otwórz nawias okrągły cudzysłów x cudzysłów przecinek end znak równości apostrof lewy ukośnik t apostrof zamknij nawias okrągły.
Linia 17. print otwórz nawias okrągły cudzysłów lewy ukośnik n cudzysłów zamknij nawias okrągły.
Linia 18. zamekWeza kropka release otwórz nawias okrągły zamknij nawias okrągły.
Linia 19. time kropka sleep otwórz nawias okrągły 1 zamknij nawias okrągły.
Zgodnie z ustaleniami dopisujemy do gry fragment odpowiedzialny za zamianę wartości atrybutu wartoscPola z klasy Pole na wartość atrybutu liczbaPunktow, z klasy Waz. W tym celu wystarczy dopisać do metody przesun() następujące dwie linijki:
Linia 1. pole znak równości plansza kropka pole otwórz nawias kwadratowy self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy otwórz nawias kwadratowy self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy.
Linia 2. pole kropka wartoscPola znak równości pole kropka wartoscPola znak równości 1.
Ostatecznie sama metoda przesun() wygląda następująco:
Linia 1. def przesun otwórz nawias okrągły self przecinek zamekWeza przecinek zamekGrafiki przecinek plansza zamknij nawias okrągły dwukropek.
Linia 2. import time.
Linia 3. while 1 dwukropek.
Linia 4. zamekWeza kropka acquire otwórz nawias okrągły zamknij nawias okrągły.
Linia 5. if self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka polnoc kropka value dwukropek.
Linia 6. self kropka pozycjaWezaY minus znak równości 1.
Linia 7. elif self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka poludnie kropka value dwukropek.
Linia 8. self kropka pozycjaWezaY plus znak równości 1.
Linia 9. elif self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka zachod kropka value dwukropek.
Linia 10. self kropka pozycjaWezaX minus znak równości 1.
Linia 11. elif self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka wschod kropka value dwukropek.
Linia 12. self kropka pozycjaWezaX plus znak równości 1.
Linia 14. if self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły otwórz nawias ostrokątny 0 or self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias ostrokątny znak równości plansza kropka rozmiarYPlanszy or self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły otwórz nawias ostrokątny 0 or self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias ostrokątny znak równości plansza kropka rozmiarXPlanszy dwukropek.
Linia 15. zamekGrafiki kropka release otwórz nawias okrągły zamknij nawias okrągły.
Linia 16. break.
Linia 17. else dwukropek.
Linia 18. owoc znak równości plansza kropka pole otwórz nawias kwadratowy self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy otwórz nawias kwadratowy self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy kropka owoc.
Linia 19. if owoc wykrzyknik znak równości None dwukropek.
Linia 20. self kropka zjedzOwoc otwórz nawias okrągły plansza kropka pole otwórz nawias kwadratowy self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy otwórz nawias kwadratowy self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy zamknij nawias okrągły.
Linia 21. pole znak równości plansza kropka pole otwórz nawias kwadratowy self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy otwórz nawias kwadratowy self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy.
Linia 22. pole kropka wartoscPola znak równości self kropka liczbaPunktow.
Linia 23. time kropka sleep otwórz nawias okrągły 1 zamknij nawias okrągły.
Linia 24. zamekGrafiki kropka release otwórz nawias okrągły zamknij nawias okrągły.
Możemy zauważyć, że po zjedzeniu owocu wąż – zamiast ogona, który się za nim porusza – generuje coś w rodzaju linii, którą ciągnie. Jest to spowodowane brakiem mechanizmu kasującego ogon. Zaimplementujemy go teraz w ramach metody zaaktualizujOgonWeza(), która znajduje się w klasie Plansza.
W tym celu iterujemy po wszystkich obiektach klasy Pole i dekrementujemy wartości atrybutów wartoscPola tam, gdzie wartość tego atrybutu jest większa od zera.
Alternatywnie moglibyśmy zmniejszać te wartości we wszystkich obiektach, ponieważ teoretycznie nie powinno to stanowić dla programu różnicy – nie jest to jednak prawda. Jeżeli co klatkę zmniejszalibyśmy wartości we wszystkich obiektach typu Pole, to zaistniałaby możliwość, że po długiej grze w danym atrybucie wartość zostałaby tak bardzo zmniejszona, że mogłoby dojść do przepełnienia (z ang. overflow) atrybutu i zmieniłby on swoją wartość na dodatnią.
Ostatecznie metoda zaaktualizujOgonWeza() mogłaby przyjąć następującą postać:
Linia 1. def zaaktualizujOgonWeza otwórz nawias okrągły self zamknij nawias okrągły dwukropek.
Linia 2. for i in range otwórz nawias okrągły self kropka rozmiarYPlanszy zamknij nawias okrągły dwukropek.
Linia 3. for j in range otwórz nawias okrągły self kropka rozmiarXPlanszy zamknij nawias okrągły dwukropek.
Linia 4. if otwórz nawias okrągły self kropka pole otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias kwadratowy j zamknij nawias kwadratowy kropka wartoscPola zamknij nawias ostrokątny 0 zamknij nawias okrągły dwukropek.
Linia 5. self kropka pole otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias kwadratowy j zamknij nawias kwadratowy kropka wartoscPola znak równości self kropka pole otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias kwadratowy j zamknij nawias kwadratowy kropka wartoscPola minus 1.
Linia 6. return.
Dodajemy do kodu wywołanie metody zaaktualizujOgonWeza(). Robimy to w ramach metody przesun() z klasy Waz.
Linia 1. def przesun otwórz nawias okrągły self przecinek zamekWeza przecinek zamekGrafiki przecinek plansza zamknij nawias okrągły dwukropek.
Linia 2. import time.
Linia 3. while 1 dwukropek.
Linia 4. zamekWeza kropka acquire otwórz nawias okrągły zamknij nawias okrągły.
Linia 5. if self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka polnoc kropka value dwukropek.
Linia 6. self kropka pozycjaWezaY minus znak równości 1.
Linia 7. elif self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka poludnie kropka value dwukropek.
Linia 8. self kropka pozycjaWezaY plus znak równości 1.
Linia 9. elif self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka zachod kropka value dwukropek.
Linia 10. self kropka pozycjaWezaX minus znak równości 1.
Linia 11. elif self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka wschod kropka value dwukropek.
Linia 12. self kropka pozycjaWezaX plus znak równości 1.
Linia 14. if self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły otwórz nawias ostrokątny 0 or self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias ostrokątny znak równości plansza kropka rozmiarYPlanszy or self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły otwórz nawias ostrokątny 0 or self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias ostrokątny znak równości plansza kropka rozmiarXPlanszy dwukropek.
Linia 15. zamekGrafiki kropka release otwórz nawias okrągły zamknij nawias okrągły.
Linia 16. break.
Linia 17. else dwukropek.
Linia 18. owoc znak równości plansza kropka pole otwórz nawias kwadratowy self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy otwórz nawias kwadratowy self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy kropka owoc.
Linia 19. if owoc wykrzyknik znak równości None dwukropek.
Linia 20. self kropka zjedzOwoc otwórz nawias okrągły plansza kropka pole otwórz nawias kwadratowy self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy otwórz nawias kwadratowy self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy zamknij nawias okrągły.
Linia 21. pole znak równości plansza kropka pole otwórz nawias kwadratowy self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy otwórz nawias kwadratowy self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy.
Linia 22. plansza kropka zaaktualizujOgonWeza otwórz nawias okrągły zamknij nawias okrągły.
Linia 23. pole kropka wartoscPola znak równości self kropka liczbaPunktow.
Linia 24. time kropka sleep otwórz nawias okrągły 1 zamknij nawias okrągły.
Linia 25. zamekGrafiki kropka release otwórz nawias okrągły zamknij nawias okrągły.
Po zaktualizowaniu metody przesun() widzimy, że po uruchomieniu gry ogon węża jest o jedno pole za krótki. Jest to spowodowane tym, że co „klatkę animacji” wszystkie pola są aktualizowane, włącznie z tą dopiero co ustawioną.
Problem ten można naprawić na wiele sposobów. Możemy np. zapamiętywać poprzednią lokalizację węża i na tym polu nie aktualizować długości jego ogona, jednak taka operacja wymagałaby dużej liczby zmian w kodzie.
Skorzystamy z prostszego rozwiązania polegającego na ustawianiu wartości atrybutu wartoscPola na o jeden większą, niż atrybutu liczbaPunktow, dzięki czemu po przesunięciu się węża zobaczymy na planszy oczekiwaną długość ogona.
Metoda przesun() przyjmie ostatecznie następującą postać:
Linia 1. def przesun otwórz nawias okrągły self przecinek zamekWeza przecinek zamekGrafiki przecinek plansza zamknij nawias okrągły dwukropek.
Linia 2. import time.
Linia 3. while 1 dwukropek.
Linia 4. zamekWeza kropka acquire otwórz nawias okrągły zamknij nawias okrągły.
Linia 5. if self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka polnoc kropka value dwukropek.
Linia 6. self kropka pozycjaWezaY minus znak równości 1.
Linia 7. elif self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka poludnie kropka value dwukropek.
Linia 8. self kropka pozycjaWezaY plus znak równości 1.
Linia 9. elif self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka zachod kropka value dwukropek.
Linia 10. self kropka pozycjaWezaX minus znak równości 1.
Linia 11. elif self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka wschod kropka value dwukropek.
Linia 12. self kropka pozycjaWezaX plus znak równości 1.
Linia 14. if self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły otwórz nawias ostrokątny 0 or self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias ostrokątny znak równości plansza kropka rozmiarYPlanszy or self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły otwórz nawias ostrokątny 0 or self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias ostrokątny znak równości plansza kropka rozmiarXPlanszy dwukropek.
Linia 15. zamekGrafiki kropka release otwórz nawias okrągły zamknij nawias okrągły.
Linia 16. break.
Linia 17. else dwukropek.
Linia 18. owoc znak równości plansza kropka pole otwórz nawias kwadratowy self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy otwórz nawias kwadratowy self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy kropka owoc.
Linia 19. if owoc wykrzyknik znak równości None dwukropek.
Linia 20. self kropka zjedzOwoc otwórz nawias okrągły plansza kropka pole otwórz nawias kwadratowy self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy otwórz nawias kwadratowy self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy zamknij nawias okrągły.
Linia 21. pole znak równości plansza kropka pole otwórz nawias kwadratowy self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy otwórz nawias kwadratowy self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy.
Linia 22. plansza kropka zaaktualizujOgonWeza otwórz nawias okrągły zamknij nawias okrągły.
Linia 23. pole kropka wartoscPola znak równości self kropka liczbaPunktow plus 1.
Linia 24. time kropka sleep otwórz nawias okrągły 1 zamknij nawias okrągły.
Linia 25. zamekGrafiki kropka release otwórz nawias okrągły zamknij nawias okrągły.
Dotychczas napisany kod w języku Python pobierzesz z tego linku:
RdiOeaJBBvngG
Koniec rozgrywki
Ustaliliśmy wcześniej, że gra kończy się, gdy:
W grze zebrano wszystkie owoce, jakie były dostępne.
Wąż wyjedzie poza planszę.
Wąż dotknie własnego ogona.
Są to wszystkie warunki, które muszą zostać obligatoryjnie dodane, zgodnie z wymaganiami opisanymi we wcześniejszych materiałach.
Dla zainteresowanych
Można dodać jeszcze inne warunki zakończenia – np. opcje wyjścia z gry przy zatrzymaniu rozgrywki.
Zakończenie rozgrywki w języku C++
Zakończenie rozgrywki, czyli zatrzymanie gry, obejmuje zatrzymanie dodatkowo powołanych wątków, wyświetlenie liczby punktów, jakie zdobył gracz, a także wyświetlenie menu, umożliwiającego wyjście z gry.
Zdefiniowane wcześniej warunki zakończenia rozgrywki możemy „wykrywać” w ramach metody przesun(), ponieważ w niej mamy dostęp do wszystkich potrzebnych informacji, a dodatkowo to właśnie przesunięcie głowy węża wywołuje wszystkie modyfikacje.
W przypadku implementacji w języku C++ mamy trzy nieskończone pętle. Jedną dostępną w ramach metody zinterpretujWcisnietyPrzycisk() z klasy ObslugaWejscia, kolejną w metodzie przesun() z klasy Waz, następną z kolei w metodzie wyswietlGre(), dostępnej w ramach klasy WyswietlanieGrafiki.
Należy teraz dodać do wszystkich wymienionych wyżej klas nowy atrybut o nazwie czyGraSieToczy lub dopisać go do definicji wymienionych powyżej metod.
Dodanie atrybutu do definicji metod spowoduje, że staną się one bardziej rozbudowane.
Dodanie atrybutu do klas będzie oznaczało, że nie wykonaliśmy programu zgodnie z założonym na początku diagramem.
Stajemy przed trudną decyzją, czy lepiej trzymać się diagramu, czy jednak zgłosić uwagę do projektantów.
W przypadku zgłoszenia uwagi, po uzyskaniu zgody projektantów na zmianę, dodajemy we wcześniej wspomnianych klasach następujący atrybut wraz z konstruktorem:
Linia 1. bool asterysk czyGraDziala średnik.
Natomiast wszystkie nieskończone pętle typu:
Linia 1. while otwórz nawias okrągły true zamknij nawias okrągły.
Linia 2. otwórz nawias klamrowy.
Linia 3. kropka kropka kropka.
Linia 4. zamknij nawias klamrowy.
lub
Linia 1. while otwórz nawias okrągły 1 zamknij nawias okrągły.
Linia 2. otwórz nawias klamrowy.
Linia 3. kropka kropka kropka.
Linia 4. zamknij nawias klamrowy.
zamieniamy na:
Linia 1. while otwórz nawias okrągły asterysk czyGraDziala zamknij nawias okrągły.
Linia 2. otwórz nawias klamrowy.
Linia 4. zamknij nawias klamrowy.
W funkcji main() – przed samym stworzeniem i uruchomieniem wątków – dopisujemy także:
Linia 1. wejscie kropka czyGraDziala znak równości ampersant czyGraPracuje średnik.
Linia 2. grafika kropka czyGraDziala znak równości ampersant czyGraPracuje średnik.
Linia 3. obiektWaz kropka czyGraDziala znak równości ampersant czyGraPracuje średnik.
Następnie przechodzimy do umieszczenia w ramach metody przesun() warunków, które grę zatrzymają:
Linia 1. if otwórz nawias okrągły sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły otwórz nawias ostrokątny 0 kreska pionowa kreska pionowa sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias ostrokątny znak równości plansza minus zamknij nawias ostrokątny rozmiarYPlanszy.
Linia 2. kreska pionowa kreska pionowa sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły otwórz nawias ostrokątny 0 kreska pionowa kreska pionowa sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias ostrokątny znak równości plansza minus zamknij nawias ostrokątny rozmiarXPlanszy zamknij nawias okrągły.
Linia 3. otwórz nawias klamrowy prawy ukośnik prawy ukośnik waz wyjezdza poza mape.
Linia 4. asterysk czyGraDziala znak równości false średnik.
Linia 5. break średnik.
Linia 6. zamknij nawias klamrowy.
Linia 7. if otwórz nawias okrągły owocNaPolu minus zamknij nawias ostrokątny sprawdzPunkty otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły.
Linia 8. otwórz nawias klamrowy.
Linia 9. zjedzOwoc otwórz nawias okrągły owocNaPolu przecinek plansza zamknij nawias okrągły średnik.
Linia 10. if otwórz nawias okrągły plansza minus zamknij nawias ostrokątny liczbaOwocow znak równości znak równości 0 zamknij nawias okrągły prawy ukośnik prawy ukośnik wszystkie owoce zjedzone.
Linia 11. otwórz nawias klamrowy.
Linia 12. asterysk czyGraDziala znak równości false średnik.
Linia 13. zamknij nawias klamrowy.
Linia 14. zamknij nawias klamrowy.
Linia 15. if otwórz nawias okrągły plansza minus zamknij nawias ostrokątny pole otwórz nawias kwadratowy pozycjaWezaY zamknij nawias kwadratowy otwórz nawias kwadratowy pozycjaWezaX zamknij nawias kwadratowy kropka wartoscPola zamknij nawias ostrokątny 0 zamknij nawias okrągły prawy ukośnik prawy ukośnik waz dotyka swojego ogona.
Linia 16. asterysk czyGraDziala znak równości false średnik.
W funkcji main() po wszystkich metodach join() dopisujemy następujący fragment kodu:
Oczywiście Mona Lisa pełni tutaj funkcję placeholdera, który możemy zastąpić dowolnym, lepiej pasującym obrazkiem lub napisem. Możemy też użyć np. logotypu jakiejś firmy.
Po uruchomieniu i przetestowaniu tego kodu zauważymy jednak pewien problem. W momencie, gdy spełniony zostanie którykolwiek z warunków zakończenia rozgrywki (wyjście poza plansze, dotknięcie ogona lub zjedzenie wszystkich owoców), wąż się zatrzymuje, ale nie wyświetla się wynik. Dzieje się to dopiero po jeszcze jednym naciśnięciu jakiegokolwiek klawisza. Dlaczego?
Przyczyną jest sposób, w jaki odczytujemy klawiaturę. Ma to miejsce w metodzie zinterpretujWcisnietyPrzycisk() klasy ObslugaWejscia:
Linia 1. void zinterpretujWcisnietyPrzycisk otwórz nawias okrągły Waz asterysk waz zamknij nawias okrągły.
Linia 2. otwórz nawias klamrowy.
Linia 3. while otwórz nawias okrągły true zamknij nawias okrągły.
Linia 4. otwórz nawias klamrowy.
Linia 5. char przycisk znak równości getch otwórz nawias okrągły zamknij nawias okrągły średnik.
Linia 6. switch otwórz nawias okrągły przycisk zamknij nawias okrągły.
Linia 7. otwórz nawias klamrowy.
Linia 8. case apostrof a apostrof dwukropek.
Linia 10. kropka kropka kropka.
W piątej linii wywoływana jest funkcja getch(), która odczytuje stan klawisza. Czeka ona na naciśnięcie klawisza, dlatego (mimo że wątek ma być już przerwany) w tym miejscu pojawia się dodatkowe oczekiwanie. Dopiero po naciśnięciu klawisza następuje zakończenie wątku. Jak sobie z tym poradzić?
Najprościej będzie zmodyfikować kod, aby najpierw program sprawdzał, czy jakiś klawisz został faktycznie naciśnięty, a dopiero potem odczytywał za pomocą funkcji getch(), jaki to był klawisz. Można to zrobić w taki sposób:
Linia 1. while otwórz nawias okrągły asterysk czyGraDziala zamknij nawias okrągły.
Linia 2. otwórz nawias klamrowy.
Linia 3. char przycisk znak równości 0 średnik.
Linia 4. if otwórz nawias okrągły kbhit otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły.
Linia 5. przycisk znak równości getch otwórz nawias okrągły zamknij nawias okrągły średnik.
Linia 6. switch otwórz nawias okrągły przycisk zamknij nawias okrągły.
Linia 7. otwórz nawias klamrowy.
Linia 8. case apostrof a apostrof dwukropek.
Funkcja kbhit() zwraca true, jeżeli naciśnięto jakiś klawisz, a false, jeżeli nie. Całość metody zinterpretujWcisnietyPrzycisk():
Linia 1. void zinterpretujWcisnietyPrzycisk otwórz nawias okrągły Waz asterysk waz zamknij nawias okrągły.
Linia 2. otwórz nawias klamrowy.
Linia 3. while otwórz nawias okrągły asterysk czyGraDziala zamknij nawias okrągły.
Linia 4. otwórz nawias klamrowy.
Linia 5. char przycisk znak równości 0 średnik.
Linia 6. if otwórz nawias okrągły kbhit otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły.
Linia 7. przycisk znak równości getch otwórz nawias okrągły zamknij nawias okrągły średnik.
Linia 8. switch otwórz nawias okrągły przycisk zamknij nawias okrągły.
Linia 9. otwórz nawias klamrowy.
Linia 10. case apostrof a apostrof dwukropek.
Linia 11. if otwórz nawias okrągły waz minus zamknij nawias ostrokątny kierunekRuchu znak równości znak równości waz minus zamknij nawias ostrokątny polnoc zamknij nawias okrągły.
Linia 12. waz minus zamknij nawias ostrokątny kierunekRuchu znak równości waz minus zamknij nawias ostrokątny wschod średnik.
Linia 13. else if otwórz nawias okrągły waz minus zamknij nawias ostrokątny kierunekRuchu znak równości znak równości waz minus zamknij nawias ostrokątny wschod zamknij nawias okrągły.
Linia 14. waz minus zamknij nawias ostrokątny kierunekRuchu znak równości waz minus zamknij nawias ostrokątny poludnie średnik.
Linia 15. else if otwórz nawias okrągły waz minus zamknij nawias ostrokątny kierunekRuchu znak równości znak równości waz minus zamknij nawias ostrokątny poludnie zamknij nawias okrągły.
Linia 16. waz minus zamknij nawias ostrokątny kierunekRuchu znak równości waz minus zamknij nawias ostrokątny zachod średnik.
Linia 17. else.
Linia 18. waz minus zamknij nawias ostrokątny kierunekRuchu znak równości waz minus zamknij nawias ostrokątny polnoc średnik.
Linia 19. break średnik.
Linia 20. case apostrof d apostrof dwukropek.
Linia 21. if otwórz nawias okrągły waz minus zamknij nawias ostrokątny kierunekRuchu znak równości znak równości waz minus zamknij nawias ostrokątny polnoc zamknij nawias okrągły.
Linia 22. waz minus zamknij nawias ostrokątny kierunekRuchu znak równości waz minus zamknij nawias ostrokątny zachod średnik.
Linia 23. else if otwórz nawias okrągły waz minus zamknij nawias ostrokątny kierunekRuchu znak równości znak równości waz minus zamknij nawias ostrokątny zachod zamknij nawias okrągły.
Linia 24. waz minus zamknij nawias ostrokątny kierunekRuchu znak równości waz minus zamknij nawias ostrokątny poludnie średnik.
Linia 25. else if otwórz nawias okrągły waz minus zamknij nawias ostrokątny kierunekRuchu znak równości znak równości waz minus zamknij nawias ostrokątny poludnie zamknij nawias okrągły.
Linia 26. waz minus zamknij nawias ostrokątny kierunekRuchu znak równości waz minus zamknij nawias ostrokątny wschod średnik.
Linia 27. else.
Linia 28. waz minus zamknij nawias ostrokątny kierunekRuchu znak równości waz minus zamknij nawias ostrokątny polnoc średnik.
Linia 29. break średnik.
Linia 30. case apostrof apostrof dwukropek.
Linia 32. break średnik.
Linia 33. zamknij nawias klamrowy.
Linia 34. zamknij nawias klamrowy.
Linia 35. return średnik.
Linia 36. zamknij nawias klamrowy.
Po modyfikacji program poprawnie kończy swoje działanie.
Napisany dotąd kod C++ można pobrać, korzystając z tego linku:
R1QCx5enMB1pn
Zakończenie rozgrywki w języku Java
Wcześniej zdefiniowaliśmy różne warunki zakończenia rozgrywki. Wszystkie jesteśmy w stanie sprawdzić z poziomu metody przesun().
W przypadku implementacji gry w języku Java musimy przekazać informację do obydwu pracujących wątków, a tylko w jednym z nich będziemy sprawdzać, czy gra się zakończyła.
Dodatkowym problemem jest, że w diagramie klas nie umieściliśmy atrybutu odpowiadającego za sprawdzanie, czy gra aktualnie działa, czy nie. Mamy dwie możliwości: możemy spróbować obejść ten problem albo zgłosić uwagę do projektantów, że należy na diagram nanieść poprawkę, żeby gdzieś taki atrybut się znalazł.
Wybieramy tę drugą opcję. Gdy projektanci zaakceptują poprawkę, zamieścimy atrybut czyGraDziala w ramach klasy Waz. Należy pamiętać o przetestowaniu kodu po wprowadzonych poprawkach.
Dzięki temu rozwiązaniu uzyskamy również dostęp do tego atrybutu w ramach klasy WyswietlGrafike, ponieważ klasa ta ma jako atrybut obiekt klasy Waz.
Dlatego jako atrybut klasy Waz dodamy:
Linia 1. boolean czyGraDziala średnik.
Następnie edytujemy metodę run() w klasie WyswietlanieGrafiki oraz w klasie Waz – w taki sposób, żeby obecne tam nieskończone pętle while zmienić z postaci:
Linia 1. while otwórz nawias okrągły true zamknij nawias okrągły otwórz nawias klamrowy.
Linia 2. kropka kropka kropka.
Linia 3. zamknij nawias klamrowy.
na:
Linia 1. while otwórz nawias okrągły czyGraDziala zamknij nawias okrągły otwórz nawias klamrowy prawy ukośnik prawy ukośnik w klasie Waz.
Linia 2. kropka kropka kropka.
Linia 3. zamknij nawias klamrowy.
Linia 5. while otwórz nawias okrągły waz kropka czyGraDziala zamknij nawias okrągły otwórz nawias klamrowy prawy ukośnik prawy ukośnik w klasie WyswietlanieGrafii.
Linia 6. kropka kropka kropka.
Linia 7. zamknij nawias klamrowy.
W metodzie main() w klasie Main dodajemy również:
Linia 1. waz kropka czyGraDziala znak równości true średnik.
Dopisujemy ten fragment przed uruchomieniem wątków, ponieważ domyślną wartością zmiennej boolean w Javie jest false.
Dopisujemy trzy wcześniej zdefiniowane warunki zakończenia rozgrywki:
Linia 1. if otwórz nawias okrągły plansza kropka pole otwórz nawias kwadratowy sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy otwórz nawias kwadratowy sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy kropka wartoscPola zamknij nawias ostrokątny 0 zamknij nawias okrągły otwórz nawias klamrowy.
Linia 2. czyGraDziala znak równości false średnik.
Linia 3. zamknij nawias klamrowy.
Linia 1. if otwórz nawias okrągły sprawdzCzyNaPlanszy otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy.
Linia 2. czyGraDziala znak równości false średnik.
Linia 3. zamknij nawias klamrowy.
Linia 1. if otwórz nawias okrągły plansza kropka liczbaOwocow znak równości znak równości 0 zamknij nawias okrągły otwórz nawias klamrowy.
Linia 2. czyGraDziala znak równości false średnik.
Linia 3. zamknij nawias klamrowy.
Metoda przesun() przyjmuje następującą postać:
Linia 1. void przesun otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy.
Linia 2. if otwórz nawias okrągły kierunekRuchu znak równości znak równości Kierunek kropka polnoc zamknij nawias okrągły otwórz nawias klamrowy.
Linia 3. pozycjaWezaY minus znak równości 1 średnik.
Linia 4. zamknij nawias klamrowy else if otwórz nawias okrągły kierunekRuchu znak równości znak równości Kierunek kropka poludnie zamknij nawias okrągły otwórz nawias klamrowy.
Linia 5. pozycjaWezaY plus znak równości 1 średnik.
Linia 6. zamknij nawias klamrowy else if otwórz nawias okrągły kierunekRuchu znak równości znak równości Kierunek kropka zachod zamknij nawias okrągły otwórz nawias klamrowy.
Linia 7. pozycjaWezaX plus znak równości 1 średnik.
Linia 8. zamknij nawias klamrowy else if otwórz nawias okrągły kierunekRuchu znak równości znak równości Kierunek kropka wschod zamknij nawias okrągły otwórz nawias klamrowy.
Linia 9. pozycjaWezaX minus znak równości 1 średnik.
Linia 10. zamknij nawias klamrowy.
Linia 11. if otwórz nawias okrągły sprawdzCzyNaPlanszy otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy.
Linia 12. Owoc owoc znak równości plansza kropka pole otwórz nawias kwadratowy pozycjaWezaY zamknij nawias kwadratowy otwórz nawias kwadratowy pozycjaWezaX zamknij nawias kwadratowy kropka owoc średnik.
Linia 13. if otwórz nawias okrągły owoc wykrzyknik znak równości null zamknij nawias okrągły.
Linia 14. zjedzOwoc otwórz nawias okrągły plansza kropka pole otwórz nawias kwadratowy pozycjaWezaY zamknij nawias kwadratowy otwórz nawias kwadratowy pozycjaWezaX zamknij nawias kwadratowy zamknij nawias okrągły średnik.
Linia 15. plansza kropka zaaktualizujOgonWeza otwórz nawias okrągły zamknij nawias okrągły średnik.
Linia 16. if otwórz nawias okrągły plansza kropka pole otwórz nawias kwadratowy sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy otwórz nawias kwadratowy sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy kropka wartoscPola zamknij nawias ostrokątny 0 zamknij nawias okrągły otwórz nawias klamrowy.
Linia 17. czyGraDziala znak równości false średnik prawy ukośnik prawy ukośnik zakonczenie gry przecinek dotkniecie ogona kropka.
Linia 18. zamknij nawias klamrowy.
Linia 19. plansza kropka pole otwórz nawias kwadratowy pozycjaWezaY zamknij nawias kwadratowy otwórz nawias kwadratowy sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy kropka wartoscPola znak równości liczbaPunktow plus 1 średnik.
Linia 20. zamknij nawias klamrowy.
Linia 21. else otwórz nawias klamrowy.
Linia 22. czyGraDziala znak równości false średnik prawy ukośnik prawy ukośnik zakonczenie gry przecinek poza planszą.
Linia 23. zamknij nawias klamrowy.
Linia 24. if otwórz nawias okrągły plansza kropka liczbaOwocow znak równości znak równości 0 zamknij nawias okrągły otwórz nawias klamrowy.
Linia 25. czyGraDziala znak równości false średnik prawy ukośnik prawy ukośnik zakończenie gry przecinek wszystkie owoce.
Linia 26. zamknij nawias klamrowy.
Linia 27. zamknij nawias klamrowy.
Dodatkowo przeciążamy metodę wyswietlGre(), żeby móc wyświetlić graczowi komunikat po zakończeniu rozgrywki.
Linia 1. public void wyswietlGre otwórz nawias okrągły String wiadomosc zamknij nawias okrągły otwórz nawias klamrowy.
Linia 2. grafikaTekstowa kropka setText otwórz nawias okrągły wiadomosc zamknij nawias okrągły średnik.
Linia 3. zamknij nawias klamrowy.
W metodzie main() na końcu dopisujemy:
Linia 1. try otwórz nawias klamrowy.
Linia 2. watekGrafiki kropka join otwórz nawias okrągły zamknij nawias okrągły średnik.
Linia 3. watekWeza kropka join otwórz nawias okrągły zamknij nawias okrągły średnik.
Linia 4. zamknij nawias klamrowy catch otwórz nawias okrągły InterruptedException e zamknij nawias okrągły otwórz nawias klamrowy.
Linia 5. e kropka printStackTrace otwórz nawias okrągły zamknij nawias okrągły średnik.
Linia 6. zamknij nawias klamrowy.
Linia 7. grafika kropka wyswietlGre otwórz nawias okrągły cudzysłów Twoj wynik punktowy to dwukropek cudzysłów plus waz kropka sprawdzWynik otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły średnik.
W ten sposób kończymy implementację zakończenia rozgrywki w języku Java.
Cały kod źródłowy w języku Java można pobrać, korzystając z poniższego linku:
R1XTzBG5Pip8B
Zakończenie rozgrywki w języku Python
Podobnie jak w przypadku innych języków możemy sprawdzić wszystkie zakończenia rozgrywki w ramach metody przesun(). Zrobimy to więc w tej metodzie, jednak pojawia się pytanie, jak przekażemy tę informację z wątku węża do innych wątków?
Najprościej będzie zgłosić projektantom uwagę, że nie jesteśmy w stanie zrealizować funkcji bez dodatkowego atrybutu, który musi zostać dodany.
Po uzyskaniu zgody projektantów, dodajemy wspomniany atrybut do klasy Waz. Atrybut ten domyślnie ustawimy na wartość True.
Linia 1. czyGraDziala znak równości True.
Diagram klas po zmianie wygląda następująco:
REm7mScYTFuzZ
Następnie zamieniamy nieskończone pętle (związane z animacją oraz z poruszaniem się węża) z postaci:
Linia 1. while 1 dwukropek.
Linia 2. kropka kropka kropka.
na:
Linia 1. while self kropka czyGraDziala dwukropek kratka w wnętrzu klasy Waz.
Linia 2. kropka kropka kropka.
Linia 3. while waz kropka czyGraDziala dwukropek kratka w klasie WyswietlanieGrafiki.
Linia 4. kropka kropka kropka.
Dopisujemy wcześniej zdefiniowane warunki, a w nich:
Linia 1. waz kropka czyGraDziala znak równości False.
Tymi warunkami są:
Linia 1. if pole kropka wartoscPola zamknij nawias ostrokątny 0 dwukropek.
Linia 2. waz kropka czyGraDziala znak równości False kratka dotkniecie ogona.
Linia 1. if self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły otwórz nawias ostrokątny 0 or self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias ostrokątny znak równości plansza kropka rozmiarYPlanszy or self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły otwórz nawias ostrokątny 0 or self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias ostrokątny znak równości plansza kropka rozmiarXPlanszy dwukropek.
Linia 2. waz kropka czyGraDziala znak równości False kratka wyjscie poza plansze.
Linia 1. if plansza kropka ileOwocow znak równości znak równości 0 dwukropek.
Linia 2. waz kropka czyGraDziala znak równości False kratka wszystkie owoce z planszy zjedzone.
Po wprowadzeniu tych warunków metoda przesun() wygląda następująco:
Linia 1. def przesun otwórz nawias okrągły self przecinek zamekWeza przecinek zamekGrafiki przecinek plansza zamknij nawias okrągły dwukropek.
Linia 2. import time.
Linia 3. while self kropka czyGraDziala dwukropek.
Linia 4. zamekWeza kropka acquire otwórz nawias okrągły zamknij nawias okrągły.
Linia 5. if self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka polnoc kropka value dwukropek.
Linia 6. self kropka pozycjaWezaY minus znak równości 1.
Linia 7. elif self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka poludnie kropka value dwukropek.
Linia 8. self kropka pozycjaWezaY plus znak równości 1.
Linia 9. elif self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka zachod kropka value dwukropek.
Linia 10. self kropka pozycjaWezaX minus znak równości 1.
Linia 11. elif self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka wschod kropka value dwukropek.
Linia 12. self kropka pozycjaWezaX plus znak równości 1.
Linia 13. if plansza kropka ileOwocow znak równości znak równości 0 dwukropek.
Linia 14. waz kropka czyGraDziala znak równości False.
Linia 15. if self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły otwórz nawias ostrokątny 0 or self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias ostrokątny znak równości plansza kropka rozmiarYPlanszy or self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły otwórz nawias ostrokątny 0 or self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias ostrokątny znak równości plansza kropka rozmiarXPlanszy dwukropek.
Linia 16. zamekGrafiki kropka release otwórz nawias okrągły zamknij nawias okrągły.
Linia 17. waz kropka czyGraDziala znak równości False.
Linia 18. break.
Linia 19. else dwukropek.
Linia 20. owoc znak równości plansza kropka pole otwórz nawias kwadratowy self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy otwórz nawias kwadratowy self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy kropka owoc.
Linia 21. if owoc wykrzyknik znak równości None dwukropek.
Linia 22. self kropka zjedzOwoc otwórz nawias okrągły plansza kropka pole otwórz nawias kwadratowy self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy otwórz nawias kwadratowy self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy zamknij nawias okrągły.
Linia 23. pole znak równości plansza kropka pole otwórz nawias kwadratowy self kropka sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy otwórz nawias kwadratowy self kropka sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły zamknij nawias kwadratowy.
Linia 24. plansza kropka zaaktualizujOgonWeza otwórz nawias okrągły zamknij nawias okrągły.
Linia 26. if pole kropka wartoscPola zamknij nawias ostrokątny 0 dwukropek.
Linia 27. waz kropka czyGraDziala znak równości False kratka dotkniecie ogona.
Linia 28. pole kropka wartoscPola znak równości self kropka liczbaPunktow plus 1.
Linia 29. time kropka sleep otwórz nawias okrągły 1 zamknij nawias okrągły.
Linia 30. zamekGrafiki kropka release otwórz nawias okrągły zamknij nawias okrągły.
Teraz modyfikujemy program tak, by po zakończeniu rozgrywki pokazywał wynik punktowy.
Linia 1. watekGrafiki kropka join otwórz nawias okrągły zamknij nawias okrągły.
Linia 2. watekWeza kropka join otwórz nawias okrągły zamknij nawias okrągły.
Linia 3. import os.
Linia 4. os kropka system otwórz nawias okrągły apostrof CLS apostrof zamknij nawias okrągły.
Linia 5. print otwórz nawias okrągły cudzysłów kropka cudzysłów zamknij nawias okrągły.
Linia 6. print otwórz nawias okrągły cudzysłów kropka kreska pionowa kropka cudzysłów zamknij nawias okrągły.
Linia 7. print otwórz nawias okrągły cudzysłów kreska pionowa kreska pionowa kreska pionowa cudzysłów zamknij nawias okrągły.
Linia 8. print otwórz nawias okrągły cudzysłów kreska pionowa kreska pionowa kreska pionowa cudzysłów zamknij nawias okrągły.
Linia 9. print otwórz nawias okrągły cudzysłów kreska pionowa kreska pionowa kreska pionowa cudzysłów zamknij nawias okrągły.
Linia 10. print otwórz nawias okrągły cudzysłów kreska pionowa kreska pionowa kreska pionowa cudzysłów zamknij nawias okrągły.
Linia 11. print otwórz nawias okrągły cudzysłów j podkreślnik I cudzysłów zamknij nawias okrągły.
Linia 12. print otwórz nawias okrągły cudzysłów kropka zamknij nawias okrągły podkreślnik otwórz nawias okrągły kropka cudzysłów zamknij nawias okrągły.
Linia 13. print otwórz nawias okrągły cudzysłów kreska pionowa znak równości znak równości znak równości kreska pionowa cudzysłów zamknij nawias okrągły.
Linia 14. print otwórz nawias okrągły cudzysłów prawy ukośnik podkreślnik podkreślnik podkreślnik lewy ukośnik cudzysłów zamknij nawias okrągły.
Linia 15. print otwórz nawias okrągły cudzysłów prawy ukośnik prawy ukośnik podkreślnik podkreślnik podkreślnik lewy ukośnik lewy ukośnik cudzysłów zamknij nawias okrągły.
Linia 16. print otwórz nawias okrągły cudzysłów prawy ukośnik znak równości znak równości znak równości znak równości znak równości znak równości znak równości lewy ukośnik cudzysłów zamknij nawias okrągły.
Linia 17. print otwórz nawias okrągły cudzysłów prawy ukośnik kropka minus lewy ukośnik cudzysłów lewy ukośnik cudzysłów lewy ukośnik cudzysłów minus kropka lewy ukośnik cudzysłów zamknij nawias okrągły.
Linia 18. print otwórz nawias okrągły cudzysłów kreska pionowa podkreślnik podkreślnik kreska pionowa kreska pionowa podkreślnik podkreślnik kreska pionowa cudzysłów zamknij nawias okrągły.
Linia 19. print otwórz nawias okrągły cudzysłów Twoj wynik punktowy to dwukropek cudzysłów plus str otwórz nawias okrągły waz kropka liczbaPunktow zamknij nawias okrągły zamknij nawias okrągły.
Linia 20. watekWejscia kropka join otwórz nawias okrągły zamknij nawias okrągły.
W przedstawionym fragmencie dopisaliśmy jeszcze wyświetlanie się wieży, przed wyświetleniem wyniku. Tak skonstruowany obrazek nazywa się ASCII ArtASCII ArtASCII Art.
W tym przypadku został on wybrany losowo, żeby pokazać koncepcję. Sugerujemy napisanie własnego lub wybranie innego, bardziej pasującego tematycznie.
Cały napisany dotąd kod w języku Python można pobrać, korzystając z linku:
RHxemUGIYA683
Słownik
ASCII Art
ASCII Art
tworzenie prostych rysunków za pomocą znaków ASCII