Piętnastka - algorytm


Podrozdziały:

 

obrazek

Algorytm gry otrzymamy stosując metodę zstępującą. Polega ona na podzieleniu większego zadania na mniejsze. Zaletą jest to, iż nie musimy rozważać całego problemu, lecz jego poszczególne fragmenty, które są już dużo prostsze i łatwiejsze do ogarnięcia umysłem.

Algorytm wykonuje się w pętli. Pierwsza operacja wyświetla odpowiednią stronę tytułową. Opisywaliśmy tę procedurę wielokrotnie w poprzednich programach, zatem teraz zrezygnujemy z opisu.

Kolejna procedura ma za zadanie wygenerować planszę gry i odpowiednio pomieszać znajdujące się na niej segmenty.

Teraz rozpoczynamy pętlę wewnętrzną, która obsługuje ruchy gracza. Najpierw wykonujemy ruch wybranego przez gracza segmentu. Następnie sprawdzamy, czy segmenty są już ułożone w odpowiedniej kolejności. Jeśli tak, wypisujemy odpowiedni napis z gratulacjami i kończymy pętlę wewnętrzną. W przeciwnym razie pętla jest kontynuowana.

Po zakończeniu pętli wewnętrznej sprawdzamy, czy gracz ma ochotę zakończyć grę. Jeśli nie, powracamy na początek pętli głównej. W przeciwnym razie kończymy.

Generowanie nowej planszy gry

Symbol Przeznaczenie w algorytmie
plansza zmienna łańcuchowa przechowująca segmenty
cplansza szablon planszy
i zmienna licznikowa pętli
t zmienna pomocnicza
obrazek

W procedurze tworzenia planszy dla gry ponownie skorzystamy z metody zstępującej i rozbijemy ją na prostsze elementy. Najpierw zmienną plansza ustawimy zgodnie z szablonem cplansza, który zawiera segmenty poukładane na prawidłowych pozycjach. Plansza jest zatem ułożona.

Teraz w 1000 obiegach pętli iteracyjnej sterowanej przez zmienną i zaburzymy ten porządek. W każdym obiegu losujemy liczbę pseudolosową z zakresu od 0 do 3 i w zależności od tego, co wypadnie, przesuwamy jeden z segmentów planszy odpowiednio w jednym z czterech kierunków. Operację tę realizują właściwe procedury. 1000 obiegów pętli gwarantuje nam, iż plansza zostanie dobrze wymieszana.

Po zakończeniu tej pętli wyświetlamy planszę w okienku konsoli - gra jest gotowa na ruchy gracza.

Wyświetlanie planszy gry

Symbol Przeznaczenie w algorytmie
i,j zmienne sterujące pętli
planszai segment planszy na i-tej pozycji w zmiennej plansza.
cplanszai i-ty segment szablonu planszy
k kolumna, w której rozpoczyna się wydruk segmentu
w wiersz, w którym rozpoczyna się wydruk segmentu
obrazek

Zadaniem tej procedury jest wyświetlenie planszy z segmentami. Poszczególne segmenty wyświetlamy kolejno w pętli iteracyjnej sterowanej zmienną i, która przebiega wartości od 1 do 16. Segmenty do wyświetlenia są pobierane ze zmiennej plansza. Jeśli i-ty segment jest segmentem pustym (tzn. jest to znak spacji), ustawiamy kolor wydruku czarny. W przeciwnym razie sprawdzamy, czy segment i-ty w zmiennej plansza jest na swoim właściwym miejscu. Dokonujemy tego porównując go z i-tym elementem wzorca ułożonej planszy cplansza. Jeśli tak, ustawiamy tło zielone i tusz biały. W przeciwnym wypadku kolor tła staje się żółty a tuszu czerwony. Cała ta operacja ma na celu ustalenie kolorów dla wydruku segmentu.

Teraz obliczamy położenie segmentu w obszarze okna konsoli. W zmiennej k będziemy mieli początkową kolumnę, a w zmiennej w początkowy wiersz segmentu.

Pętla wewnętrzna sterowana zmienną j ma za zadanie umieścić segment w odpowiednim obszarze okna konsoli. W każdym jej obiegu ustawiamy odpowiednią pozycję wydruku. Pierwszy i trzeci wydruk składa się z 3 spacji. Środkowy, drugi wydruk składa się z początkowej spacji, literki segmentu, którą określa element planszai oraz spacji zamykającej, aby powstał mniej więcej kwadrat. Po zakończeniu tej pętli zwiększamy licznik pętli głównej i przechodzimy do wydruku kolejnego segmentu układanki.

Gdy pętla główna się zakończy, wokół planszy rysujemy ramkę i kończymy procedurę.

Ruchy na planszy

Symbol Przeznaczenie w algorytmie
p położenie pustego miejsca (spacji) w zmiennej plansza
x,y współrzędne pustego miejsca na planszy gry
planszap element zmiennej plansza zawierający puste miejsce
obrazek

Zadaniem tych czterech procedur jest przesunięcie segmentu przyległego do pustego miejsca w zadanym kierunku. Wszystkie działają wg tego samego schematu:

Najpierw znajdujemy położenie pustego segmentu w zmiennej plansza. Wynik trafia do p. Dana ta jest nam potrzebna do przemieszczenia na to miejsce sąsiedniego segmentu. Ponieważ pusty segment zawsze kodowany jest spacją, to zadanie w prosty sposób zrealizujemy stosując standardową funkcję Pascala Pos:


Pos(tekst_do_znalezienia,tekst_do_przeszukania)


Pierwszy parametr funkcji Pos określa poszukiwany tekst. U nas będzie to po prostu spacja. Drugi parametr określa zmienną łańcuchową, która ma zostać przeszukana. Wynikiem działania tej funkcji jest położenie tekstu_do_znalezienia  w obrębie tekstu_do_przeszukania. Jeśli pozycja ta nie zostanie znaleziona, to funkcja zwraca wartość 0. Oczywiście w naszym przypadku sytuacja taka nie wystąpi, ponieważ spacja zawsze będzie się znajdowała w zmiennej plansza. Zatem odpowiednie polecenie przyjmie postać:


p := Pos(' ',plansza);  // wyszukanie pozycji pustego elementu


Mając p obliczamy pozostałe współrzędne x i y wg podanych wcześniej wzorów. Współrzędne te będą potrzebne do określenia wykonywalności operacji przesunięcia segmentu. Jeśli segment da się przesunąć w odpowiednim kierunku, to element reprezentujący go w zmiennej plansza przemieszczamy na puste miejsce, a w miejsce elementu umieszczamy spację.

Obsługa ruchów gracza

obrazek

W procedurze obsługi ruchów gracza korzystamy intensywnie z już utworzonych procedur przesuwających segmenty w zadanym kierunku oraz z procedury wyświetlającej zawartość planszy. Dzięki temu algorytm ulegnie znacznemu uproszczeniu.

Najpierw odczytujemy klawisz kursora. Następnie analizujemy jego kod i w zależności od wybranego przez gracza kierunku ruchu dokonujemy przesunięcia segmentów. Procedury przesuwające troszczą się o wykonalność tej operacji.

Po przesunięciu segmentu wyświetlamy planszę i kończymy procedurę.

UWAGA.

Przy odczytywaniu klawisza kursora musimy być przygotowani na to, iż kod należy odczytywać dwukrotnie. Za pierwszym razem funkcja ReadKey zwraca kod #0, co jest sygnałem, iż naciśnięto klawisz specjalny spoza zbioru znaków ASCII. Drugi odczyt daje nam informację, jaki to był klawisz. Zatem sekwencja odczytu klawisza kursora powinna być następująca:

repeat
  klawisz := ReadKey;
until klawisz <> #0

 

Pętla jest wykonywana maksymalnie dwa razy. Jeśli przy pierwszym obiegu będzie odczytany kod #0, to drugi obieg zwróci kod matrycowy klawisza funkcyjnego. Co prawda, jeśli gracz naciśnie klawisz inny od funkcyjnego, to pętla zwróci nam po prostu kod ASCII klawisza zamiast oczekiwanego kodu matrycowego. Czy jest to błąd? Nie. Po prostu nasz program będzie reagował w ten sam sposób na klawisze kursora jak i na niektóre klawisze znakowe. Pozostawiam wam odkrycie, jakie będą to klawisze. A może zmodyfikujecie metodę odczytu klawiatury tak, aby reagowała tylko i wyłącznie na klawisze kursora.


 

Sprawdzanie ułożenia segmentów

Sprawdzenie ułożenia segmentów jest bardzo proste. Wystarczy porównać zmienną plansza ze wzorcem cplansza. Jeśli będą identyczne, to gracz ułożył segmenty we właściwej kolejności. W takim przypadku wyświetlamy gratulacje i zwracamy true, co spowoduje przerwanie pętli gry i przejście do zakończenia.

 


   I Liceum Ogólnokształcące   
im. Kazimierza Brodzińskiego
w Tarnowie

©2021 mgr Jerzy Wałaszek

Dokument ten rozpowszechniany jest zgodnie z zasadami licencji
GNU Free Documentation License.

Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl

W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe