ChessMemory 2.0 – napiszmy prostą grę z wykorzystaniem JavaScript i SVG (część 3)


Wakacje, jak zawsze, skończyły się szybciej, niż byśmy chcieli. A to znaczy, że niebawem rozpocznie się druga edycja studiów podyplomowych (http://eti.pg.edu.pl/katedra-architektury-systemow-komputerowych/studia-podyplomowe/) organizowanych przy współudziale Ateny. Zanim to nastąpi, postanowiłem uzupełnić grę ChessMemory o dodatkową funkcjonalność, o której wspomniałem w poprzednim artykule (http://blog.atena.pl/chessmemory-napiszmy-prosta-gre-z-wykorzystaniem-javascript-i-svg-czesc-2). Zapraszam do lektury kolejnej części tutoriala, w którym piszemy grę planszową z wykorzystaniem języka JavaScript oraz grafiki w formacie SVG.

Co nowego?

Tym razem zrealizujemy pomysł rozwinięcia gry w taki sposób, aby łatwiej można było  ręcznie umieszczać figury na szachownicy w prawidłowym ustawieniu. Okazuje się bowiem, że odnajdywanie właściwego ułożenia figur samo w sobie jest ciekawą łamigłówką, a po dołożeniu stopera i liczników (prawie identycznych jak w poprzednim artykule), można to robić na czas. Przypomnijmy sobie zasady prawidłowego rozstawienia figur w grze ChessMemory.

  • Każda figura występuje dokładnie po osiem razy, w dwóch różnych kolorach (białym i czarnym), po cztery pary na kolor. Razem mamy 64 figury rozstawione na 64 polach szachownicy.
  • Każda figura może mieć tylko jedną figurę do pary – musi być taka sama, mieć ten sam kolor oraz stać w „zasięgu” bicia swojej pary. Jeśli figura nie ma żadnej pary, lub ma więcej niż jedną, to jest to błąd.

Tym razem zmieniła się też zawartość pliku SVG, w którym mamy osiem nowych pól – w dalszej części artykułu będziemy je nazywać nagłówkami. Służą one do zaznaczania aktywnej figury, czyli tej, którą aktualnie zamierzamy postawić na szachownicy (lub z niej zdjąć). Każdy nagłówek dodatkowo wyposażony został w mały licznik, który wyświetla liczbę pozostałych figur do ustawienia. Po wykorzystaniu wszystkich figur, liczniki w nagłówkach powinny wskazywać zero.

Nie będę opisywać szczegółów związanych z edycją pliku SVG w programie Inkscape, wspomnę tylko, że w pierwszej części tutoriala (http://blog.atena.pl/chessmemory-napiszmy-prosta-gre-z-wykorzystaniem-javascript-i-svg-czesc-1) dokładnie przedstawiłem sposób utworzenia szachownicy. Dla osób korzystających z tego opisu dodanie ośmiu nagłówków nie powinno stanowić większego problemu. Należy tylko zwrócić uwagę na nazwy elementów, do których będziemy się odwoływać z poziomu języka JavaScript. W naszym przypadku są to trzy elementy w każdym nagłówku, a ich nazwy powstały według następującego klucza:

  • ‘count’ + [nazwa figury] (np.: countH, counts) – obiekt tekstowy zawierający licznik nagłówka;
  • ‘click’ + [nazwa figury] (np.: clickw, clickG) – obiekt typu rectangle, który obsługuje zdarzenie onClick na nagłówku (musi „leżeć” na samym wierzchu);
  • ‘back’ + [nazwa figury] (np.: backS, backW) – obiekt typu rectangle, który służy do podświetlania zaznaczonego nagłówka (musi “leżeć” pod obiektem wyświetlającym daną figurę, co da efekt podświetlenia tła).

Nowa szachownica z nagłówkami wygląda następująco:

Jak widać, do wyświetlenia nowego pliku SVG użyto pliku index.html napisanego w poprzedniej części. Pozostały w nim też takie pola, jak: licznik kliknięć, licznik figur oraz stoper. Dodatkowo na dole strony pojawią się też dwa nowe pola (dBrawo oraz dWynik), w których będziemy wyświetlać końcowy rezultat. Aktualnie plik ten wygląda następująco:

Oczywiście w powyższym listingu zawartość węzła SVG została pominięta ze względu na rozmiar. Kompletny plik index.html jest jednak dostępny na końcu tego artykułu.

Inicjacja gry

Konstrukcja programu głównego jest podobna do poprzedniej wersji, jednak w praktyce można je uważać za dwie niezależne od siebie gry. Dlatego też obsługa głównych zdarzeń, np. kliknięcia na polu szachownicy, lub też samo uruchomienie gry, będą wyglądać nieco inaczej. Nie zmieniamy natomiast pliku bicia.js, zawierającego wszystkie możliwe pola bite przez każdą z czterech figur, w zależności od tego, na którym polu ją postawiono. Główny plik z programem obsługującym grę znajduje się w pliku app.js, który na początku wygląda tak:

Konstruktory obiektów komorka oraz stoper w całości skopiowano z poprzedniego artykułu. Natomiast głównym obiektem programu jest gra, który tworzony jest w pliku index.html poprzez wywołanie funkcji Gra(). Tym razem jej zawartość będzie nieco inna, chociaż pewne fragmenty się powtórzą – np. obsługa liczników kliknięć i figur oraz utworzenie stopera. W funkcji init() również znajdziemy fragment kodu podobny do poprzedniej wersji, a mianowicie pętlę w pętli. W taki sposób inicjalnie wiążemy wszystkie interesujące nas elementy szachownicy z listą obiektów typu komorka. Licznik figur natomiast został nieznacznie zmodyfikowany poprzez dołożenie parametru ile do funkcji zwiekszFigury(ile). Dzięki temu będzie można zmieniać licznik o wartość argumentu przekazanego do funkcji. To potrzebne, ponieważ w tej grze figury będzie można nie tylko dodawać, czyli stawiać na szachownicy, ale i je zdejmować. Licznik figur powinien więc również się zmniejszać.

Po uruchomieniu programu w takim kształcie na razie jeszcze nic się nie dzieje, poza tym, że pierwsze kliknięcie na dowolnym polu szachownicy uruchamia stoper. Musimy więc teraz obsłużyć przełączanie się pomiędzy różnymi figurami w nagłówkach.

Przełączanie nagłówków

Zacznijmy od dodania zmiennej oznaczającej zaznaczony nagłówek w konstruktorze obiektu gra:

Zmienna ta będzie przechowywać informację o aktualnie zaznaczonym nagłówku, czyli obowiązującej figurze. Ponadto dodajemy jeszcze jedną zmienną naglowki, tym razem lokalną (będzie dostępna tylko wewnątrz obiektu):

Zmienna ta posłuży nam jako lista wszystkich figur występujących w nagłówkach. Następnie dodajemy obsługę obiektu naglowek, tworząc jego konstruktor oraz funkcję klik(), która będzie wywoływana na zdarzeniu onClick. Jak widać, obsługa nagłówka jest bardzo podobna do obsługi komórki.

Teraz musimy powiązać zdarzenia onClick wspomnianych wcześniej elementów pliku SVG z funkcją klik() nagłówka. Wystarczy zrobić to raz, tuż po rozpoczęciu gry, więc ten fragment kodu umieszczamy w funkcji init obiektu gra. I to właśnie tutaj korzystamy z utworzonej wcześniej zmiennej naglowki:

W ostatniej linijce wywołujemy funkcję klik() dla figury „h”, czyli czarnego hetmana – będzie on domyślnie zaznaczoną figurą zaraz po uruchomieniu gry.

Ustawianie i zdejmowanie figur

Do efektu pojawiania się i znikania figury na szachownicy potrzebujemy metody pokaz_figure(widocznosc), którą dodajemy w prototypie obiektu komorka:

Logikę kliknięć, czyli algorytm odpowiedzialny za ustawianie i zdejmowanie figur z szachownicy, uzupełniamy w funkcji klik() obiektu komorka (nie mylić z funkcją klik() obiektu nagłówek):

Właśnie dodaliśmy fragment kodu odpowiadający za sprawdzenie, czy figura jest już odkryta i – w zależności od wyniku – wykonanie jednej z dwóch opcji. Albo ukrywamy tę figurę z powrotem (ale tylko pod warunkiem, że kliknięta figura jest taka sama jak w zaznaczonym nagłówku), albo odkrywamy nową figurę, ustawiając ją na pustym polu. Żeby to zrobić, musimy wykonać kilka czynności wewnątrz aktualnie zajmowanej komórki: przypisać komórce wartość figury z nagłówka, ustawić jej literę (wartość) w polu tekstowym komórki, ustawić jej kolor, a na koniec wywołać funkcję pokaz_figure(1) z parametrem określającym jej widoczność.

W tej chwili gra wygląda już całkiem przyzwoicie: można przełączać się pomiędzy nagłówkami i ustawiać poszczególne figury na szachownicy, jednak jeszcze nie wpływa to na stan liczników w nagłówkach. W powyższym fragmencie sprawdzamy już, czy możemy odkryć figurę, kiedy licznik jest niezerowy, jednak nigdzie jeszcze tego licznika nie zmieniamy, więc ciągle pokazuje on inicjalną wartość 8. W celu obsługi licznika w nagłówku musimy dodać do niego metodę zmien_licznik(widocznosc):

Trzeba ponadto dodać wywołanie tej metody. Najlepszym miejscem będzie funkcja pokaz_figure(widocznosc) w prototypie komórki. Funkcja ta jest uruchamiana za każdym razem, gdy pojawia się lub znika figura z szachownicy. O tym, czy figurę zdejmujemy czy dodajemy, mówi znany już atrybut widocznosc – z niego skorzystamy.

I tyle wystarczy. Właściwie w tym momencie gra jest już gotowa, ale chcielibyśmy do niej dołożyć jeszcze sprawdzenie, czy ustawione figury spełniają zasady gry. W tym celu umieszczamy w obiekcie gra nową zmienną lokalną o nazwie poprawna, która posłuży jako flaga mówiąca o tym, czy figury ustawione są prawidłowo czy nie.

Następnie w prototypie obiektu komorka dodajemy nową metodę pokaz_podswietlenie(widocznosc, kolor), która będzie służyć do podświetlania danej komórki bądź ukrywania tego podświetlenia. O tym, czy i jaki rodzaj podświetlenia ma się pojawić, będą informować dwa atrybuty funkcji: widocznosc oraz kolor.

Dodatkowo, w metodzie klik() obiektu komorka dodajemy fragment kodu przypisujący listę pól bicia dla danej figury i pola na szachownicy. Listę tę pobieramy z obiektu bicia, który został zdefiniowany w pliku bicia.js utworzonym w poprzednim artykule. We fragmencie kodu odpowiedzialnym za usunięcie figury z szachownicy dodajemy czyszczenie pól bicia:

Natomiast we fragmencie, który odpowiada za przypisanie figury do danej komórki szachownicy, dodajemy taką instrukcję:

Musimy tylko pamiętać, żeby ten fragment umieścić już po przypisaniu figury do zmiennej this.figura. Teraz możemy napisać funkcję sprawdz_figury(), którą umieszczamy wewnątrz obiektu gra:

Działanie funkcji polega najpierw na wyczyszczeniu całej szachownicy z podświetlenia wszystkich komórek, a następnie na podświetlaniu tych komórek, na których stoją figury, które nie posiadają dokładnie jednej pary. Jeśli dana figura nie ma żadnej pary, to taką komórkę podświetlamy na zielono. Jeżeli natomiast ma więcej niż jedną, podświetlamy ją na czerwono. W obu przypadkach podświetlenie powoduje również ustawienie flagi poprawna na -1. Z kolei do zliczenia pasujących figur wykorzystujemy przypisaną wcześniej do komórki listę pól bicia[].

Mamy funkcję sprawdzającą poprawność ustawienia figur, więc pozostało nam ją już tylko wywołać w odpowiednim miejscu. Chcemy, żeby podświetlanie odświeżało się po każdym kliknięciu, więc na końcu metody klik() w obiekcie komorka dodajemy linijkę:

Efekt prezentuje się następująco:

Zakończenie gry

Do kompletu pozostało jeszcze obsłużenie zakończenia gry, czyli zatrzymanie stopera i wyświetlenie wyniku. Wynikiem będzie napis „Brawo!”, dodany na początku tego artykułu w pliku index.html, oraz ciąg znaków wygenerowany na podstawie ustawienia figur. Taki ciąg może zostać wykorzystany jako parametr wejściowy funkcji init(), czyli do zainicjowania nowej gry w wersji z poprzedniego artykułu. Koniec gry następuje, gdy kliknięto ostatnią figurę (licznik figur wskazuje liczbę 64), a sprawdzenie ustawienia nie wykazało błędów. A zatem ostatni już raz w metodzie klik() komórki dodajemy fragment kodu:

W powyższym fragmencie kodu zatrzymujemy stoper oraz wyświetlamy wynik, który do prawidłowego działania potrzebuje jeszcze jednej funkcji – a mianowicie podaj_wynik(), którą dodajemy jako metodę do obiektu gra:

I to wystarczyło, żeby obsłużyć grę ChessMemory w wersji 2.0. Plik app.js znów jest niewielki, bo zajmuje ok. 250 linijek kodu, przy czym zdecydowanie dałoby się go znacznie zmniejszyć przez usunięcie komentarzy i pustych linii.

Ze zdziwieniem odkryłem, że granie w tę wersję jest dużo trudniejsze niż w poprzednią. Na początku oczywiście jest łatwo, ale im mniej wolnych miejsc pozostaje na szachownicy, tym trudniej znaleźć prawidłowe ustawienie dla pozostałych figur. Jednak dla chcącego nic trudnego i – jak widać na poniższym obrazku – po około 20 minutach udało mi się wybrnąć z tej łamigłówki zwycięsko.

Jeśli nie wierzycie, że ta łamigłówka wcale nie jest łatwa, to zachęcam do wypróbowania swoich sił poniżej:

Czy udało się Wam pobić mój wynik, czyli skończyć grę w mniej niż 1293 sekundy i/lub wykorzystać mniej niż 302 kliknięcia? Pochwalcie się swoim osiągnięciem w komentarzu!

Co dalej?

W poprzednim artykule wspomniałem jeszcze o jednym pomyśle, czyli generatorze losowym inicjalnego ustawienia figur. Na razie pozostawię ten temat otwarty do realizacji w ramach zajęć z podstaw programowania. Jedno mogę obiecać: kiedy tylko taki generator powstanie, na pewno go opiszę!

Linki

  1. Pierwsza część artykułu: http://blog.atena.pl/chessmemory-napiszmy-prosta-gre-z-wykorzystaniem-javascript-i-svg-czesc-1
  2. Druga część artykułu: http://blog.atena.pl/chessmemory-napiszmy-prosta-gre-z-wykorzystaniem-javascript-i-svg-czesc-2
  3. Strona kierunku „Aplikacje i usługi internetowe”: http://eti.pg.edu.pl/katedra-architektury-systemow-komputerowych/studia-podyplomowe/
  4. Pliki wykorzystane w tym artykule można pobrać stąd: index.html, app.js, bicia.js.

Jarosław Fostacz

O Jarosław Fostacz

Od 2003 roku jestem związany z firmą ATENA – początkowo jako programista, następnie projektant. Aktualnie jestem kierownikiem Zespołu ds. Rozwoju i Technologii, oraz opiekunem niniejszego Bloga Technologicznego. Moją siłą napędową jest nieustanne poszukiwanie innowacji oraz nowych technologii.


Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

6 komentarzy do “ChessMemory 2.0 – napiszmy prostą grę z wykorzystaniem JavaScript i SVG (część 3)