Az n királynő probléma

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 az 8 királynő problémáját általánosítjuk tetszőleges négyzetes táblára. A feladatunk az lesz, hogy helyezzünk el n darab királynőt egy n × n méretű táblára úgy hogy azok ne üssék egymást!

A királynőket azok pozíciójával (Position) reprezentáljuk, amelyek egész értékekből álló rendezett párok (sor és oszlop páros). A tábla sorait és oszlopait az 1-es indextől indítjuk.

A táblát Board egy rendezett párral reprezentálunk, melynek első eleme a tábla mérete (Size) egy egész érték, második eleme pedig a királynők pozícióinak listája ([Position]).

type Position = (Int, Int)
type Size = Int
type Board = (Size, [Position])

Üres tábla előállítása (1 pont)

Adjuk meg az üres táblát előállító műveletet! Az előállítandó tábla mérete legalább 4 legyen. Amennyiben kisebb méretű táblát szeretnénk létrehozni, adjunk vissza hibaüzenetet az error függvény segítségével (lásd tesztesetek).

emptyBoard :: Int -> Board

Test>
(5, []) :: Board
Test>
(4, []) :: Board
Test>
(8, []) :: Board
Test>
(9, []) :: Board
Test>
⊥₁ :: Board
⊥₁: emptyBoard: Invalid board size 1
CallStack (from HasCallStack):
  error, called at ./NQueens.lhs:86:19 in main:NQueens
Test>
⊥₁ :: Board
⊥₁: emptyBoard: Invalid board size 3
CallStack (from HasCallStack):
  error, called at ./NQueens.lhs:86:19 in main:NQueens
Test>
⊥₁ :: Board
⊥₁: emptyBoard: Invalid board size -10
CallStack (from HasCallStack):
  error, called at ./NQueens.lhs:86:19 in main:NQueens

Érvényes-e a pozíció? (1 pont)

Adjuk meg azt a függvényt, amely eldönti, hogy egy pozíció érvényes-e az adott táblán! Egy pozíciót akkor tekintünk érvényesnek, ha:

isValidPos :: Position -> Board -> Bool

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

Rendezett beszúrás (2 pont)

Definiáljuk a pozíciók rendezett listába való beszúrását! A beszúrást a sor, majd az oszlop értéke szerint kell elvégezni.

Megjegyzés: Nem használható rendezés (pl. sort) a megoldásban.

insert :: Position -> [Position] -> [Position]

Test>
[(1, 2)] :: [Position]
Test>
[(1, 2)] :: [Position]
Test>
[(1, 1), (2, 2), (3, 3)] :: [Position]
Test>
[(1, 1), (1, 2), (2, 1), (3, 1), (4, 2)] :: [Position]

Két pozíció vizsgálata (1 pont)

Adjuk meg azt a függvényt, amely eldönti két királynőről, hogy pozíciójuk alapján “megférnek-e” egymással a táblán, azaz nem ütik egymást!

Két királynő akkor nem üti egymást, ha:

isAllowed :: Position -> Position -> Bool

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

Szabad-e elhelyezni? (1 pont)

Adjuk meg azt a függvényt, amely megadja hogy az adott pozícióra helyezhetünk-e el királynőt úgy, hogy egyetlen táblán lévő királynővel sem fog ütközést okozni! A megoldásban használjuk az előző isAllowed függvényt.

isSafeStep :: Position -> Board -> Bool

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

Egy királynő elhelyezése (1 pont)

Adjuk meg azt a műveletet, amely elhelyez egy királynőt a táblára, amennyiben ez szabályos!

putQueen :: Board -> Position -> Board

Test>
(8, [(1, 1), (2, 5), (3, 8), (4, 6), (5, 3), (6, 7), (7, 2), (8, 4)]) :: Board
Test>
(8, [(1, 1), (2, 5), (3, 8), (4, 6), (5, 3), (6, 7), (7, 2), (8, 4)]) :: Board
Test>
⊥₁ :: Board
⊥₁: PutQueen: invalid position
CallStack (from HasCallStack):
  error, called at ./NQueens.lhs:207:24 in main:NQueens
Test>
⊥₁ :: Board
⊥₁: PutQueen: invalid position
CallStack (from HasCallStack):
  error, called at ./NQueens.lhs:207:24 in main:NQueens

Megoldások keresése (3 pont)

Adjuk meg azt a műveletet, amely egy adott táblának megkeresi az összes lehetséges kitöltését!

Segítség: Kezdjük az utolsó sorral és haladjunk visszafelé, válasszuk ki minden sorhoz az összes lehetséges oszlop párosítást, folytassuk ezt amíg minden sorhoz végig nem próbáltuk az összes oszlopot értéket.

fillQueens :: Board -> [Board]

Test>
[(4, [(1, 3), (2, 1), (3, 4), (4, 2)]), (4, [(1, 2), (2, 4), (3, 1), (4, 3)])] :: [Board]
Test>
[(5, [(1, 4), (2, 2), (3, 5), (4, 3), (5, 1)]), (5, [(1, 3), (2, 5), (3, 2), (4, 4), (5, 1)]), (5, [(1, 5), (2, 3), (3, 1), (4, 4), (5, 2)]), (5, [(1, 4), (2, 1), (3, 3), (4, 5), (5, 2)]), (5, [(1, 5), (2, 2), (3, 4), (4, 1), (5, 3)]), (5, [(1, 1), (2, 4), (3, 2), (4, 5), (5, 3)]), (5, [(1, 2), (2, 5), (3, 3), (4, 1), (5, 4)]), (5, [(1, 1), (2, 3), (3, 5), (4, 2), (5, 4)]), (5, [(1, 3), (2, 1), (3, 4), (4, 2), (5, 5)]), (5, [(1, 2), (2, 4), (3, 1), (4, 3), (5, 5)])] :: [Board]
Test>
[(6, [(1, 5), (2, 3), (3, 1), (4, 6), (5, 4), (6, 2)]), (6, [(1, 4), (2, 1), (3, 5), (4, 2), (5, 6), (6, 3)]), (6, [(1, 3), (2, 6), (3, 2), (4, 5), (5, 1), (6, 4)]), (6, [(1, 2), (2, 4), (3, 6), (4, 1), (5, 3), (6, 5)])] :: [Board]
Test>
92 :: Int
Test>
Test>
Test>
Test>

Tábla tükrözése az x tengelyre és elforgatás 90 fokkal (1 pont)

Test>

Adjuk meg azt a műveletet, amely egy tábla összes koordinátáját tükrözi az x tengelyre!

Egy elem (x, y) koordináta x tengelyre való tükrözését az alábbi szabállyal adhatjuk meg: (x, y) → (x, n − y + 1) (ahol az n a tábla mérete)

mirrorX :: Board -> Board

Test>
(4, [(1, 2), (2, 4), (3, 1), (4, 3)]) :: Board
Test>

Adjuk meg azt a műveletet, amely egy tábla összes koordinátáját elforgatja 90 fokkal!

Egy elem (x, y) koordináta x tengelyre való tükrözését az alábbi szabállyal adhatjuk meg: (x, y) → (n − y + 1, x) (ahol az n a tábla mérete)

rotate90 :: Board -> Board

Test>
(4, [(2, 1), (4, 2), (1, 3), (3, 4)]) :: Board
Test>

Szimmetria vizsgálat (2 pont)

Egy tábla szimmetrikus egy másikkal, ha a tükrözés és forgatás műveleteinek segítségével áttranszformálható a másik táblával megegyező táblává.

Adjuk meg azt a műveletet, amely az összes tükrözés és forgatás kombináció segítségével eldönti, hogy a két tábla szimmetrikus-e vagy sem!

Figyeljünk arra, hogy a forgatás/tükrözés során nem feltétlen a sor/oszlop szerint rendezettek a táblák. Összehasonlítás előtt ezt növekvő sorrendbe kellene rendezni. (Nem használható a sort művelet!)

isSymmetryOf :: Board -> Board -> Bool

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

Szimmetriák kiszűrése (1 pont)

Adjuk meg azokat a megoldásokat, amelyek páronként nem szimmetrikusak egymással! Azaz, ha tetszőlegesen kiválasztunk két megoldást, azok nem transzformálhatók át egymásba.

realSolutions :: Board -> [Board]

Test>
12 :: Int
Test>
1 :: Int

Pontozás (elmélet + gyakorlat)