Wstęp

Zacznijmy od przypomnienia diagramu klas:

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

W tym e‑materiale skupimy się na implementacji wątkówwątekwątków oraz sterowania, dlatego istotne są dla nas dwie klasy: ObsługaWejścia oraz WyświetlanieGrafiki. Jednak, żebyśmy mogli je zaimplementować, należy napisać – co najmniej – pozostałe klasy.

Wielowątkowość i sterowanie w języku Java

Zaczynamy od zadeklarowania istnienia wszystkich klas.

Wszystkie przedstawione klasy znajdują się w różnych plikach.

Linia 1. public class Main otwórz nawias klamrowy. Linia 3. public static void main otwórz nawias okrągły String otwórz nawias kwadratowy zamknij nawias kwadratowy args zamknij nawias okrągły otwórz nawias klamrowy. Linia 5. zamknij nawias klamrowy. Linia 6. zamknij nawias klamrowy.
Linia 1. public class ObslugaWejscia otwórz nawias klamrowy. Linia 3. zamknij nawias klamrowy.
Linia 1. public class WyswietlanieGrafiki implements Runnable otwórz nawias klamrowy. Linia 2. Waz waz średnik. Linia 3. Plansza plansza średnik. Linia 4. at Override. Linia 5. public void run otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 7. zamknij nawias klamrowy. Linia 8. zamknij nawias klamrowy.

W klasie WyswietlanieGrafiki zaimplementujemy interfejs Runnable, aby potem uruchomić wyświetlanie jako osobny wątek.

Linia 1. public class Waz otwórz nawias klamrowy. Linia 2. private int liczbaPunktow znak równości 0 średnik. Linia 3. private int pozycjaWezaX znak równości 0 średnik. Linia 4. private int pozycjaWezaY znak równości 0 średnik. Linia 5. private int kierunekRuchu znak równości 0 średnik. Linia 7. zamknij nawias klamrowy.
Linia 1. public class Plansza otwórz nawias klamrowy. Linia 2. public int rozmiarXPlanszy średnik. Linia 3. public int rozmiarYPlanszy średnik. Linia 4. public Pole pole otwórz nawias kwadratowy zamknij nawias kwadratowy otwórz nawias kwadratowy zamknij nawias kwadratowy średnik. Linia 6. zamknij nawias klamrowy.
Linia 1. public class Owoc otwórz nawias klamrowy. Linia 2. private int liczbaPunktow znak równości 0 średnik. Linia 3. zamknij nawias klamrowy.
Linia 1. public class Pole otwórz nawias klamrowy. Linia 2. Owoc owoc średnik. Linia 3. int wartoscPola znak równości 0 średnik. Linia 4. zamknij nawias klamrowy.

Zwróćmy uwagę, że właśnie zaimplementowaliśmy niektóre zależności i wszystkie zmienne, które znajdowały się na grafie. Nie zaimplementowaliśmy jednak zależności, w których – chwilowo – korzystamy z innych klas.

Spróbujemy zacząć od zaimplementowania klasy WyswietlanieGrafiki.

Linia 1. public class WyswietlanieGrafiki implements Runnable otwórz nawias klamrowy. Linia 2. Waz waz średnik. Linia 3. Plansza plansza średnik. Linia 4. at Override. Linia 5. public void run otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 6. try otwórz nawias klamrowy. Linia 7. Thread kropka sleep otwórz nawias okrągły 100 zamknij nawias okrągły średnik. Linia 8. zamknij nawias klamrowy catch otwórz nawias okrągły InterruptedException e zamknij nawias okrągły otwórz nawias klamrowy. Linia 9. e kropka printStackTrace otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 10. zamknij nawias klamrowy. Linia 11. wyswietlGre otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 12. zamknij nawias klamrowy. Linia 13. WyswietlanieGrafiki otwórz nawias okrągły Waz waz przecinek Plansza plansza zamknij nawias okrągły otwórz nawias klamrowy. Linia 14. this kropka waz znak równości waz średnik. Linia 15. this kropka plansza znak równości plansza średnik. Linia 16. zamknij nawias klamrowy. Linia 17. public void wyswietlGre otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 18. 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 19. 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 20. 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 21. System kropka out kropka print otwórz nawias okrągły cudzysłów plus cudzysłów zamknij nawias okrągły średnik. Linia 22. zamknij nawias klamrowy else otwórz nawias klamrowy. Linia 23. System kropka out kropka print otwórz nawias okrągły cudzysłów x cudzysłów zamknij nawias okrągły średnik. Linia 24. zamknij nawias klamrowy. Linia 25. zamknij nawias klamrowy. Linia 26. System kropka out kropka println otwórz nawias okrągły cudzysłów lewy ukośnik n cudzysłów zamknij nawias okrągły średnik. Linia 27. zamknij nawias klamrowy. Linia 28. zamknij nawias klamrowy. Linia 29. zamknij nawias klamrowy.

Metoda run() została nadpisana przez naszą wersję, w której co 100 milisekund czyścimy ekran, a następnie rysujemy za pomocą wyswietlGre() obecny stan gry. Na ten moment zaimplementowaliśmy wyświetlanie samej planszy – do węża wrócimy potem. Jako znak + wyświetlamy pola puste, a jako x pola, na których jest w tym momencie „coś” bliżej nieokreślonego.

Rozmiar mapy, zgodnie z wymaganiami funkcjonalnymi, ma być docelowo dostępny z konfigurowalnych plików gry, jednak nie możemy zaimplementować wszystkiego jednocześnie. Stworzymy więc zmienną, której wartość na razie ustawimy ręcznie z poziomu kodu. Postępujemy tak, ponieważ najpierw chcemy dokonać implementacji – żeby mapa zaczęła się generować w jakiejkolwiek formie.

Istotną wadą kodu jest to, że dany wątek uruchamia się tylko raz. Do samego wyświetlania wrócimy na późniejszym etapie. W międzyczasie kod ten ulegnie znacznej modyfikacji.

Sama inicjalizacja Planszy wyglądałaby w następujący sposób:

Linia 1. public class Plansza otwórz nawias klamrowy. Linia 2. public int rozmiarXPlanszy średnik. Linia 3. public int rozmiarYPlanszy średnik. Linia 4. public Pole pole otwórz nawias kwadratowy zamknij nawias kwadratowy otwórz nawias kwadratowy zamknij nawias kwadratowy średnik. Linia 5. Plansza otwórz nawias okrągły int rozmiar zamknij nawias okrągły otwórz nawias klamrowy. Linia 6. rozmiarXPlanszy znak równości rozmiar średnik. Linia 7. rozmiarYPlanszy znak równości rozmiar średnik. Linia 8. 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 9. 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 10. 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 11. 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 12. zamknij nawias klamrowy. Linia 13. zamknij nawias klamrowy. Linia 14. zamknij nawias klamrowy. Linia 15. zamknij nawias klamrowy.

Czas na zaimplementowanie klasy Pole.

Linia 1. public class Pole otwórz nawias klamrowy. Linia 2. Owoc owoc średnik. Linia 3. int wartoscPola znak równości 0 średnik. Linia 4. Pole otwórz nawias okrągły boolean czyZOwocem zamknij nawias okrągły otwórz nawias klamrowy. Linia 5. if otwórz nawias okrągły czyZOwocem zamknij nawias okrągły otwórz nawias klamrowy. Linia 6. 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.

Klasa Owoc nie jest złożona, udało się ją zaimplementować już wcześniej.

Pojawia się jednak pewna nieścisłość – wcześniej w konstruktorze klasy Plansza używaliśmy bezargumentowych konstruktorów klasy Pole. Edytujemy więc jedną z dwóch klas w taki sposób, żeby zachować kompatybilność z diagramem klas.

W klasie Plansza ma się ostatecznie znaleźć metoda losujPlansze(), dlatego też w niej będziemy do konkretnych pól dodawać obiekty klasy Owoc. Wrócimy do tego później, jednak najpierw poprawimy klasę Pole.

Linia 1. public class Pole otwórz nawias klamrowy. Linia 2. Owoc owoc średnik. Linia 3. int wartoscPola znak równości 0 średnik. Linia 4. Pole otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 5. zamknij nawias klamrowy. Linia 6. zamknij nawias klamrowy.

W celu przetestowania, czy w grze dobrze działa wyświetlanie, wpierw implementujemy klasę Wąż, której instancję będziemy powoływać w klasie Main().

Linia 1. public class Waz otwórz nawias klamrowy. Linia 2. private int liczbaPunktow znak równości 0 średnik. Linia 3. private int pozycjaWezaX znak równości 0 średnik. Linia 4. private int pozycjaWezaY znak równości 0 średnik. Linia 5. private int kierunekRuchu znak równości 0 średnik. Linia 6. Waz otwórz nawias okrągły int pozycjaWezaX przecinek int pozycjaWezaY przecinek int kierunekRuchu zamknij nawias okrągły otwórz nawias klamrowy. Linia 7. this kropka pozycjaWezaX znak równości pozycjaWezaX średnik. Linia 8. this kropka pozycjaWezaY znak równości pozycjaWezaY średnik. Linia 9. this kropka kierunekRuchu znak równości kierunekRuchu średnik. Linia 10. zamknij nawias klamrowy. Linia 11. zamknij nawias klamrowy.

Zrobienie tego w ten sposób, pozwala łatwo zmieniać miejsce na planszy, w którym wąż ma swoją pozycję startową oraz startowy kierunek ruchu.

Klasa Main() przyjmie postać:

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

Po uruchomieniu program wyświetli się następujący obraz:

Linia 1. plus plus plus plus plus. Linia 3. plus plus plus plus plus. Linia 5. plus plus plus plus plus. Linia 7. plus plus plus plus plus. Linia 9. plus plus plus plus plus.

Teraz dochodzimy do problematycznej części – sterowania. W językach C++Python możliwe jest przechwytywanie klawiszy z konsoli za pomocą funkcji lub metody getch(), która nie ma swojego odpowiednika w języku Java.

Istnieje natomiast opcja tworzenia interfejsów graficznych, które mogą przechwytywać pojedyncze kliknięcia. Zależy nam na interpretacji pojedynczych kliknięć, ponieważ gra byłaby dużo mniej użytkowa, gdybyśmy co chwilę potwierdzali klawiszem Enter decyzję.

Zaimplementujmy więc interfejs graficzny. W tym celu skorzystamy z biblioteki Swing, dostępnej w języku Java.

Linia 1. import javax kropka swing kropka asterysk średnik. Linia 3. public class WyswietlanieGrafiki implements Runnable otwórz nawias klamrowy. Linia 4. Waz waz średnik. Linia 5. Plansza plansza średnik. Linia 6. private JFrame okienkoGraficzne średnik. Linia 7. private JTextArea grafikaTekstowa średnik. Linia 8. at Override. Linia 9. public void run otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 10. try otwórz nawias klamrowy. Linia 11. Thread kropka sleep otwórz nawias okrągły 1000 zamknij nawias okrągły średnik. Linia 12. zamknij nawias klamrowy catch otwórz nawias okrągły InterruptedException e zamknij nawias okrągły otwórz nawias klamrowy. Linia 13. e kropka printStackTrace otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 14. zamknij nawias klamrowy. Linia 15. wyswietlGre otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 16. zamknij nawias klamrowy. Linia 17. WyswietlanieGrafiki otwórz nawias okrągły Waz waz przecinek Plansza plansza zamknij nawias okrągły otwórz nawias klamrowy. Linia 18. this kropka waz znak równości waz średnik. Linia 19. this kropka plansza znak równości plansza średnik. Linia 20. this kropka okienkoGraficzne znak równości new JFrame otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 21. this kropka grafikaTekstowa znak równości new JTextArea otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 22. grafikaTekstowa kropka setSize otwórz nawias okrągły 500 przecinek 500 zamknij nawias okrągły średnik. Linia 23. grafikaTekstowa kropka setEditable otwórz nawias okrągły false zamknij nawias okrągły średnik. Linia 24. okienkoGraficzne kropka setSize otwórz nawias okrągły 500 przecinek 500 zamknij nawias okrągły średnik. Linia 25. okienkoGraficzne kropka add otwórz nawias okrągły grafikaTekstowa zamknij nawias okrągły średnik. Linia 26. okienkoGraficzne kropka setVisible otwórz nawias okrągły true zamknij nawias okrągły średnik. Linia 27. zamknij nawias klamrowy. Linia 29. kropka kropka kropka kropka. Linia 30. zamknij nawias klamrowy.

Przypomnienie: JFrame to samo okno, a JTextArea to pole tekstowe, które musi być osadzone w oknie. Ustawiamy rozmiar obu obiektów i wyłączamy możliwość edycji okienka tekstowego. Następnie dodajemy okienko tekstowe do obiektu klasy JFrame, a potem wyświetlamy ten obiekt.

Do zmiennych obiektu WyswietlanieGrafiki() dodaliśmy również obiekty utworzone w konstruktorze. Zrobiliśmy to, ponieważ później będziemy potrzebowali tych informacji, do swobodnej modyfikacji zawartości okna.

Wyświetlanie w nowej postaci, która wypisuje stan planszy do zdefiniowanego przed chwilą okienka tekstowego, wyglądałoby w następujący sposób:

Linia 1. public void wyswietlGre otwórz nawias okrągły zamknij nawias okrągły otwórz nawias klamrowy. Linia 2. 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 3. 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 4. 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 5. grafikaTekstowa kropka append otwórz nawias okrągły cudzysłów plus cudzysłów zamknij nawias okrągły średnik. Linia 6. zamknij nawias klamrowy else otwórz nawias klamrowy. Linia 7. grafikaTekstowa kropka append otwórz nawias okrągły cudzysłów x cudzysłów zamknij nawias okrągły średnik. Linia 8. zamknij nawias klamrowy. Linia 9. zamknij nawias klamrowy. Linia 10. grafikaTekstowa kropka append otwórz nawias okrągły cudzysłów lewy ukośnik n cudzysłów zamknij nawias okrągły średnik. Linia 11. zamknij nawias klamrowy. Linia 12. zamknij nawias klamrowy.

Metoda append() dopisuje tekst do okienka analogicznie do metody System.out.print().

Z racji tego, że chcemy, aby okienko odświeżało się co jakiś czas automatycznie, to potrzebujemy również sposobu na czyszczenie go. Zrobimy to w następujący sposób:

Linia 1. grafikaTekstowa kropka setText otwórz nawias okrągły cudzysłów cudzysłów zamknij nawias okrągły średnik.

Zapisany przed chwilą fragment kodu ustawi zawartość okienka na pusty tekst, efektywnie czyszcząc je.

Spróbujmy teraz zaimplementować sterowanie. W tym celu skorzystamy z interfejsu KeyListener, za pomocą którego zaimplementujemy wykrywanie kliknięcia poszczególnych klawiszy. Klasa ObslugaWejscia() będzie teraz wyglądać w następujący sposób:

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. at Override. Linia 6. public void keyTyped otwórz nawias okrągły KeyEvent e zamknij nawias okrągły otwórz nawias klamrowy. Linia 8. zamknij nawias klamrowy. Linia 9. at Override. Linia 10. public void keyPressed otwórz nawias okrągły KeyEvent e zamknij nawias okrągły otwórz nawias klamrowy. Linia 11. char znak znak równości e kropka getKeyChar otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 12. if otwórz nawias okrągły znak znak równości znak równości apostrof a apostrof zamknij nawias okrągły System kropka out kropka println otwórz nawias okrągły cudzysłów Naciśnięto a cudzysłów zamknij nawias okrągły średnik. Linia 13. else if otwórz nawias okrągły znak znak równości znak równości apostrof d apostrof zamknij nawias okrągły System kropka out kropka println otwórz nawias okrągły cudzysłów Naciśnięto d cudzysłów zamknij nawias okrągły średnik. Linia 14. else 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 Naciśnięto spacje cudzysłów zamknij nawias okrągły średnik. Linia 15. zamknij nawias klamrowy. Linia 17. at Override. Linia 18. public void keyReleased otwórz nawias okrągły KeyEvent e zamknij nawias okrągły otwórz nawias klamrowy. Linia 20. zamknij nawias klamrowy. Linia 21. zamknij nawias klamrowy.

Obiekt klasy KeyListener , aby mógł działać, musi zostać dołączony do JFrame'a lub innego kontenera. Postępujemy więc podobnie jak w przypadku dodawania JTextArea. W tym celu najprościej będzie dodać obiekt klasy KeyListener do konstruktora klasy WyswietlanieGrafiki – w ten sposób przyjmie on następującą postać:

Linia 1. WyswietlanieGrafiki otwórz nawias okrągły Waz waz przecinek Plansza plansza przecinek KeyListener obslugaWejscia zamknij nawias okrągły otwórz nawias klamrowy. Linia 2. this kropka waz znak równości waz średnik. Linia 3. this kropka plansza znak równości plansza średnik. Linia 4. this kropka okienkoGraficzne znak równości new JFrame otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 5. this kropka grafikaTekstowa znak równości new JTextArea otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 6. grafikaTekstowa kropka setSize otwórz nawias okrągły 500 przecinek 500 zamknij nawias okrągły średnik. Linia 7. grafikaTekstowa kropka setEditable otwórz nawias okrągły false zamknij nawias okrągły średnik. Linia 8. okienkoGraficzne kropka setSize otwórz nawias okrągły 500 przecinek 500 zamknij nawias okrągły średnik. Linia 9. okienkoGraficzne kropka add otwórz nawias okrągły grafikaTekstowa zamknij nawias okrągły średnik. Linia 10. grafikaTekstowa kropka addKeyListener otwórz nawias okrągły obslugaWejscia zamknij nawias okrągły średnik. Linia 11. okienkoGraficzne kropka setVisible otwórz nawias okrągły true zamknij nawias okrągły średnik. Linia 12. zamknij nawias klamrowy.

W ten sposób, gdy wybierzemy okienko, a potem przyciski:
a, d, spacja , shift + a, shift + d, to otrzymamy następujący wydruk w konsoli:

Linia 1. Naciśnięto a. Linia 2. Naciśnięto d. Linia 3. Naciśnięto spacje.

Możemy zatem wywnioskować, że metoda getKeyChar() jest case sensitivecase sensitivecase sensitive.

W tym momencie klasa main przyjmie następującą postać:

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

Następne mechaniki rozgrywki zaimplementujemy w kolejnych e‑materiałach.

Dotychczas napisany kod źródłowy w języku Java można pobrać, korzystając z linku:

R1Nr83QRWThAz

Plik zawierający źródłowy w Java

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

Wielowątkowość i sterowanie w języku C++

Zacznijmy od napisania samych nagłówków klas, wraz ze zmiennymi o odpowiednich specyfikatorach dostępu.

Linia 1. class ObslugaWejscia. Linia 2. otwórz nawias klamrowy. Linia 3. public dwukropek. Linia 4. void zinterpretujWcisnietyPrzycisk otwórz nawias okrągły zamknij nawias okrągły. Linia 5. otwórz nawias klamrowy. Linia 7. zamknij nawias klamrowy. Linia 8. zamknij nawias klamrowy średnik. Linia 10. class Waz. Linia 11. otwórz nawias klamrowy. Linia 12. private dwukropek. Linia 13. int liczbaPunktow znak równości 0 średnik. Linia 14. int pozycjaWezaX znak równości 0 średnik. Linia 15. int pozycjaWezaY znak równości 0 średnik. Linia 16. public dwukropek. Linia 17. int kierunekRuchu znak równości 0 średnik. Linia 18. zamknij nawias klamrowy średnik. Linia 20. class Owoc. Linia 21. otwórz nawias klamrowy. Linia 22. private dwukropek. Linia 23. int liczbaPunktow znak równości 0 średnik. Linia 24. public dwukropek. Linia 25. int sprawdzPunkty otwórz nawias okrągły zamknij nawias okrągły. Linia 26. otwórz nawias klamrowy. Linia 27. return this minus zamknij nawias ostrokątny liczbaPunktow średnik. Linia 28. zamknij nawias klamrowy. Linia 29. void ustawPunkty otwórz nawias okrągły int liczbaPunktow zamknij nawias okrągły. Linia 30. otwórz nawias klamrowy. Linia 31. this minus zamknij nawias ostrokątny liczbaPunktow znak równości liczbaPunktow średnik. Linia 32. zamknij nawias klamrowy. Linia 33. zamknij nawias klamrowy średnik. Linia 35. class WyswietlanieGrafiki. Linia 36. otwórz nawias klamrowy. Linia 37. public dwukropek. Linia 38. void wyswietlGre otwórz nawias okrągły zamknij nawias okrągły. Linia 39. otwórz nawias klamrowy. Linia 41. zamknij nawias klamrowy. Linia 42. WyswietlanieGrafiki otwórz nawias okrągły zamknij nawias okrągły. Linia 43. otwórz nawias klamrowy. Linia 45. zamknij nawias klamrowy. Linia 46. zamknij nawias klamrowy średnik. Linia 48. class Pole. Linia 49. otwórz nawias klamrowy. Linia 50. public dwukropek. Linia 51. Owoc owoc średnik. Linia 52. int wartoscPola znak równości 0 średnik. Linia 53. zamknij nawias klamrowy średnik.

Przepisaliśmy tu jedynie informacje ze stworzonego przez nas diagramu klas. Problem zaczyna się podczas tworzenia klasy Plansza. Według diagramu trzeba w niej utworzyć dwuwymiarową tablicę obiektów klasy Pole. Z racji tego, że wiemy z wyprzedzeniem, że plansza ma mieć modyfikowalną postać z poziomu plików konfiguracyjnych, nie możemy zadeklarować jej wielkości na stałe w kodzie.

Już wiesz

O ile w C++ możemy zadeklarować wskaźnik na tablicę jednowymiarową:

Linia 1. int tab otwórz nawias kwadratowy zamknij nawias kwadratowy średnik.

o tyle nie możemy zrobić tego w identyczny sposób z tablicą dwu- lub więcej wymiarową.

Linia 1. int tab otwórz nawias kwadratowy zamknij nawias kwadratowy otwórz nawias kwadratowy zamknij nawias kwadratowy średnik prawy ukośnik prawy ukośnik błąd kompilacji.

Żeby zadeklarować tablicę dwu- lub więcej wymiarową, podajemy jej drugi (i każdy kolejny) wymiar.

Linia 1. int tab otwórz nawias kwadratowy zamknij nawias kwadratowy otwórz nawias kwadratowy 100 zamknij nawias kwadratowy średnik prawy ukośnik prawy ukośnik działa.

Zadeklarujemy zatem klasę Plansza w następujący sposób:

Linia 1. class Plansza. Linia 2. otwórz nawias klamrowy. Linia 3. public dwukropek. Linia 4. int rozmiarXPlanszy średnik. Linia 5. int rozmiarYPlanszy średnik. Linia 6. Pole asterysk asterysk pole średnik prawy ukośnik prawy ukośnik wskaźnik do tablicy wskaźników. Linia 8. void losujPlansze otwórz nawias okrągły zamknij nawias okrągły. Linia 9. otwórz nawias klamrowy. Linia 11. zamknij nawias klamrowy. Linia 12. void zresetujPlansze otwórz nawias okrągły zamknij nawias okrągły. Linia 13. otwórz nawias klamrowy. Linia 15. zamknij nawias klamrowy. Linia 16. void zaaktualizujOgonWeza otwórz nawias okrągły zamknij nawias okrągły. Linia 17. otwórz nawias klamrowy. Linia 19. zamknij nawias klamrowy. Linia 20. Plansza otwórz nawias okrągły int rozmiarX przecinek int rozmiarY zamknij nawias okrągły. Linia 21. otwórz nawias klamrowy. Linia 22. rozmiarXPlanszy znak równości rozmiarX średnik. Linia 23. rozmiarYPlanszy znak równości rozmiarY średnik. Linia 25. pole znak równości new Pole asterysk otwórz nawias kwadratowy rozmiarYPlanszy zamknij nawias kwadratowy średnik. Linia 27. 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 28. otwórz nawias klamrowy. Linia 29. pole otwórz nawias kwadratowy i zamknij nawias kwadratowy znak równości new Pole otwórz nawias kwadratowy rozmiarXPlanszy zamknij nawias kwadratowy średnik. Linia 30. 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 31. otwórz nawias klamrowy. Linia 32. pole otwórz nawias kwadratowy i zamknij nawias kwadratowy otwórz nawias kwadratowy j zamknij nawias kwadratowy znak równości Pole otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 33. zamknij nawias klamrowy. Linia 34. zamknij nawias klamrowy. Linia 35. zamknij nawias klamrowy. Linia 36. zamknij nawias klamrowy średnik.

Pamiętamy, że każda „normalna” tablica jest wskaźnikiem pozwalającym nam bezpośrednio odwoływać się do elementów, które w sobie agreguje.

Już wiesz

Zapis:

Linia 1. tab otwórz nawias kwadratowy 5 zamknij nawias kwadratowy znak równości 10 średnik.

Co w zasadzie jest przez program rozumiane jako:

Linia 1. asterysk otwórz nawias okrągły tab plus 5 zamknij nawias okrągły znak równości 10 średnik.

To z kolei na język naturalny przekładamy następująco: „Weź adres pamięci, który zapisany jest pod zmienną tab, a następnie zwiększ ją o 5 * długość pojedynczego elementu w tablicy. Zapisz liczbę 10 pod obliczonym w ten sposób adresem”.

Jeśli tab byłoby tablicą liczb int, to wtedy, zakładając że mamy czterobajtową liczbę całkowitą (int), odwołalibyśmy się do następującej komórki pamięci o numerze:

t a b   +   5     4

W przedstawionym równaniu tab reprezentuje adres logiczny w pamięci.

W omawianym przypadku utworzymy dynamiczną tablicę, tzn. taką, której rozmiar nie jest znany w czasie kompilacji. Wykorzystujemy do tego celu możliwości, jakie daje nam dynamiczne alokowanie pamięci za pomocą słowa kluczowego new. W klasie Plansza deklarujemy wskaźnik do tablicy wskaźników.

Konstruktor klasy Plansza alokuje dynamicznie pamięć na tablicę wskaźników o rozmiarze rozmiarYPlanszy. Dzieje się to w linii 25 przedstawionego kodu. Będą to wskaźniki do kolejnych tablic, które będą odpowiadać wierszom planszy. Następnie dla każdego wiersza odpowiadającemu mu wskaźnikowi przypisywana jest kolejna tablica, alokowana dynamicznie w linii 29. Te tablice będą już zawierały konkretne pola, a więc odpowiadające im obiekty klasy Pole (linia 32). Rozmiary tych tablic wynoszą rozmiarXPlanszy (a więc odpowiadają liczbie kolumn).

W ten sposób utworzyliśmy faktycznie dynamiczną tablicę dwuwymiarową, która zawiera w pierwszym wymiarze wskaźniki do tablic odpowiadających konkretnym polom planszy. Jak widać w linii 32, odwołanie się do tak zadeklarowanej i zaalokowanej tablicy jest bardzo proste – wystarczy zapis pole[wiersz][kolumna].

Dodatkowo powinniśmy również utworzyć destruktor, ponieważ używaliśmy słowa kluczowego new. W destruktorze będziemy usuwać obiekty utworzone przez wspomniane słowo kluczowe.

Linia 1. tylda Plansza otwórz nawias okrągły zamknij nawias okrągły. Linia 2. otwórz nawias klamrowy. Linia 3. for otwórz nawias okrągły int i znak równości 0 średnik i otwórz nawias ostrokątny rozmiarYPlanszy średnik plus plus i zamknij nawias okrągły. Linia 4. delete otwórz nawias kwadratowy zamknij nawias kwadratowy pole otwórz nawias kwadratowy i zamknij nawias kwadratowy średnik. Linia 5. delete otwórz nawias kwadratowy zamknij nawias kwadratowy pole średnik. Linia 6. zamknij nawias klamrowy.

Mamy już wygenerowaną planszę w pamięci, spróbujmy teraz ją wyświetlić w konsoli – zaimplementujmy klasę WyswietlanieGrafiki.

Ważne!

Żeby program dobrze zadziałał, klasa WyswietlaniaGrafiki powinna zostać umieszczona po deklaracjach klas WazPlansza.

W samym konstruktorze nie ma potrzeby, abyśmy cokolwiek uzupełniali. W diagramie klas umieściliśmy pomiędzy klasą WyswietlanieGrafiki a Plansza agregację częściową. Zrobiliśmy to, ponieważ sam obiekt klasy WyświetlanieGrafiki może istnieć bez istnienia obiektów klasy Waz i klasy Plansza, dlatego też sam konstruktor zostawiamy pusty.

Za to uzupełniamy samą metodę wyswietlGre().

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. system otwórz nawias okrągły cudzysłów CLS cudzysłów zamknij nawias okrągły średnik. Linia 4. 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 5. 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 plansza minus zamknij nawias ostrokątny rozmiarXPlanszy średnik j plus plus zamknij nawias okrągły. Linia 7. otwórz nawias klamrowy. Linia 8. std dwukropek dwukropek 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 9. zamknij nawias klamrowy. Linia 10. std dwukropek dwukropek cout otwórz nawias ostrokątny otwórz nawias ostrokątny std dwukropek dwukropek endl średnik. Linia 11. zamknij nawias klamrowy. Linia 12. zamknij nawias klamrowy.

Jako argumenty tej metody podaliśmy wskaźnik do obiektu klasy wąż i wskaźnik do obiektu klasy plansza. Informacje o tych obiektach będziemy przekazywać bezpośrednio przez referencje, zamiast przez kopie obiektów. Postępujemy tak, gdyż w przypadku obiektu klasy plansza, nasza plansza nie zostałaby poprawnie skopiowana – doszłoby do kopiowania płytkiego.

Funkcja system() pozwala przekazywać polecenia, które podamy jako argument, bezpośrednio do konsoli. Polecenie cls służy do czyszczenia konsoli. Jeśli chcielibyśmy to sprawdzić, wystarczy uruchomić konsole systemu Windows, a następnie wpisać w niej cls, polecenie to nie jest case sensitive. Używamy tutaj poleceń z systemuWindows, ponieważ zgodnie z założeniami podanymi w pierwszym materiale mamy zrobić grę pod „System Windows 10 lub nowsze”.

Jak widać w linijkach 4 – 9, pętle przechodzą kolejno po wierszach i kolumnach planszy, odwołując się do obiektów reprezentujących pola w taki sposób jak pokazaliśmy, omawiając konstruktor klasy Plansza.

Teraz, jeśli wywołamy main w ten sposób:

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 waz znak równości Waz otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 6. grafika kropka wyswietlGre otwórz nawias okrągły ampersant waz przecinek ampersant plansza zamknij nawias okrągły średnik. Linia 7. zamknij nawias klamrowy.

Wyświetli się nam w konsoli następujący wydruk:

Linia 1. 0 0 0 0 0. Linia 2. 0 0 0 0 0. Linia 3. 0 0 0 0 0. Linia 4. 0 0 0 0 0. Linia 5. 0 0 0 0 0.

Jednak pojawia się problem: program wyświetla to tylko raz. Jeśli zrobilibyśmy nieskończoną pętlę w samej metodzie wyswietlGre(), uzyskalibyśmy wtedy odświeżanie.

Ważne!

Z racji tego, że mamy tutaj do czynienia z nieskończoną pętlą, w której nie występują żadne opóźnienia, pętla ta będzie wykonywała się najszybciej jak jest to możliwe dla danego procesora. Tym samym, niestety, będzie ona próbować zajmować możliwie jak najwięcej zasobów, potencjalnie zajmując istotną ich część – spowoduje to powolne działanie twojego komputera.

Warto też dodać, że nie ma sensu, by odświeżanie wykonywało się tak często. Dlatego też spowalniamy działanie tej pętli przed przetestowaniem zaprezentowanego fragmentu kodu.

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. std dwukropek dwukropek 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. std dwukropek dwukropek cout otwórz nawias ostrokątny otwórz nawias ostrokątny std dwukropek dwukropek endl średnik. Linia 13. zamknij nawias klamrowy. Linia 14. zamknij nawias klamrowy. Linia 15. zamknij nawias klamrowy.

Ewentualnie linijkę trzecią zmienimy potem, aby wyświetlanie wykrywało, kiedy gra się kończy. Problemem pozostaje jednak fakt, że sama gra musi otrzymywać jakieś sterowanie. W tym celu posłużymy się wątkami.

Ważne!

W kodzie korzystamy z wątków dostępnych od wersji C++11.

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 waz znak równości Waz otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 6. std dwukropek dwukropek thread watekGrafiki otwórz nawias okrągły ampersant WyswietlanieGrafiki dwukropek dwukropek wyswietlGre przecinek ampersant grafika przecinek ampersant waz przecinek ampersant plansza zamknij nawias okrągły średnik. Linia 7. watekGrafiki kropka join otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 8. zamknij nawias klamrowy.

Uruchomienie wątku będzie wyglądać tak, jak to przedstawiliśmy. W tym przypadku w pierwszym argumencie wskazujemy metodę, którą będziemy się posługiwać, a w drugim argumencie – wskaźnik this wybranego przez nas obiektu tej klasy, w ramach którego wskazaną wcześniej metodę będziemy wywoływać w wątku. W dalszych argumentach znajdują się dane, które przekazujemy jako argumenty samej metody, czyli w naszym przypadku wskazujemy na adresy obiektów.

Przykład 1

Dla metody z klasy WyswietlanieGrafiki o następującej deklaracji:

Linia 1. void wyswietlGre otwórz nawias okrągły Waz asterysk waz przecinek Plansza asterysk plansza zamknij nawias okrągły.

Deklaracja wątku będzie wyglądała następująco:

Linia 1. std dwukropek dwukropek thread watekGrafiki otwórz nawias okrągły ampersant WyswietlanieGrafiki dwukropek dwukropek wyswietlGre przecinek ampersant grafika przecinek ampersant waz przecinek ampersant plansza zamknij nawias okrągły średnik.

Gdzie:

  • std::thread – nazwa klasy, z przestrzeni nazw std,

  • watekGrafiki – to nazwa zmiennej przechowujących obiekt klasy thread,

  • &WyswietlanieGrafiki::wyswietlGre – wskazuje, że z przestrzeni nazw klasy WyswietlanieGrafiki, użyjemy metody wyswietlGre,

  • &grafika – wskazuje na adres wcześniej zdeklarowanego obiektu klasy WyswietlanieGrafiki,

  • &waz, &plansza – są argumentami dostarczanymi do metody wyswietlGre(), czyli odpowiednio: Waz* waz oraz Plansza* plansza.

Dla optymalnego działania programu spowolnimy działanie tego wątku.

W tym celu skorzystamy z następującego fragmentu kodu:

Linia 1. std dwukropek dwukropek this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły std dwukropek dwukropek chrono dwukropek dwukropek milliseconds otwórz nawias okrągły 200 zamknij nawias okrągły zamknij nawias okrągły średnik.

Kod ten zatrzymuje działanie danego wątku na 200 milisekund, czyli na dwie dziesiąte sekundy. Możemy to sobie skonfigurować pod nasze potrzeby.

Dla zainteresowanych

Jeśli chcesz sprawdzić, czy podany fragment kodu rzeczywiście stopuje działanie wątku na tyle milisekund, ile deklaruje, to zobacz, w jaki sposób zadziałałaby metoda wyswietlGre() w następującej postaci:

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. std dwukropek dwukropek 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. std dwukropek dwukropek this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły std dwukropek dwukropek chrono dwukropek dwukropek milliseconds otwórz nawias okrągły 200 zamknij nawias okrągły zamknij nawias okrągły średnik. Linia 12. zamknij nawias klamrowy. Linia 13. std dwukropek dwukropek cout otwórz nawias ostrokątny otwórz nawias ostrokątny std dwukropek dwukropek endl średnik. Linia 14. zamknij nawias klamrowy. Linia 15. zamknij nawias klamrowy. Linia 16. zamknij nawias klamrowy.

Metoda – z użyciem wcześniej wspominanego fragmentu – wyglądałaby następująco:

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. std dwukropek dwukropek 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. std dwukropek dwukropek cout otwórz nawias ostrokątny otwórz nawias ostrokątny std dwukropek dwukropek endl średnik. Linia 13. zamknij nawias klamrowy. Linia 14. std dwukropek dwukropek this podkreślnik thread dwukropek dwukropek sleep podkreślnik for otwórz nawias okrągły std dwukropek dwukropek chrono dwukropek dwukropek milliseconds otwórz nawias okrągły 200 zamknij nawias okrągły zamknij nawias okrągły średnik. Linia 15. zamknij nawias klamrowy. Linia 16. zamknij nawias klamrowy.

Powodując zatrzymanie wątku na dwie dziesiąte sekundy po każdorazowym narysowaniu planszy.

Ważne!

Jeżeli po przetestowaniu, okazało się, że program zużywa wciąż zbyt dużo zasobów twojego komputera, to rozważ zwiększenie opóźnienia do – np. – 1000 milisekund lub więcej.

Linia 1. class ObslugaWejscia. Linia 2. otwórz nawias klamrowy. Linia 3. public dwukropek. Linia 4. void zinterpretujWcisnietyPrzycisk otwórz nawias okrągły zamknij nawias okrągły. Linia 5. otwórz nawias klamrowy. Linia 6. while otwórz nawias okrągły true zamknij nawias okrągły. Linia 7. otwórz nawias klamrowy. Linia 8. char przycisk znak równości getch otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 9. switch otwórz nawias okrągły przycisk zamknij nawias okrągły. Linia 10. otwórz nawias klamrowy. Linia 11. case apostrof a apostrof dwukropek. Linia 12. std dwukropek dwukropek cout otwórz nawias ostrokątny otwórz nawias ostrokątny cudzysłów Nacisnieto a lewy ukośnik n cudzysłów średnik. Linia 13. break średnik. Linia 14. case apostrof d apostrof dwukropek. Linia 15. std dwukropek dwukropek cout otwórz nawias ostrokątny otwórz nawias ostrokątny cudzysłów Nacisnieto d lewy ukośnik n cudzysłów średnik. Linia 16. break średnik. Linia 17. case apostrof apostrof dwukropek. Linia 18. std dwukropek dwukropek cout otwórz nawias ostrokątny otwórz nawias ostrokątny cudzysłów Nacisnieto spacje lewy ukośnik n cudzysłów średnik. Linia 19. break średnik. Linia 20. zamknij nawias klamrowy. Linia 21. zamknij nawias klamrowy. Linia 22. zamknij nawias klamrowy. Linia 23. zamknij nawias klamrowy średnik.

W tym przypadku ponownie zdecydowaliśmy się zostawić, przynajmniej na razie, pusty konstruktor. Funkcja getch() czyta każdy kliknięty przez nas w konsoli znak od razu, bez potrzeby potwierdzania enterem.

Funkcja ta dostępna jest w ramach pliku nagłówkowego conio.h.

Ważne!

Jeżeli korzystasz z Visual Studio, to możliwe, że otrzymujesz błąd o treści:
„'getch': The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name: _getch”.

W takim przypadku należy dopisać na samym początku kodu:

Linia 1. kratka define podkreślnik CRT podkreślnik NONSTDC podkreślnik NO podkreślnik DEPRECATE.

Lub korzystać z funkcji _getch().

Teraz sprawdźmy, czy obsługa wejścia działa – w tym celu zakomentujemy linie, które uruchamiają wątek obsługujący grafikę, oraz utworzymy obiekt klasy ObslugaWejscia, wraz z odpowiednim wątkiem.

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 waz znak równości Waz otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 6. prawy ukośnik prawy ukośnik std dwukropek dwukropek thread watekGrafiki otwórz nawias okrągły ampersant WyswietlanieGrafiki dwukropek dwukropek wyswietlGre przecinek ampersant grafika przecinek ampersant waz przecinek ampersant plansza zamknij nawias okrągły średnik. Linia 7. ObslugaWejscia wejscie średnik. Linia 8. std dwukropek dwukropek thread watekWejscia otwórz nawias okrągły ampersant ObslugaWejscia dwukropek dwukropek zinterpretujWcisnietyPrzycisk przecinek ampersant wejscie zamknij nawias okrągły średnik. Linia 9. prawy ukośnik prawy ukośnik watekGrafiki kropka join otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 10. watekWejscia kropka join otwórz nawias okrągły zamknij nawias okrągły średnik. Linia 11. zamknij nawias klamrowy.

Po skompilowaniu i użyciu klawiszy: ad, spacja oraz n wyświetlą się następujące komunikaty.

Linia 1. Nacisnieto a. Linia 2. Nacisnieto d. Linia 3. Nacisnieto spacje.

Przycisk n nie wyświetla żadnego komunikatu, ponieważ nie zdefiniowaliśmy dla tego przycisku żadnego zachowania. Kombinacja klawiszy shift + a, oraz shift + d również nie daje nam żadnego komunikatu.

Kolejne funkcje gry zaimplementujemy w następnych e‑materiałach. Stworzony do tego momentu kod można pobrać, korzystając z linku:

R1KOlX6CWYLoH

Plik zawierający kod w C++

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

Wielowątkowość i sterowanie w języku Python

Zanim zaczniemy omawiać program, przypomnimy sobie, jak uruchomić program w konsoli systemu Windows.

Już wiesz

Z racji tego, że piszemy program dla systemu Windows, otwieramy wiersz poleceń, czyli cmd.

Następnie w wierszu poleceń wpisujemy:

Linia 1. python ścieżkaBezpośredniaDoPliku lewy ukośnik plik kropka py.

Przykład:

Linia 1. python C dwukropek lewy ukośnik Users lewy ukośnik Sebastian lewy ukośnik PycharmProjects lewy ukośnik rename lewy ukośnik scratch kropka py.

Wtedy program uruchomi się z konsoli. To, jak stworzyć program, który będzie uruchamiał się jak typowy plik.exe, omówimy w kolejnych e‑materiałach. Na potrzeby testów na razie to wystarczy.
Z racji tego, że odczytujemy wszystkie klawisze, niestety nie możemy użyć kombinacji ctrl + c, żeby zamknąć proces. Dlatego za każdym testem programu włączamy go ponownie. Aby przyspieszyć proces, utwórz plik .bat z następującą treścią:

Linia 1. start python asterysk scieżka asterysk.

Np.:

Linia 1. start python C dwukropek lewy ukośnik Users lewy ukośnik Sebastian lewy ukośnik PycharmProjects lewy ukośnik rename lewy ukośnik scratch kropka py.

Wtedy będziemy mogli na swoim komputerze, na którym zainstalowany jest Python, bezproblemowo uruchamiać pisany przez nas skrypt do testów w konsoli systemu Windows. Jak stworzyć plik .exe, omówimy w kolejnych e‑materiałach.

Następnie, tak jak w przypadku dwóch poprzednich języków, zaczniemy od stworzenia wszystkich klas, zgodnie z diagramem klas.

Linia 1. class Waz dwukropek. Linia 2. liczbaPunktow znak równości 0. Linia 3. pozycjaWezaX znak równości 0. Linia 4. pozycjaWezaY znak równości 0. Linia 5. kierunekRuchu znak równości 0. Linia 6. def podkreślnik podkreślnik init podkreślnik podkreślnik otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 7. self kropka liczbaPunktow znak równości 0. Linia 8. self kropka pozycjaWezaX znak równości 0. Linia 9. self kropka pozycjaWezaY znak równości 0. Linia 10. self kropka kierunekRuchu znak równości 0. Linia 12. class Owoc dwukropek. Linia 13. liczbaPunktow znak równości 0. Linia 14. def podkreślnik podkreślnik init podkreślnik podkreślnik otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 15. self kropka liczbaPunktow znak równości 0. Linia 16. def sprawdzPunkty otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 17. return. Linia 18. def ustawPunkty otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 19. return. Linia 21. class Plansza dwukropek. Linia 22. rozmiarXPlanszy znak równości 0. Linia 23. rozmiarYPlanszy znak równości 0. Linia 24. pole znak równości None. Linia 25. def podkreślnik podkreślnik init podkreślnik podkreślnik otwórz nawias okrągły self przecinek rozmiarXPlanszy przecinek rozmiarYPlanszy zamknij nawias okrągły dwukropek. Linia 26. self kropka rozmiarXPlanszy znak równości rozmiarXPlanszy. Linia 27. self kropka rozmiarYPlanszy znak równości rozmiarYPlanszy. Linia 28. kratka self kropka pole znak równości kratka tu musimy dopisac tworzenie sie samej dwuwymiarowej planszy. Linia 29. return. Linia 30. def losujPlansze otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 31. return. Linia 32. def zresetujPlansze otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 33. return. Linia 34. def zaaktualizujOgonWeza otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 35. return. Linia 37. class Pole dwukropek. Linia 38. owoc znak równości None. Linia 39. wartoscPola znak równości 0. Linia 40. def podkreślnik podkreślnik init podkreślnik podkreślnik otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 41. self kropka owoc znak równości None. Linia 42. self kropka wartoscPola znak równości 0. Linia 44. class WyswietlanieGrafiki dwukropek. Linia 45. def wyswietlGre otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 46. return. Linia 48. class ObslugaWejscia dwukropek. Linia 49. def zinterpretujWcisnietyPrzycisk otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 50. return.

W kolejnym kroku stworzymy obsługę wejścia – z racji tego, że tworzymy program na system Windows, to skorzystamy z biblioteki: msvcrt

Linia 1. class ObslugaWejscia dwukropek. Linia 2. def zinterpretujWcisnietyPrzycisk otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 3. import msvcrt. Linia 4. while 1 dwukropek. Linia 5. 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 6. print otwórz nawias okrągły x zamknij nawias okrągły.

Przedstawiony fragment kodu w nieskończonej pętli wyświetla na wyjście to, co dostanie na wejściu. Metoda getch() zapewnia nam odczyt wejścia bajt po bajcie. Metoda decode() dekoduje wejście – a podany argument sprawia, że uznajemy znaki za takie, które występują w ASCII. Na razie zostawimy to w tej formie. Żeby przetestować, czy działa to u ciebie, spróbuj wywołać następujący kod:

Linia 1. x znak równości ObslugaWejscia otwórz nawias okrągły zamknij nawias okrągły. Linia 2. x kropka zinterpretujWcisnietyPrzycisk otwórz nawias okrągły zamknij nawias okrągły.

Spróbujmy teraz stworzyć i wyświetlić planszę.

Linia 1. class Plansza dwukropek. Linia 2. rozmiarXPlanszy znak równości 0 średnik. Linia 3. rozmiarYPlanszy znak równości 0 średnik. Linia 4. pole znak równości None średnik. Linia 5. def podkreślnik podkreślnik init podkreślnik podkreślnik otwórz nawias okrągły self przecinek rozmiarXPlanszy przecinek rozmiarYPlanszy zamknij nawias okrągły dwukropek. Linia 6. self kropka rozmiarXPlanszy znak równości rozmiarXPlanszy. Linia 7. self kropka rozmiarYPlanszy znak równości rozmiarYPlanszy. Linia 8. self kropka pole znak równości otwórz nawias kwadratowy otwórz nawias kwadratowy Pole otwórz nawias okrągły zamknij nawias okrągły for x in range otwórz nawias okrągły rozmiarXPlanszy zamknij nawias okrągły zamknij nawias kwadratowy for y in range otwórz nawias okrągły rozmiarYPlanszy zamknij nawias okrągły zamknij nawias kwadratowy. Linia 9. return. Linia 10. def losujPlansze otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 11. return. Linia 12. def zresetujPlansze otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 13. return. Linia 14. def zaaktualizujOgonWeza otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 15. return.

W tym fragmencie kodu, w konstruktorze klasy Plansza, umieściliśmy tworzenie dwuwymiarowej listy obiektów klasy Pole.

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 zamknij nawias okrągły dwukropek. Linia 3. for i in range otwórz nawias okrągły plansza kropka rozmiarYPlanszy zamknij nawias okrągły dwukropek. Linia 4. for j in range otwórz nawias okrągły plansza kropka rozmiarXPlanszy zamknij nawias okrągły dwukropek. Linia 5. print otwórz nawias okrągły plansza kropka pole otwórz nawias kwadratowy j zamknij nawias kwadratowy otwórz nawias kwadratowy i zamknij nawias kwadratowy kropka wartoscPola przecinek end znak równości apostrof lewy ukośnik t apostrof zamknij nawias okrągły. Linia 6. print otwórz nawias okrągły cudzysłów lewy ukośnik n cudzysłów zamknij nawias okrągły.

Następnie umieściliśmy wypisywanie wszystkich linii, umieszczonych w klasie plansza.

Przykładowe wywołanie:

Linia 1. plansza znak równości Plansza otwórz nawias okrągły 5 przecinek 5 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. wyswietlanieGrafiki kropka wyswietlGre otwórz nawias okrągły plansza zamknij nawias okrągły.

Wynik ostatniego fragmentu kodu:

Linia 1. 0 0 0 0 0. Linia 3. 0 0 0 0 0. Linia 5. 0 0 0 0 0. Linia 7. 0 0 0 0 0. Linia 9. 0 0 0 0 0.

Na razie robimy to tylko raz, potem zmienimy w taki sposób, aby się odświeżało.

Nie możemy jednak jednocześnie odczytywać wejścia i rysować planszy, ponieważ obie czynności są w praktyce nieskończone. Skorzystamy w tym przypadków z omówionych we wcześniejszych e‑materiałach wątków.

Możemy zrobić to na dwa sposoby – albo wywoływać metody klas w ramach wątków, albo odziedziczyć wątki w ramach poszczególnych klas.

My skorzystamy z pierwszej opcji.

Poprawne uruchomienie wątku wejścia wyglądałoby w następujący sposób:

Linia 1. obslugaWejscia znak równości ObslugaWejscia otwórz nawias okrągły zamknij nawias okrągły. Linia 2. 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 zamknij nawias okrągły zamknij nawias okrągły. Linia 3. watekWejscia kropka start otwórz nawias okrągły zamknij nawias okrągły. Linia 4. watekWejscia kropka join otwórz nawias okrągły zamknij nawias okrągły.
Ważne!

Do użycia klasy threading może być konieczne jej zaimportowanie.

Wskazujemy w nim target jako metodę danej klasy, a następnie – jako argument self– obiekt tej klasy.
Potem uruchamiany wątek i robimy join(), żeby program się nie zakończył przed końcem uruchomionego wcześniej wątku.

Analogicznie robimy z wątkiem grafiki:

Linia 1. plansza znak równości Plansza otwórz nawias okrągły 5 przecinek 5 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. wyswietlanieGrafiki kropka wyswietlGre otwórz nawias okrągły plansza zamknij nawias okrągły. Linia 4. 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 zamknij nawias okrągły zamknij nawias okrągły. Linia 5. watekGrafiki kropka start otwórz nawias okrągły zamknij nawias okrągły. Linia 6. watekGrafiki kropka join otwórz nawias okrągły zamknij nawias okrągły.

Po stworzeniu obu wątków wywołania wyglądałyby następująco:

Linia 1. plansza znak równości Plansza otwórz nawias okrągły 5 przecinek 5 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. 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 zamknij nawias okrągły zamknij nawias okrągły. Linia 4. obslugaWejscia znak równości ObslugaWejscia otwórz nawias okrągły zamknij nawias okrągły. Linia 5. 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 zamknij nawias okrągły zamknij nawias okrągły. Linia 6. watekWejscia kropka start otwórz nawias okrągły zamknij nawias okrągły. Linia 7. watekGrafiki kropka start otwórz nawias okrągły zamknij nawias okrągły. Linia 8. watekWejscia kropka join otwórz nawias okrągły zamknij nawias okrągły. Linia 9. watekGrafiki kropka join otwórz nawias okrągły zamknij nawias okrągły.

Poprawiamy wyświetlanie grafiki, żeby ekran się odświeżał.

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 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. print otwórz nawias okrągły plansza kropka pole otwórz nawias kwadratowy j zamknij nawias kwadratowy otwórz nawias kwadratowy i zamknij nawias kwadratowy kropka wartoscPola przecinek end znak równości apostrof lewy ukośnik t apostrof zamknij nawias okrągły. Linia 10. print otwórz nawias okrągły cudzysłów lewy ukośnik n cudzysłów zamknij nawias okrągły. Linia 11. time kropka sleep otwórz nawias okrągły 1 zamknij nawias okrągły.

Cały poprzedni kod umieściliśmy w nieskończonej pętli oraz zaimportowaliśmy jeszcze klasę time, dzięki której spowolniliśmy odświeżanie ekranu, żeby mniej obciążać komputer. Zrobiliśmy tak, ponieważ – w przypadku tej gry – niepotrzebne jest odświeżanie częściej niż co sekundę.

Polecenie os.system() powoduje wysłanie polecenia do konsoli. Polecenie cls powoduje wyczyszczenie okna konsoli.

Na koniec tego e‑materiału poprawmy jeszcze obsługę wejścia, żeby powoli zaczynała interpretować kliknięcia odpowiednich klawiszy. Chwilowo zakomentujemy również działanie wątku generującego grafikę.

Linia 1. class ObslugaWejscia otwórz nawias okrągły zamknij nawias okrągły dwukropek. Linia 2. def zinterpretujWcisnietyPrzycisk otwórz nawias okrągły self zamknij nawias okrągły dwukropek. Linia 3. import msvcrt. Linia 4. while 1 dwukropek. Linia 5. 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 6. if x znak równości znak równości apostrof a apostrof dwukropek. Linia 7. print otwórz nawias okrągły cudzysłów Naciśnięto a cudzysłów zamknij nawias okrągły. Linia 8. elif x znak równości znak równości apostrof d apostrof dwukropek. Linia 9. print otwórz nawias okrągły cudzysłów Naciśnięto d cudzysłów zamknij nawias okrągły. Linia 10. elif x znak równości znak równości apostrof apostrof dwukropek. Linia 11. print otwórz nawias okrągły cudzysłów Naciśnięto spacje cudzysłów zamknij nawias okrągły.

Przedstawiliśmy edycję klasy, teraz zapiszmy przykładowe wywołanie wątku:

Linia 1. plansza znak równości Plansza otwórz nawias okrągły 5 przecinek 5 zamknij nawias okrągły. Linia 2. wyswietlanieGrafiki znak równości WyswietlanieGrafiki. Linia 3. kratka 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 zamknij nawias okrągły zamknij nawias okrągły. Linia 4. obslugaWejscia znak równości ObslugaWejscia. Linia 5. 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 zamknij nawias okrągły zamknij nawias okrągły. Linia 6. kratka watekGrafiki kropka start otwórz nawias okrągły zamknij nawias okrągły. Linia 7. watekWejscia kropka start otwórz nawias okrągły zamknij nawias okrągły. Linia 8. watekWejscia kropka join otwórz nawias okrągły zamknij nawias okrągły. Linia 9. kratka watekGrafiki kropka join otwórz nawias okrągły zamknij nawias okrągły.

Po użyciu kombinacji klawiszy a, d, spacja, a + shift, d + shift wyświetlą się w konsoli następujące komunikaty:

Linia 1. Naciśnięto a. Linia 2. Naciśnięto d. Linia 3. Naciśnięto spacje.

Stąd wniosek, że program odróżnia wielkie litery od małych znaków.

Kolejne mechaniki rozgrywki zaimplementujemy w następnych e‑materiałach.

Korzystając z zamieszczonego linku, możesz pobrać końcową wersję kodu źródłowego Python dla tego e‑materiału.

RcCUeDPirLcM4

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

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

Słownik

case sensitive
case sensitive

wrażliwy na wielkość liter, tj. odróżniający wielkość liter; jeśli coś jest case sensitive, to wtedy „aBc” != „ABC”; jeśli coś nie jest case sensitive, to wtedy „AbC” == „ABC”

grafika komputerowa
grafika komputerowa

dział informatyki zajmujący się generowaniem oraz przetwarzaniem obrazów

wątek
wątek

część programu (sekwencja instrukcji), która jest działa w ramach danego procesu; w jednym procesie może działać wiele wątków