Elemi sejtautomata

Használható segédanyagok: Haskell könyvtárainak dokumentációja, (lokális!) Hoogle, a tárgy honlapja és a BE-AD rendszerbe feltöltött beadandók. Ha bármilyen kérdés, észrevétel felmerül, azt a felügyelőknek kell jelezni, nem a diáktársaknak!

A feladatok egymásra épülnek ezért érdemes ezeket a megadásuk sorrendjében megoldani, de legalább megérteni az aktuális feladatot megelőző feladatokat! A függvények definíciójában lehet, sőt javasolt is alkalmazni a korábban definiált függvényeket (függetlenül attól, hogy sikerült-e azokat megadni).

Tekintve, hogy a tesztesetek, bár odafigyelés mellett íródnak, nem fedik le minden esetben a függvény teljes működését, határozottan javasolt még külön próbálgatni a megoldásokat beadás előtt, vagy megkérdezni a felügyelőket!

Részpontszámokat csak az elégséges szint elérése után lehet kapni!

A feladat összefoglaló leírása

A feladatban egy elemi sejtautomata (Elementary Cellular Automata) szimulációját adjuk meg. A sejtautomata egy mindkét irányban végtelen vonalon (számegyenesen) játszódik. A vonal egyes pozícióit (egész számokat) celláknak nevezzük. Egy cella vagy üres, vagy egy élő sejt található benne. Gyakori ábrázolási mód, hogy az üres cellákat fehér, míg az élő sejtet tartalmazó cellákat fekete négyzetek jelölik, így egy kép rajzolható ki az aktuális állapotból. Ilyen látható a következő ábrán:

......

A jellemző kezdeti állapot az, hogy csupán egy élő sejt van. Ez után minden körben kiszámítunk egy új generációt, tehát azoknak a celláknak a helyét, ahol a lépés után élő sejtek lesznek. Három eset lehetséges: egy sejt életben maradhat a következő generációra, elpusztulhat, vagy új sejt születhet egy üres cellában. A cella következő pillanatban való lakott vagy lakatlan állapotának megadásához annak bal oldali szomszédjának, saját állapotának és jobb oldali szomszédjának állapotát veszi számításba. Ha egy sejt elpusztul, akkor a cellát üresnek tekintjük.

A sejtek életciklusát és új sejtek születését sejtautomata szabályokkal fogjuk megadni. 256 féle ilyen szabály létezik, mind más-más sejtautomatát határoz meg. Az egyes sejt-generációkat jellemzően egymás alá rajzoljuk ki, így egy ábrán a szimuláció egész folyamatát megtekinthetjük.

A szimulációt az egész számok tartományán végezzük, egy cellát (Cell) egy egész érték fog reprezentálni. Hogy egy cellában található-e élő sejt egy logikai érték (Alive) fogja megadni.

type Cell = Integer
type Alive = Bool

Tekintve, hogy a számegyenes végtelen, ezért mi csak egy szeletét fogjuk ennek vizsgálni, amit egy zárt intervallummal (Interval) adunk meg.

type Interval = (Integer, Integer)

A sejtautomata szabályokhoz szükségünk lesz a szomszédos cellák értékeire, ezért bevezetünk a szinonimát (Neighbourhood) az logikai értékek rendezett hármasaira. A rendezett hármas sorrendben a következő információt tartalmazza:

type Neighbourhood = (Alive, Alive, Alive)

Előkészületek a szimulációhoz

A szimulációhoz szükségünk lesz néhány egyszerűbb függvényre, amely egyes cellákról megadja, hogy élő sejtet tartalmaz-e, mintavétel a számegyenesből, illetve a sejtek új generációinak előállításához szükséges műveletek.

Kezdőállapot (1 pont)

Adjuk meg azt a függvényt, amely csak a 0-ik pozíción lévő sejtről állítja azt, hogy az élő!


Test>
True :: Alive
Test>
False :: Alive
Test>
False :: Alive

A vonal egy szeletének elemzése (1 pont)

Adjunk meg azt a függvényt, ami egy adott intervallum minden pontjáról megadja a sejtek állapotát!


Test>
[False, False, False, True, False, False, False] :: [Alive]
Test>
[True] :: [Alive]
Test>
[] :: [Alive]

Példák:

Test>
Test>

Triviális automata definiálása (1 pont)

Adjuk meg az 51-es szabályt, amely használatával abban az esetben lesz egy cella élő, ha az előző körben nem volt az!


Test>
True :: Alive
Test>
False :: Alive
Test>
True :: Alive
Test>
False :: Alive

Egy automata definiálása (2 pont)

Adjuk meg a 150-es szabályt, amely azokat a sejteket teszi élővé, amiknek a közvetlen szomsédságában páratlan számú élő sejt van (önmagát is beleértve)!


Test>
False :: Alive
Test>
True :: Alive
Test>
True :: Alive
Test>
False :: Alive
Test>
True :: Alive

Szimuláció műveletei

Bevezetünk egy absztrakciót a State típusszinonimával, azaz a sejtautomata állapotát egy függvénnyel fogjuk megadni.

type State = (Cell -> Alive)

Állapot (State) alatt azokat a függvényeket értjük, amelyek egy cellából (Cell) logikai típusú értéket (Alive) adnak meg. Tehát, ezek a függvények mind predikátumok, amelyek egy adott cellára True vagy False értéket adnak meg, a definíciójuknak megfelelően.

Ilyen volt például a korábban definiált initial függvény, vagy lehetne az alábbi művelet is:

everyFifth :: State
everyFifth x = x `mod` 5 == 0

A következőkben ilyen State állapotokat fogunk transzformálni (függvényeket komponálni más műveletekkel), hogy egy új állapotot kapjunk.

Az automata léptetése (2 pont)

Adjuk meg azt a függvényt, amely egy szabály segítségével transzformál egy cella tulajdonságát vizsgáló művelet egy újabb műveletté! A függvény első paramétere egy szabály ami megadja egy cella életképességét a következő körre vetítve, a második paraméter pedig egy egy cella állapotát vizsgáló művelet.

Segítség:


Test>
True :: Alive
Test>
True :: Alive
Test>
False :: Alive
Test>
[True, True, True, False, True, True] :: [Alive]

Példák:

Az alábbi tábla szemlélteti az 51-es szabály kezdeti állapotból induló egymás követő alkalmazásának sorozatát (9 lépés) a (-10,10) intervallumot tekintve:

Az alábbi tábla szemlélteti az 150-es szabály kezdeti állapotból induló egymás követő alkalmazásának sorozatát (9 lépés) a (-10,10) intervallumot tekintve:

Az automata futtatása (1 pont)

Definiáljuk azt a függvényt, ami felsorolja a szimuláció végrehajtása során bekövetkező végtelen állapotsorozatot! A függvény első paramétere a szabály, ami alapján az sejtek keletkezhetnek, vagy pusztulhatnak el. A második paraméter a kiinduló állapot, amelyből a szimulációt el szeretnénk indítani.

A függvény által visszaadott végtelen lista első eleme a kezdőállapot legyen, minden további elemét pedig úgy kapjuk, hogy a step függvényt alkalmazzuk az előző állapotra.


Test>
[[True, False, False, False, False, False], [True, True, False, False, False, False], [True, False, True, False, False, False], [True, False, True, True, False, False], [True, False, False, False, True, False], [True, True, False, True, True, True], [True, False, False, False, True, False], [True, True, False, True, True, False], [True, False, False, False, False, False], [True, True, False, False, False, False]] :: [[Alive]]
Test>
[[True, False, False, False, False, False], [False, True, True, True, True, True], [True, False, False, False, False, False], [False, True, True, True, True, True], [True, False, False, False, False, False], [False, True, True, True, True, True], [True, False, False, False, False, False], [False, True, True, True, True, True], [True, False, False, False, False, False], [False, True, True, True, True, True]] :: [[Alive]]

Példa:

A rule150 szabályt használva a initial által megadott kezdőállapotból előállított végtelen állapotsorozat első 10 állapota a (-10,10) intervallumon a következőképpen néz ki:

Test>

Szimuláció eredményének megjelenítése

Az általunk megírt műveletek vizuális ellenőrzéséhez adunk meg a továbbiakban műveleteket.

Egy cella megjelenítése (1 pont)

Adjuk meg azt a függvényt, amelyik egy cella tartalmát jeleníti meg! Amennyiben a cellában élő sejt található, úgy a kettőskeresztet ('#'), ellenkező esetben az aláhúzás ('_') karaktert adja eredményül.


Test>
'#' :: Char
Test>
'_' :: Char

Egy állapot megjelenítése (2 pont)

Készítsünk el azt a függvényt, amelyik megjeleníti egy állapot adott intervallumon vett szeletét!

Segítség: Használjuk a displayCell és takeInterval függvényeket.


Test>
"#______" :: String
Test>
"___#___" :: String
Test>
"####" :: String
Test>
"_" :: String

Állapotsorozat megjelenítése (3 pont)

Definiáljuk azt a függvényt, amelyik egy adott végtelen állapotsorozat első n elemét jeleníti meg a megadott intervallumon. A függvény első paramétere a vizsgált intervallum , a második a megjelenítendő sorok száma n, a harmadik pedig a végtelen állapotsorozat.

Az eredmény egy szöveg legyen, amely az összes sort tartalmazza sorvége jelekkel ('\n') tördelve.

Segítség: A sorok megfelelő ősszefűzéséhez használjuk az intercalate műveletet!


Test>
"_\n#\n_\n#\n_" :: String
Test>
"_\n_\n_\n#\n_" :: String

Pontozás (elmélet + gyakorlat)