datalog-0.2.0.1: tests/NQueens.hs
{-# LANGUAGE OverloadedStrings, FlexibleContexts #-}
module Main ( main ) where
import Control.Monad ( forM_ )
import Data.List ( sort )
import qualified Data.Set as S
import Test.Framework ( defaultMain, testGroup, Test )
import Test.Framework.Providers.HUnit
import Test.HUnit hiding ( Test )
import Database.Datalog
main :: IO ()
main = defaultMain tests
tests :: [Test]
tests = [ testGroup "t1" [ testCase "4queens" t4
, testCase "5queens" t5
, testCase "6queens" t6
] ]
type Position = (Int, Int)
-- dbN :: (Failure DatalogError m) => Int -> m (Database Position)
dbN :: Int -> IO (Database Position)
dbN n = makeDatabase $ do
let posTuples = [ (x, y) | x <- [1..n], y <- [1..n] ]
position <- addRelation "position" 1
forM_ posTuples $ \(x, y) -> assertFact position [ (x, y) ]
-- Note, the restrictions on x and y equality also imply that the
-- position rule can't select the same position more than once in a
-- solution
posCanAttack :: Position -> Position -> Bool
posCanAttack (x1, y1) (x2, y2) =
x1 == x2 || y1 == y2 || (abs (x1 - x2) == abs (y1 - y2))
unique :: [[Position]] -> [[Position]]
unique = S.toList . S.fromList
-- Return False if any position can attack any others
noneCanAttack :: [Position] -> Bool
noneCanAttack [] = True
noneCanAttack [_] = True
noneCanAttack (p:ps) = not (any (posCanAttack p) ps) && noneCanAttack ps
t4 :: Assertion
t4 = do
db4 <- dbN 4
res <- queryDatabase db4 q
let res' = unique $ map sort res
print res'
assertBool "t4" $ all noneCanAttack res' && length res' == 2
where
q = do
position <- relationPredicateFromName "position"
canAttack <- inferencePredicate "canAttack"
let x = LogicVar "X"
y = LogicVar "Y"
(canAttack, [x, y]) |- [ lit position [x]
, lit position [y]
, cond2 posCanAttack (x, y)
]
let p1 = LogicVar "P1"
p2 = LogicVar "P2"
p3 = LogicVar "P3"
p4 = LogicVar "P4"
queens <- inferencePredicate "queens"
(queens, [p1, p2, p3, p4]) |- [ lit position [p1]
, lit position [p2]
, negLit canAttack [p1, p2]
, lit position [p3]
, negLit canAttack [p1, p3]
, negLit canAttack [p2, p3]
, lit position [p4]
, negLit canAttack [p1, p4]
, negLit canAttack [p2, p4]
, negLit canAttack [p3, p4]
]
issueQuery queens [p1, p2, p3, p4]
t5 :: Assertion
t5 = do
db5 <- dbN 5
res <- queryDatabase db5 q
let res' = unique $ map sort res
print res'
assertBool "t5" $ all noneCanAttack res' && length res' == 10
where
q = do
position <- relationPredicateFromName "position"
canAttack <- inferencePredicate "canAttack"
let x = LogicVar "X"
y = LogicVar "Y"
(canAttack, [x, y]) |- [ lit position [x]
, lit position [y]
, cond2 posCanAttack (x, y)
]
let p1 = LogicVar "P1"
p2 = LogicVar "P2"
p3 = LogicVar "P3"
p4 = LogicVar "P4"
p5 = LogicVar "P5"
queens <- inferencePredicate "queens"
(queens, [p1, p2, p3, p4, p5]) |- [ lit position [p1]
, lit position [p2]
, negLit canAttack [p1, p2]
, lit position [p3]
, negLit canAttack [p1, p3]
, negLit canAttack [p2, p3]
, lit position [p4]
, negLit canAttack [p1, p4]
, negLit canAttack [p2, p4]
, negLit canAttack [p3, p4]
, lit position [p5]
, negLit canAttack [p1, p5]
, negLit canAttack [p2, p5]
, negLit canAttack [p3, p5]
, negLit canAttack [p4, p5]
]
issueQuery queens [p1, p2, p3, p4, p5]
t6 :: Assertion
t6 = do
db6 <- dbN 6
res <- queryDatabase db6 q
let res' = unique $ map sort res
print res'
assertBool "t6" $ all noneCanAttack res' && length res' == 4
where
q = do
position <- relationPredicateFromName "position"
canAttack <- inferencePredicate "canAttack"
let x = LogicVar "X"
y = LogicVar "Y"
(canAttack, [x, y]) |- [ lit position [x]
, lit position [y]
, cond2 posCanAttack (x, y)
]
let p1 = LogicVar "P1"
p2 = LogicVar "P2"
p3 = LogicVar "P3"
p4 = LogicVar "P4"
p5 = LogicVar "P5"
p6 = LogicVar "P6"
queens <- inferencePredicate "queens"
(queens, [p1, p2, p3, p4, p5, p6]) |- [ lit position [p1]
, lit position [p2]
, negLit canAttack [p1, p2]
, lit position [p3]
, negLit canAttack [p1, p3]
, negLit canAttack [p2, p3]
, lit position [p4]
, negLit canAttack [p1, p4]
, negLit canAttack [p2, p4]
, negLit canAttack [p3, p4]
, lit position [p5]
, negLit canAttack [p1, p5]
, negLit canAttack [p2, p5]
, negLit canAttack [p3, p5]
, negLit canAttack [p4, p5]
, lit position [p6]
, negLit canAttack [p1, p6]
, negLit canAttack [p2, p6]
, negLit canAttack [p3, p6]
, negLit canAttack [p4, p6]
, negLit canAttack [p5, p6]
]
issueQuery queens [p1, p2, p3, p4, p5, p6]