Teknősgrafika

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 rajzvászon (Canvas) egy rendezett hármas, melyben eltároljuk a vászon szélességét és magasságát, és a vászon képpontjait sorfolytonosan. Ha egy képpont True, akkor be van festve, különben False.

A pozíciókat (Position) sor-oszlop párokként ábrázoljuk. A rajzvászon bal felső sarkának koordinátája (0,0). A sorszámozás nullával kezdődik, a sorszámok lefelé haladva nőnek, az oszlopszámok jobbra haladva nőnek.

A teknősben eltároljuk a pozícióját, hogy melyik égtáj (Direction) felé néz éppen, és hogy lent van-e a tolla (True) vagy sem (False). Az égtáj lehet 'n', 'w', 's' vagy 'e' egyike, jelentésük rendre észak, nyugat, dél, kelet.

A teknős utasításokat hajt végre, melyeket karakterekkel ábrázolunk. A teknős négy utasítást ismer: egy lépést előre ('f') és jobbra fordulás ('r') 90 fokkal, tollat fel ('u') és tollat le ('d').

type Position = (Int, Int)
type Canvas = (Int, Int, [Bool])
type Direction = Char
type Turtle = (Position,Direction,Bool)
type Statement = Char
type Sequence = [Statement]
Test>
(0,0)(1,0)(1,1)
Test>
(0,0)(1,0)(1,1)
Test>
(0,0)(2,0)(2,2)
Test>
(0,0)(12,0)(12,12)
Test>
(0,0)(12,0)(12,12)
canvas :: Canvas
canvas = (11, 11, replicate (11 * 11) False)

Üres rajzvászon (1 pont)

Hozzunk létre egy n × m méretű üres rajzvásznat! Ha valamelyik méret nem pozitív, adjunk hibaüzenetet error függvénnyel (lásd példák)!

empty :: Int -> Int -> Canvas

Test>
(2, 2, [False, False, False, False]) :: Canvas
Test>
(2, 3, [False, False, False, False, False, False]) :: Canvas
Test>
⊥₁ :: Canvas
⊥₁: empty: invalid arguments
CallStack (from HasCallStack):
  error, called at ./Turtle.lhs:127:26 in main:Turtle
Test>
⊥₁ :: Canvas
⊥₁: empty: invalid arguments
CallStack (from HasCallStack):
  error, called at ./Turtle.lhs:127:26 in main:Turtle

Listaelem cseréje (2 pont)

Definiáljuk a függvényt, mely kicseréli a lista egy adott indexű elemét egy adott értékre!

Ha az index érvénytelen (negatív vagy túl nagy), akkor adjunk hibaüzenetet az error függvénnyel (lásd példák)!

A rajzvászon pontjainak befestéséhez szükség lesz, hogy egy adott üres pontot kicseréljünk egy befestett pontra a pontok listájában.

replaceAt :: Int -> a -> [a] -> [a]

Test>
[5, 4, 3, 2] :: [Integer]
Test>
[4, 6, 3, 2] :: [Integer]
Test>
[1, 2, 0] :: [Integer]
Test>
⊥₁ :: [Integer]
⊥₁: replaceAt: invalid index
CallStack (from HasCallStack):
  error, called at ./Turtle.lhs:151:21 in main:Turtle
Test>
[2, 3] ++ ⊥₁ :: [Integer]
⊥₁: replaceAt : invalid index
CallStack (from HasCallStack):
  error, called at ./Turtle.lhs:147:22 in main:Turtle

Egy pont befestése (2 pont)

Fessünk be egy megadott képpontot a rajzvásznon! A rajzvászon képpontjait sorfolytonosan ábrázoljuk, így a befestendő (row,col) képpont indexe row * w + col a w szélességű rajzvásznon.

Ha a képpont nincs rajta a rajzvásznon, akkor adjuk vissza a rajzvásznat változatlanul.

draw :: Position -> Canvas -> Canvas

Test>
(2, 2, [True, False, False, False]) :: Canvas
Test>
(2, 2, [False, False, False, True]) :: Canvas
Test>
(2, 2, [False, False, False, False]) :: Canvas
Test>
(2, 2, [False, False, False, False]) :: Canvas

Egy parancs végrehajtása a teknősön (2 pont)

Hajtsuk végre a megadott utasítást ('f','r','u' vagy 'd' - jelentésük: ‘lépj előre’, ‘fordulj jobbra’, ‘tollat fel’, ‘tollat le’) a teknősön! Az előrelépésnél vegyük figyelembe, hogy milyen irányba néz a teknős! Ne feledjük, hogy felfele (észak felé) lépéskor csökken a sorszám, míg lefele (dél felé) haladáskor nő. Ha jobbra (kelet felé) lépünk, nő az oszlopszám, ha balra (nyugat felé), csökken.

execute :: Statement -> Turtle -> Turtle

Test>
((1, 0), 's', False) :: Turtle
Test>
((0, 0), 'n', False) :: Turtle
Test>
((5, 4), 'n', False) :: Turtle
Test>
((5, 4), 'n', False) :: Turtle
Test>
((5, 4), 'n', True) :: Turtle

Megengedett-e az utasítás (2 pont)

Egy utasítás megengedett, ha a végrehajtása után a teknős nem lépett le a rajzvászonról (a teknős sora legalább 0 és a rajzvászon magasságánál kisebb, a teknős oszlopa legalább 0 és a rajzvászon szélességénél kisebb).

validStep :: Statement -> Turtle -> Canvas -> Bool

Test>
True :: Bool
Test>
False :: Bool

Érvényes-e a szekvencia? (1 pont)

Döntsük el, hogy egy szekvencia érvényes-e, azaz csak a megengedett karakterekből ('f','d','u','r', '(', ')') áll! A helyes zárójelezést nem ellenőrizzük.

isValidSequence :: Sequence -> Bool

Test>
True :: Bool
Test>
True :: Bool
Test>
True :: Bool
Test>
False :: Bool

Lent van-e a toll (1 pont)

Döntsük el, hogy a teknős tolla lent van-e éppen!

isPenDown :: Turtle -> Bool

Test>
True :: Bool
Test>
False :: Bool
Test>
True :: Bool

Szekvencia felbontása vonalakra és téglalapokra (3 pont)

Bontsunk fel egy szekvenciát alakzatokra! A téglalapokat meghatározó utasítások zárójelek között szerepelnek, míg a vonalakat meghatározó utasítások nincsenek bezárójelezve.

Feltesszük, hogy a szekvencia helyesen zárójelezett, és a zárójelek nincsenek egymásba ágyazva (nem fordulhat elő a "f(f(fr)r)f" szekvencia).

A partition működési elve a következő:

partition :: Sequence -> [(Bool,Sequence)]

Test>
[] :: [(Bool, Sequence)]
Test>
[(False, "ffrr")] :: [(Bool, Sequence)]
Test>
[(True, "fff")] :: [(Bool, Sequence)]
Test>
[(False, "ff"), (True, "frf"), (False, "rrf")] :: [(Bool, Sequence)]
Test>
[(False, "ff"), (True, "rfr")] :: [(Bool, Sequence)]
Test>
[(False, "ff"), (True, "frf"), (False, "r"), (True, "r"), (False, "f")] :: [(Bool, Sequence)]

Vonal rajzolása (2 pont)

Hajtsuk végre a szekvenciát, mely egy töröttvonalat ír le!

run :: Sequence -> Turtle -> Canvas -> (Turtle,Canvas)

Test>
(((2, 0), 's', True), (3, 3, [True, False, False, True, False, False, False, False, False])) :: (Turtle, Canvas)
Test>
(((2, 0), 's', True), (3, 3, [False, False, False, False, False, False, False, False, False])) :: (Turtle, Canvas)
Test>
(((0, 1), 'e', False), (3, 3, [True, False, False, False, False, False, False, False, False])) :: (Turtle, Canvas)
Test>
⊥₁ :: (Turtle, Canvas)
⊥₁: run: invalid step f at ((0,0),'n',True)
CallStack (from HasCallStack):
  error, called at ./Turtle.lhs:326:42 in main:Turtle
Test>
(0,0)(2,0)(2,2)

Téglalap kitöltése (3 pont)

Rajzoljuk meg a kitöltött téglalapot, melyet leír a szekvencia!

  1. Jegyezzük meg azt a pontot, ahol éppen áll a teknős! Ez lesz a téglalap egyik sarka.

  2. Futtassuk le a szekvenciát (run)! Jegyezzük meg azt a pontot, ahova eljutottunk! Ez lesz a téglalap átlós sarka.

  3. Állítsuk elő a téglalap pozícióinak listáit! Figyeljünk arra, hogy a második sarok lehet az első fölött vagy alatt is a rajzvásznon!

  4. Fessük be a téglalap összes pozícióit (draw)!

fill :: Sequence -> Turtle -> Canvas -> (Turtle,Canvas)

Test>
(((1, 2), 'e', True), (5, 5, [False, False, False, False, False, True, True, True, False, False, True, True, True, False, False, True, True, True, False, False, True, True, True, False, False])) :: (Turtle, Canvas)
Test>
(((3, 2), 'e', True), (5, 5, [True, True, True, False, False, True, True, True, False, False, True, True, True, False, False, True, True, True, False, False, False, False, False, False, False])) :: (Turtle, Canvas)
Test>
(0,0)(4,0)(4,4)
Test>
(0,0)(4,0)(4,4)

Szekvencia kirajzolása (2 pont)

Rajzoljuk meg a szekvenciát, mely vegyesen tartalmaz töröttvonalakat és téglalapokat!

  1. Bontsuk fel a szekvenciát töröttvonalakra és téglalapokra!

  2. Sorba rajzoljuk le az alakzatokat! Miután végeztünk egy alakzattal, onnan folytassuk a következő alakzat rajzolását!

drawSequence :: Sequence -> Turtle -> Canvas -> Canvas

Test>
(5, 5, [False, False, False, False, False, True, True, False, False, False, True, False, False, False, False, True, False, False, False, False, True, False, False, False, False]) :: Canvas
Test>
(11, 11, [True, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, True, True, True, False, False, False, False, False, False, False, False, True, True, True, False, False, False, False, False, False, False, False, True, True, True, False, False, False, False, False, False, False, False, True, True, True, False, False, False, False, False, False, False, False, True, True, True, False, False, False, False, False, False, False, False, False, ……]) :: Canvas
Test>
(0,0)(10,0)(10,10)

Pontozás