Kontakt
|
PRS 170
|
ALK 420
|
PZR 420
|
SIK 420
|
JPR 222
|
SOP 121
Języki programowania - program na zaliczenie
Temat: Zmodyfikowana gra w życie.
Opis.
1. Założenia ogólne.
Dany jest świat jako obszar prostokątny, składający się z r wierszy po c komórek w każdym wierszu (r i c
nie są z góry znane - należą one do parametrów problemu).
Każda komórka może być albo pusta, albo może zawierać osobnika pewnego gatunku.
Gatunki oznaczane są literami alfabetu i zbiór gatunków
również nie jest z góry znany. Osobniki każdego gatunku
mogą się rozmnażać, mogą również pożerać osobniki innego gatunku. Symulacja podzielona jest na rundy.
W każdej rundzie każdy osobnik może wykonać co najwyżej jedną czynność:
może albo się rozmnożyć, albo pożreć osobnika innego gatunku (zasady
rozmnażania i pożerania zostały opisane niżej). Jeśli sytuacja danego
osobnika jest taka, że może zarówno pożreć, jak i się rozmnożyć,
decyzja o wykonywanej czynności podejmowana jest w sposób losowy
- prawdopodobieństwo wyboru każdej z czynności jest identyczne (wynosi
0.5). Każdy osobnik posiada czas życia, który wynosi z
rund. Jeśli osobnik w danej rundzie pożre osobnika innego gatunku,
jego czas życia wzrasta o jedną rundę. Zatem jeśli pewien osobnik
pojawił się na świecie w rundzie k, w wyniku rozmnożenia innych osobników w rundzie k-1, to o ile nie pożre innego osobnika w rundach k, k+1,...,k+z-1, nie będzie go już na świecie począwszy od rundy k+z.
Oczywiście osobnik pożarty znika ze świata począwszy od następnej
rundy. W szczególnym przypadku dany osobnik może żyć wiecznie,
jeśli w każdej rundzie uda mu się pożreć osobnika innego gatunku.
Stan świata w rundzie k+1 generowany jest na podstawie stanu świata w rundzie k, jednak stan w rundzie k jest stały. Oznacza to, że jeśli w rundzie k osobnik X zostanie pożarty przez osobnika Y, to mimo to może on jeszcze brać udział w rozmnażaniu lub pożeraniu w rundzie k.
2. Zasady pożerania i rozmnażania.
Osobik X gatunku A może się rozmnożyć (utworzyć nowego osobnika Z gatunku A)
jeśli w danej rundzie w jednej z sąsiednich komórek świata
(przez komórkę sąsiednią rozumiemy komórkę położoną
powyżej, poniżej, po lewej i po prawej stroniej komórki zajętej
przez X, ale nie komórki położone po skosie typu lewo-góra czy prawo-góra) znajduje się osobnik Y tego samego gatunku. Jeśli osobnik X rozmnożył się w rundzie k przy pomocy osobnika Y, to osobnik Y nie może już rozmnażać się w rundzie k
(może jednak w tej rundzie pożerać). Dodatkowym warunkiem
rozmnażania jest istnienie co najmniej jednej pustej komórki
świata w sąsiedztwie (przez sąsiedztwo rozumiemy tutaj znowu
komórkę powyżej, poniżej, po lewej i po prawej, ale nie na skos)
rozmnażającego się osobnika. Jeśli taka komórka nie istnieje,
osobnik nie może się rozmnożyć. Nowo powstały osobnik zajmuje jedną z
wolnych komórek sąsiednich swojego rodzica (jeśli jest więcej
niż jedna wolna komórka, zajmuje którąkolwiek z nich -
można np. wylosować którą komórkę ma zająć).
Aby osobnik X gatunku A mógł pożreć osobnika Z gatunku B, muszą być spełnione następujące warunki:
- Z musi znajdować się w komórce sąsiedniej do tej zajmowanej przez X (sąsiednie komórki definiujemy tak jak przy rozmnażaniu).
- W innej komórce sąsiadującej z tą zajmowaną przez pożeranego osobnika Z musi znajdować się inny osobnik Y gatunku A (pożerającego).
Dany osobnik może być pożarty tylko raz. Oznacza to, że jeśli X i Y (tego samego gatunku) sąsiadują z Z (innego gatunku), oraz X pożarł Z wykorzystując do tego obecność Y, to Y nie może już ponownie pożreć Z, wykorzystując do tego obecność X.
Powyższe reguły dopuszczają sytuację, w której w pewnej rundzie osobnik X pożre Z i Z pożre X. Godzimy się na taki paradoks aby dodatkowo nie komplikować reguł.
Jeśli jakaś sytuacja nie została uwzględniona w powyższym opisie, można dla niej przyjąć dowolny sposób postępowania.
3. Sposób przeprowadzenia symulacji.
Stan
początkowy symulacji powinien obejmować rozmiar świata, liczbę
gatunków oraz stan świata w rundzie 1. Rozmiar świata i liczba
gatunków zawsze będą określane przez użytkownika, natomiast
początkowy stan świata powinien być albo generowany w sposób
losowy (użytkownik określa, jaki procent komórek świata ma być
zajęty przez osobniki, a program generuje zawartość
poszczególnych komórek losując gatunek, z którego
osobnik zajmie konkretną komórkę; alternatywnie można wylosować
ciąg osobników określonej długości, a następnie losowo
przydzielić osobnikom komórki, które będą zajmować), albo
program powinien umożliwić wczytanie początkowego stanu świata z pliku
tekstowego. W pliku takim znak '.' (kropka) oznacza pustą
komórkę, a oznaczenie literowe ('a','b',...) oznacza
komórkę zajętą przez osobnika danego gatunku. Zakładamy, że plik
będzie zawierał r wierszy, po c znaków w każdym wierszu i że gatunki zawsze będą oznaczane kolejnymi, małymi, literami alfabetu, począwszy od 'a'.
Następnie
przeprowadzamy symulację świata, wyliczając stan następnej rundy po
wciśnięciu klawisza przez użytkownika. Po określeniu stanu świata w
danej rundzie należy przestawić go na ekranie, drukując r wierszy tekstu, po c
znaków w każdym wierszu, oznaczając kropką puste komórki,
a odpowiednią literą osobniki danego gatunku. W momencie wyliczania
stanu świata należy dodatkowo drukować informację o wykonanych
czynnościach (typu: "osobnik gatunku a z komórki (2,1) pożarł
osobnika gatunku c z komórki (3,1)").
Symulacja kończy się po wciśnięciu przez użytkownika określonego klawisza (np. 'q').
Wyliczając stan świata w rundzie k+1 na podstawie stanu w rundzie k
należy po kolei sprawdzić sytuację każdego osobnika. Ponieważ jednak
kolejność, w której będziemy "przetwarzać" osobniki może mieć
wpływ na nowy stan świata, należy przyjąć kolejność losową. Metoda
określenia takiej kolejności może być następująca (jest to tylko
sugestia):
- Mając dany wektor n osobników, utwórz nowy wektor, zdolny pomieścić n osobników.
- Dla i=1,2,...,n do komórki i-tej nowego wektora wstaw osobnika wylosowanego spośród n-i+1
osobników ze starego wektora, po czym usuń wylosowanego osobnika
ze starego wektora (długość starego wektora zmniejszy się o 1).
4. Wymagania co do konstrukcji programu.
Każdy
osobnik powinien być reprezentowany przez obiekt określonej klasy (np.
Osobnik). Świat również ma być reprezentowany przez obiekt (np.
klasy Swiat). Obiekt-świat powinien zawierać aktualny stan "mapy"
świata, oraz udostępniać metody, za pomocą których
obiekty-osobniki będą mogły ten stan sprawdzać. Każdy obiekt-osobnik
odpowiada za zachowanie pojedynczego osobnika. Funkcjonalność
obiektów-osobników powinna zawierać opisane wyżej reguły
dotyczące czasu życia, rozmnażania i pożerania (zatem osobnik sam
powinien decydować np. o swojej śmierci, powiadamiając świat że taki
fakt nastąpił). Obiekt-świat powinien zawierać również metody
generujące stan w kolejnej rundzie oraz wyświetlające stan świata na
konsoli. Procedura kontrolująca symulację powinna w zasadzie wyłącznie
wywoływać kolejno odpowiednie metody obiektu-świata, które to
metody z kolei powinny korzystać kolejno z odpowiednich metod
obiektów-osobników (które mogą ponownie korzystać
z metod obiektu-świata w celu określenia lokalnego otoczenia danego
osobnika, umożliwiając mu w ten sposób podjęcie decyzji o
wykonywanej w danej rundzie czynności). Zatem obiekt-świat powinien
posiadać wskaźniki (referencje) do wszystkich
obiektów-osobników, natomiast każdy obiekt-osobnik
powinien posiadać wskaźnik (referencję) do świata, w którym
żyje. Wygodnie będzie określić interfejsy osobnika i świata jako
interfejsy (Java, C#) lub klasy abstrakcyjne (C++) i posługiwać się
wskaźnikami (referencjami) do tych interfejsów w kodzie
implementującym klasę świata i osobników. W ten sposób
unikniemy zapętlonych referencji (świat-osobnik-świat) i skorzystamy z
polimorfizmu. Proszę traktować tą uwagę jako wymaganie. Oczywiście w
programie mogą być wykorzystane również obiekty innych klas
(pomocniczych), jeśli okaże się to potrzebne, jednak wariant minimalny
to: interfejs osobnika, interfejs świata, implementacja osobnika,
implementacja świata.
Sposób zaliczenia programu.
Programy zaliczane będą na ćwiczeniach 21 maja 2007r.
Każdy autor programu zaprezentuje działanie swojego dzieła i będzie
zobowiązany do udzielenia odpowiedzi na kilka pytań dotyczących kodu
źródłowego. Dodatkowo program zostanie sprawdzony na
spreparowanych wcześniej stanach początkowych świata, pod kątem
zgodności z opisanymi regułami postępowania.
Program można
wykonywać w parach, jednak w takim przypadku należy wyraźnie podzielić
się rolami (np. jedna osoba implementuje klasę świata, a druga klasę
osobnika) i poinformować przy zaliczaniu programu jaki był zakres
odpowiedzialności danego współautora.
