Tetris - algorytm


Podrozdziały:

 

obrazek

 

 

Ze względu na znaczną złożoność procedur, algorytm gry Tetris opiszemy tylko w skrócie, pozostawiając czytelnikowi sprawę dopracowania szczegółów. Jeśli samodzielnie wykonałeś poprzednie projekty gier, to z grą Tetris nie będziesz miał większych kłopotów. Jeśli nie, to cóż... programowanie nie jest sztuką dostępną dla wszystkich.

Wykorzystujemy metodę zstępującą. Rozbijamy problem na kilka powiązanych ze sobą problemów mniejszych. I tak na samym początku musimy zainicjować generator liczb pseudolosowych, ponieważ nasz program będzie z nich korzystał przy losowaniu figur. W języku Pascal służy do tego celu pojedyncza instrukcja Randomize.

Następnie wchodzimy w pętlę gry, która wykonuje się tak długo, aż gracz straci ochotę na dalszą rozgrywkę.

Wewnątrz pętli wyświetlamy stronę tytułową po czym przechodzimy do procedury, która pozwala wybrać początkowy poziom gry w zakresie od 1 (najprostszy) do 9 (najtrudniejszy). Oprócz poziomu mamy tutaj również do wyboru opcję zapełniania pola gry przypadkowo rozłożonymi kwadratami. Oczywiście możesz zrezygnować z tej procedury i wybierać zawsze poziom nr 1 bez zapełniania pola gry. Jednakże uważam, iż dzięki tej opcji gra staje się o wiele ciekawsza.

Kolejna procedura obsługuje całą rozgrywkę. Na koniec upewniamy się, czy użytkownik chce zakończyć grę. Jeśli tak, kończymy. W przeciwnym razie pętla dokonuje kolejnego obiegu.

Wybór Poziomu

Symbol Przeznaczenie w algorytmie
poziom zmienna określa startowy poziom gry. Zakres od 1 do 9
zapelnienie Ilość dolnych wierszy wypełnionych losowo kwadratami. Zakres od 0 do 15.
klawisz przechowuje kod naciśniętego klawisza.
obrazek

Procedura ma na celu ustalenie początkowego poziomu gry oraz zapełnienia planszy. Poziom gry przyjmuje wartości od 1 do 9. Poziom 1 jest najłatwiejszy, figury spadają bardzo wolno. Z kolei na poziomie 9 gracz ma bardzo mało czasu na analizę sytuacji i wykonanie poprawnych ruchów. Błędy są częstsze i trudno je naprawić. Musimy myśleć szybko i prawidłowo, w przeciwnym razie gra się niebawem zakończy.

Z kolei parametr zapełnienie pozwala na wypełnianie zadanej ilości dolnych wierszy planszy gry przypadkową zawartością. Dzięki niemu gra może stać się dużo ciekawsza - gracz musi logicznie analizować planszę oraz rozgrywane figury, aby skutecznie zlikwidować wstępne poziomy kwadratów.

Procedurę rozpoczynamy od ustalenia początkowych wartości dla poziomu oraz zapełnienia. Następnie wchodzimy do pętli sterującej edycją tych parametrów. Wewnątrz pętli wyświetlamy poziom oraz zapełnienie, odczytujemy klawisz i podejmujemy odpowiednie działania w zależności od rodzaju wciśniętego przez gracza klawisza. Procedura kończy się po wykryciu naciśnięcia klawisza Enter. 

Gra

obrazek

Procedura gry również zostanie rozwiązana metodą zstępującą. Na początku wyświetlamy planszę gry wraz całą jej zawartością. Następnie rozpoczynamy pętlę, w której obsługiwane są kolejno losowane w programie figury. Pętla ta wykonuje się dotąd, aż skończy się miejsce na planszy dla nowych figur lub gracz wciśnie klawisz [Escape].

W pętli wykonujemy tylko dwie operacje: losujemy nową figurę dla gracza i obsługujemy jej spadek za pomocą funkcji Brak Miejsca.

Po zakończeniu pętli czyścimy bufor klawiatury - jest to konieczne, ponieważ następną procedurą jest procedura sprawdzania zakończenia gry i przypadkowo naciśnięty klawisz mógłby zostać tam nieprawidłowo zinterpretowany.

Plansza

Symbol Przeznaczenie w algorytmie
nastepny określa kształt następnej figury w grze.
nastepny_kolor definiuje kolor następnej figury
wiersze zlicza usunięte z planszy pełne wiersze.
punkty zawiera punktację w grze
obrazek

Zadaniem procedury jest przygotowanie planszy dla przyszłej gry. Na samym początku inicjujemy kilka zmiennych wykorzystywanych przez Tetris.

Procedura Pole Gry przygotowuje planszę gry oraz wyświetla ją w oknie konsoli.

Procedura Opis wyświetla po lewej stronie okna konsoli krótki opis używanych w trakcie gry klawiszy sterujących.

Procedura Logo wyświetla w prawym górnym rogu okna konsoli nazwę gry oraz informację o autorze.

Procedura Następna Figura na podstawie zmiennej nastepny oraz nastepny_kolor wyświetla z prawej strony okna konsoli następną figurę w grze.

Procedura Poziom i Punkty wyświetla punktację zdobytą przez gracza w grze Tetris. Całość powinna wyglądać jak poniżej:

Opis

Pole Gry Logo
obrazek

Poziom i Punkty                

Z podanych procedur opiszemy jedynie procedury Pole Gry i Następna Figura. Pozostałe są dosyć proste i czytelnik może popisać się swoją własną inwencją przy ich tworzeniu.

 

Pole Gry

Symbol Przeznaczenie w algorytmie
i,j zmienne dla liczników pętli
pole_gry[] tablica 24 łańcuchów znakowych, każdy o szerokości 14 znaków.
zapelnienie zmienna kontroluje początkowe wypełnienie dolnych wierszy pola gry
obrazek

Pierwsza pętla służy do zainicjowania zmiennej pole_gry[ ]. Po jej wykonaniu pierwsze 22 wiersze zawierają pole gry z dwuznakową barierką X z każdej strony. Pozostałe wiersze nr 23 i 24 są w całości wypełnione znakami X:

pole_gry[1]  := 'XX          XX';
pole_gry[2]  := 'XX          XX';
pole_gry[3]  := 'XX          XX';
pole_gry[4]  := 'XX          XX';
pole_gry[5]  := 'XX          XX';
pole_gry[6]  := 'XX          XX';
pole_gry[7]  := 'XX          XX';
pole_gry[8]  := 'XX          XX';
pole_gry[9]  := 'XX          XX';
pole_gry[10] := 'XX          XX';
pole_gry[11] := 'XX          XX';
pole_gry[12] := 'XX          XX';
pole_gry[13] := 'XX          XX';
pole_gry[14] := 'XX          XX';
pole_gry[15] := 'XX          XX';
pole_gry[16] := 'XX          XX';
pole_gry[17] := 'XX          XX';
pole_gry[18] := 'XX          XX';
pole_gry[19] := 'XX          XX';
pole_gry[20] := 'XX          XX';
pole_gry[21] := 'XX          XX';
pole_gry[22] := 'XX          XX';
pole_gry[23] := 'XXXXXXXXXXXXXX';
pole_gry[24] := 'XXXXXXXXXXXXXX';

 

W dalszej kolejności przystępujemy do wyświetlenia pola gry w oknie konsoli. Najpierw czyścimy ekran i rysujemy ramkę otaczającą nasze pole gry. Wewnątrz ramki drukujemy kolejne wiersze pola gry. Dla wierszy pustych będzie to wypisanie 10 spacji na białym kolorze tła. Dla wierszy wstępnie zapełnionych wchodzimy w pętlę wewnętrzną, w której dla kolejnych pozycji wiersza losujemy kwadrat lub jego brak. Jeśli wylosujemy kwadrat, wyświetlamy go w oknie konsoli wpisując na odpowiednią pozycję zmiennej pole_gry[] literkę X. Jeśli wylosujemy brak, do zmiennej pole_gry nic nie wpisujemy, a w okienku konsoli drukujemy spację na białym kolorze tła.



Po wykonaniu procedury w oknie konsoli widnieje pole gry odpowiednio wypełnione, a zmienna pole_gry[] zostaje odpowiednio ustawiona. Np:

 

Widok pola gry Zawartość zmiennej pole_gry[ ]

                    
                    
                    
                    
                    
                    
                    
                    
                    
                    
  __        __      
          __        
      ____      __  
      __            
  __    ____    __  
    __    __  __    
            __      
  __  __    __      
      __      __    
  __    ____      __
                    
  __    __    __  __

pole_gry[1]  := 'XX          XX';
pole_gry[2]  := 'XX          XX';
pole_gry[3]  := 'XX          XX';
pole_gry[4]  := 'XX          XX';
pole_gry[5]  := 'XX          XX';
pole_gry[6]  := 'XX          XX';
pole_gry[7]  := 'XX          XX';
pole_gry[8]  := 'XX          XX';
pole_gry[9]  := 'XX          XX';
pole_gry[10] := 'XX          XX';
pole_gry[11] := 'XX X    X   XX';
pole_gry[12] := 'XX     X    XX';
pole_gry[13] := 'XX   XX   X XX';
pole_gry[14] := 'XX   X      XX';
pole_gry[15] := 'XX X  XX  X XX';
pole_gry[16] := 'XX  X  X X  XX';
pole_gry[17] := 'XX      X   XX';
pole_gry[18] := 'XX X X  X   XX';
pole_gry[19] := 'XX   X   X  XX';
pole_gry[20] := 'XX X  XX   XXX';
pole_gry[21] := 'XX          XX';
pole_gry[22] := 'XX X  X  X XXX';
pole_gry[23] := 'XXXXXXXXXXXXXX';
pole_gry[24] := 'XXXXXXXXXXXXXX';

 

Następna Figura

Symbol Przeznaczenie w algorytmie
i,j zmienne dla liczników pętli
ksztalt zmienna łańcuchowa o długości 4 znaków zawierająca jeden wiersz figury
zestaw tablica definiująca kształty figur
nastepny zawiera numer następnej figury, która będzie użyta w grze
nastepny_kolor zawiera kod koloru dla następnej figury
obrazek

Zadaniem procedury jest wyświetlenie obok pola gry kształtu następnej figury w grze. Kształt figury pobieramy z tablicy definicji figur zestaw[ ]. Wszystkie znaki 'X' wyświetlamy jako '_' w odpowiednim kolorze, spacje pozostawiamy bez zmian.

Całość realizowana jest w dwóch zagnieżdżonych pętlach. Pętla sterowana zmienną i pobiera z tablicy zestaw[ ] kolejne wiersze kształtu figury i umieszcza je w zmiennej ksztalt. Następnie pętla wewnętrzna sterowana zmienną j przegląda znak po znaku pobrany wiersz i napotykając literkę 'X' wyświetla podkreślony kwadracik w polu następnej figury o kolorze zdefiniowanym przez zawartość zmiennej nastepny_kolor.

Zwróć uwagę, iż pozycja wydruku ustawiana jest zawsze przed wejściem do pętli wewnętrznej. W pętli tej wyświetlamy po prostu obok siebie znak za znakiem, aż zostanie zanalizowany cały wiersz ze zmiennej ksztalt.

Po wykonaniu pętli zewnętrznej kształt następnej figury pojawia się w oknie konsoli i procedurę kończymy.

Losowanie figury

Symbol Przeznaczenie w algorytmie
obecny zawiera numer obecnej figury w grze (1..7)
nastepny zawiera numer następnej figury w grze (1..7)
obecny_kolor kolor obecnej figury - używany jest tylko kolor tła, kolor tuszu zawsze 0.
nastepny_kolor kolor następnej figury
obecny_x,obecny_y współrzędne lewego górnego narożnika obecnej figury
obecny_obrot zawiera numer obrotu figury od 1 do 4
obrazek

Losowanie figury jest bardzo proste. Na początku przepisujemy dane ze zmiennych nastepny i nastepny_kolor odpowiednio do obecny i obecny_kolor. W dalszej kolejności losujemy następną figurę oraz jej kolor. Wyświetlamy następną figurę i ustalamy współrzędne nowej figury na polu gry oraz jej początkowe położenie w zmiennych obecny_x, obecny_y i obecny_obrot.

Brak Miejsca

Symbol Przeznaczenie w algorytmie
obecny zawiera numer obecnej figury w grze (1..7)
obecny_x,obecny_y współrzędne lewego górnego narożnika obecnej figury
obecny_obrot zawiera numer obrotu figury od 1 do 4
nastepny zawiera numer następnej figury w grze (1..7)
lc, li liczniki obiegów pętli
poziom numer aktualnego poziomu gry od 1 do 9
punkty bieżąca punktacja
obrazek

Funkcja obsługuje cały ruch figury od momentu jej pojawienia się na planszy do zatrzymania się na spodzie. Na samym początku sprawdzamy, czy nowa figura może zostać wprowadzona na planszę gry. Jeśli nie, pokazujemy ją i kończymy rozgrywkę zwracając wartość logiczną true. Wartość ta spowoduje zakończenie pętli głównej gry.

Jeśli figura mieści się na planszy, inicjujemy liczniki opóźnień. Licznik lc zawiera wyliczoną wartość opóźnienia, natomiast licznik li zlicza wstecz od wartości lc do 0. Gdy li osiągnie 0, figura jest przemieszczana o 1 wiersz w dół i o ile nie osiągnęła dna planszy gry, cykl się powtarza. Zwróćcie uwagę, iż opóźnienie lc zależne jest od aktualnego poziomu gry. Gdy poziom jest niski, lc zawiera dużą wartość i figury spadają wolno. Wraz ze wzrostem poziomu gry opóźnienie to staje się coraz mniejsze, zatem figury będą poruszały się szybciej w dół.

W dalszej kolejności obsługujemy klawiaturę wykonując odpowiednie akcje w zależności od naciśniętego klawisza przez użytkownika:

Po obsłudze klawiatury zmniejszamy licznik li o 1, odczekujemy 8 milisekund, sprawdzamy, czy li osiągnął 0, a jeśli nie, przechodzimy do punktu obsługi klawiatury. Jeśli licznik li osiągnął 0, to odtwarzamy go z lc i wykonujemy ruch w dół. Jeśli figura osiadła na dnie planszy gry, sprawdzamy, czy są jakieś wiersze do usunięcia, doliczamy do wyniku 5 punktów, wyświetlamy punktację gracza i kończymy funkcję zwracając false. Wartość ta spowoduje wylosowanie nowej figury i kontynuację gry.. Jeśli po wykonaniu ruchu w dół figura nie osiadła na dnie, wracamy do punktu obsługi klawiatury.

Kolizja

Symbol Przeznaczenie w algorytmie
i,j zmienne licznikowe pętli
ksztalt[ ] zmienna 4-znakowa przechowująca jeden wiersz kształtu figury
zestaw[ ] tablica definicji kształtów figur
pole_gry[ ] zmienna zawierająca pole gry
obrazek

Funkcja sprawdza, czy podana jako parametr figura koliduje z elementami planszy. Przyjmuje ona następujące parametry:

Kolizja(figura,obrót,x,y)
  figura - numer figury od 1 do 7
  obrót - numer obrotu od 1 do 4
  x,y - współrzędne lewego górnego rogu figury w obrębie planszy gry

Jeśli figura koliduje z elementami planszy, to zwrócona zostanie wartość logiczna true.

Najpierw pobieramy z tablicy zestaw[] odpowiedni kształt figury, który zależy od jej numeru oraz obrotu. Następnie w zależności od współrzędnych x i y porównujemy kolejne segmenty kształtu figury z odpowiednimi elementami pola gry. Jeśli segment 'X' figury występuje na tym samym miejscu co segment 'X' pola gry, mamy kolizję i zwracamy true. Jeśli żaden segment 'X' figury nie koliduje z segmentem 'X' pola gry, figura mieści się na planszy i zwracamy false.

Pokaż Figurę

Zadaniem tej procedury jest wyświetlenie lub usunięcie bieżącej figury z planszy. Rozróżnienie dokonywane będzie przy pomocy przekazanego procedurze parametru:

Zadanie wykonujemy następująco:

Z tablicy zestaw[] pobieramy kształt figury, który zależy od jej numeru oraz od obrotu. Przeglądamy kolejne znaki kształtu i natrafiwszy na 'X' umieszczamy na planszy gry kwadrat o kolorze figury dla parametru true lub spację o kolorze tła planszy dla parametru false. Położenie tego znaku wyznaczamy na podstawie współrzędnych obecny_x, obecny_y.

 

Ruch w prawo/lewo oraz obrót

Wykonanie przesuwu figury oraz obrót wokół osi wykonywane jest bardzo podobnie, zatem opiszemy te procedury w jednym podrozdziale. Najpierw obliczamy dla figury nową współrzędną nie zmieniając starej. Następnie wykorzystujemy funkcję Kolizja do sprawdzenia, czy w nowym położeniu figura nie koliduje z planszą gry. Jeśli tak, to nie dokonujemy przesunięcia lub obrotu. W przypadku braku kolizji usuwamy bieżącą figurę z ekranu za pomocą procedury PokazFigure(false), uaktualniamy współrzędną obecny_x lub obecny_obrot i umieszczamy figurę w nowym położeniu na planszy gry za pomocą procedury PokazFigure(true).

Zwróć uwagę, iż nasza plansza posiada ograniczenia z boków i na dole zbudowane ze znaków 'X'. Dzięki nim figura automatycznie będzie utrzymywana w obrębie planszy, gdyż próba wyjścia poza planszę spowoduje kolizję i operacja nie zostanie wykonana.

 

Upuszczenie

Symbol Przeznaczenie w algorytmie
punkty punktacja gracza
obrazek

Procedura zrzuca figurę na spód pola gry. W pętli sprawdzamy możliwość ruchu w dół. Jeśli występuje, to zwiększamy punkty o 1 za każdy wiersz spadku figury, wyświetlamy nową punktację i czekamy 10 milisekund, po czym pętla jest kontynuowana. Funkcja Ruch w dół sprawdza, czy figurę można opuścić o jeden wiersz. Jeśli tak, to zostaje ona opuszczona i funkcja zwraca wartość logiczną true. W przeciwnym razie zwrócona zostanie wartość logiczna false i pętla zakończy się.

Po wyjściu z pętli figura jest na spodzie planszy. Wpisujemy jej kształt do pola gry - od tej chwili staje się ona częścią tego pola i będzie blokowała inne figury. Następnie sprawdzamy, czy na planszy są pełne wiersze do usunięcia - jeśli tak, usuwamy je odpowiednio punktując tę operację. Do punktów doliczamy 5 za położenie figury, wyświetlamy punktację i kończymy procedurę.

Zwróć uwagę, iż w funkcja Brak Miejsca po wywołaniu procedury Upuszczenie następuje zakończenie - figura została zgrana do końca, leży na spodzie planszy, zatem należy teraz wylosować nową i rozpocząć grę z następną figurą.

Ruch w dół

Sprawdzamy za pomocą funkcji Kolizja, czy figura opuszczona o 1 wiersz koliduje z elementami planszy. Jeśli tak, zwracamy false. Jeśli figura może być opuszczona, usuwamy jej obraz z planszy gry za pomocą procedury PokazFigure(false), zwiększamy zmienną obecny_y o 1, umieszczamy na planszy figurę w nowym położeniu za pomocą PokazFigure(true) i zwracamy true.

 

Wpisz

Procedura pobiera kształt obecnej figury z tablicy zestaw[] wg zmiennych obecny i obecny_obrot, a następnie wprowadza wszystkie znaki 'X' zawarte w definicji kształtu na pole gry wg zmiennych obecny_x i obecny_y. Po wykonaniu tej operacji figura staje się integralną częścią pola gry i będzie blokowała ruch innych figur uczestniczących w grze.

 

Usuwanie Pełnych Wierszy

Symbol Przeznaczenie w algorytmie
licznik wierszy przechowuje informację o ilości usuniętych wierszy z planszy gry
i,j zmienne licznikowe pętli
pole_gry[...] tablica przechowująca zawartość planszy gry
punkty zmienna przechowująca bieżącą punktację gracza
wiersze zmienna zliczająca usunięte wiersze w całej rozgrywce
poziom numer poziomu gry od 1 do 9
obrazek

Procedura przegląda planszę gry i usuwa z niej zapełnione wiersze. Ilość usuniętych wierszy jest zapamiętywana i na jej podstawie na końcu procedury ustalamy punktację gracza. Zmienna wiersze pamięta ilość usuniętych wierszy w całej rozgrywce. Jeśli jej zawartość przekroczy liczbę 50, zwiększony zostanie poziom gry, co daje w efekcie coraz szybszą rozgrywkę.

Do usuwania wierszy z okna konsoli wykorzystujemy procedurę Przesuń Okno, którą opisaliśmy w poprzednim podrozdziale. Oczywiście musimy również usunąć wiersze z pola gry, czego dokonujemy w pętli poprzedzającej wywołanie tej procedury. Dzięki tej operacji zawartość okna konsoli będzie zgodna z zawartością zmiennej przechowującej planszę gry.

Zwróć uwagę, iż na dziewiątym poziomie osiągnięcie 50 usuniętych wierszy nie zwiększa już dalej poziomu gry - jednakże gracz otrzymuje bonus w wysokości 1000 punktów za przejście do kolejnego poziomu.


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

©2024 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