-----------------------------------------------------------------------------
-- |
-- Module    : Documentation.SBV.Examples.Puzzles.HexPuzzle
-- Copyright : (c) Levent Erkok
-- License   : BSD3
-- Maintainer: erkokl@gmail.com
-- Stability : experimental
--
-- We're given a board, with 19 hexagon cells. The cells are arranged as follows:
--
-- @
--                     01  02  03
--                   04  05  06  07
--                 08  09  10  11  12
--                   13  14  15  16
--                     17  18  19
-- @
--
--   - Each cell has a color, one of @BLACK@, @BLUE@, @GREEN@, or @RED@.
--
--   - At each step, you get to press one of the center buttons. That is,
--     one of 5, 6, 9, 10, 11, 14, or 15.
--
--   - Pressing a button that is currently colored @BLACK@ has no effect.
--
--   - Otherwise (i.e., if the pressed button is not @BLACK@), then colors
--     rotate clockwise around that button. For instance if you press 15
--     when it is not colored @BLACK@, then 11 moves to 16, 16 moves to 19,
--     19 moves to 18, 18 moves to 14, 14 moves to 10, and 10 moves to 11.
--
--   - Note that by "move," we mean the colors move: We still refer to the buttons
--     with the same number after a move.
--
-- You are given an initial board coloring, and a final one. Your goal is
-- to find a minimal sequence of button presses that will turn the original board
-- to the final one.
-----------------------------------------------------------------------------

{-# LANGUAGE DeriveAnyClass      #-}
{-# LANGUAGE DeriveDataTypeable  #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving  #-}
{-# LANGUAGE TemplateHaskell     #-}

{-# OPTIONS_GHC -Wall -Werror #-}

module Documentation.SBV.Examples.Puzzles.HexPuzzle where

import Data.SBV
import Data.SBV.Control

-- | Colors we're allowed
data Color = Black | Blue | Green | Red

-- | Make 'Color' a symbolic value.
mkSymbolicEnumeration ''Color

-- | Use 8-bit words for button numbers, even though we only have 1 to 19.
type Button  = Word8

-- | Symbolic version of button.
type SButton = SBV Button

-- | The grid is an array mapping each button to its color.
type Grid = SArray Button Color

-- | Given a button press, and the current grid, compute the next grid.
-- If the button is "unpressable", i.e., if it is not one of the center
-- buttons or it is currently colored black, we return the grid unchanged.
next :: SButton -> Grid -> Grid
next :: SButton -> Grid -> Grid
next SButton
b Grid
g = forall a. Mergeable a => SBool -> a -> a -> a
ite (forall (array :: * -> * -> *) a b.
SymArray array =>
array a b -> SBV a -> SBV b
readArray Grid
g SButton
b forall a. EqSymbolic a => a -> a -> SBool
.== SBV Color
sBlack) Grid
g
         forall a b. (a -> b) -> a -> b
$ forall a. Mergeable a => SBool -> a -> a -> a
ite (SButton
b forall a. EqSymbolic a => a -> a -> SBool
.==  SButton
5)                        ([Button] -> Grid
rot [ Button
1,  Button
2,  Button
6, Button
10,  Button
9,  Button
4])
         forall a b. (a -> b) -> a -> b
$ forall a. Mergeable a => SBool -> a -> a -> a
ite (SButton
b forall a. EqSymbolic a => a -> a -> SBool
.==  SButton
6)                        ([Button] -> Grid
rot [ Button
2,  Button
3,  Button
7, Button
11, Button
10,  Button
5])
         forall a b. (a -> b) -> a -> b
$ forall a. Mergeable a => SBool -> a -> a -> a
ite (SButton
b forall a. EqSymbolic a => a -> a -> SBool
.==  SButton
9)                        ([Button] -> Grid
rot [ Button
4,  Button
5, Button
10, Button
14, Button
13,  Button
8])
         forall a b. (a -> b) -> a -> b
$ forall a. Mergeable a => SBool -> a -> a -> a
ite (SButton
b forall a. EqSymbolic a => a -> a -> SBool
.== SButton
10)                        ([Button] -> Grid
rot [ Button
5,  Button
6, Button
11, Button
15, Button
14,  Button
9])
         forall a b. (a -> b) -> a -> b
$ forall a. Mergeable a => SBool -> a -> a -> a
ite (SButton
b forall a. EqSymbolic a => a -> a -> SBool
.== SButton
11)                        ([Button] -> Grid
rot [ Button
6,  Button
7, Button
12, Button
16, Button
15, Button
10])
         forall a b. (a -> b) -> a -> b
$ forall a. Mergeable a => SBool -> a -> a -> a
ite (SButton
b forall a. EqSymbolic a => a -> a -> SBool
.== SButton
14)                        ([Button] -> Grid
rot [ Button
9, Button
10, Button
15, Button
18, Button
17, Button
13])
         forall a b. (a -> b) -> a -> b
$ forall a. Mergeable a => SBool -> a -> a -> a
ite (SButton
b forall a. EqSymbolic a => a -> a -> SBool
.== SButton
15)                        ([Button] -> Grid
rot [Button
10, Button
11, Button
16, Button
19, Button
18, Button
14]) Grid
g
  where rot :: [Button] -> Grid
rot [Button]
xs = forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (\(Button
i, SBV Color
c) Grid
a -> forall (array :: * -> * -> *) b a.
(SymArray array, SymVal b) =>
array a b -> SBV a -> SBV b -> array a b
writeArray Grid
a (forall a. SymVal a => a -> SBV a
literal Button
i) SBV Color
c) Grid
g (forall a b. [a] -> [b] -> [(a, b)]
zip [Button]
new [SBV Color]
cur)
          where cur :: [SBV Color]
cur = forall a b. (a -> b) -> [a] -> [b]
map (forall (array :: * -> * -> *) a b.
SymArray array =>
array a b -> SBV a -> SBV b
readArray Grid
g forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. SymVal a => a -> SBV a
literal) [Button]
xs
                new :: [Button]
new = forall a. [a] -> [a]
tail [Button]
xs forall a. [a] -> [a] -> [a]
++ [forall a. [a] -> a
head [Button]
xs]

-- | Iteratively search at increasing depths of button-presses to see if we can
-- transform from the initial board position to a final board position.
search :: [Color] -> [Color] -> IO ()
search :: [Color] -> [Color] -> IO ()
search [Color]
initial [Color]
final = forall a. Symbolic a -> IO a
runSMT forall a b. (a -> b) -> a -> b
$ do Grid
emptyGrid :: Grid <- forall (array :: * -> * -> *) a b.
(SymArray array, HasKind a, HasKind b) =>
String -> Maybe (SBV b) -> Symbolic (array a b)
newArray String
"emptyGrid" (forall a. a -> Maybe a
Just SBV Color
sBlack)
                                   let initGrid :: Grid
initGrid = forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (\(Button
i, Color
c) Grid
a -> forall (array :: * -> * -> *) b a.
(SymArray array, SymVal b) =>
array a b -> SBV a -> SBV b -> array a b
writeArray Grid
a (forall a. SymVal a => a -> SBV a
literal Button
i) (forall a. SymVal a => a -> SBV a
literal Color
c)) Grid
emptyGrid (forall a b. [a] -> [b] -> [(a, b)]
zip [Button
1..] [Color]
initial)
                                   forall a. Query a -> Symbolic a
query forall a b. (a -> b) -> a -> b
$ forall {t}.
(Show t, Num t) =>
t -> Grid -> [SButton] -> QueryT IO ()
loop (Int
0 :: Int) Grid
initGrid []

  where loop :: t -> Grid -> [SButton] -> QueryT IO ()
loop t
i Grid
g [SButton]
sofar = do forall a. IO a -> Query a
io forall a b. (a -> b) -> a -> b
$ String -> IO ()
putStrLn forall a b. (a -> b) -> a -> b
$ String
"Searching at depth: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show t
i

                            -- Go into a new context, and see if we've reached a solution:
                            Int -> QueryT IO ()
push Int
1
                            forall (m :: * -> *). SolverContext m => SBool -> m ()
constrain forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map (forall (array :: * -> * -> *) a b.
SymArray array =>
array a b -> SBV a -> SBV b
readArray Grid
g forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. SymVal a => a -> SBV a
literal) [Button
1..Button
19] forall a. EqSymbolic a => a -> a -> SBool
.== forall a b. (a -> b) -> [a] -> [b]
map forall a. SymVal a => a -> SBV a
literal [Color]
final
                            CheckSatResult
cs <- Query CheckSatResult
checkSat

                            case CheckSatResult
cs of
                              CheckSatResult
Unk    -> forall a. HasCallStack => String -> a
error forall a b. (a -> b) -> a -> b
$ String
"Solver said Unknown, depth: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show t
i

                              DSat{} -> forall a. HasCallStack => String -> a
error forall a b. (a -> b) -> a -> b
$ String
"Solver returned a delta-satisfiable result, depth: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show t
i

                              CheckSatResult
Unsat  -> do -- It didn't work out. Pop and try again with one more move:
                                           Int -> QueryT IO ()
pop Int
1
                                           SButton
b <- forall a. SymVal a => String -> Query (SBV a)
freshVar (String
"press_" forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show t
i)
                                           forall (m :: * -> *). SolverContext m => SBool -> m ()
constrain forall a b. (a -> b) -> a -> b
$ SButton
b forall a. EqSymbolic a => a -> [a] -> SBool
`sElem` forall a b. (a -> b) -> [a] -> [b]
map forall a. SymVal a => a -> SBV a
literal [Button
5, Button
6, Button
9, Button
10, Button
11, Button
14, Button
15]
                                           t -> Grid -> [SButton] -> QueryT IO ()
loop (t
iforall a. Num a => a -> a -> a
+t
1) (SButton -> Grid -> Grid
next SButton
b Grid
g) ([SButton]
sofar forall a. [a] -> [a] -> [a]
++ [SButton
b])

                              CheckSatResult
Sat    -> do [Button]
vs <- forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM forall a. SymVal a => SBV a -> Query a
getValue [SButton]
sofar
                                           forall a. IO a -> Query a
io forall a b. (a -> b) -> a -> b
$ String -> IO ()
putStrLn forall a b. (a -> b) -> a -> b
$ String
"Found: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show [Button]
vs
                                           forall {b}. (SymVal b, Show b) => [SBV b] -> [b] -> QueryT IO ()
findOthers [SButton]
sofar [Button]
vs

        findOthers :: [SBV b] -> [b] -> QueryT IO ()
findOthers [SBV b]
vs = [b] -> QueryT IO ()
go
                where go :: [b] -> QueryT IO ()
go [b]
curVals = do forall (m :: * -> *). SolverContext m => SBool -> m ()
constrain forall a b. (a -> b) -> a -> b
$ [SBool] -> SBool
sOr forall a b. (a -> b) -> a -> b
$ forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (\SBV b
v b
c -> SBV b
v forall a. EqSymbolic a => a -> a -> SBool
./= forall a. SymVal a => a -> SBV a
literal b
c) [SBV b]
vs [b]
curVals
                                      CheckSatResult
cs <- Query CheckSatResult
checkSat
                                      case CheckSatResult
cs of
                                       CheckSatResult
Unk    -> forall a. HasCallStack => String -> a
error String
"Unknown!"
                                       DSat{} -> forall a. HasCallStack => String -> a
error String
"Delta-sat!"
                                       CheckSatResult
Unsat  -> forall a. IO a -> Query a
io forall a b. (a -> b) -> a -> b
$ String -> IO ()
putStrLn String
"There are no more solutions."
                                       CheckSatResult
Sat    -> do [b]
newVals <- forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM forall a. SymVal a => SBV a -> Query a
getValue [SBV b]
vs
                                                    forall a. IO a -> Query a
io forall a b. (a -> b) -> a -> b
$ String -> IO ()
putStrLn forall a b. (a -> b) -> a -> b
$ String
"Found: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show [b]
newVals
                                                    [b] -> QueryT IO ()
go [b]
newVals

-- | A particular example run. We have:
--
-- >>> example
-- Searching at depth: 0
-- Searching at depth: 1
-- Searching at depth: 2
-- Searching at depth: 3
-- Searching at depth: 4
-- Searching at depth: 5
-- Searching at depth: 6
-- Found: [10,10,9,11,14,6]
-- Found: [10,10,11,9,14,6]
-- There are no more solutions.
example :: IO ()
example :: IO ()
example = [Color] -> [Color] -> IO ()
search [Color]
initBoard [Color]
finalBoard
   where initBoard :: [Color]
initBoard  = [Color
Black, Color
Black, Color
Black, Color
Red, Color
Blue, Color
Green, Color
Red, Color
Black, Color
Green, Color
Green, Color
Green, Color
Black, Color
Red, Color
Green, Color
Green, Color
Red, Color
Black, Color
Black, Color
Black]
         finalBoard :: [Color]
finalBoard = [Color
Black, Color
Red, Color
Black, Color
Black, Color
Green, Color
Green, Color
Black, Color
Red, Color
Green, Color
Blue, Color
Green, Color
Red, Color
Black, Color
Green, Color
Green, Color
Black, Color
Black, Color
Red, Color
Black]