Życie - algorytm


Podrozdziały:

 

obrazek

 


Grę w Życie opracujemy poznaną w poprzednich przykładach metodą zstępującą. Przypominamy, iż polega ona na rozbiciu problemu na mniejsze podproblemy i opracowanie każdego z nich z osobna. Dzięki takiemu podejściu zadanie staje się prostsze.

W naszym przypadku grę rozpoczniemy wyświetleniem strony tytułowej oraz krótkiej instrukcji obsługi programu. Operacji tych nie będziemy opisywać, ponieważ są to działania standardowe i na pewno nie sprawią kłopotu początkującym programistom.

Kolejna procedura ma za zadanie wyzerować planszę gry, umieścić gracza na jej środku oraz wyświetlić planszę w oknie konsoli.

Wszelkie działania realizuje specjalna funkcja Akcja. Oczekuje ona na wciśnięcie klawisza przez użytkownika, interpretuje go wywołując odpowiednie procedury i jeśli naciśniętym klawiszem był klawisz różny od [Escape], zwraca true, co powoduje kontynuację pętli. W przypadku klawisza [Escape] funkcja zwraca false, pętla ulega przerwaniu i gra kończy się.

Usuwanie Organizmów

Symbol Przeznaczenie w algorytmie
i,j zmienne licznikowe pętli
pobc[] tablica logiczna reprezentująca planszę gry
pozycja zmienna typu COORD zawierająca pozycję gracza w obrębie planszy
pokolenie numer pokolenia organizmów
obrazek

Plansza gry zrealizowana jest w postaci logicznej tablicy dwuwymiarowej. Indeksy elementów odpowiadają dokładnie pozycjom znakowym w okienku konsoli. Dlatego tablica ma następującą definicję:

var pobc : array[2..24,2..79] of boolean;


Elementy tej tablicy są wartościami logicznymi. Wartość false oznacza, iż dana komórka nie zawiera żywego organizmu, a wartość true oznacza, że zawiera. Pierwsze dwie pętle adresują kolejno wszystkie elementy tej tablicy i wprowadzają do nich wartość logiczną false - komórki puste. Następnie ustawiamy pozycję gracza na współrzędne X=40 i Y=12, co odpowiada środkowi okna konsoli i planszy gry. Zmienna pokolenie zawiera numer generowanego pokolenia. Teraz zerujemy ją.

Na koniec wywołujemy procedurę Plansza, która wyczyści nam okno konsoli i wyświetli w nim pustą planszę gry. Po tej operacji procedurę kończymy.

Plansza gry

Symbol Przeznaczenie w algorytmie
i,j zmienne licznikowe pętli
pobc[] tablica logiczna reprezentująca planszę gry
pokolenie numer pokolenia organizmów
populacja zlicza ilość żywych komórek na planszy
obrazek

Procedura generuje planszę dla gry. Na środku pierwszego wiersza okna konsoli wypisujemy aktualny numer pokolenia. Informacja ta przydaje się przy badaniu rozwoju różnych układów żywych komórek.

Plansza gry jest w niebieskim kolorze tła. Znaki są intensywnie białe. Wyświetlenie planszy polega na drukowaniu spacji na pozycjach znakowych okna konsoli, dla których komórka planszy ma wartość logiczną false, a znaków 'O' dla komórek planszy zawierających żywy organizm.

Ponieważ odwołania do procedury GotoXY są dosyć wolne, przy wyświetlaniu planszy tworzymy w zmiennej s cały wiersz komórek, który następnie drukujemy przy pomocy jednej instrukcji. Przeglądanie planszy realizowane jest w dwóch pętlach sterowanych zmiennymi i i j.

W trakcie tworzenia planszy zliczamy w zmiennej populacja liczbę komórek zawierających żywy organizm. Po wyświetleniu planszy umieszczamy na środku ostatniego wiersza informację o ilości żywych komórek.

Zmienna populacja jest zmienną globalną. Wykorzystywana jest również przez procedurę cyklicznego generowania pokoleń do wstrzymania tej generacji, jeśli wszystkie organizmy uległy wymarciu. W takim przypadku nie ma sensu tworzenie następnych pokoleń, ponieważ plansza jest pusta i nic na niej już nie wyrośnie.

Akcja

Symbol Przeznaczenie w algorytmie
pobc[] dwuwymiarowa tablica logiczna reprezentująca planszę gry
pokolenie numer pokolenia organizmów
populacja zlicza ilość żywych komórek na planszy
pozycja rekord typu COORD zawierający bieżącą pozycję kursora gracza
obrazek

Funkcja Akcja pełni rolę centralną w naszej grze. Ponieważ schemat blokowy byłby dosyć rozbudowany, pokazaliśmy go w wersji uproszczonej, co z resztą bardzo często się praktykuje - schematy blokowe mają za główne zadanie naszkicowanie idei działania algorytmu oraz powiązania jego elementów ze sobą, a szczegóły implementacyjne pozostawia się do fazy kodowania operacji algorytmu za pomocą wybranego języka programowania.

Na początku wywołujemy procedurę Kursor, która powinna w sposób widoczny wyróżnić pozycję gracza na planszy. My zrealizowaliśmy kursor mrugający, gdyż ruch przyciąga uwagę. Procedura wykonywana jest do momentu naciśnięcia jakiegokolwiek klawisza.

Odczytujemy kod naciśniętego przez gracza klawisza i badamy go podejmując odpowiednie działania. Jeśli jest to klawisz Enter, wywołujemy procedurę Następne Pokolenie, która na bazie bieżącego stanu planszy wygeneruje nowe pokolenie organizmów zgodnie z podanymi na początku rozdziału regułami. Kończymy algorytm zwracając wartość logiczną true, która spowoduje kontynuację pętli w programie głównym.

Wykrycie naciśnięcia klawisza Escape spowoduje zwrócenie przez funkcję wartości logicznej false, która przerwie pętlę programu głównego i zakończy działanie programu. Wszystkie pozostałe opcje zawsze kończą zwracając wartość true, zatem nie będziemy już o tym pisać.

Klawisz spacji powoduje zmianę stanu komórki na pozycji gracza. Ponieważ plansza gry jest tablicą wartości logicznych, wystarczy dokonać prostej negacji zawartości odpowiedniej komórki. Dodatkowo zerujemy zmienną pokolenie, ponieważ dokonaliśmy modyfikacji i rezultat jest czymś nowym, co nie powstało z poprzedniej generacji komórek. Zatem pokolenia będą liczone od tego momentu od nowa. Wywołujemy procedurę rysowania planszy i kończymy.

Klawisz F1 pełni funkcję włączania/wyłączania samoczynnej animacji kolejnych pokoleń. Po naciśnięciu tego klawisza algorytm wchodzi w pętlę warunkową, w której generuje następne pokolenie komórek, odczekuje około 1/3 sekundy i sprawdza naciśnięcie klawisza. Jeśli żaden klawisz nie został naciśnięty lub nie jest to klawisz F1, pętla kontynuuje się. W przypadku wykrycia klawisza F1 lub wymarcia wszystkich żywych komórek na planszy pętla zostaje zakończona.

Klawisze ze strzałkami zmieniają współrzędne bieżącej pozycji gracza, które umieszczono w zmiennej pozycja. Zasada jest taka, iż wyjście gracza poza krawędź planszy powinno przewinąć jego pozycję do krawędzi przeciwnej - współrzędne w zmiennej pozycja muszą zawsze wskazywać jedną z komórek planszy gry.

Na koniec klawisz Delete powoduje wymazanie całego pola gry i umieszczenie gracza na środku. Wykorzystujemy tutaj poznaną już wcześniej procedurę usuwania organizmów z planszy.

Następne Pokolenie

Symbol Przeznaczenie w algorytmie
i,j zmienne licznikowe pętli adresujących kolejne elementy tablic pobc[] i pnst[]
pobc[] tablica zawierająca obecne pokolenie organizmów
pnst[] tablica, w której tworzymy następne pokolenie organizmów
pokolenie numer pokolenia
ls liczba żywych sąsiadów dla komórki pnst[i,j]
k zmienne licznikowa kolumn
w zmienna licznikowa wierszy
x,y przechowuje współrzędne sąsiedniej komórki w stosunku do badanej komórki pnst[i,j]
obrazek

Zadaniem tej procedury jest wyznaczenie nowego pokolenia organizmów. Na początku tworzymy dwie zagnieżdżone pętle sterowane zmiennymi i i j. Pętle te adresują kolejne elementy tablicy pnst[]. Następnie dla każdej takiej komórki znajdujemy liczbę sąsiadów w tablicy pobc[]. Do tego celu służą kolejne dwie zagnieżdżone pętle sterowane zmiennymi k - kolumny i w - wiersze. Zwróć uwagę, iż wartości k oraz w są przesunięciami względem aktualnej pozycji i przebiegają wartości od -1 do 1. Współrzędne komórki obliczamy jako sumy współrzędnych i,j oraz k,w: Pozwala to zaadresować wszystkie 9 komórek leżących w prostokącie obejmującym pozycję (i,j):


  k=(-1) k=0 k=1
w=(-1) (i-1,j-1) (i,j-1) (i+1,j-1)
w=0 (i-1,j) (i,j) (i+1,j)
w=1 (i-1,j+1) (i,j+1) (i+1,j+1)


Współrzędne sąsiednich komórek wyliczane są w zmiennych x i y. Ponieważ komórka może przylegać do krawędzi planszy, to musimy sprawdzić, czy wyliczone współrzędne nie wychodzą poza obszar gry. Jeśli tak, to muszą być odpowiednio zmodyfikowane - krawędzie przeciwne zawsze się łączą ze sobą. Po wyznaczeniu współrzędnych komórki sąsiedniej sprawdzamy, czy zawiera ona żywy organizm i jeśli tak, zwiększamy zawartość zmiennej ls.

Po zakończeniu pętli k i w w zmiennej ls mamy liczbę żywych komórek otaczających komórkę pnst[i,j]. Jeśli w obecnym pokoleniu komórka ta żyje, to w następnym będzie wciąż żywa, gdy liczba jej sąsiadów wynosi 2 lub 3. Jeśli komórka w obecnym pokoleniu jest martwa, to ożyje w następnym, jeśli liczba sąsiadów wyniesie dokładnie 3.

Po zakończeniu pętli i i j w tablicy pnst[] mamy ustawione następne pokolenie organizmów. Przepisujemy zawartość tej tablicy do pobcn[], czyli następne pokolenie staje się pokoleniem obecnym. Zwiększamy numer pokolenia, wyświetlamy zmodyfikowaną planszę gry i kończymy algorytm.
 



   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