{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeSynonymInstances #-}
module Data.SBV.Examples.Puzzles.U2Bridge where
import Control.Monad (unless)
import Control.Monad.State (State, runState, put, get, modify, evalState)
import Data.Generics (Data)
import GHC.Generics (Generic)
import Data.SBV
data U2Member = Bono | Edge | Adam | Larry deriving (Eq, Ord, Show, Read, Data, SymWord, HasKind, SatModel)
type SU2Member = SBV U2Member
bono, edge, adam, larry :: SU2Member
[bono, edge, adam, larry] = map literal [Bono, Edge, Adam, Larry]
type Time = Word32
type STime = SBV Time
crossTime :: U2Member -> Time
crossTime Bono = 1
crossTime Edge = 2
crossTime Adam = 5
crossTime Larry = 10
sCrossTime :: SU2Member -> STime
sCrossTime m = ite (m .== bono) (literal (crossTime Bono))
$ ite (m .== edge) (literal (crossTime Edge))
$ ite (m .== adam) (literal (crossTime Adam))
(literal (crossTime Larry))
data Location = Here | There deriving (Eq, Ord, Show, Read, Data, SymWord, HasKind, SatModel)
type SLocation = SBV Location
here, there :: SLocation
[here, there] = map literal [Here, There]
data Status = Status { time :: STime
, flash :: SLocation
, lBono :: SLocation
, lEdge :: SLocation
, lAdam :: SLocation
, lLarry :: SLocation
} deriving (Generic, Mergeable)
start :: Status
start = Status { time = 0
, flash = here
, lBono = here
, lEdge = here
, lAdam = here
, lLarry = here
}
type Move a = State Status a
instance Mergeable a => Mergeable (Move a) where
symbolicMerge f t a b
= do s <- get
let (ar, s1) = runState a s
(br, s2) = runState b s
put $ symbolicMerge f t s1 s2
return $ symbolicMerge f t ar br
peek :: (Status -> a) -> Move a
peek f = do s <- get
return (f s)
whereIs :: SU2Member -> Move SLocation
whereIs p = ite (p .== bono) (peek lBono)
$ ite (p .== edge) (peek lEdge)
$ ite (p .== adam) (peek lAdam)
(peek lLarry)
xferFlash :: Move ()
xferFlash = modify $ \s -> s{flash = ite (flash s .== here) there here}
xferPerson :: SU2Member -> Move ()
xferPerson p = do [lb, le, la, ll] <- mapM peek [lBono, lEdge, lAdam, lLarry]
let move l = ite (l .== here) there here
lb' = ite (p .== bono) (move lb) lb
le' = ite (p .== edge) (move le) le
la' = ite (p .== adam) (move la) la
ll' = ite (p .== larry) (move ll) ll
modify $ \s -> s{lBono = lb', lEdge = le', lAdam = la', lLarry = ll'}
bumpTime1 :: SU2Member -> Move ()
bumpTime1 p = modify $ \s -> s{time = time s + sCrossTime p}
bumpTime2 :: SU2Member -> SU2Member -> Move ()
bumpTime2 p1 p2 = modify $ \s -> s{time = time s + sCrossTime p1 `smax` sCrossTime p2}
whenS :: SBool -> Move () -> Move ()
whenS t a = ite t a (return ())
move1 :: SU2Member -> Move ()
move1 p = do f <- peek flash
l <- whereIs p
whenS (f .== l) $ do bumpTime1 p
xferFlash
xferPerson p
move2 :: SU2Member -> SU2Member -> Move ()
move2 p1 p2 = do f <- peek flash
l1 <- whereIs p1
l2 <- whereIs p2
whenS (f .== l1 &&& f .== l2) $ do bumpTime2 p1 p2
xferFlash
xferPerson p1
xferPerson p2
type Actions = [(SBool, SU2Member, SU2Member)]
run :: Actions -> Move [Status]
run = mapM step
where step (b, p1, p2) = ite b (move1 p1) (move2 p1 p2) >> get
isValid :: Actions -> SBool
isValid as = time end .<= 17 &&& bAll check as &&& zigZag (cycle [there, here]) (map flash states) &&& bAll (.== there) [lBono end, lEdge end, lAdam end, lLarry end]
where check (s, p1, p2) = (bnot s ==> p1 .> p2)
&&& (s ==> p2 .== bono)
states = evalState (run as) start
end = last states
zigZag reqs locs = bAnd $ zipWith (.==) locs reqs
solveN :: Int -> IO Bool
solveN n = do putStrLn $ "Checking for solutions with " ++ show n ++ " move" ++ plu n ++ "."
let genAct = do b <- exists_
p1 <- exists_
p2 <- exists_
return (b, p1, p2)
res <- allSat $ isValid `fmap` mapM (const genAct) [1..n]
cnt <- displayModels disp res
if cnt == 0 then return False
else do putStrLn $ "Found: " ++ show cnt ++ " solution" ++ plu cnt ++ " with " ++ show n ++ " move" ++ plu n ++ "."
return True
where plu v = if v == 1 then "" else "s"
disp :: Int -> (Bool, [(Bool, U2Member, U2Member)]) -> IO ()
disp i (_, ss)
| lss /= n = error $ "Expected " ++ show n ++ " results; got: " ++ show lss
| True = do putStrLn $ "Solution #" ++ show i ++ ": "
go False 0 ss
return ()
where lss = length ss
go _ t [] = putStrLn $ "Total time: " ++ show t
go l t ((True, a, _):rest) = do putStrLn $ sh2 t ++ shL l ++ show a
go (not l) (t + crossTime a) rest
go l t ((False, a, b):rest) = do putStrLn $ sh2 t ++ shL l ++ show a ++ ", " ++ show b
go (not l) (t + crossTime a `max` crossTime b) rest
sh2 t = let s = show t in if length s < 2 then ' ' : s else s
shL False = " --> "
shL True = " <-- "
solveU2 :: IO ()
solveU2 = go 1
where go i = do p <- solveN i
unless p $ go (i+1)