W poprzednich e‑materiałach z serii poświęconej programowaniu obiektowemu napisaliśmy program, który rejestrował wybieranie przez użytkownika przycisków na klawiaturze. Nie przekładało się to jednak na ruch węża. Teraz zajmiemy się tworzeniem rzeczywiście działających mechanik rozgrywki. Zacznijmy od zaimplementowania węża poruszającego się w kierunku, w którym jest zwrócony.

Przypomnijmy sobie, jak wygląda diagram klas, na podstawie którego tworzymy program:

R1MbrEUMaed6o
Źródło: Contentplus.pl sp. z o.o., licencja: CC BY-SA 3.0.

Prześledźmy, jak w poszczególnych językach programowania stworzyć i wczytać satysfakcjonujący nas plik konfiguracyjny.

Tworzenie gry w języku C++

Wróćmy do kodu napisanego we wcześniejszych e‑materiałach z tej serii. Zaimplementujmy metodę sprawdzWynik() z klasy Wąż, która powinna sprawdzać aktualną liczbę punktów. Możemy się spodziewać, że zwróci ona jedynie wartość atrybutu liczbaPunktów.

Linia 1. int sprawdzWynik otwórz nawias okrągły zamknij nawias okrągły. Linia 2. otwórz nawias klamrowy. Linia 3. return liczbaPunktow średnik. Linia 4. zamknij nawias klamrowy.

Z kolei metoda ustawWynik() powinna modyfikować atrybut liczbaPunktow na ten podany w argumencie.

Linia 1. int ustawWynik otwórz nawias okrągły int liczbaPunktowDoUstawienia zamknij nawias okrągły. Linia 2. otwórz nawias klamrowy. Linia 3. liczbaPunktow znak równości liczbaPunktowDoUstawienia średnik. Linia 4. zamknij nawias klamrowy.

Metoda zmienKierunek(), jak wskazuje jej nazwa, zmienia kierunek ruchu węża, czyli modyfikuje atrybut kierunekRuchu. Nie zdefiniowaliśmy dotychczas, jaki ruch oznacza dana wartość. Stwórzmy więc do tego celu wyliczeniowy typ danych:

Linia 1. enum kierunek otwórz nawias klamrowy polnoc znak równości 0 przecinek wschod znak równości 1 przecinek poludnie znak równości 2 przecinek zachod znak równości 3 zamknij nawias klamrowy średnik.

Dobrą praktyką jest stosowanie słów zamiast abstrakcyjnych wartości liczbowych. Powyższy kod umieścimy jako publiczny w ramach klasy Waz. Metoda zmienKierunek(), z uwzględnieniem wspomnianego typu wyliczeniowego, przyjmie następującą postać:

Linia 1. void zmienKierunek otwórz nawias okrągły kierunek nowyKierunek zamknij nawias okrągły. Linia 2. otwórz nawias klamrowy. Linia 3. kierunekRuchu znak równości nowyKierunek średnik. Linia 4. zamknij nawias klamrowy.

W diagramie klas kolejna na liście jest metoda przesun(), odpowiadająca za przesunięcie węża. Podczas tworzenia tej klasy założyliśmy, że przesunięcie odbędzie się zgodnie z kierunkiem ruchu.

Linia 1. void przesun otwórz nawias okrągły zamknij nawias okrągły. Linia 2. otwórz nawias klamrowy. Linia 3. if otwórz nawias okrągły kierunekRuchu znak równości znak równości polnoc zamknij nawias okrągły. Linia 4. otwórz nawias klamrowy. Linia 5. pozycjaWezaY minus znak równości 1 średnik. Linia 6. zamknij nawias klamrowy. Linia 7. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości poludnie zamknij nawias okrągły. Linia 8. otwórz nawias klamrowy. Linia 9. pozycjaWezaY 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 zachod zamknij nawias okrągły. Linia 12. otwórz nawias klamrowy. Linia 13. pozycjaWezaX plus znak równości 1 średnik. Linia 14. zamknij nawias klamrowy. Linia 15. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości wschod zamknij nawias okrągły. Linia 16. otwórz nawias klamrowy. Linia 17. pozycjaWezaX minus znak równości 1 średnik. Linia 18. zamknij nawias klamrowy. Linia 19. zamknij nawias klamrowy.

Patrząc na przedstawiony kod, możemy się zastanawiać, czy nie ma w nim błędu. Dlaczego ruch w kierunku północnym zmniejsza wartość y, zamiast zwiększać?

Odpowiedź związana jest ze sposobem wyświetlania danych. Przypomnijmy sobie, jak wygląda metoda wyświetlająca grafikę:

Linia 1. void wyswietlGre otwórz nawias okrągły Waz asterysk waz przecinek Plansza asterysk plansza 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. system otwórz nawias okrągły cudzysłów CLS cudzysłów zamknij nawias okrągły średnik. Linia 6. 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 7. otwórz nawias klamrowy. Linia 8. 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 9. otwórz nawias klamrowy. Linia 10. cout otwórz nawias ostrokątny otwórz nawias ostrokątny 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 otwórz nawias ostrokątny otwórz nawias ostrokątny cudzysłów lewy ukośnik t cudzysłów średnik. Linia 11. zamknij nawias klamrowy. Linia 12. cout otwórz nawias ostrokątny otwórz nawias ostrokątny endl średnik. Linia 13. zamknij nawias klamrowy. Linia 14. this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły chrono dwukropek dwukropek milliseconds otwórz nawias okrągły 400 zamknij nawias okrągły zamknij nawias okrągły średnik. Linia 15. zamknij nawias klamrowy. Linia 16. zamknij nawias klamrowy.

Plansza rysuje się zgodnie z następującym schematem:

RoXfVajMqSBoo
Źródło: Contentplus.pl sp. z o.o., licencja: CC BY-SA 3.0.

Rysujemy wszystkie wiersze – począwszy od zerowego do ostatniego. Jeśli chcemy, aby wąż poruszał się „do góry”, musi ulec zmianie wartość y. Oczywiście możemy zmienić sposób implementacji grafiki, tak żeby plansza rysowana była od ostatniego wiersza do wiersza o indeksie zero. Mamy dowolność, należy jednak pamiętać o zachowaniu konsekwencji w przyjętej orientacji.

Kolejnymi dwiema metodami z klasy Waz są bliźniacze metody: sprawdzPozycjeX() oraz sprawdzPozycjeY(). Służą one kolejno do odczytywania wartości atrybutów: pozycjaWezaX oraz pozycjaWezaY.

Linia 1. int sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły. Linia 2. otwórz nawias klamrowy. Linia 3. return pozycjaWezaY średnik. Linia 4. zamknij nawias klamrowy. Linia 5. int sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły. Linia 6. otwórz nawias klamrowy. Linia 7. return pozycjaWezaX średnik. Linia 8. zamknij nawias klamrowy.

Metoda ustawPozycje() działa analogicznie do metody ustawWynik(), z tą tylko różnicą, że potrzebujemy dwóch argumentów.

Linia 1. void ustawPozycje otwórz nawias okrągły int x przecinek int y zamknij nawias okrągły. Linia 2. otwórz nawias klamrowy. Linia 3. pozycjaWezaX znak równości x średnik. Linia 4. pozycjaWezaY znak równości y średnik. Linia 5. zamknij nawias klamrowy.

Ostatnią metodą jest metoda zjedzOwoc() – do jej zaimplementowania będziemy potrzebowali obiektu z klasy Owoc. Oznacza to, że musimy przenieść definicję klasy Owoc nad definicję klasy Waz.

Linia 1. void zjedzOwoc otwórz nawias okrągły Owoc owoc zamknij nawias okrągły. Linia 2. otwórz nawias klamrowy. Linia 3. liczbaPunktow plus znak równości owoc kropka sprawdzPunkty otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 4. zamknij nawias klamrowy.

Owoc powinien w tym momencie zniknąć. Usunięcie to zaimplementujemy jednak w innym miejscu.

Pojawia się pytanie: Co należy zrobić, aby wąż przesuwał się samodzielnie, zachowując jednocześnie podział z diagramu pakietów?

Dla przypomnienia:

R15KioZPCAlm3
Źródło: Contentplus.pl sp. z o.o., licencja: CC BY-SA 3.0.

Zamierzony efekt możemy otrzymać na kilka sposobów. Jednym z nich jest dodanie kolejnego wątku dla obiektu klasy Waz. Będzie się on poruszał niezależnie od tego, co się dzieje w obiekcie klasy Plansza.

Innym sposobem jest stworzenie wątku w klasie Plansza, tak żeby losowanie kolejnych owoców oraz ruch węża były synchroniczne. Warto odnotować, że potrzebujemy wątku w klasie Plansza, który będzie odpowiadał za losowanie owoców na mapie.

Decydujemy się więc na stworzenie dwóch nowych oddzielnych wątków. Aby to zrobić, musimy dodać do funkcji main następujące linijki:

Linia 1. Waz obiektWaz znak równości Waz otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 2. thread watekWeza otwórz nawias okrągły ampersant Waz dwukropek dwukropek przesun przecinek ampersant obiektWaz zamknij nawias okrągły średnik. Linia 3. watekWeza kropka join otwórz nawias okrągły zamknij nawias okrągły średnik.

Ostatecznie otrzymamy:

Linia 1. int main otwórz nawias okrągły zamknij nawias okrągły. Linia 2. otwórz nawias klamrowy. Linia 3. Plansza plansza znak równości Plansza otwórz nawias okrągły 5 przecinek 5 zamknij nawias okrągły średnik. Linia 4. WyswietlanieGrafiki grafika znak równości WyswietlanieGrafiki otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 5. Waz obiektWaz znak równości Waz otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 6. thread watekGrafiki otwórz nawias okrągły ampersant WyswietlanieGrafiki dwukropek dwukropek wyswietlGre przecinek ampersant grafika przecinek ampersant obiektWaz przecinek ampersant plansza zamknij nawias okrągły średnik. Linia 7. ObslugaWejscia wejscie średnik. Linia 8. thread watekWejscia otwórz nawias okrągły ampersant ObslugaWejscia dwukropek dwukropek zinterpretujWcisnietyPrzycisk przecinek ampersant wejscie zamknij nawias okrągły średnik. Linia 9. thread watekWeza otwórz nawias okrągły ampersant Waz dwukropek dwukropek przesun przecinek ampersant obiektWaz zamknij nawias okrągły średnik. Linia 10. watekGrafiki kropka join otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 11. watekWejscia kropka join otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 12. watekWeza kropka join otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 13. zamknij nawias klamrowy.

Zanim zaimplementujemy wyświetlanie się węża na mapie, zapętlimy metodę przesun(), a następnie opóźnimy ją. W przeciwnym wypadku wąż przesuwałby się zgodnie z prędkością pracy naszego procesora.

Metoda ta przyjmie następującą postać:

Linia 1. void przesun otwórz nawias okrągły 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. if otwórz nawias okrągły kierunekRuchu znak równości znak równości polnoc zamknij nawias okrągły. Linia 6. otwórz nawias klamrowy. Linia 7. pozycjaWezaY minus znak równości 1 średnik. Linia 8. zamknij nawias klamrowy. Linia 9. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości poludnie zamknij nawias okrągły. Linia 10. otwórz nawias klamrowy. Linia 11. pozycjaWezaY plus znak równości 1 średnik. Linia 12. zamknij nawias klamrowy. Linia 13. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości zachod zamknij nawias okrągły. Linia 14. otwórz nawias klamrowy. Linia 15. pozycjaWezaX plus znak równości 1 średnik. Linia 16. zamknij nawias klamrowy. Linia 17. else if otwórz nawias okrągły kierunekRuchu znak równości znak równości wschod zamknij nawias okrągły. Linia 18. otwórz nawias klamrowy. Linia 19. pozycjaWezaX minus znak równości 1 średnik. Linia 20. zamknij nawias klamrowy. Linia 21. this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły chrono dwukropek dwukropek milliseconds otwórz nawias okrągły 200 zamknij nawias okrągły zamknij nawias okrągły średnik. Linia 22. zamknij nawias klamrowy. Linia 23. zamknij nawias klamrowy.

Następnie przejdziemy do wyświetlania grafiki:

Linia 1. void wyswietlGre otwórz nawias okrągły Waz asterysk waz przecinek Plansza asterysk plansza 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. system otwórz nawias okrągły cudzysłów CLS cudzysłów zamknij nawias okrągły średnik. Linia 6. 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 7. otwórz nawias klamrowy. Linia 8. 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 9. otwórz nawias klamrowy. Linia 10. 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 11. cout otwórz nawias ostrokątny otwórz nawias ostrokątny cudzysłów at lewy ukośnik t cudzysłów średnik. Linia 12. else. Linia 13. cout otwórz nawias ostrokątny otwórz nawias ostrokątny 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 otwórz nawias ostrokątny otwórz nawias ostrokątny cudzysłów lewy ukośnik t cudzysłów średnik. Linia 14. zamknij nawias klamrowy. Linia 15. cout otwórz nawias ostrokątny otwórz nawias ostrokątny endl średnik. Linia 16. zamknij nawias klamrowy. Linia 17. this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły chrono dwukropek dwukropek milliseconds otwórz nawias okrągły 200 zamknij nawias okrągły zamknij nawias okrągły średnik. Linia 18. zamknij nawias klamrowy. Linia 19. zamknij nawias klamrowy.

W linijkach 11 i 12 dopisaliśmy, że jeżeli wąż znajduje się na obecnie badanym polu, to zamiast wartości pola wyświetli się jego głowa.

Wartość pola w przypadku naszej gry to liczbowa reprezentacja informacji, czy na danym polu znajduje się ogon węża. Jeśli tak – liczba jest większa od zera. Jeśli ogona tam nie ma, to liczba jest równa zero. Im większa liczba, tym bliżej ogona znajduje się reprezentowane przez nią pole.

Polecenie 1

Zmień wielkość planszy na 1 x 25, a potem za pomocą metody ustawPozycje() ustaw pozycję węża na 1, 25. Zastanów się, dlaczego znaku @, oznaczającego węża nie ma na mapie.

Po uruchomieniu programu zauważymy, że wąż, pomimo dwukrotnie dłuższego czasu oczekiwania, niekiedy przeskakuje dwukrotnie. Jest to spowodowane tym, że wątki te działają w 100% asynchronicznie i ich wykonania są od siebie niezależne. W celu zapewnienia większej płynności i dokładności rozgrywki, musimy dodać mechanizm synchronizacji. Jego brak uniemożliwi grę – w skrajnej sytuacji wąż mógłby zawsze znajdować się w innym miejscu, niż to, w którym byłby wyświetlany na ekranie.

W przypadku naszego kodu trudno określić, co jest sekcją krytyczną. Zastanówmy się wobec tego, czego chcemy uniknąć:

  • Nie chcemy, aby wąż przemieszczał się w trakcie ruchu mapy. Oznacza to, że przesuwanie się mapy musi czekać, aż wąż się ruszy i analogicznie, ruszenie się węża musi oczekiwać na narysowanie się mapy.

  • Niepożądana jest również sytuacja, w której wąż ruszy się dwa razy.

Aby program działał prawidłowo, musimy utworzyć co najmniej dwie blokady. Jedna będzie otwierała możliwość działania drugiemu wątkowi, zgodnie z następującym schematem:

RNaBwL5zGGCOm
Źródło: Contentplus.pl sp. z o.o., licencja: CC BY-SA 3.0.

Mając na uwadze fakt, że semafory są obecne w standardzie C++ dopiero od wersji C++20, skorzystamy z innego rozwiązania. Będziemy symulować działanie semafora za pomocą pętli while i funkcji sleep_for(). Przykładowe „czasowe spowalnianie wątku” prezentuje się następująco:

Linia 1. while otwórz nawias okrągły watekAPracuje znak równości znak równości true zamknij nawias okrągły. Linia 2. otwórz nawias klamrowy. Linia 3. this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły chrono dwukropek dwukropek milliseconds otwórz nawias okrągły 10 zamknij nawias okrągły zamknij nawias okrągły średnik. Linia 4. zamknij nawias klamrowy.

Zaprezentowane rozwiązanie, choć najprostsze, nie jest idealne. Aby uzyskać ten sam efekt, moglibyśmy skorzystać również ze zmiennych globalnych, jednak warto trzymać się dobrych praktyk. W celu uzyskania optymalnego rozwiązania stworzymy te zmienne w funkcji main, a następnie będziemy przekazywać je do odpowiednich metod jako ich argumenty:

Linia 1. bool watekWazPracuje znak równości true średnik. Linia 2. bool watekGrafikaPracuje znak równości false średnik.

Wtedy metoda przesun() przyjmie następującą postać:

Linia 1. void przesun otwórz nawias okrągły bool asterysk watekGrafikiPracuje przecinek bool asterysk watekWazPracuje 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. this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły 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. this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły chrono dwukropek dwukropek milliseconds otwórz nawias okrągły 400 zamknij nawias okrągły zamknij nawias okrągły średnik. Linia 28. zamknij nawias klamrowy. Linia 29. zamknij nawias klamrowy.

Natomiast metoda wyswietlGre() będzie wyglądać następująco:

Linia 1. 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 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. while otwórz nawias okrągły asterysk watekWazPracuje zamknij nawias okrągły. Linia 6. otwórz nawias klamrowy. Linia 7. this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły 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 watekGrafikiPracuje znak równości true średnik. Linia 10. system otwórz nawias okrągły cudzysłów CLS cudzysłów zamknij nawias okrągły średnik. Linia 11. 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 12. otwórz nawias klamrowy. Linia 13. 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 14. otwórz nawias klamrowy. Linia 15. 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 16. cout otwórz nawias ostrokątny otwórz nawias ostrokątny cudzysłów at lewy ukośnik t cudzysłów średnik. Linia 17. else. Linia 18. cout otwórz nawias ostrokątny otwórz nawias ostrokątny 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 otwórz nawias ostrokątny otwórz nawias ostrokątny cudzysłów lewy ukośnik t cudzysłów średnik. Linia 19. zamknij nawias klamrowy. Linia 20. cout otwórz nawias ostrokątny otwórz nawias ostrokątny endl średnik. Linia 21. zamknij nawias klamrowy. Linia 22. asterysk watekGrafikiPracuje znak równości false średnik. Linia 23. this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły chrono dwukropek dwukropek milliseconds otwórz nawias okrągły 200 zamknij nawias okrągły zamknij nawias okrągły średnik. Linia 24. zamknij nawias klamrowy. Linia 25. zamknij nawias klamrowy.

Istotne jest, aby odblokowanie wątków odbywało się przed funkcją sleep_for. W przeciwnym wypadku ryzykujemy, że odblokowania nigdy się nie zsynchronizują lub będą to robić w sposób nieregularny.

W tym momencie animacja powinna działać stosunkowo płynnie. Spróbuj ją uruchomić i zobacz efekty.

Zaimplementujmy sterowanie wężem. Do klasy ObslugiWejscia potrzebujemy dostarczyć informacje o naszym wężu. Najprościej będzie dodać je do konstruktora.

Ważne!

Ponieważ klasa ObslugaWejscia zdefiniowana jest przed deklaracją klasy Waz, musimy umieścić klasę ObslugaWejscia pod klasą Waz.

Biorąc pod uwagę, że jako argument podajemy referencję do istniejącego obiektu, żeby modyfikować oryginał, a w instrukcjach warunkowych if‑else robimy odpowiedni zwrot, metoda zinterpretujWcisnietyPrzycisk() przyjmie następującą postać:

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 9. 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 10. waz minus zamknij nawias ostrokątny kierunekRuchu znak równości waz minus zamknij nawias ostrokątny wschod średnik. Linia 11. 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 12. waz minus zamknij nawias ostrokątny kierunekRuchu znak równości waz minus zamknij nawias ostrokątny poludnie ś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 poludnie zamknij nawias okrągły. Linia 14. waz minus zamknij nawias ostrokątny kierunekRuchu znak równości waz minus zamknij nawias ostrokątny zachod średnik. Linia 15. else. Linia 16. waz minus zamknij nawias ostrokątny kierunekRuchu znak równości waz minus zamknij nawias ostrokątny polnoc średnik. Linia 17. break średnik. Linia 18. case apostrof d apostrof dwukropek. Linia 19. 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 20. waz minus zamknij nawias ostrokątny kierunekRuchu znak równości waz minus zamknij nawias ostrokątny zachod średnik. Linia 21. 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 22. waz minus zamknij nawias ostrokątny kierunekRuchu znak równości waz minus zamknij nawias ostrokątny poludnie ś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 poludnie zamknij nawias okrągły. Linia 24. waz minus zamknij nawias ostrokątny kierunekRuchu znak równości waz minus zamknij nawias ostrokątny wschod średnik. Linia 25. else. Linia 26. waz minus zamknij nawias ostrokątny kierunekRuchu znak równości waz minus zamknij nawias ostrokątny polnoc średnik. Linia 27. break średnik. Linia 28. case apostrof apostrof dwukropek. Linia 30. break średnik. Linia 31. zamknij nawias klamrowy. Linia 32. zamknij nawias klamrowy. Linia 33. zamknij nawias klamrowy.

Nie uwzględniliśmy jeszcze wariantu użycia spacji. Zajmiemy się tym na późniejszym etapie prac, ponieważ zatrzymywanie gry nie jest związane z implementacją skręcania.

W funkcji main() musimy w deklaracji wątku watekWejscia dopisać jako argument &obiektWaz, ponieważ dopisaliśmy go do definicji metody, której używamy.

Linia 1. int main otwórz nawias okrągły zamknij nawias okrągły. Linia 2. otwórz nawias klamrowy. Linia 3. Plansza plansza znak równości Plansza otwórz nawias okrągły 25 przecinek 25 zamknij nawias okrągły średnik. Linia 4. WyswietlanieGrafiki grafika znak równości WyswietlanieGrafiki otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 5. Waz obiektWaz znak równości Waz otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 6. obiektWaz kropka ustawPozycje otwórz nawias okrągły 0 przecinek 25 zamknij nawias okrągły średnik. Linia 7. bool watekWazPracuje znak równości true średnik. Linia 8. bool watekGrafikaPracuje znak równości false średnik. Linia 9. thread watekGrafiki otwórz nawias okrągły ampersant WyswietlanieGrafiki dwukropek dwukropek wyswietlGre przecinek ampersant grafika przecinek ampersant obiektWaz przecinek ampersant plansza przecinek ampersant watekWazPracuje przecinek ampersant watekGrafikaPracuje zamknij nawias okrągły średnik. Linia 10. ObslugaWejscia wejscie średnik. Linia 11. thread watekWejscia otwórz nawias okrągły ampersant ObslugaWejscia dwukropek dwukropek zinterpretujWcisnietyPrzycisk przecinek ampersant wejscie przecinek ampersant obiektWaz zamknij nawias okrągły średnik. Linia 12. thread watekWeza otwórz nawias okrągły ampersant Waz dwukropek dwukropek przesun przecinek ampersant obiektWaz przecinek ampersant watekWazPracuje przecinek ampersant watekGrafikaPracuje zamknij nawias okrągły średnik. Linia 13. watekGrafiki kropka join otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 14. watekWejscia kropka join otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 15. watekWeza kropka join otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 16. zamknij nawias klamrowy.

W obecnej formie głowa węża jest trudna do wyśledzenia wśród natłoku liczb. Należy poprawić czytelność kodu.

Zmienimy metodę wyswietlGre(), tak aby gra zajmowała mniej miejsca w konsoli.

Linia 1. 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 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. while otwórz nawias okrągły asterysk watekWazPracuje zamknij nawias okrągły. Linia 6. otwórz nawias klamrowy. Linia 7. this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły 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 watekGrafikiPracuje znak równości true średnik. Linia 10. system otwórz nawias okrągły cudzysłów CLS cudzysłów zamknij nawias okrągły średnik. Linia 11. 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 12. otwórz nawias klamrowy. Linia 13. 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 14. otwórz nawias klamrowy. Linia 15. 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 16. cout otwórz nawias ostrokątny otwórz nawias ostrokątny cudzysłów at cudzysłów średnik. Linia 17. else. Linia 18. cout otwórz nawias ostrokątny otwórz nawias ostrokątny cudzysłów plus cudzysłów średnik. Linia 19. zamknij nawias klamrowy. Linia 20. cout otwórz nawias ostrokątny otwórz nawias ostrokątny endl średnik. Linia 21. zamknij nawias klamrowy. Linia 22. asterysk watekGrafikiPracuje znak równości false średnik. Linia 23. this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły chrono dwukropek dwukropek milliseconds otwórz nawias okrągły 200 zamknij nawias okrągły zamknij nawias okrągły średnik. Linia 24. zamknij nawias klamrowy. Linia 25. zamknij nawias klamrowy.

Następnie zaimplementujemy metodę losujPlansze() z klasy Plansza.

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. zamknij nawias klamrowy. Linia 13. zamknij nawias klamrowy. Linia 14. zamknij nawias klamrowy. Linia 15. zamknij nawias klamrowy.

W przedstawionym kodzie losujemy, czy w danym miejscu pojawi się owoc. Szansa na to jest równa 2%. Warto zwrócić uwagę na linijkę 3 – odbywa się tu inicjalizowanie generatora liczb pseudolosowych. Dzięki temu mamy pewność, że po każdym uruchomieniu programu owoce będą rozmieszczone naprawdę losowo, w różnych przypadkowych miejscach. Generator inicjujemy w tym przypadku aktualnym czasem systemowym (który zawsze będzie inną liczbą). Warto pamiętać, że konieczne jest również dodanie odpowiednich nagłówków do obsługi czasu. Dodamy więc na początku kodu następującą linię:

Linia 1. kratka include otwórz nawias ostrokątny time kropka h zamknij nawias ostrokątny.

Metodę do wyświetlania gry zmienimy w następujący sposób:

Linia 1. 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 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. while otwórz nawias okrągły asterysk watekWazPracuje zamknij nawias okrągły. Linia 6. otwórz nawias klamrowy. Linia 7. this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły 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 watekGrafikiPracuje znak równości true średnik. Linia 10. system otwórz nawias okrągły cudzysłów CLS cudzysłów zamknij nawias okrągły średnik. Linia 11. 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 12. otwórz nawias klamrowy. Linia 13. 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 14. otwórz nawias klamrowy. Linia 15. 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 16. cout otwórz nawias ostrokątny otwórz nawias ostrokątny cudzysłów at cudzysłów średnik. Linia 17. 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 znak równości znak równości 0 zamknij nawias okrągły. Linia 18. cout otwórz nawias ostrokątny otwórz nawias ostrokątny cudzysłów plus cudzysłów średnik. Linia 19. else. Linia 20. cout otwórz nawias ostrokątny otwórz nawias ostrokątny cudzysłów $ cudzysłów średnik. Linia 21. zamknij nawias klamrowy. Linia 22. cout otwórz nawias ostrokątny otwórz nawias ostrokątny endl średnik. Linia 23. zamknij nawias klamrowy. Linia 24. asterysk watekGrafikiPracuje znak równości false średnik. Linia 25. this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły chrono dwukropek dwukropek milliseconds otwórz nawias okrągły 200 zamknij nawias okrągły zamknij nawias okrągły średnik. Linia 26. zamknij nawias klamrowy. Linia 27. zamknij nawias klamrowy.

Spróbujmy przygotować plik konfiguracyjny. Zgodnie z wymaganiami opisanymi w pierwszym e‑materiale z tej serii, musimy stworzyć mapę o zmiennym rozmiarze – zrobimy to właśnie z poziomu pliku konfiguracyjnego.

Plik konfiguracyjny przyjmie następującą postać:

Linia 1. szerokosc 20. Linia 2. wysokosc 25.

Dane zapisywane są zgodnie z konwencją:

Linia 1. nazwaZmiennej wartośćZmiennej.

W celu wczytania danych z pliku skorzystamy z nagłówka <fstream> oraz z klasy ifstream. Należy pamiętać, aby ten nagłówek dołączyć do kodu źródłowego, dodając na początku następującą linię:

Linia 1. kratka include otwórz nawias ostrokątny fstream zamknij nawias ostrokątny.

Pamiętajmy o przestrzeni nazw:

Linia 1. using namespace std średnik.

Wczytamy plik konfiguracyjny zgodnie z zasadą, którą przyjęliśmy. Nie będziemy uwzględniać obsługi błędnie utworzonych plików konfiguracyjnych.

Dodatkowo, ponieważ plik konfiguracyjny jest otwierany raz na całą grę, możemy zrobić to w taki sposób, że zmienne będziemy przyjmować zgodnie z ich nazwami, zamiast według kolejności ich występowania, tzn.:

Linia 1. ifstream plikKonf otwórz nawias okrągły cudzysłów config kropka ini cudzysłów zamknij nawias okrągły średnik. Linia 2. int wartoscZmiennej średnik. Linia 3. string zmienna średnik. Linia 4. int rozmiarX znak równości 0 średnik. Linia 5. int rozmiarY znak równości 0 średnik. Linia 6. if otwórz nawias okrągły plikKonf kropka is podkreślnik open otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły. Linia 7. otwórz nawias klamrowy. Linia 8. plikKonf zamknij nawias ostrokątny zamknij nawias ostrokątny zmienna zamknij nawias ostrokątny zamknij nawias ostrokątny wartoscZmiennej średnik. Linia 9. if otwórz nawias okrągły zmienna znak równości znak równości cudzysłów wysokosc cudzysłów zamknij nawias okrągły rozmiarY znak równości wartoscZmiennej średnik. Linia 10. else if otwórz nawias okrągły zmienna znak równości znak równości cudzysłów szerokosc cudzysłów zamknij nawias okrągły rozmiarX znak równości wartoscZmiennej średnik. Linia 11. zamknij nawias klamrowy.

Inaczej mówiąc: liczba zmiennych w naszym kodzie jest skończona i mają one typ liczbowy. Dodatkowo w pliku konfiguracyjnym nie przewidujemy dodawania łańcuchów znaków do wczytywania. Wszystko to sprawia, że możemy wczytać wszelkie dostępne informacje w odpowiedniej pętli. Zakładamy przy tym, że każda nazwa zmiennej jest łańcuchem znaków, a każdy parametr bez problemu może zostać zrzutowany na typ int. Na tym etapie wczytujemy jedynie dwie informacje, czyli rozmiar i szerokość planszy, ale w przyszłości będziemy z pliku konfiguracyjnego wczytywać także m.in. wygląd poszczególnych elementów rozgrywki.

Powyższy kod wczytuje tylko jedną linijkę – zmodyfikujmy go tak, żeby wczytywał wszystkie dane. Użyjmy do tego metody eof(), dostępnej w klasie ifstream.

Linia 1. ifstream plikKonf otwórz nawias okrągły cudzysłów config kropka ini cudzysłów zamknij nawias okrągły średnik. Linia 2. int wartoscZmiennej średnik. Linia 3. string zmienna średnik. Linia 4. int rozmiarX znak równości 0 średnik. Linia 5. int rozmiarY znak równości 0 średnik. Linia 6. while otwórz nawias okrągły wykrzyknik plikKonf kropka eof otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły. Linia 7. otwórz nawias klamrowy. Linia 8. if otwórz nawias okrągły plikKonf kropka is podkreślnik open otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły. Linia 9. otwórz nawias klamrowy. Linia 10. plikKonf zamknij nawias ostrokątny zamknij nawias ostrokątny zmienna zamknij nawias ostrokątny zamknij nawias ostrokątny wartoscZmiennej średnik. Linia 11. if otwórz nawias okrągły zmienna znak równości znak równości cudzysłów wysokosc cudzysłów zamknij nawias okrągły rozmiarY znak równości wartoscZmiennej średnik. Linia 12. else if otwórz nawias okrągły zmienna znak równości znak równości cudzysłów szerokosc cudzysłów zamknij nawias okrągły rozmiarX znak równości wartoscZmiennej średnik. Linia 13. zamknij nawias klamrowy. Linia 14. zamknij nawias klamrowy. Linia 15. plikKonf kropka close otwórz nawias okrągły zamknij nawias okrągły średnik.

W programie umieścimy ten fragment kodu w funkcji main(), otrzymując:

Linia 1. int main otwórz nawias okrągły zamknij nawias okrągły. Linia 2. otwórz nawias klamrowy. Linia 3. ifstream plikKonf otwórz nawias okrągły cudzysłów config kropka ini cudzysłów zamknij nawias okrągły średnik. Linia 4. int wartoscZmiennej średnik. Linia 5. string zmienna średnik. Linia 6. int rozmiarX znak równości 0 średnik. Linia 7. int rozmiarY znak równości 0 średnik. Linia 8. while otwórz nawias okrągły wykrzyknik plikKonf kropka eof otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły. Linia 9. otwórz nawias klamrowy. Linia 10. if otwórz nawias okrągły plikKonf kropka is podkreślnik open otwórz nawias okrągły zamknij nawias okrągły zamknij nawias okrągły. Linia 11. otwórz nawias klamrowy. Linia 12. plikKonf zamknij nawias ostrokątny zamknij nawias ostrokątny zmienna zamknij nawias ostrokątny zamknij nawias ostrokątny wartoscZmiennej średnik. Linia 13. if otwórz nawias okrągły zmienna znak równości znak równości cudzysłów wysokosc cudzysłów zamknij nawias okrągły rozmiarY znak równości wartoscZmiennej średnik. Linia 14. else if otwórz nawias okrągły zmienna znak równości znak równości cudzysłów szerokosc cudzysłów zamknij nawias okrągły rozmiarX znak równości wartoscZmiennej średnik. Linia 15. zamknij nawias klamrowy. Linia 16. zamknij nawias klamrowy. Linia 17. plikKonf kropka close otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 18. Plansza plansza znak równości Plansza otwórz nawias okrągły rozmiarX przecinek rozmiarY zamknij nawias okrągły średnik. Linia 19. plansza kropka losujPlansze otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 20. WyswietlanieGrafiki grafika znak równości WyswietlanieGrafiki otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 21. Waz obiektWaz znak równości Waz otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 22. obiektWaz kropka ustawPozycje otwórz nawias okrągły 5 przecinek 15 zamknij nawias okrągły średnik. Linia 23. bool watekWazPracuje znak równości true średnik. Linia 24. bool watekGrafikaPracuje znak równości false średnik. Linia 25. thread watekGrafiki otwórz nawias okrągły ampersant WyswietlanieGrafiki dwukropek dwukropek wyswietlGre przecinek ampersant grafika przecinek ampersant obiektWaz przecinek ampersant plansza przecinek ampersant watekWazPracuje przecinek ampersant watekGrafikaPracuje zamknij nawias okrągły średnik. Linia 26. ObslugaWejscia wejscie średnik. Linia 27. thread watekWejscia otwórz nawias okrągły ampersant ObslugaWejscia dwukropek dwukropek zinterpretujWcisnietyPrzycisk przecinek ampersant wejscie przecinek ampersant obiektWaz zamknij nawias okrągły średnik. Linia 28. thread watekWeza otwórz nawias okrągły ampersant Waz dwukropek dwukropek przesun przecinek ampersant obiektWaz przecinek ampersant watekWazPracuje przecinek ampersant watekGrafikaPracuje zamknij nawias okrągły średnik. Linia 29. watekGrafiki kropka join otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 30. watekWejscia kropka join otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 31. watekWeza kropka join otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 32. zamknij nawias klamrowy.

Przedstawione rozwiązanie jest co prawda bardziej złożone obliczeniowo dla procesora, jednak wczytanie pliku konfiguracyjnego odbywa się tylko raz. Zaletą tego rozwiązania jest fakt, że stworzony przez nas plik konfiguracyjny jest odporny na zmiany w kodzie.

Załóżmy, że w toku pisania programu stwierdzimy, iż nie chcemy już wczytywać jakiejś zmiennej lub że potrzebna nam jest nowa. W sytuacji, w której zaistnieje potrzeba wczytania nowej zmiennej, możemy ją dopisać w dowolnym miejscu pliku bez konieczności uwzględniania kolejności występowania parametrów. W sytuacji, gdy już nie będziemy chcieli wczytywać jakiejś zmiennej, wystarczy, że usuniemy jeden if w kodzie, a sama obecność nieużywanego parametru w pliku konfiguracyjnym nie będzie miała wpływu na zachowanie programu.

Plik, zawierający cały stworzony dotychczas kod w języku C++, możesz pobrać tutaj:

Raa8tTx8o0XK8

Plik zawierający kod źródłowy w C++

Kod źródłowy C++
Plik ZIP o rozmiarze 1.73 KB w języku polskim

Tworzenie gry w języku Java

Zaczniemy od zaimplementowania metod z klasy Wąż, a konkretnie sprawdzWynik(), która zwraca aktualną liczbę punktów, czyli atrybutu liczbaPunktów.

Linia 1. int sprawdzWynik otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 2. return liczbaPunktow średnik. Linia 3. zamknij nawias klamrowy.

Następnie zaimplementujemy metodę ustawWynik(), zmieniającą wartość atrybutu liczbaPunktów na podaną w argumencie.

Linia 1. void ustawWynik otwórz nawias okrągły int liczbaPunktowDoUstawienia zamknij nawias okrągły. Linia 2. otwórz nawias klamrowy. Linia 3. liczbaPunktow znak równości liczbaPunktowDoUstawienia średnik. Linia 4. zamknij nawias klamrowy.

Tych dwóch wspomnianych metod będziemy używać do sprawdzania i ustawiania wyniku punktowego w trakcie rozgrywki.

Kolejną metodą z tej klasy jest zmienKierunek() – jak sama nazwa wskazuje, służy ona do zmieniania kierunku ruchu głowy węża. Jeśli chcemy, by wąż skręcił, modyfikujemy atrybut kierunekRuchu, odpowiadający za wyznaczanie kierunku przemieszczania się węża.

Dla uproszczenia naszego programu skorzystamy z klasy enumeracyjnej, dzięki czemu będziemy używać nazw zamiast niewiele mówiących liczb.

Klasę enumeracyjną Kierunek umieścimy w ramach klasy Waz.

Linia 1. enum Kierunek otwórz nawias klamrowy polnoc przecinek wschod przecinek poludnie przecinek zachod zamknij nawias klamrowy średnik.

Będziemy z niej korzystać prawdopodobnie w ramach innych klas, dlatego też zostawimy ją jako publiczną. Wróćmy do implementacji metody zmienKierunek(), która przyjmie teraz następującą postać:

Linia 1. void zmienKierunek otwórz nawias okrągły kierunek nowyKierunek zamknij nawias okrągły. Linia 2. otwórz nawias klamrowy. Linia 3. kierunekRuchu znak równości nowyKierunek średnik. Linia 4. zamknij nawias klamrowy.

W języku Java nie można – bez dodatkowych kroków – zmienić typu enumerowanego na typ liczbowy. Dlatego też atrybut kierunekRuchu zmienimy z int na klasę enumeracyjną Kierunek. Robiąc to, odchodzimy od przyjętego wcześniej diagramu. Warto przypomnieć, że stworzyliśmy go w taki sposób, aby jak najwięcej języków było z nim kompatybilnych. Nasze odstępstwo nie jest więc błędem. Klasę enumeracyjną da się w łatwy sposób zrzutować na typ int, jednak w naszym przypadku zmniejszyłoby to jedynie czytelność kodu.

Dodatkowo implementujemy metodę, która pozwoli nam sprawdzić aktualny kierunek ruchu węża:

Linia 1. Kierunek sprawdzKierunek otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 2. return kierunekRuchu średnik. Linia 3. zamknij nawias klamrowy.

W tym momencie klasa Waz przyjęłaby następującą postać:

Linia 1. public class Waz 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. Waz otwórz nawias okrągły int pozycjaWezaX przecinek int pozycjaWezaY przecinek Kierunek kierunekRuchu zamknij nawias okrągły otwórz nawias klamrowy. Linia 8. this kropka pozycjaWezaX znak równości pozycjaWezaX średnik. Linia 9. this kropka pozycjaWezaY znak równości pozycjaWezaY średnik. Linia 10. this kropka kierunekRuchu znak równości kierunekRuchu średnik. Linia 11. zamknij nawias klamrowy. Linia 13. int sprawdzWynik otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 14. return liczbaPunktow średnik. Linia 15. zamknij nawias klamrowy. Linia 17. void ustawWynik otwórz nawias okrągły int liczbaPunktowDoUstawienia zamknij nawias okrągły otwórz nawias klamrowy. Linia 18. liczbaPunktow znak równości liczbaPunktowDoUstawienia średnik. Linia 19. zamknij nawias klamrowy. Linia 21. void zmienKierunek otwórz nawias okrągły Kierunek nowyKierunek zamknij nawias okrągły otwórz nawias klamrowy. Linia 22. kierunekRuchu znak równości nowyKierunek średnik. Linia 23. zamknij nawias klamrowy. Linia 25. Kierunek sprawdzKierunek otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 26. return kierunekRuchu średnik. Linia 27. zamknij nawias klamrowy. Linia 28. zamknij nawias klamrowy.

Musimy jednak poprawić w klasie Main miejsce, w którym powołaliśmy do życia jeden obiekt klasy Waz, otrzymując:

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 4. ObslugaWejscia wejscie znak równości new ObslugaWejscia otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 5. WyswietlanieGrafiki grafika znak równości new WyswietlanieGrafiki otwórz nawias okrągły new Waz otwórz nawias okrągły 0 przecinek 0 przecinek Waz kropka Kierunek kropka polnoc zamknij nawias okrągły przecinek new Plansza otwórz nawias okrągły 5 zamknij nawias okrągły przecinek wejscie zamknij nawias okrągły średnik. Linia 6. Thread watekGrafiki znak równości new Thread otwórz nawias okrągły grafika zamknij nawias okrągły średnik. Linia 7. watekGrafiki kropka run otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 8. zamknij nawias klamrowy. Linia 9. zamknij nawias klamrowy.

Wróćmy do implementacji metod w klasie Waz. Kolejną na liście jest metoda przesun(), która będzie modyfikowała atrybuty pozycjaWezaXpozycjaWezaY, w zależności od atrybutu kierunekRuchu.

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. zamknij nawias klamrowy.

Warto wyjaśnić, dlaczego kierunek północ dodaje „-1” do współrzędnej Y.

Rk42ts2di12ZV
Źródło: Contentplus.pl sp. z o.o., licencja: CC BY-SA 3.0.

Na rysunku znajdują się dwie osie układu współrzędnych, które pokazują, w jakim kierunku rosną dane współrzędne. Nasza plansza w lewym górnym rogu ma pole o współrzędnych (0,0), a w prawym dolnym rogu (n‑1, n‑1). Moglibyśmy oczywiście w implementacji programu odwrócić współrzędne w taki sposób, żeby liczone były one od lewego dolnego rogu.

Dla zainteresowanych

Spróbuj zmodyfikować program w ten sposób, żeby punkt (0,0) znajdował się w lewym dolnym rogu wyświetlanej planszy. Możesz zrobić to np. poprzez zmianę kolejności wyświetlania wierszy. Należy jednak pamiętać, że jeśli wprowadzisz w kodzie zmiany, możesz napotkać nieścisłości podczas realizacji kroków opisanych w dalszej części tego e‑materiału.

Kolejnymi dwiema metodami z klasy Waz, które zaimplementujemy, będą metody: sprawdzPozycjeX() oraz sprawdzPozycjeY(). Służą one do odczytywania wartości atrybutów pozycjaWezaX oraz pozycjaWezaY.

Linia 1. int sprawdzPozycjeY otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 2. return pozycjaWezaY średnik. Linia 3. zamknij nawias klamrowy. Linia 4. int sprawdzPozycjeX otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 5. return pozycjaWezaX średnik. Linia 6. zamknij nawias klamrowy.

Metoda ustawPozycje() działa bardzo podobnie do metody ustawWynik(), z tą tylko różnicą, że tym razem modyfikujemy wspomniane przed chwilą atrybuty, które będziemy też podawali jako argumenty naszej metody.

Linia 1. void ustawPozycje otwórz nawias okrągły int x przecinek int y zamknij nawias okrągły otwórz nawias klamrowy. Linia 2. pozycjaWezaX znak równości x średnik. Linia 3. pozycjaWezaY znak równości y średnik. Linia 4. zamknij nawias klamrowy.

W ramach tej klasy pozostała nam do zaimplementowania jedna metoda – czyli zjedzOwoc(). Wykorzysta ona metodę sprawdzPunkty() dostępną z poziomu obiektów klasy Owoc.

Linia 1. int sprawdzPunkty otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 2. return liczbaPunktow średnik. Linia 3. zamknij nawias klamrowy.

Za jej pomocą będziemy sprawdzać, ile punktów wart jest dany owoc, a następnie liczba ta zostanie przyznana wężowi.

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. zamknij nawias klamrowy.

Usuwanie owocu po zjedzeniu go przez węża zaimplementujemy w kolejnym etapie prac.

Teraz nadszedł moment, w którym możemy zaimplementować poruszanie się węża po planszy. Odpowiedzialna za to metoda powinna odświeżać się w takim tempie, aby wąż z perspektywy gracza poruszał się płynnie.

Dodatkowo ruch węża trzeba zsynchronizować z rysowaniem się mapy, tak by nie poruszał się on w momencie, gdy mapa jest rysowana. Powodowałoby to bowiem takie niepożądane efekty, jak: przeskakiwanie węża o dwa pola, zniknięcie głowy, przerwy w ogonie. Są to potencjalne błędy, których możemy uniknąć, synchronizując w odpowiedni sposób wyświetlanie planszy i poruszanie się węża.

Zrobimy to w osobnych wątkach. Jeden, już zaimplementowany i gotowy do pracy, odpowiada za rysowanie. Drugi, który właśnie implementujemy, wywołuje poruszanie węża. Natomiast trzeci – główny wątek gry uruchamiany przy włączaniu programu – będzie odpowiadał za zarządzanie całą rozgrywką.

Do zaimplementowania dwóch osobnych wątków, skorzystamy z interfejsu Runnable.

Zaczniemy od klasy Waz, w której po zadeklarowaniu nazwy dopiszemy słowo kluczowe implements, a potem nazwę interfejsu Runnable. Otrzymamy:

Linia 1. public class Waz implements Runnable otwórz nawias klamrowy. Linia 2. kropka kropka kropka.

Następnie zaimplementujemy wszystkie metody, jakich ten interfejs wymaga, czyli jedynie metody run().

Linia 1. at Override. Linia 2. public void run otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 3. przesun otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 4. zamknij nawias klamrowy.

W tym momencie uruchomienie wątku spowodowałoby jednorazowe przesunięcie węża po planszy, dlatego zapętlimy działanie tej metody w nieskończoność. Dla większej czytelności zapętlenie zrobimy w ramach metody run().

Linia 1. at Override. Linia 2. public void run otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 3. while otwórz nawias okrągły true zamknij nawias okrągły otwórz nawias klamrowy. Linia 4. przesun otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 5. zamknij nawias klamrowy. Linia 6. zamknij nawias klamrowy.

Następnie w klasie Main zaimplementujemy uruchamianie wątku obiektu klasy Waz. Robimy to analogicznie jak w przypadku uruchamiania wątku generującego grafikę.

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 4. ObslugaWejscia wejscie znak równości new ObslugaWejscia otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 5. Waz waz znak równości new Waz otwórz nawias okrągły 0 przecinek 0 przecinek Waz kropka Kierunek kropka polnoc zamknij nawias okrągły średnik. Linia 6. WyswietlanieGrafiki grafika znak równości new WyswietlanieGrafiki otwórz nawias okrągły waz przecinek new Plansza otwórz nawias okrągły 5 zamknij nawias okrągły przecinek wejscie zamknij nawias okrągły średnik. Linia 7. Thread watekGrafiki znak równości new Thread otwórz nawias okrągły grafika zamknij nawias okrągły średnik. Linia 8. watekGrafiki kropka start otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 9. Thread watekWeza znak równości new Thread otwórz nawias okrągły waz zamknij nawias okrągły średnik. Linia 10. watekWeza kropka start otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 11. zamknij nawias klamrowy. Linia 12. zamknij nawias klamrowy.

Uruchomienie gry w tym momencie nie da nam jeszcze zadowalającego efektu. Wątek węża pracuje z pełną szybkością procesora, czyli szybciej niż człowiek byłby w stanie zareagować. W celu spowolnienia przesuwania węża użyjemy metody sleep(), która zatrzymuje wątek na co najmniej podaną liczbę milisekund.

Umieścimy ją w metodzie run() klasy Waz, przez co przyjmie ona 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. zamknij nawias klamrowy. Linia 10. zamknij nawias klamrowy.

Dopisana powyżej obsługa wyjątku InterruptedException jest konieczna, ponieważ wyjątek ten może być rzucony w momencie, kiedy wątek zostanie przerwany.

Wątek węża i wątek grafiki działają teraz asynchronicznie. Nie zaimplementowaliśmy jednak jeszcze wyświetlania samego węża na planszy, zatem pora to zrobić.

Warunkiem, który będzie decydował o wyświetlaniu ikonki węża na danym polu, jest zgodność jego koordynatów dostępnych pod atrybutami pozycjaWezaYpozycjaWezaX z koordynatami danego pola.

Dodajemy taki warunek w metodzie wyswietlGre(), dostępnej w ramach klasy WyswietlanieGrafiki. Przypomnijmy, że znakiem @ oznaczamy głowę węża.

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 wartoscPola znak równości znak równości 0 zamknij nawias okrągły otwórz nawias klamrowy. Linia 8. grafikaTekstowa kropka append otwórz nawias okrągły cudzysłów plus cudzysłów zamknij nawias okrągły średnik. Linia 9. zamknij nawias klamrowy else otwórz nawias klamrowy. Linia 10. grafikaTekstowa kropka append otwórz nawias okrągły cudzysłów x cudzysłów zamknij nawias okrągły średnik. Linia 11. zamknij nawias klamrowy. Linia 12. zamknij nawias klamrowy. Linia 13. grafikaTekstowa kropka append otwórz nawias okrągły cudzysłów lewy ukośnik n cudzysłów zamknij nawias okrągły średnik. Linia 14. zamknij nawias klamrowy. Linia 15. zamknij nawias klamrowy.

Przed uruchomieniem gry należy jeszcze zapętlić wyświetlanie grafiki, analogicznie jak w wypadku poruszania się węża (również z obsługą wyjątku InterruptedException).

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. wyswietlGre otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 9. zamknij nawias klamrowy. Linia 10. zamknij nawias klamrowy.

Uruchamiając rozgrywkę w tym momencie, zobaczylibyśmy, że wąż się przemieszcza, jednak nie mielibyśmy nad nim żadnej kontroli. Wyświetlałyby się za to w konsoli komunikaty informujące o wciskanych przyciskach. Należy w tym wypadku poprawić metodę keyPressed() w klasie ObsługaWejscia, tak byśmy rzeczywiście sterowali wężem.

Dodamy do tej klasy referencję umożliwiającą zmienianie atrybutów konkretnej instancji klasy Waz. Zrobimy to za pomocą nowo utworzonego konstruktora tej klasy. Następnie zmodyfikujemy wcześniej wspominaną metodę tak, aby wąż skręcał w poprawny sposób.

Ostatecznie klasa ObslugaWejscia przyjęłaby następującą postać:

Linia 1. import java kropka awt kropka event kropka KeyEvent średnik. Linia 2. import java kropka awt kropka event kropka KeyListener średnik. Linia 4. public class ObslugaWejscia implements KeyListener otwórz nawias klamrowy. Linia 5. Waz waz średnik. Linia 7. ObslugaWejscia otwórz nawias okrągły Waz waz zamknij nawias okrągły otwórz nawias klamrowy. Linia 8. this kropka waz znak równości waz średnik. Linia 9. zamknij nawias klamrowy. Linia 11. at Override. Linia 12. public void keyTyped otwórz nawias okrągły KeyEvent e zamknij nawias okrągły otwórz nawias klamrowy. Linia 14. zamknij nawias klamrowy. Linia 16. at Override. Linia 17. public void keyPressed otwórz nawias okrągły KeyEvent e zamknij nawias okrągły otwórz nawias klamrowy. Linia 18. char znak znak równości e kropka getKeyChar otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 19. switch otwórz nawias okrągły znak zamknij nawias okrągły otwórz nawias klamrowy. Linia 20. case apostrof a apostrof dwukropek. Linia 21. if otwórz nawias okrągły waz kropka sprawdzKierunek otwórz nawias okrągły zamknij nawias okrągły znak równości znak równości Waz kropka Kierunek kropka polnoc zamknij nawias okrągły otwórz nawias klamrowy. Linia 22. waz kropka zmienKierunek otwórz nawias okrągły Waz kropka Kierunek kropka wschod zamknij nawias okrągły średnik. Linia 23. zamknij nawias klamrowy else if otwórz nawias okrągły waz kropka sprawdzKierunek otwórz nawias okrągły zamknij nawias okrągły znak równości znak równości Waz kropka Kierunek kropka wschod zamknij nawias okrągły otwórz nawias klamrowy. Linia 24. waz kropka zmienKierunek otwórz nawias okrągły Waz kropka Kierunek kropka poludnie zamknij nawias okrągły średnik. Linia 25. zamknij nawias klamrowy else if otwórz nawias okrągły waz kropka sprawdzKierunek otwórz nawias okrągły zamknij nawias okrągły znak równości znak równości Waz kropka Kierunek kropka poludnie zamknij nawias okrągły otwórz nawias klamrowy. Linia 26. waz kropka zmienKierunek otwórz nawias okrągły Waz kropka Kierunek kropka zachod zamknij nawias okrągły średnik. Linia 27. zamknij nawias klamrowy else otwórz nawias klamrowy. Linia 28. waz kropka zmienKierunek otwórz nawias okrągły Waz kropka Kierunek kropka polnoc zamknij nawias okrągły średnik. Linia 29. zamknij nawias klamrowy. Linia 30. break średnik. Linia 32. case apostrof d apostrof dwukropek. Linia 33. if otwórz nawias okrągły waz kropka sprawdzKierunek otwórz nawias okrągły zamknij nawias okrągły znak równości znak równości Waz kropka Kierunek kropka polnoc zamknij nawias okrągły otwórz nawias klamrowy. Linia 34. waz kropka zmienKierunek otwórz nawias okrągły Waz kropka Kierunek kropka zachod zamknij nawias okrągły średnik. Linia 35. zamknij nawias klamrowy else if otwórz nawias okrągły waz kropka sprawdzKierunek otwórz nawias okrągły zamknij nawias okrągły znak równości znak równości Waz kropka Kierunek kropka zachod zamknij nawias okrągły otwórz nawias klamrowy. Linia 36. waz kropka zmienKierunek otwórz nawias okrągły Waz kropka Kierunek kropka poludnie zamknij nawias okrągły średnik. Linia 37. zamknij nawias klamrowy else if otwórz nawias okrągły waz kropka sprawdzKierunek otwórz nawias okrągły zamknij nawias okrągły znak równości znak równości Waz kropka Kierunek kropka poludnie zamknij nawias okrągły otwórz nawias klamrowy. Linia 38. waz kropka zmienKierunek otwórz nawias okrągły Waz kropka Kierunek kropka wschod zamknij nawias okrągły średnik. Linia 39. zamknij nawias klamrowy else otwórz nawias klamrowy. Linia 40. waz kropka zmienKierunek otwórz nawias okrągły Waz kropka Kierunek kropka polnoc zamknij nawias okrągły średnik. Linia 41. zamknij nawias klamrowy. Linia 42. break średnik. Linia 43. zamknij nawias klamrowy. Linia 44. if otwórz nawias okrągły znak znak równości znak równości apostrof apostrof zamknij nawias okrągły System kropka out kropka println otwórz nawias okrągły cudzysłów Kliknieto spacje cudzysłów zamknij nawias okrągły średnik. Linia 45. zamknij nawias klamrowy. Linia 47. at Override. Linia 48. public void keyReleased otwórz nawias okrągły KeyEvent e zamknij nawias okrągły otwórz nawias klamrowy. Linia 50. zamknij nawias klamrowy. Linia 51. zamknij nawias klamrowy.

Zaimplementowaliśmy już poruszającą się po mapie głowę węża. Na planszy powinny pojawiać się również owoce w losowych miejscach. Nim przejdziemy do losowania, wróćmy do wyświetlania grafiki, gdzie zaimplementujemy pojawianie się owocu na danym polu. Z racji tego, że każde pole posiada własny atrybut owoc, wystarczy sprawdzić, czy pole owoc zawiera, czy nie.

Zmodyfikowana metoda wyswietlGre() przyjmie następującą postać:

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.

Początkowe losowanie planszy zaimplementujemy w metodzie 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. zamknij nawias klamrowy. Linia 8. zamknij nawias klamrowy. Linia 9. zamknij nawias klamrowy. Linia 10. zamknij nawias klamrowy.

Aby możliwe było losowanie liczb, konieczne jest wykorzystanie klasy Random, co wymaga jej zaimportowania (linię dopisujemy na początku pliku klasy Plansza):

Linia 1. import java kropka util kropka Random średnik.

W kodzie najpierw losujemy liczbę z przedziału od 1 do 100 włącznie, a następnie sprawdzamy, czy liczba ta jest równa 100. Jeśli tak, to w tym miejscu generujemy nowy obiekt klasy Owoc.

Metody tej użyjemy na końcu konstruktora klasy Plansza.

Linia 1. Plansza otwórz nawias okrągły int rozmiar zamknij nawias okrągły otwórz nawias klamrowy. Linia 2. rozmiarXPlanszy znak równości rozmiar średnik. Linia 3. rozmiarYPlanszy znak równości rozmiar średnik. Linia 4. pole znak równości new Pole otwórz nawias kwadratowy rozmiar zamknij nawias kwadratowy otwórz nawias kwadratowy rozmiar zamknij nawias kwadratowy średnik. Linia 5. for otwórz nawias okrągły int i znak równości 0 średnik i otwórz nawias ostrokątny rozmiar średnik i plus plus zamknij nawias okrągły otwórz nawias klamrowy. Linia 6. for otwórz nawias okrągły int j znak równości 0 średnik j otwórz nawias ostrokątny rozmiar średnik j plus plus zamknij nawias okrągły otwórz nawias klamrowy. Linia 7. pole otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias kwadratowy j zamknij nawias kwadratowy znak równości new Pole otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 8. zamknij nawias klamrowy. Linia 9. zamknij nawias klamrowy. Linia 10. losujPLansze otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 11. zamknij nawias klamrowy.

Zjadanie owoców zaimplementujemy na późniejszym etapie prac.

Na koniec zaimplementujemy wczytywanie informacji o programie z pliku konfiguracyjnego. Dla uproszczenia załóżmy, że poprawny plik konfiguracyjny przyjmuje następującą postać:

Linia 1. nazwaParametru wartoscParametru.

Zaimplementowany przez nas plik konfiguracyjny będzie zawierał dwie informacje: wielkość planszy oraz startową pozycję węża. Jego przykładowa postać to:

Linia 1. rozmiar 25. Linia 2. pozycjaWezaX 10. Linia 3. pozycjaWezaY 5.

Tworząc plik konfiguracyjny, zadbamy o to, aby można było w nim podać parametry w dowolnej kolejności. Dzięki temu uzyskamy gwarancję, że w przyszłości będziemy mogli bez problemu dodawać kolejne parametry.

Ważne!

Dla uproszczenia zakładamy, że dane wejściowe są prawidłowe.
Przyjmujemy więc, że pozycja startowa węża będzie ustawiona w ramach planszy.

Dla zainteresowanych

Zmodyfikuj program w ten sposób, żeby pozycja startowa węża zawsze znajdowała się w granicach planszy. Trzeba przy tym pamiętać, że jeśli plansza ma rozmiar 20 na 20, to indeksy 19 i 19 są ostatnimi, pod którymi znajduje się jakieś pole.

Do wczytania danych z pliku wykorzystamy klasę Scanner, a do wczytania pliku – klasę File.

Wiemy, że w każdej linijce występuje najpierw nazwa parametru, a potem jego wartość liczbowa, dlatego możemy przyjąć następujący sposób wczytywania, który umieszczamy w klasie Main (zwróćmy uwagę na konieczność zaimportowania wymienionych klas obsługujących pliki):

Linia 1. import java kropka io kropka File średnik. Linia 2. import java kropka io kropka FileNotFoundException średnik. Linia 3. import java kropka util kropka Scanner średnik. Linia 5. public class Main otwórz nawias klamrowy. Linia 7. 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 9. 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 10. Scanner sc znak równości null średnik. Linia 11. try otwórz nawias klamrowy. Linia 12. sc znak równości new Scanner otwórz nawias okrągły plik zamknij nawias okrągły średnik. Linia 13. zamknij nawias klamrowy catch otwórz nawias okrągły FileNotFoundException e zamknij nawias okrągły otwórz nawias klamrowy. Linia 14. e kropka printStackTrace otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 15. zamknij nawias klamrowy. Linia 16. int rozmiar znak równości 0 średnik. Linia 17. int pozycjaWezaX znak równości 0 średnik. Linia 18. int pozycjaWezaY znak równości 0 średnik. Linia 19. 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 20. String nazwaParametru znak równości sc kropka next otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 21. switch otwórz nawias okrągły nazwaParametru zamknij nawias okrągły otwórz nawias klamrowy. Linia 22. case cudzysłów rozmiar cudzysłów dwukropek. Linia 23. 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 24. break średnik. Linia 25. case cudzysłów pozycjaWezaX cudzysłów dwukropek. Linia 26. 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 27. break średnik. Linia 28. case cudzysłów pozycjaWezaY cudzysłów dwukropek. Linia 29. 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 30. break średnik. Linia 31. zamknij nawias klamrowy. Linia 32. zamknij nawias klamrowy. Linia 34. Waz waz znak równości new Waz otwórz nawias okrągły pozycjaWezaX przecinek pozycjaWezaY przecinek Waz kropka Kierunek kropka polnoc zamknij nawias okrągły średnik. Linia 35. ObslugaWejscia wejscie znak równości new ObslugaWejscia otwórz nawias okrągły waz zamknij nawias okrągły średnik. Linia 36. WyswietlanieGrafiki grafika znak równości new WyswietlanieGrafiki otwórz nawias okrągły waz przecinek new Plansza otwórz nawias okrągły rozmiar zamknij nawias okrągły przecinek wejscie zamknij nawias okrągły średnik. Linia 37. Thread watekGrafiki znak równości new Thread otwórz nawias okrągły grafika zamknij nawias okrągły średnik. Linia 38. watekGrafiki kropka start otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 39. Thread watekWeza znak równości new Thread otwórz nawias okrągły waz zamknij nawias okrągły średnik. Linia 40. watekWeza kropka start otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 41. zamknij nawias klamrowy. Linia 42. zamknij nawias klamrowy.

Plik konfiguracyjny nazwaliśmy config.ini. Podaliśmy do niego ścieżkę względnąścieżka względna (relatywna)ścieżkę względną. Aby można było z niej skorzystać, plik musi znajdować się w folderze projektu, dlatego też umieściliśmy go w ramach głównego folderu.

Kolejne funkcjonalności, takie jak zjadanie owoców czy pojawianie się ogona węża, zaimplementujemy w ramach następnych e‑materiałów z tej serii.

Plik zawierający cały dotychczas napisany kod w języku Java możesz pobrać tutaj:

R1CxcHujgeLCe

plik zawierający kod źródłowy w Java

Kod źródłowy Java
Plik ZIP o rozmiarze 3.40 KB w języku polskim

Tworzenie gry w języku Python

Zaczniemy od zaimplementowania jednej z metod dostępnych w ramach klasy Waz, czyli metody sprawdzWynik(), która będzie sprawdzać aktualną liczbę punktów na podstawie atrybutu liczbaPunktów.

Linia 1. def sprawdzWynik otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 2. return self kropka liczbaPunktow średnik.

Następnie utworzymy metodę ustawWynik(), która powinna modyfikować atrybut liczbaPunktow na ten podany jako argument.

Linia 1. def ustawWynik otwórz nawias okrągły self przecinek liczbaPunktowDoUstawienia zamknij nawias okrągły dwukropek. Linia 2. self kropka liczbaPunktow znak równości liczbaPunktowDoUstawienia średnik.

Metodę zmienKierunek() zaimplementujemy w ten sposób, że najpierw w ramach klasy Waz dodamy klasę enumeracyjną, w której umieścimy wszystkie możliwe kierunki poruszania się węża.

Linia 1. kratka w klasie Waz. Linia 2. import enum. Linia 3. class Kierunek otwórz nawias okrągły enum kropka Enum zamknij nawias okrągły dwukropek. Linia 4. polnoc znak równości 0. Linia 5. wschod znak równości 1. Linia 6. poludnie znak równości 2. Linia 7. zachod znak równości 3.

Respektujemy wspomnianą wcześniej dobrą praktykę używania w programowaniu słów zamiast abstrakcyjnych liczb, które dla kogoś, kto widzi kod po raz pierwszy, nic nie znaczą. Znacznie łatwiej zrozumieć zapis np. kierunek == polnoc niż kierunek == 0.

Metoda służąca do zmieniania kierunku przyjmie postać:

Linia 1. def zmienKierunek otwórz nawias okrągły self przecinek kierunek zamknij nawias okrągły dwukropek. Linia 2. self kropka kierunekRuchu znak równości kierunek.

Bardziej rozbudowana jest metoda przesun(), którą będziemy teraz implementować. Odpowiada ona za ruch węża we właściwym kierunku.

Linia 1. def przesun otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 2. if self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka polnoc kropka value dwukropek. Linia 3. self kropka pozycjaWezaY minus znak równości 1. Linia 4. if self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka poludnie kropka value dwukropek. Linia 5. self kropka pozycjaWezaY plus znak równości 1. Linia 6. if self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka zachod kropka value dwukropek. Linia 7. self kropka pozycjaWezaX plus znak równości 1. Linia 8. if self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka wschod kropka value dwukropek. Linia 9. self kropka pozycjaWezaX minus znak równości 1.

Pojawia się pytanie, dlaczego poruszanie się na północ odejmuje 1 od pozycjaWezaY, zamiast dodawać, jak to jest w przypadku kierunku wschód‑zachód i co wydaje się bardziej logiczne.

Wynika to z faktu, że wyświetlamy plansze zgodnie z następującymi osiami:

RXth5U6whq6yk
Źródło: Contentplus.pl sp. z o.o., licencja: CC BY-SA 3.0.

Oznacza to, że na liście najpierw wyświetlamy rzędy od zerowego do ostatniego, a w ramach każdego rzędu od zerowej kolumny do ostatniej.

Podsumowując: aby poruszyć się na planszy w górę, musimy odjąć od pozycjaWezaY liczbę 1. Dodanie spowoduje natomiast poruszanie się w dół. Moglibyśmy oczywiście wyświetlać naszą planszę inaczej – zgodnie ze standardowym sposobem reprezentowania współrzędnych na osi. Możesz teraz spróbować zmodyfikować kod, żeby osiągnąć taki efekt. Pamiętaj jednak, że jeśli wprowadzisz w kodzie zmiany, realizując kroki opisane w dalszej części tego e‑materiału, możesz napotkać nieścisłości.

Kolejne metody z klasy Waz, które zaimplementujemy, to sprawdzPozycjeX() oraz sprawdzPozycjeY(). Zgodnie ze swoją nazwą służą one do zwracania atrybutów pozycjaWezaX oraz pozycjaWezaY.

Linia 1. def sprawdzPozycjeY otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 2. return self kropka pozycjaWezaY. Linia 3. def sprawdzPozycjeX otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 4. return self kropka pozycjaWezaX.

Następnie zaimplementujemy metodę ustawPozycje(). Działa ona podobnie jak metoda ustawWynik(), z tą różnicą, że zmienia inne atrybuty oraz że potrzebujemy tu dwóch atrybutów.

Linia 1. def ustawPozycje otwórz nawias okrągły self przecinek x przecinek y zamknij nawias okrągły dwukropek. Linia 2. self kropka pozycjaWezaX znak równości x. Linia 3. self kropka pozycjaWezaY znak równości y.

Ostatnią metodą z tej klasy, jaką musimy zaimplementować, jest metoda zjedzOwoc(). Przyjmie ona jako argument owoc, który będzie zjadany. Na razie jednak nie będziemy zajmować się znikaniem ani losowaniem owoców. Skupimy się jedynie na implementacji dodawania punktów do obiektu klasy Waz.

Linia 1. def zjedzOwoc otwórz nawias okrągły self przecinek owoc zamknij nawias okrągły dwukropek. Linia 2. self kropka liczbaPunktow plus znak równości owoc kropka sprawdzPunkty otwórz nawias okrągły zamknij nawias okrągły.

W tym fragmencie skorzystaliśmy z metody dostępnej w klasie Owoc, która sprawdza liczbę punktów przyznawaną wężowi przez konkretny obiekt tej klasy.

Zaimplementowaliśmy do tej pory wątek grafiki oraz wątek obsługujący wejście, brakuje nam jednak takiego, który obsłużyłby poruszanie się węża po planszy. Zaimplementujmy go zatem:

Linia 1. waz znak równości Waz otwórz nawias okrągły zamknij nawias okrągły. Linia 2. 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 zamknij nawias okrągły zamknij nawias okrągły.

Dodatkowo do wątku grafiki dostarczymy obiekt klasy Waz, którego będziemy używać w trakcie wyświetlania obrazu. Zrobimy to przez dodanie argumentu do metody wyswietlGre().

W tym momencie wąż mógłby się już poruszać, ale nie jesteśmy w stanie tego zweryfikować, ponieważ na naszej planszy jeszcze się on nie wyświetla. Poprawimy więc metodę wyswietlGre() z klasy WyswietlanieGrafiki, do której jako parametr musimy dodać jeszcze sam obiekt klasy Waz.

Przy okazji zaimplementujemy również wyświetlanie owoców oraz pustych komórek. Głowę węża będziemy wyświetlać jako znak @, owoc jako $, pole puste jako +, a ogon jako o.

Linia 1. class WyswietlanieGrafiki otwórz nawias okrągły zamknij nawias okrągły dwukropek. Linia 2. def wyswietlGre otwórz nawias okrągły self przecinek plansza przecinek waz zamknij nawias okrągły dwukropek. Linia 3. import os. Linia 4. import time. Linia 5. while 1 dwukropek. 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 j zamknij nawias kwadratowy otwórz nawias kwadratowy i 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 j zamknij nawias kwadratowy otwórz nawias kwadratowy i 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. time kropka sleep otwórz nawias okrągły 1 zamknij nawias okrągły.

Spróbujmy teraz uruchomić wątek węża i zobaczmy, czy się porusza.

Linia 1. watekWeza kropka start 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.

Wąż się nie porusza, ponieważ metoda przesun() z klasy Waz używana jest tylko raz.

Umieścimy ją w pętli while analogicznie jak w przypadku metody wyswietlGre(), którą zaimplementowaliśmy wcześniej. W metodzie przesun() umieścimy również time.sleep(), które będziemy musieli następnie zsynchronizować z wyświetlaniem grafiki. Metoda ta przyjmie następującą postać:

Linia 1. def przesun otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 2. import time. Linia 3. while 1 dwukropek. Linia 4. if self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka polnoc kropka value dwukropek. Linia 5. self kropka pozycjaWezaY minus znak równości 1. Linia 6. if self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka poludnie kropka value dwukropek. Linia 7. self kropka pozycjaWezaY plus znak równości 1. Linia 8. if self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka zachod kropka value dwukropek. Linia 9. self kropka pozycjaWezaX plus znak równości 1. Linia 10. if self kropka kierunekRuchu znak równości znak równości self kropka Kierunek kropka wschod kropka value dwukropek. Linia 11. self kropka pozycjaWezaX minus znak równości 1. Linia 12. time kropka sleep otwórz nawias okrągły 1 zamknij nawias okrągły.

Jeśli uruchomimy teraz rozgrywkę, wąż natychmiast zniknie z planszy. Spowodowane jest to tym, że domyślnie zmierza w górę, a rozpoczyna ruch w lewym górnym rogu.

Dodajmy więc do konstruktora klasy Waz jego pozycję startową. Dla uproszczenia zakładamy, że pozycja węża podana w konstruktorze znajduje się już na planszy.

Konstruktor jego klasy przyjmie następującą wartość:

Linia 1. def podkreślnik podkreślnik init podkreślnik podkreślnik otwórz nawias okrągły self przecinek pozycjaWezaX przecinek pozycjaWezaY zamknij nawias okrągły dwukropek. Linia 2. self kropka liczbaPunktow znak równości 0. Linia 3. self kropka pozycjaWezaX znak równości pozycjaWezaX. Linia 4. self kropka pozycjaWezaY znak równości pozycjaWezaY. Linia 5. self kropka kierunekRuchu znak równości 0.

Powołanie obiektu klasy Waz może więc teraz wyglądać następująco:

Linia 1. waz znak równości Waz otwórz nawias okrągły 4 przecinek 4 zamknij nawias okrągły.

Jeśli uruchomimy program teraz, wąż będzie przemieszczał się po planszy. Zapewne okaże się, że jego ruch nie jest płynny, może też przeskakiwać lub znikać. Wynika to z niezsynchronizowanego wyświetlania grafiki i węża.

Brak synchronizacji działa tak, że wąż porusza się po narysowaniu planszy (w tym wypadku możemy zaobserwować przeskok) albo podczas rysowania planszy (wąż może zniknąć).

Do synchronizacji tych wątków użyjemy zamkazamekzamka dostępnego w ramach modułu threading.

Otwieranie i zamykanie wątków obsługujących węża i wyświetlanie grafiki będzie wyglądać następująco:

RNaBwL5zGGCOm
Źródło: Contentplus.pl sp. z o.o., licencja: CC BY-SA 3.0.

W naszym przypadku wątkiem A będzie ten obsługujący grafikę, a wątkiem B – obsługujący węża.

Przez „zamknięcie wątku” mamy na myśli zatrzymanie działania tego wątku do czasu jego ponownego „otwarcia”.

Zamki możemy umieścić w programie na kilka sposobów. Jednym z nich jest zadeklarowanie na początku dwóch zmiennych, odpowiadających za jeden wątek. Jednakże takie rozwiązanie spowoduje, że nasz kod przestanie być modułowy, co oznacza, że nie będziemy mogli np. wymienić klasy Waz na inną, ponieważ będzie się w niej znajdowało odwołanie do globalnej zmiennej. Jest to wada, której staramy się uniknąć m.in. poprzez utworzenie diagramów klas i pakietów.

Dlatego też do odpowiednich metod klasy Waz i klasy WyswietlanieGrafiki dodamy obydwa zamki, aby metody same mogły się do nich bez problemu odwoływać – przez referencje.

W metodzie przesun() dodamy jako argumenty zamekWeza oraz zamekGrafiki i analogicznie postąpimy w metodzie wyswietlanieGrafiki().

Utworzenie wcześniej wspomnianych wątków będzie wyglądać następująco:

Linia 1. plansza znak równości Plansza otwórz nawias okrągły 7 przecinek 7 zamknij nawias okrągły. Linia 2. wyswietlanieGrafiki znak równości WyswietlanieGrafiki otwórz nawias okrągły zamknij nawias okrągły. Linia 3. waz znak równości Waz otwórz nawias okrągły 6 przecinek 6 zamknij nawias okrągły. Linia 4. zamekWeza znak równości threading kropka Lock otwórz nawias okrągły zamknij nawias okrągły. Linia 5. zamekWyswietlania znak równości threading kropka Lock otwórz nawias okrągły zamknij nawias okrągły. Linia 6. zamekWeza kropka acquire otwórz nawias okrągły zamknij nawias okrągły. Linia 7. watekGrafiki znak równości threading kropka Thread otwórz nawias okrągły target znak równości WyswietlanieGrafiki kropka wyswietlGre przecinek args znak równości otwórz nawias okrągły wyswietlanieGrafiki przecinek plansza przecinek waz przecinek zamekWeza przecinek zamekWyswietlania zamknij nawias okrągły zamknij nawias okrągły. Linia 8. obslugaWejscia znak równości ObslugaWejscia otwórz nawias okrągły zamknij nawias okrągły. Linia 9. watekWejscia znak równości threading kropka Thread otwórz nawias okrągły target znak równości ObslugaWejscia kropka zinterpretujWcisnietyPrzycisk przecinek args znak równości otwórz nawias okrągły obslugaWejscia przecinek waz zamknij nawias okrągły zamknij nawias okrągły. Linia 10. 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 zamknij nawias okrągły zamknij nawias okrągły. Linia 11. watekGrafiki kropka start otwórz nawias okrągły zamknij nawias okrągły. Linia 12. watekWejscia kropka start otwórz nawias okrągły zamknij nawias okrągły. Linia 13. watekWeza kropka start otwórz nawias okrągły zamknij nawias okrągły. Linia 14. watekWejscia kropka join otwórz nawias okrągły zamknij nawias okrągły. Linia 15. watekGrafiki kropka join otwórz nawias okrągły zamknij nawias okrągły. Linia 16. watekWeza kropka join otwórz nawias okrągły zamknij nawias okrągły.

Natomiast metody przesun() oraz wyswietlGrafike() prezentują się tak:

Linia 1. def przesun otwórz nawias okrągły self przecinek zamekWeza przecinek zamekGrafiki 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 plus 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 minus znak równości 1. Linia 13. time kropka sleep otwórz nawias okrągły 1 zamknij nawias okrągły. Linia 14. zamekGrafiki kropka release otwórz nawias okrągły zamknij nawias okrągły.
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.

Wątek obsługi wejścia jest już utworzony i działa w tle w naszym programie. Nadal jednak nie mamy wpływu na ruch węża, wyświetlają się tylko komunikaty informujące o wciskanych klawiszach. Dlatego do metody obsługującej wejście dostarczymy obiekt klasy Waz, którego atrybut kierunekRuchu będziemy następnie modyfikować.

Jeśli wąż porusza się w kierunku północnym, a my wciśniemy przycisk A, to oczekujemy, że zacznie przemieszczać się na zachód. Analogicznie, jeśli porusza się na zachód, będziemy oczekiwać, że po ponownym użyciu klawisza A zacznie przemieszczać się na południe.

Oznacza to, że w zależności od tego, czy klikniemy przycisk A czy B, wąż zmieni swój kierunek o 90 lub o -90 stopni.

Biorąc to pod uwagę oraz dodając do argumentów metody zinterpretujWcisnietyPrzycisk(), otrzymamy ostatecznie:

Linia 1. def zinterpretujWcisnietyPrzycisk otwórz nawias okrągły self przecinek waz zamknij nawias okrągły dwukropek. Linia 2. import msvcrt. Linia 3. while 1 dwukropek. Linia 4. x znak równości msvcrt kropka getch otwórz nawias okrągły zamknij nawias okrągły kropka decode otwórz nawias okrągły apostrof ASCII apostrof zamknij nawias okrągły. Linia 5. if x znak równości znak równości apostrof a apostrof dwukropek. Linia 6. if waz kropka kierunekRuchu znak równości znak równości waz kropka Kierunek kropka polnoc kropka value dwukropek. Linia 7. waz kropka kierunekRuchu znak równości waz kropka Kierunek kropka zachod kropka value. Linia 8. elif waz kropka kierunekRuchu znak równości znak równości waz kropka Kierunek kropka wschod kropka value dwukropek. Linia 9. waz kropka kierunekRuchu znak równości waz kropka Kierunek kropka polnoc kropka value. Linia 10. elif waz kropka kierunekRuchu znak równości znak równości waz kropka Kierunek kropka poludnie kropka value dwukropek. Linia 11. waz kropka kierunekRuchu znak równości waz kropka Kierunek kropka wschod kropka value. Linia 12. elif waz kropka kierunekRuchu znak równości znak równości waz kropka Kierunek kropka zachod kropka value dwukropek. Linia 13. waz kropka kierunekRuchu znak równości waz kropka Kierunek kropka poludnie kropka value. Linia 14. elif x znak równości znak równości apostrof d apostrof dwukropek. Linia 15. if waz kropka kierunekRuchu znak równości znak równości waz kropka Kierunek kropka polnoc kropka value dwukropek. Linia 16. waz kropka kierunekRuchu znak równości waz kropka Kierunek kropka wschod kropka value. Linia 17. elif waz kropka kierunekRuchu znak równości znak równości waz kropka Kierunek kropka wschod kropka value dwukropek. Linia 18. waz kropka kierunekRuchu znak równości waz kropka Kierunek kropka poludnie kropka value. Linia 19. elif waz kropka kierunekRuchu znak równości znak równości waz kropka Kierunek kropka poludnie kropka value dwukropek. Linia 20. waz kropka kierunekRuchu znak równości waz kropka Kierunek kropka zachod kropka value. Linia 21. elif waz kropka kierunekRuchu znak równości znak równości waz kropka Kierunek kropka zachod kropka value dwukropek. Linia 22. waz kropka kierunekRuchu znak równości waz kropka Kierunek kropka polnoc kropka value. Linia 23. elif x znak równości znak równości apostrof apostrof dwukropek. Linia 24. print otwórz nawias okrągły cudzysłów kliknieto spacje cudzysłów zamknij nawias okrągły.

Zmodyfikowaniem obsługi przycisku spacji zajmiemy się w ramach kolejnych e‑materiałów z tej serii.

Po uruchomieniu gry mamy już poruszającego się poprawnie węża. Pozostaje jeszcze tylko zaimplementować wczytanie do pliku.

Nasz plik konfiguracyjny wygląda następująco:

Linia 1. otwórz nawias kwadratowy plansza zamknij nawias kwadratowy. Linia 2. rozmiarX znak równości 7. Linia 3. rozmiarY znak równości 7.

Odczytamy go z pomocą klasy ConfigParser, dostępnej w ramach nagłówka configparser.

Dla naszego pliku przykładowy kod przyjąłby następującą postać:

Linia 1. ustawienia znak równości configparser kropka ConfigParser otwórz nawias okrągły zamknij nawias okrągły. Linia 2. ustawienia kropka read otwórz nawias okrągły apostrof plikKonfiguracyjny kropka ini apostrof zamknij nawias okrągły. Linia 3. rozmiarX znak równości ustawienia kropka getint otwórz nawias okrągły apostrof plansza apostrof przecinek apostrof rozmiarX apostrof zamknij nawias okrągły.

W linijce 2 otwieramy odpowiedni plik konfiguracyjny. Plik ten, o ile podajemy ścieżkę bezwzględnąścieżka bezwzględnaścieżkę bezwzględną, a na tym nam zależy, musimy umieścić w folderze z kodem źródłowym.

W linijce 3 pobieramy wartość rozmiarX z działu [plansza]. Jako pierwszy argument tej metody podajemy nazwę nagłówka, a jako drugi – nazwę parametru.

Ostatecznie fragment zawierający wczytywanie ustawień i wielkość planszy prezentuje się tak (należy zwrócić uwagę na konieczność zaimportowania nagłówka klasy configparser):

Linia 1. import configparser. Linia 2. ustawienia znak równości configparser kropka ConfigParser otwórz nawias okrągły zamknij nawias okrągły. Linia 3. ustawienia kropka read otwórz nawias okrągły apostrof plikKonfiguracyjny kropka ini apostrof zamknij nawias okrągły. Linia 4. rozmiarX znak równości ustawienia kropka getint otwórz nawias okrągły apostrof plansza apostrof przecinek apostrof rozmiarX apostrof zamknij nawias okrągły. Linia 5. rozmiarY znak równości ustawienia kropka getint otwórz nawias okrągły apostrof plansza apostrof przecinek apostrof rozmiarY apostrof zamknij nawias okrągły. Linia 6. plansza znak równości Plansza otwórz nawias okrągły rozmiarX przecinek rozmiarY zamknij nawias okrągły.

Inne funkcje, takie jak działanie ogona węża, pojawianie się i znikanie owoców na mapie, zaimplementujemy w ramach kolejnych e‑materiałów z tej serii.

Plik zawierający cały dotychczas napisany kod w języku Python możesz pobrać tutaj:

RwHKa34ayxj16

Plik zawierający kod źródłowy w Python

Plik źródłowy Python
Plik ZIP o rozmiarze 1.49 KB w języku polskim

Słownik

ścieżka bezwzględna
ścieżka bezwzględna

ścieżka wskazywana bez uwzględnienia obecnej pozycji w systemie

ścieżka względna (relatywna)
ścieżka względna (relatywna)

ścieżka wskazywana od obecnej pozycji w systemie

zamek
zamek

w programowaniu współbieżnym zamki pomagają w trakcie synchronizacji kilku współbieżnie pracujących wątków; fragment kodu, do którego kilka wątków potrzebuje dostępu, ale tylko jeden w danym momencie może go otrzymać, nazywamy sekcją krytyczną