A 8 királynő probléma

Feladat

Helyezzünk el 8 királynőt a sakktáblára úgy hogy ne üssék egymást!

Megjegyzés: Az itt található anyag az ábrák kivételével a 2011. február 23-i előadás anyaga valamennyire lerövidítve és kiegészítve. Az ábrák kirajzolásának kódja gyakorlatokon készíthető el a gyakorlatvezetőtől függően.

A tábla kirajzolása

A probléma megjelenítésével kezdünk, ez majd a tesztelésben is segít.

A sakktáblát 2x2-es négyzetekből építjük fel, a bal alsó sarka lesz az origó, a jobb felső sarka pedig a (16,16) koordinátájú pont. A függvény elkészítése későbbi gyakorlatok anyaga.

board :: Diagram

Test>

A focus függvény csak azért kell hogy a megfelelő részletet jelenítsük meg az ábrából:

focus d = clip (-1,-1) (17,17) d

Tábla kirajzolása királynőkkel

Ábrázoljuk a királynőt egy sárga koronával:

queenDiagram :: Diagram
queenDiagram 
  = polygon [(-0.6,-0.8),(0.6,-0.8),(0.8,0.8),(0.5,0),(0,0.8),(-0.5,0),(-0.8,0.8)]
      `fill` yellow

Így néz ki:

Test>

Egy adott pozíción álló királynőt az (oszlop, sor) indexével reprezentálunk:

type Queen = (Int, Int)

A type kulcsszóról a félév második felében lesz szó.

A következő függvénnyel ellenőrizhetjük hogy egy egész számpár valamely királynő pozíciót reprezentálja-e:

checkQueen :: Queen -> Bool
checkQueen (x, y) = 1 <= x && x <= 8 && 1 <= y && y <= 8

Királynők elhelyezése helyes ha nincs két azonos királynő:

checkQueens :: [Queen] -> Bool
checkQueens qs = all checkQueen qs && uniqueList qs
uniqueList :: Eq a => [a] -> Bool
uniqueList xs = xs == nub xs

Királynők ábrázolása a táblán (a függvény elkészítése későbbi gyakorlatok anyaga):

showQueens :: [Queen] -> Diagram

Test>

Üti-e az egyik királynő a másikat?

attacks :: Queen -> Queen -> Bool
(a,b) `attacks` (c,d) 
  = a == c || b == d || abs (a-c) == abs (b-d)

Példák:

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

Az ütési irányok ábrázolása

Adott pozíciójú királynő esetén berajzoljuk hogy milyen irányokba mozoghat (a függvény elkészítése későbbi gyakorlatok anyaga):


Test>

Az ütések ábrázolása

Piros vonalakkal jelezzük ha két királynő üti egymást (a függvény elkészítése későbbi gyakorlatok anyaga):

showAttacks :: [Queen] -> Diagram

Test>

Üt-e egy királynőt a többi?

attack :: [Queen] -> Queen -> Bool
qs `attack` q 
  = or [ x `attacks` q | x <- qs ]

Példák:

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

Üti-e egymást több királynő?

anyAttack :: [Queen] -> Bool
anyAttack qs
  = or [ delete q qs `attack` q | q <- qs ]

Példa:

Test>
True :: Bool

N királynő elhelyezése ütésvizsgálattal

N darab királynő elhelyezése az első N oszlopba úgy, hogy ne üssék egymást:

queensWithoutAttackN :: Int -> [[Queen]]
queensWithoutAttackN 1 
    = [ [(1,y)] | y<-[1..8] ]
queensWithoutAttackN n 
    = [ k : kk
      | kk <- queensWithoutAttackN (n-1)
      , y<-[1..8]
      , let k = (n,y)
      , not (kk `attack` k)
      ]

Példák:

Test>
344 :: Int
Test>

8 darab királynő elhelyezése úgy, hogy ne üssék egymást:

queensWithoutAttack :: [[Queen]]
queensWithoutAttack = queensWithoutAttackN 8

Példák:

Test>
92 :: Int
Test>

Haladó rész: Szimmetriák

Az itt következő rész nem szerepelt az előadáson.

A tábla szimmetriáit pozíció-transzformációkkal fejezzük ki.

Tükrözés az x tengellyel párhuzamos tengelyre:

mirrorX :: Queen -> Queen
mirrorX (x,y) = (x, 9-y)

Tükrözés az x=y tengelyre:

mirrorXY :: Queen -> Queen
mirrorXY (x,y) = (y,x)

90 fokos elforgatás:

rotate90 :: Queen -> Queen
rotate90 = mirrorXY . mirrorX

A . operátor a függvénykompozíció, aminek a használatát később fogjuk gyakorolni.

Összes szimmetria

Királynők egy elhelyezéséhez hasonló összes elhelyezés:

symmetries :: [Queen] -> [[Queen]]
symmetries qs 
  = tr 4 (map rotate90) (tr 2 (map mirrorX) [qs])
 where
  tr n f = concatMap (take n . iterate f)

Az itt található magasabbrendű függvényekkel (map, concatMap, iterate) később fogunk megismerkedni.

Példa:

Test>

A showMany függvényt később definiáljuk.

Szimmetriák kiszűrése

Két elhelyezés hasonló-e?

similar :: [Queen] -> [Queen] -> Bool
similar a b = sort a `elem` map sort (symmetries b)

Példák:

Test>
True :: Bool

Szimmetriák szűrése:

filterSymmetries :: [[Queen]] -> [[Queen]]
filterSymmetries qs = nubBy similar qs

A nubBy magasabbrendű függvény eltávolítja az azonos elemeket egy adott ekvivalencia alapján.

A 8 királynő probléma összes különböző megoldása:

Test>

Sok elhelyezés ábrázolása

A függvény elkészítése későbbi gyakorlatok anyaga.

showMany :: [[Queen]] -> Diagram
boardWithQueens' :: (([Queen] -> Diagram) -> Diagram) -> Diagram

Test>